5

最近、プロジェクトに取り組んでいるときにこの質問に出くわしましたが、少し混乱しました。そこで、決定的な答えを得るためにテスト プログラムを作成することにしました。

#include <iostream>

using namespace std;

class layer3{
public:
    layer3(){}
    ~layer3(){}     
private:

};


class layer2{
public:
    layer2(){}
    ~layer2(){}

    layer3* GetBAddress(){return &b;}
private:
    layer3 b;
};


class layer1{
public:
    layer1(){}
    ~layer1(){}

    //returns the address of a, which is a 'layer2' object
    layer2* GetaAddress(){return &a;}
    //returns the address of b, which is is a layer 3 object
    layer3* GetDeepBAddress(){return a.GetBAddress();}
private:
    layer2 a;

};

int main(){

    layer1 t;
    cout << &t << "  : layer 1's address" << endl;
    cout << t.GetaAddress() <<  "  : layer 2's address" <<endl;
    cout << t.GetDeepTLAddress() <<  "  : layer 3's address" <<endl;

}

このプログラムは 3 つのオブジェクトを作成します。レイヤー 2 はレイヤー 1 内に作成され、レイヤー 3 はレイヤー 2 内に作成されます。次に、layer1、layer2、および layer3 のアドレスを取得するために呼び出すと、前と同じように、次の出力が得られます。

$ ./a.exe
0x28ac4f  : layer 1's address
0x28ac4f  : layer 2's address
0x28ac4f  : layer 3's address

これら 3 つのオブジェクトすべてがメモリ内の同じ場所を共有するにはどうすればよいでしょうか? このプログラムを 50 個のレイヤー (オブジェクト) を持つようにスケーリングするとどうなりますか? それとも10,000?これがどのように可能かはよくわかりません。誰か私を私の代わりに置いて、ここで何が起こっているのか説明してもらえますか?

編集:おそらく、オブジェクトのコンストラクターではなくプライベートでオブジェクトをインスタンス化したためでしょうか? ああ、わからない。

4

4 に答える 4

12

最も決定的な答えは、C++標準によって与えられたものです。

ビットフィールドではない2つのオブジェクトは、一方が他方のサブオブジェクトである場合、または少なくとも1つがゼロサイズの基本クラスサブオブジェクトであり、それらが異なるタイプである場合、同じアドレスを持つ可能性があります。それ以外の場合は、個別のアドレスを使用する必要があります。

つまり、オブジェクトが別のオブジェクトのサブジェクトである場合、それらは同じアドレスを持っている可能性があります。

C ++ 11では、標準レイアウト構造体(その名前にも関わらず、それも可能ですclass)オブジェクトの最初のメンバーは、オブジェクト自体と同じアドレスを持つことが保証されています。

を使用して適切に変換された標準レイアウト構造体オブジェクトへのポインタは、reinterpret_castその最初のメンバー(または、そのメンバーがビットフィールドの場合は、それが存在するユニット)を指し、その逆も同様です。

クラスはすべて標準レイアウトであるため、観察した動作はC++11によって保証されています。

C ++ 03では、ルールは似ていますが、標準レイアウトの構造体タイプではなく、POD構造体タイプに適用されます。ただし、クラスにはユーザー定義のデストラクタがあるため、POD構造体タイプではありません。したがって、ここに表示される動作はC++03によって保証されていません。

では、なぜこれが発生するのでしょうか。すべてのクラスは、実際には、いくつかのデータをグループ化し、そのデータに対する操作を提供する方法です。int次のようなものを含むクラスを考えてみましょう。

class A
{
  int x;
};

このクラスはすべてそれで構成されていintます。タイプのオブジェクトを作成するとき、A実際に行っているのは、その内部に十分なスペースを割り当てて初期化することです(この場合、初期化しないでください)。次の2つのインスタンスを作成するとしますA

A a1;
A a2;

私たちは記憶に何を持っていますか?あなたはそれがこのように見えると想像することができます:

   a1     a2
┌──────┬──────┐┄┄
│  A   │  A   │
└──────┴──────┘┄┄
Memory ------->

Aが含まれているだけであることがわかっている場合int、つまり、Aオブジェクトは実際には(一部のパディングを除いて)にすぎないことがわかっている場合、もう少し分解すると、メモリは実際には次のようになります。int

   a1     a2
┌──────┬──────┐┄┄
│ int  │ int  │
└──────┴──────┘┄┄
Memory ------->

ここで、はタイプのオブジェクトのサブオブジェクトであるため、Aintは両方とも同じアドレスを持っていることがわかります。との両方が含まれている場合は、次のようになります。intAAintchar

       a1            a2
┌──────┬──────┬──────┬──────┐┄┄
│ int  │ char │ int  │ char │
└──────┴──────┴──────┴──────┘┄┄
Memory ------->

繰り返しになりますが、標準では次のように規定されているため、これはcharより高いアドレスを持つことになります。int

同じアクセス制御(条項11)を持つ(非ユニオン)クラスの非静的データメンバーは、後のメンバーがクラスオブジェクト内でより高いアドレスを持つように割り当てられます。

サブオブジェクトは、最初のサブオブジェクトであっても、そのアドレスが含まれているオブジェクトと必ずしも共有されないことに注意してください。それは完全にコンパイラー次第です。

于 2013-01-23T13:47:39.940 に答える
3

メモリ内では、クラスのインスタンスは(通常)そのデータメンバーのスペースを占有します(さらに、仮想テーブルポインターまたは他の実装で定義されたビット)。

純粋なデータメンバーの観点からは、のインスタンスにlayer1はのインスタンスのみが含まれるため、に格納されている最初の要素(インスタンス自体と同じアドレスを持つ要素)がインスタンスでlayer2あるのは当然です。同じ理由がとにも当てはまります。layer1layer1layer2layer2layer3

于 2013-01-23T13:47:48.307 に答える
0

あなたが観察しているものに奇妙なものは何も見つかりません。オブジェクトは、メモリ内に次々に書き込まれるメンバーで構成されているため、オブジェクトのアドレスと最初のメンバーのアドレスは同じです。レイヤーがいくつあってもそうなります。

于 2013-01-23T13:46:59.603 に答える
0

これは、オブジェクトがメモリに保存される方法です。C ++は、メンバーオブジェクトの順序を保証するだけです。多くのことはコンパイラー次第です。アライメント、メンバーの切り替え(制限に従います)。必要がないのに、なぜメモリまたはオフセットメンバーを追加するのですか?アドレスが同じでない場合は、未使用のメモリを持つオブジェクトがあります。

virtualテストとして、関数を追加してlayer2何が起こるかを確認できます。

于 2013-01-23T13:47:05.943 に答える