5

次のような菱形継承問題があります。

    __ A
  /    |\
 |  B  |  \
v|/v v\|v  \v
 B2   B3    C
  \v  /v   /
    B4    /
     \   /
       D

重複がないように最良の仮想継承を作成するために多くの方法を試しましたが、解決策を見つけることができませんでした。クラスAにはポジションが含まれています。出力例は次のとおりです。

Call: A() position pointer is: 0x2203be8
Call: B()
Call: B2() position pointer is: 0x2203be8
Call: B3() position pointer is: 0x2203be8
Call: C() position pointer is: 0x2203a28
Call: B4() position pointer is: 0x2203be8
Call: D() position pointer is: 0x2203a28

なぜDとCは位置の同じポインタを持っていないのですか?このA::positionのコンストラクターがないのはなぜですか?これを解決するには、どのような仮想継承を行う必要がありますか?ありがとう。

編集:

コードサンプルは次のとおりです。

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;

編集2:出力を作成するために、このコードを各コンストラクター内に配置します。

A::A()
{
    std::cerr << "Call: A() position pointer is: " << &_position << std::endl;
}
4

3 に答える 3

4

私が持っている実装で動作する以下のコードが壊れているとあなたが言うので、明らかにコードは問題ではありません。問題は、セットアップ内の他の何かにあります。おそらくコンパイラのバグです。他に何が問題の原因であるかを絞り込む必要があります。コード自体は問題として除外されているため、おそらく次の最善のステップはコンパイラを更新することです。

いずれにせよ、この質問はあなたの設定にかなり固有のものになります。他の人に適用できる解決策を見つけた場合は、戻って投稿する必要があります。それまでは、この質問を閉じることに投票します。


問題を再現しようとしています。私が使用しているコードは次のとおりです。

#include <iostream>

struct A { int a; };
struct B { int b; };
struct B2 : virtual B, virtual A {};
struct B3 : virtual B, virtual A {};
struct B4 : virtual B2, virtual B3 {}; // these virtuals are unnecessary in this case...
struct C : virtual A {};
struct D : B4, C {};

int main() {
    D d;
    std::cout << &((B4*)&d)->a << '\n';
    std::cout << &((B3*)(B4*)&d)->a << '\n';
    std::cout << &((B2*)(B4*)&d)->a << '\n';
    std::cout << &((A*)(B2*)(B4*)&d)->a << '\n';
    std::cout << &((A*)(B3*)(B4*)&d)->a << '\n';
    std::cout << &((C*)&d)->a << '\n';
    std::cout << &((A*)(C*)&d)->a << '\n';
}

しかし、私が得た結果は期待通りで、aメンバーはすべてのオブジェクトで同じです。コンストラクターでもアドレスを出力すると、同じ結果が得られます: http://ideone.com/8FdQ1O

わずかな変更を加えて、virtualC の定義からキーワードを削除すると、次のようになります。

...
struct C : A {};
...

(コンストラクタを使用したバージョン)

次に、CがB2、B3、およびB4で使用される仮想サブオブジェクトとは異なる独自のAサブオブジェクトを持っている場所について説明した問題を確認します。

必要なすべての場所でキーワードを使用していvirtualますか? あなたが示す結果は、どこかでそれを見逃していることを示しているようです。また、表示される出力は、表示されるコードフラグメントと同じ順序のコンストラクターを反映していないことに注意してください。出力には A() が最初に表示されますが、コードは B() を最初に実行する必要があることを示しています。


仮想継承が機能する方法は、ほとんどの派生型が、継承ツリーのどこかで仮想的に継承される型ごとに 1 つの仮想サブオブジェクトを含むことです。さらに、最も派生した型には、非仮想継承の各インスタンスのサブオブジェクトが含まれます。

struct A {};
struct B : virtual A {};
struct C : A, B {};
struct D : virtual A, C {};
struct E : A, D {};
struct F : virtual A, E {};
struct G : A, F {};

G g;

g合計 4 つのAサブオブジェクトが含まれています。one for each timeAは非仮想的に継承され ( CE、およびG)、once for all timesAは仮想的に継承されます ( BD、およびF)。

于 2012-11-16T20:38:41.653 に答える
1

現在、どのようなコードがありますか?解決策は次のようになります。

class D;
class C : public virtual D;
class B4 : public virtual D;
class B2 : public virtual B4;
class B3 : public virtual B4;
class B : public B2, public B3;
class A : public B2, public B3, public C;

あなたの図に基づいています。私がそれを間違って読んでいて、Aがベースであり、Dではない場合、次のようになります。

class A;
class B;
class B2 : public virtual B, public virtual A;
class B3 : public virtual B, public virtual A;
class C : public virtual A;
class B4 : public virtual B2, public virtual B3;
class D : public B4, public C;
于 2012-11-16T19:25:06.503 に答える
0

D と C の位置ポインタが同じでないのはなぜですか?

B4 および C から非仮想的に D を継承しているためです。これは、A の 2 つのコピー (および 2 つのポインター) があることを意味します。

D コンストラクターでは &B4::position が &C::position とは異なります

この A::position のコンストラクターがないのはなぜですか?

A クラスに複数のコンストラクターがあり、デフォルトのサイレント コンストラクターが C::C() によって呼び出される可能性はありますか?

これを解決するには、どの仮想継承を作成する必要がありますか?

すべてを仮想化します。つまり、D::D() からすべてのコンストラクターを明示的に呼び出す必要があることを意味します (つまり、A::A()、B::B()、B2::B2()、B3::B3()、C:: C() )。

Tbh ヒエラルキーを再考する必要があると思います。詳細はわかりませんが、コンポーネント設計によるよりクリーンな解決策があるようです。

于 2012-11-16T19:54:32.990 に答える