web-dev-qa-db-ja.com

メモリ内のC ++オブジェクトの構造と構造体

次のようなクラスがある場合

   class Example_Class 
   {
       private:
         int x; 
         int y; 
       public: 
         Example_Class() 
         { 
             x = 8;
             y = 9;
         }
       ~Example_Class() 
       { } 
   };

そして、次のような構造体

struct
{
   int x;
   int y;
} example_struct;

example_structと同様のExample_Classのメモリ内の構造です

たとえば、私が次のことをした場合

struct example_struct foo_struct;
Example_Class foo_class = Example_Class();

memcpy(&foo_struct, &foo_class, sizeof(foo_struct));

foo_struct.x = 8およびfoo_struct.y = 9(つまり、foo_classのx、y値と同じ値)になりますか?

私が尋ねる理由は、Cコードでオブジェクトを共有しているC++ライブラリ(変更したくない)があり、C++ライブラリからのオブジェクトを表すために構造体を使用したいからです。オブジェクトの属性のみに関心があります。

CとC++コード間の一般的な構造をExample_classでラップするのが理想的な状況であることはわかっていますが、使用中のC++ライブラリを変更するのは簡単ではありません。

45
hhafez

C++標準guarantees C structとC++ class(またはstruct-同じもの)のメモリレイアウトは同一であることが条件となりますC++ class/struct[〜#〜] pod [〜#〜]の基準に適合すること(「プレーンな古いデータ」)。それで、PODはどういう意味ですか?

次の場合、クラスまたは構造体はPODです。

  • すべてのデータメンバーはパブリックで、それ自体がPODまたは基本型(参照型またはメンバーへのポインター型ではない)、またはそのような配列
  • ユーザー定義のコンストラクタ、代入演算子、デストラクタはありません
  • 仮想機能はありません
  • 基本クラスはありません

許可される唯一の「C++-isms」は、非仮想メンバー関数、静的メンバー、およびメンバー関数です。

クラスにはコンストラクタとデストラクタの両方があるため、正式にはPODタイプではないため、保証はありません。 (ただし、他の人が述べたように、実際には、仮想関数がない限り、2つのレイアウトはどのコンパイラでも同じになる可能性があります)。

詳細は C++ FAQ Lite のセクション[26.7]を参照してください。

62
j_random_hacker

Example_structのメモリ内の構造は、Example_Classの構造と類似しています。

動作は保証されておらず、コンパイラに依存しています。

そうは言っても、Example_Classに仮想メソッドが含まれていない(および基本クラスから継承されていない)場合、答えは「はい、私のマシンでは」です。

10
ChrisW

あなたが説明する場合、答えは「おそらくはい」です。ただし、クラスに仮想関数(基本クラスから継承できる仮想デストラクタを含む)がある場合、または多重継承を使用している場合は、クラスのレイアウトが異なる場合があります。

7
Greg Hewgill

他の人が言ったことに追加するには(たとえば:コンパイラ固有、仮想関数がない限り動作する可能性があります):

これを行っている場合は、sizeof(Example_class)== sizeof(example_struct)という静的アサート(コンパイル時のチェック)を強くお勧めします。 BOOST_STATIC_ASSERT、または同等のコンパイラ固有またはカスタム構築を参照してください。これは、誰か(またはコンパイラの変更など)がクラスを変更して一致を無効にする場合の優れた防御の第一線です。追加のチェックが必要な場合は、メンバーへのオフセットが同じであることをランタイムチェックすることもできます。これにより、(静的サイズアサートと共に)正確さが保証されます。

3
Nick

C++コンパイラの初期の頃は、コンパイラが最初に構造体のキーワードをクラスで変更し、次にコンパイルする例がありました。あまりにも類似点について。

違いは、クラスの継承、特に仮想関数にあります。クラスに仮想関数が含まれる場合、レイアウトの最初に型記述子へのポインターが必要です。また、クラスBがクラスAから継承する場合、クラスAのレイアウトが最初に来て、次にクラスBの独自のレイアウトが続きます。

したがって、クラスインスタンスを構造体インスタンスにキャストするだけの質問に対する正確な答えは、クラスの内容によって異なります。メソッドを持つ特定のクラス(コンストラクターと非仮想デストラクター)の場合、レイアウトはおそらく同じになります。デストラクタが仮想として宣言されている場合、レイアウトは構造とクラスの間で間違いなく異なります。

C構造体からC++クラスにステップするために必要なことはそれほど多くないことを示す記事があります。 レッスン1-構造体からクラスへ

そして、これは仮想関数テーブルが仮想関数を持つクラスにどのように導入されるかを説明する記事です: レッスン4-ポリモーフィズム

1
Zoran Horvat

C++のクラスと構造体は同等ですが、構造体のすべてのメンバーはデフォルトでパブリックです(クラスメンバーはデフォルトでプライベートです)。これにより、C++コンパイラでのレガシーCコードのコンパイルが期待どおりに機能することが保証されます。

構造体ですべての豪華なC++機能の使用を妨げるものは何もありません。

struct ReallyAClass
{
    ReallyAClass();
    virtual !ReallAClass();

    /// etc etc etc
};
0
Mike Thompson

データをCに渡したいときに、クラスのメンバーを構造体のメンバーに明示的に割り当てませんか?これにより、コードがどこでも機能することがわかります。

0
Chuck