2016-07-19 10 views
1

これはthis section on learncpp.comを読んでいるときに遭遇した質問です。私はここに記載されているコードを使用し、テストのために少し変更を加えました。C++での仮想継承中にコンストラクタを呼び出す

背景

仮想継承は、二つの効果を有する基底クラスに共通の基準を作成します。

最初に、基底のメンバーのコピーが作成されると(例えば、PoweredDeviceにprint()関数を追加し、main()でそれを呼び出すとコンパイラエラーが発生する)ので、あいまいさは取り除かれます。

第2に、最も派生したクラスは、基本コンストラクタの呼び出しになります。中間クラスの1つが初期化リストで基本コンストラクタを呼び出そうとすると、呼び出しはignoredになるはずです。

問題

私がコンパイルしたコードを実行し、それが返されます。

PoweredDevice: 3 
PoweredDevice: 3 
Scanner: 1 
PoweredDevice: 3 
Printer: 2 

それは返す必要があります:

PoweredDevice: 3 
Scanner: 1 
Printer: 2 

私はGDBを使用して、実行を追跡する場合(7.11 .1)、中間関数も初期化リストを介してPoweredDeviceを呼び出していることを示していますが、これらは無視する必要があります。 PoweredDeviceを複数回初期化してもメンバーのあいまいさはありませんが、コードは一度しか実行されない場合に複数回実行されるため、問題があります。より複雑な問題については、私は仮想継承を使用するのが快適ではないでしょう。

なぜこれらの中間クラスはまだベースを初期化していますか?それは私のコンパイラ(gcc 5.4.0)の奇妙なものですか、あるいは私は仮想継承の仕組みを誤解していますか?

編集:コード

#include <iostream> 
using namespace std; 

class PoweredDevice 
{ 
public: 
    int m_nPower; 
public: 
    PoweredDevice(int nPower) 
     :m_nPower {nPower} 
    { 
     cout << "PoweredDevice: "<<nPower<<endl; 
    } 
    void print() { cout<<"Print m_nPower: "<<m_nPower<<endl; } 
}; 

class Scanner : public virtual PoweredDevice 
{ 
public: 
    Scanner(int nScanner, int nPower) 
     : PoweredDevice(nPower) 
    { 
     cout<<"Scanner: "<<nScanner<<endl; 
    } 
}; 

class Printer : public virtual PoweredDevice 
{ 
public: 
    Printer(int nPrinter, int nPower) 
     : PoweredDevice(nPower) 
    { 
     cout<<"Printer: "<<nPrinter<<endl; 
    } 
}; 

class Copier : public Scanner, public Printer 
{ 
public: 
    Copier(int nScanner, int nPrinter, int nPower) 
     :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower} 
    { } 
}; 

int main() 
{ 
    Copier cCopier {1,2,3}; 
    cCopier.print(); 
    cout<<cCopier.m_nPower<<'\n'; 
    return 0; 
} 
+2

どのような「わずかな変更」をしましたか?とにかくここにコードを投稿してください – user463035818

+2

ようこそスタックオーバーフロー! ** [編集] **あなたの質問は[mcve]または[SSCCE(ショート、自己完結型、正しい例)](http://sscce.org) – NathanOliver

+0

編集にコードを追加しました。バリエーションは、異なるアクセス指定子を含む基本クラスのメンバ値と関数を調べました。私はまた、仮想継承アクセスレベルの変更を試みました。 –

答えて

7

これは、均一な初期化が仮想継承で使用されたときにトリガGCCのバグ、ように見えます。


我々は変更する場合:

Copier(int nScanner, int nPrinter, int nPower) 
    :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower} 
{ } 

へ:

Copier(int nScanner, int nPrinter, int nPower) 
    :Scanner (nScanner, nPower), Printer (nPrinter, nPower), PoweredDevice (nPower) 
{ } 

エラーが消え、そして期待どおりに動作します。

PoweredDevice: 3 
Scanner: 1 
Printer: 2 
Print m_nPower: 3 
3 

ClangとVisual Studioの両方で、元のコードを適切にコンパイルして、期待される出力を得ることができます。

+0

これはうまくいった。ありがとうございました! –

+2

@ RyanBよろしくお願いします。特定のコンパイラのバグか言語の問題かどうかを確認するには、通常、インストールしていないコンパイラのオンライン環境を使用します。私はこのために[Rextester](http://www.rextester.com/)が好きです。ドロップダウンメニューからClang、GCC、VC++を入手できます。各ページは別々のページにありますので、別のページに切り替える前にコードをコピーしてください。 –

+0

誰もこれに対してバグを報告しましたか?さもなければ、私達はただそれに苦しめなければならない。 –

関連する問題