15

結果を配列メンバーに保持する行列を解析するクラスがあります。

class Parser
{
  ...
  double matrix_[4][4];
};

このクラスのユーザーは、次のようなAPI関数(たとえば、私が制御できない関数のように、インターフェイスを変更して作業を簡単にすることはできません)を呼び出す必要があります。

void api_func(const double matrix[4][4]);

呼び出し元が配列の結果を関数に渡すために私が思いついた唯一の方法は、メンバーをパブリックにすることです。

void myfunc()
{
  Parser parser;
  ...
  api_func(parser.matrix_);
}

これが物事を行う唯一の方法ですか?このように宣言された柔軟性のない多次元配列に驚いています。matrix_私は本質的にaと同じだろうと思っていたのでdouble**、2つの間で(安全に)キャストできました。結局のところ、私は物事の間でキャストするための危険な方法を見つけることさえできません。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*キャストは1人のクラスのメンバーが公開されているよりもきれいではないため、この問題は純粋に学術的なものです。

4

5 に答える 5

16

これは素晴らしくきれいな方法です:

class Parser
{
public:
   typedef double matrix[4][4];

   // ...

   const matrix& getMatrix() const
   {
      return matrix_;
   }

   // ...

private:
  matrix matrix_;
};

現在、配列ではなくわかりやすい型名を使用していますが、これは配列であるためtypedef、コンパイラは基本型を取る変更不可能な API 関数に渡すことを引き続き許可します。

于 2008-09-10T19:53:16.443 に答える
6

これを試して。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;
}
于 2008-09-10T19:59:33.167 に答える
4

過去に行列を渡すために、このようなユニオンを使用しました。

union matrix {
    double dflat[16];
    double dmatr[4][4];
};

次に、ポインターをセッターに渡し、データをクラスのマトリックスにコピーします。

それ以外の方法でこれを処理する方法はありますが (より一般的です)、私の経験では、この解決策が最終的に最もクリーンになる傾向があります。

于 2008-09-10T19:38:02.207 に答える
4

matrix_ は本質的に double** と同じだと思いました

C には、配列へのポインターの配列ではなく、真の多次元配列があるため、double[4][4] は 4 つの double[4] 配列の連続した配列であり、(double[16] ではなく double[16] と同等です。 *)[4]。

配列への参照またはポインターへの変換はありますが、配列型への変換はありません double[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();
}
于 2008-09-10T20:07:17.347 に答える
2

選択した回答について詳しく説明するには、この行を観察してください

const matrix& getMatrix() const

これは素晴らしいことです。ポインタやキャストについて心配する必要はありません。基になるマトリックス オブジェクトへの参照を返しています。IMHO 参照は、C++ の最高の機能の 1 つですが、ストレート C でコーディングするときには見逃してしまいます。

C++ での参照とポインターの違いに慣れていない場合は、これを読んでください。

いずれにせよParser、基礎となる行列オブジェクトを実際に所有するオブジェクトがスコープ外になる場合、その参照を介して行列にアクセスしようとするコードはスコープ外のオブジェクトを参照することになることに注意する必要があります。あなたはクラッシュします。

于 2008-09-10T21:15:18.540 に答える