C++에서 배열(스택에 선언된)을 처리하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/55093

  •  09-06-2019
  •  | 
  •  

문제

배열 멤버에 결과를 유지하는 행렬을 구문 분석하는 클래스가 있습니다.

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 기본 행렬 개체를 실제로 소유하는 개체가 범위를 벗어나면 해당 참조를 통해 행렬에 액세스하려고 시도하는 모든 코드는 이제 범위 밖 개체를 참조하게 되어 충돌이 발생합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top