C++에서 배열(스택에 선언된)을 처리하는 방법은 무엇입니까?
문제
배열 멤버에 결과를 유지하는 행렬을 구문 분석하는 클래스가 있습니다.
class Parser
{
...
double matrix_[4][4];
};
이 클래스의 사용자는 다음과 같은 API 함수(내가 제어할 수 없는 함수이므로 인터페이스를 변경하여 작업을 더 쉽게 만들 수 없음)를 호출해야 합니다.
void api_func(const double matrix[4][4]);
호출자가 배열 결과를 함수에 전달하도록 내가 생각해낸 유일한 방법은 멤버를 공개로 만드는 것입니다.
void myfunc()
{
Parser parser;
...
api_func(parser.matrix_);
}
이것이 일을 하는 유일한 방법입니까?이렇게 선언된 다차원 배열이 얼마나 융통성이 없는지 보고 놀랐습니다.나는 생각했다 matrix_
본질적으로 double**
그리고 나는 둘 사이에 (안전하게) 캐스팅할 수 있었습니다.결과적으로는 찾을 수도 없습니다. 위험한 사물 사이에 캐스팅하는 방법.다음에 접근자를 추가한다고 가정해 보겠습니다. Parser
수업:
void* Parser::getMatrix()
{
return (void*)matrix_;
}
이렇게 하면 컴파일되지만 사용할 수 없습니다. 이상한 배열 유형으로 다시 캐스팅할 수 있는 방법이 없는 것 같기 때문입니다.
// A smorgasbord of syntax errors...
api_func((double[][])parser.getMatrix());
api_func((double[4][4])parser.getMatrix());
api_func((double**)parser.getMatrix()); // cast works but it's to the wrong type
오류는 다음과 같습니다
오류 C2440:'형변환':'void *'에서 'const double [4][4]'로 변환할 수 없습니다.
...흥미로운 부록 포함:
배열에 대한 참조나 포인터로의 변환은 있지만 배열 유형으로의 변환은 없습니다.
여기서는 도움이 되지 않을 수도 있지만 배열에 대한 참조나 포인터로 캐스팅하는 방법을 결정할 수 없습니다.
확실히 이 시점에서 문제는 순전히 학술적인 문제입니다. void*
캐스트는 한 명의 학급 구성원이 공개된 것보다 거의 깨끗하지 않습니다!
해결책
훌륭하고 깨끗한 방법은 다음과 같습니다.
class Parser
{
public:
typedef double matrix[4][4];
// ...
const matrix& getMatrix() const
{
return matrix_;
}
// ...
private:
matrix matrix_;
};
이제 배열이 아닌 설명이 포함된 유형 이름을 사용하여 작업하고 있습니다. typedef
컴파일러는 기본 유형을 사용하는 변경 불가능한 API 함수에 이를 전달하는 것을 계속 허용합니다.
다른 팁
이 시도.gcc 4.1.3에서 깔끔하게 컴파일됩니다.
typedef double FourSquare[4][4];
class Parser
{
private:
double matrix_[4][4];
public:
Parser()
{
for(int i=0; i<4; i++)
for(int j=0; j<4; j++)
matrix_[i][j] = i*j;
}
public:
const FourSquare& GetMatrix()
{
return matrix_;
}
};
void api_func( const double matrix[4][4] )
{
}
int main( int argc, char** argv )
{
Parser parser;
api_func( parser.GetMatrix() );
return 0;
}
나는 과거에 행렬을 전달하기 위해 이와 같은 공용체를 사용했습니다.
union matrix {
double dflat[16];
double dmatr[4][4];
};
그런 다음 포인터를 setter에 전달하고 데이터를 클래스의 행렬에 복사합니다.
이를 다르게 처리하는 방법(보다 일반적인 방법)이 있지만 내 경험상 이 솔루션이 결국 가장 깔끔한 경향이 있습니다.
나는 Matrix_가 본질적으로 double과 동일할 것이라고 생각했습니다**
C에는 배열에 대한 포인터 배열이 아닌 진정한 다차원 배열이 있으므로 double[4][4]는 4개의 double[4] 배열로 구성된 연속 배열입니다. 이는 (double이 아닌 double[16]과 동일합니다. *)[4].
배열 유형으로의 전환은 없지만, 더블로 값을 캐스팅하는 배열로의 전환이 있거나 [4] [4]는 스택에 하나를 구성하려고 시도합니다 - std :: string (parser.getmatrix) )) - 배열이 적합한 생성자를 공급하지 않는 것을 제외하고.가능하더라도 그렇게 하고 싶지 않았을 것입니다.
유형이 보폭을 인코딩하므로 전체 유형이 필요합니다(double[][]은 수행하지 않음).void*를 ((double[4][4])*)로 재해석한 다음 참조를 가져올 수 있습니다.하지만 행렬을 형식 정의하고 처음부터 올바른 형식의 참조를 반환하는 것이 가장 쉽습니다.
typedef double matrix_t[4][4];
class Parser
{
double matrix_[4][4];
public:
void* get_matrix () { return static_cast<void*>(matrix_); }
const matrix_t& get_matrix_ref () const { return matrix_; }
};
int main ()
{
Parser p;
matrix_t& data1 = *reinterpret_cast<matrix_t*>(p.get_matrix());
const matrix_t& data2 = p.get_matrix_ref();
}
선택한 답변을 자세히 설명하려면 다음 줄을 따르세요.
const matrix& getMatrix() const
이것은 훌륭합니다. 포인터와 캐스팅에 대해 걱정할 필요가 없습니다.당신은 참조 기본 매트릭스 객체에.IMHO 참조는 C++의 가장 좋은 기능 중 하나인데, 직접 C로 코딩할 때 놓친 부분입니다.
C++에서 참조와 포인터의 차이점을 잘 모르신다면, 이것을 읽어보세요
어쨌든, 당신은 다음과 같은 사실을 알아야 합니다. Parser
기본 행렬 개체를 실제로 소유하는 개체가 범위를 벗어나면 해당 참조를 통해 행렬에 액세스하려고 시도하는 모든 코드는 이제 범위 밖 개체를 참조하게 되어 충돌이 발생합니다.