2013-01-22 17 views
6

型階層のコンストラクタの呼び出しのルールに厄介な時間があります。 (今コンストラクタ呼び出しの階層

class A{ 
protected: 
    int _i; 
public: 
    A(){i = 0;} 
    A(int i) : _i(i){} 
    virtual ~A(){} 
    virtual void print(){std::cout<<i<<std::endl;} 
}; 

class B : virtual public A{ 
protected: 
    int _j; 
public: 
    B() : A(){_j = 0;} 
    B(int i, int j) : A(i), _j(j){} 
    virtual ~B(){} 
    virtual void print(){std::cout<<i<<", "<<j<<std::endl;} 
}; 

class C : virtual public B{ 
protected: 
    int _k; 
public: 
    C() : B(){_k = 0;} 
    C(int i, int j, int k} : B(i,j), _k(k){} 
    virtual ~C(){} 
    virtual void print(){std::cout<<i<<", "<<j<<", "<<k<<std::endl;} 
}; 

int main(){ 
    C* myC = new C(1,2,3); 
    myC->print(); 
    delete myC; 
    return 0; 
} 

、私はその後、今度はコンストラクタAを呼び出す必要があり、新たなC(1,2,3)B(1,2)のコンストラクタを呼び出すがしたい1:ここで私は何をすべきかです)を使用して、_i = 1、_j = 2、_k = 3を格納します。しかし、クラスCのインスタンスmyCを作成するとき、何らかの理由で私は理解できませんが、呼び出される最初のコンストラクタはAの標準コンストラクタ、つまりA :: A()です。これは明らかに、保護された変数_iに値0が割り当てられているため、間違った結果につながります。コンストラクターA(1)は決して呼び出されません。なぜこれはそうですか?私はこれを非常に直感的なものと見なします。目的の動作を達成するために、型階層内のすべてのコンストラクタを明示的に呼び出すことを避けるための方法がありますか?

ヘルプのためのThx!

+0

種類の回答はThxです。だから、私はStroustrupに戻って、仮想継承の概念を再読するつもりだと思う。デフォルトでそれを使用することは賢明ではないと思われます;) – user1999920

+0

多くの人々は、継承がデフォルトで仮想ではない理由を不思議に思っています。さて、あなた自身で答えを見つけました:) – Gorpik

答えて

5

、ほとんどの派生クラスは、直接そのすべての仮想拠点のコンストラクタを呼び出す必要があります。この場合、Cのコンストラクタは、BAのコンストラクタを呼び出す必要があります。 Bコンストラクタを呼び出すだけなので、デフォルトのAコンストラクタが使用されます。 Bコンストラクタが別のAコンストラクタを呼び出すことは重要ではありません。仮想ベースクラスなので、この呼び出しは無視されます。

あなたはこの問題を回避する二つの方法があります。

C(int i, int j, int k} : A (i), B(i,j), _k(k){} 

または通常の継承の代わりに、仮想を使用します。明示的A(int)コンストラクタを呼び出すを。

+1

最も派生したクラスは、その仮想基底クラスのコンストラクタを明示的に呼び出すことはありませんが、暗黙的にデフォルトで初期化されるようにすることができます。ただし、仮想ベースを初期化したクラスが常に最も派生しているということは間違いありません。 –

+0

@CharlesBailey私の答えを読み返して、あなたは正しいです:私は正しく説明しませんでした(それは*明示的に誤解を招く)。私はそれを修正しています。 – Gorpik

6

これは、仮想継承を使用したためです。これは複数の継承がある場合にのみ意味があります。正常に継承され、すべてが期待どおりになります。

+0

'A'がインターフェースで、' B'がそのインターフェースを拡張すると、仮想継承は本当に唯一正しい解です。 (もちろん、 'A'がインターフェースであれば、それはデータメンバーを持たないので、ユーザー定義のコンストラクターはありません。一般に、仮想継承はインターフェースにのみ適用されます) –

+0

@JamesKanze、それは間違っています共通の祖先を持つ2つ以上のクラスから複数の継承を行っていない場合は、仮想継承が必要です。さらに、仮想継承を使用せずに複数のインターフェイスを実装することもできます。例えば、COMを参照してください。 – Steed

+0

私はあなたの "インターフェース"の定義を得ていないと思います。 Javaの種類のインターフェース(つまり、メンバーのない抽象クラス)を意味しますか?しかし、私はC++でさまざまな種類のものをたくさん見てきましたが、itnerfaceと呼ぶこともできますが、仮想継承は必要ありませんでした。もちろん、抽象クラスは複数の継承グラフに絡み合っているかもしれませんが、メンバーがないため、仮想継承がなくてもvtableだけが動作するはずです。 –

7

実際にvirtualの継承が必要ですか? 非常に最初の仮想ベースツールが最初に呼び出されますが、から継承するときには何も指定しないため、B(後者はすでにAが実質的に継承されているため、デフォルトが呼び出されています)

1つの解決策は、仮想継承を削除することです... Arne Mertzの回答に記載されています。 別(あなたは本当に仮想継承をしたい場合)C CTORから明示的Aを呼び出すことです:あなたは仮想継承を使用する場合

C(int i, int j, int k} : A(i), B(i,j), _k(k){} 
0

なぜ仮想継承を宣言しますか?クラスBからvirtualキーワードを削除した場合:virtual public A {...あなたのコードは正常に動作します。仮想Aを宣言することによって、CはA()を直接呼び出します。仮想を削除すると、CはA()を呼び出しません。

関連する問題