14

純粋な仮想クラスである基本クラス 'Base' があります。

class Base {

public:
    virtual void A() = 0;
    virtual void B() = 0;

    virtual ~Base() { } // Eclipse complains that a class with virtual members must have virtual destructor
};

他にも 2 つのクラスがあり、そのうちの 1 つは A() を実装し、もう 1 つは B() を実装しています。

class DerivedA : public virtual Base
{
public:
    virtual void A() {
        printf("Hello from A");
    }
};

class DerivedB : public virtual Base
{
public:
    virtual void B() {
        printf("Hello from B");
    }
};

宣言の virtual キーワードは、ひし形の問題を解決するはずです。

次のように、A() と B() の両方が実装されるように、2 つのクラスを別のクラスに結合したいと思います。

class DerivedC: public DerivedA, public DerivedB {
     // Now DerivedA and DerivedB are combined
};

// Somewhere else in the code
DerivedC c;
c.A();
c.B();

問題: G++ はコードを正常にコンパイルしますが、Eclipse はエラーを出します: The type 'DerivedC' must implement the inherited pure virtual method 'Base::B'. Visual Studio でコンパイルすると、次の 2 つの警告が表示されます。

warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance

問題は次のとおりです。これを行う正しい方法は何ですか? 上記のコードは未定義の動作を生成しますか?

注:タイトルは少し誤解を招く可能性があります。この質問の適切なタイトルがわかりません。

4

5 に答える 5

8

これを行う正しい方法は何ですか?上記のコードは未定義の動作を生成しますか?

コードは完全に有効です。ここには未定義動作はありません。クラスオブジェクトを介した
非修飾呼び出しは常にを呼び出しますが、クラスオブジェクトを介した非修飾呼び出しは常にインスタンスを呼び出します。A()DerivedCDerivedA::A()B()DerivedCDerivedB::B()

Visual C ++は警告を表示します。これは、コードが仮想継承のあまり知られていない機能を使用しているためです。これは、ほとんどの一般的なユーザーには明らかではなく、驚く可能性があります。この場合、警告は警告ではなく、 有益なNitpickとして解釈する必要があります。

C ++標準は、コンパイラが完全に有効なコードに対して有益な警告を発することを制限していないことに注意してください。警告C4250のドキュメントには、VisualC++がこの警告を表示することを選択した理由を示す例が示されています。

于 2012-08-15T08:09:55.330 に答える
6

これを試してみてください:

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};

Eclipse や VC++ で自分でテストすることはできません...

于 2012-08-15T07:40:42.803 に答える
4

コンパイラがこれについて不平を言う理由はわかりません。これは単なる標準のミックスイン手法です。クラスBaseDerivedAおよび DerivedBは抽象的であり、インスタンス化することはできませんが、通常はミックスインの場合です。ミックスインの要点は、すべてのインターフェイスを実装しているわけではないということです。そして、継承されたメンバーを介して両方 をDerivedC実装します。A()B()

コンパイラがこのコードのコンパイルを拒否すると、コードは壊れます。

警告に関しては...コンパイラは、好きなことについて自由に警告できます。

  • 仮想メンバーを持つクラスに仮想デストラクタがある必要はありません。ただし、実際には、通常は(デストラクタが保護されていない限り)それは良い考えであり、コンパイラの警告が適切です。

  • Visual Studioからの警告は「有益」だと思いますが、これが言語が機能するように設計されている方法です。そして、それは確かに避けるべきものではありません。Baseちなみに、の機能は純粋な仮想であるため、ここでは優位性が実際に役割を果たすとは思いません。Visual Studiosが言おうとしているように見える のはDerivedC、の実際のオーバーロードはでA()ありDerivedA::A()、ではないということ Base::A()です。これは、直感的に私に期待するもののようです。支配に関する規則は、実際には、直感的に期待できることの正式な声明にすぎません。

とにかく、私は間違いなく支配についての警告をオフにします。その点で心配することは確かにありません。そして、私はコードをコンパイルしなかったコンパイラについて大声で不平を言うでしょう。

于 2012-08-15T08:20:32.130 に答える
2

Visual Studio には、優先関数が純粋仮想である状況で、警告 C4250 を発行するコンパイラ バグがあることが知られています。バグは「修正しない」としてクローズされました。受け入れられている解決策は、次を使用して警告を抑制することです。

#pragma warning( disable: 4250 ) /* 'class1' : inherits 'class2::member' via dominance */

http://msdn.microsoft.com/en-us/library/6b3sy7ae(VS.80).aspx#CommunityContentHeaderの説明も参照してください。

于 2012-08-15T08:31:42.563 に答える
0

Base クラスは抽象的です。インスタンス化できません。B および A クラスも、メソッドを 1 つしか実装していないため、抽象クラスです。

2つのソリューションは、ファイル内にありますDerivedC.cpp

void DerivedC::A(){ 
   DerivedA::A();
}
void DerivedC::B(){
   Derived:B();
}

またはusing、ヘッダー ファイルでキーワードを使用できます。

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};
于 2012-08-15T07:44:26.130 に答える