4

次のコードを実行すると、別のアドレスが出力されます。なんで?

class Base1 {
    int x;
};

class Base2 {
    int y;
};

class Derived : public Base1, public Base2 {

};

union U {
    Base2* b;
    Derived* d;
    U(Base2* b2) : b(b) {}
};

int main()
{
    Derived* d = new Derived;

    cout << d << "\n";
    cout << U(d).d << "\n";

    return 0;
}

さらに楽しいのは、このように、ユニオンに繰り返し出入りする場合、アドレスが4ずつ増加し続けることです。

int main()
{
    Derived* d = new Derived;

    cout << d << "\n";
    d = U(d).d;
    cout << d << "\n";
    d = U(d).d;
    cout << d << "\n";

    return 0;
}

ユニオンをこのように変更すると、問題は解決します

union U {
    void* v;
    Base2* b;
    Derived* d;
    U(void* v) : v(v) {}
};

また、いずれかの基本クラスを空にすると、問題は解決します。これはコンパイラのバグですか?私はそれが私のポインターを地獄だけに残して欲しいです。

4

2 に答える 2

3

次のコードを実行すると、別のアドレスが出力されます。なんで?

オブジェクトのBase2サブオブジェクトがDerivedオブジェクトの先頭にないためDerivedです。したがって、アドレスは異なります。コンパイラがaからaDerived*への暗黙的なキャストを実行する場合Base2*、アドレスを調整する必要があります。

Base1およびクラスの定義を考えると、Base2クラスの両方のサブオブジェクトがオブジェクトDerivedの開始アドレスにある可能性はありませんDerived。そのアドレスには、両方のサブオブジェクト用のスペースがありません。

あなたがこのコードを持っていたとしましょう:

Derived* d = new Derived;

Base1* pb1 = d;
Base2* pb2 = d;

同じアドレスをポイントするpb1ことはどのように可能でしょうか?アイテムを指している必要があり、アイテムを指している必要があります(そしてそれらのアイテムは別個でなければなりません)。pb2pb1Base1::xpb2Base2::y

さらに楽しいのは、ユニオンに繰り返し出入りする場合、アドレスが4ずつ増え続けることです。

dメンバーを書き込んだ後にユニオンのメンバーから読み取っているためb、これは未定義の動作です(基本的reinterpret_cast<Derived*>()にはaのようなものを実行していますBase2*)。

私はそれが私のポインターを地獄だけに残して欲しいです。

Base2*ポインタが必要な場合は違います。多重継承は物事をより複雑にします-それが絶対に必要でない限り多くの人々がそれを避けることを提案する理由です。

于 2012-10-20T04:52:21.487 に答える
2

ユニオンコンストラクターがメンバーdを初期化することはありません

ユニオンコンストラクターにはバグがあり、パラメーターb2でメンバーbを初期化する代わりに、それ自体でbを初期化します。

// b(b) should probably be b(b2)
U(Base2* b2) : b(b) {}

最初のメイン関数の例でUのインスタンスを作成し、メンバーdを出力しようとすると、メンバーdが初期化されておらず、アクセス可能であることが保証されていないため、実際には未定義の値が出力されます。

// U(d) doesn't construct member d, so .d returns an undefined value
cout << U(d).d << "\n";

2番目の主な機能の例について

// d is set to a newly constructed instance of Derived
Derived* d = new Derived;

// current address of d is printed
cout << d << "\n";

// a new instance of U is constructed. The address of member d will be in close
// proximity to the newly initialized U instance, and is what will be printed
d = U(d).d;
cout << d << "\n";

// yet another new instance of U is constructed, and again, the address of member
// d will be in close proximity to the newly initialized U instance, and is
//what will be printed
d = U(d).d;
cout << d << "\n";
于 2012-10-20T04:53:40.197 に答える