12

C++ では、基本クラスと派生クラスに単一のインターフェイスを実装させることは可能ですか?

例えば:

class Interface
{
    public:
        virtual void BaseFunction() = 0;
        virtual void DerivedFunction() = 0;
};

class Base
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public Base, public Interface
{
    public: 
        void DerivedFunction(){}
};

void main()
{
    Derived derived;
}

Derived はインスタンス化できないため、これは失敗します。コンパイラに関する限り、Interface::BaseFunction は定義されていません。

これまでのところ、私が見つけた唯一の解決策は、Derived でパススルー関数を宣言することです

class Derived : public Base, public Interface
{
    public: 
        void DerivedFunction(){}
        void BaseFunction(){ Base::BaseFunction(); }
};

より良い解決策はありますか?


編集:問題がある場合は、MFC ダイアログを使用して実際に発生した問題を次に示します。

CDialog から派生したダイアログ クラス (MyDialog と言います) があります。依存関係の問題により、抽象インターフェイス (MyDialogInterface) を作成する必要があります。MyDialogInterface を使用するクラスは、MyDialog に固有のメソッドを使用する必要がありますが、CDialog::SetParent を呼び出す必要もあります。MyDialog::SetParent を作成して CDialog::SetParent に渡すことで解決しましたが、もっと良い方法があるかどうか疑問に思っていました。

4

5 に答える 5

17

C++ は Base から継承された関数が既に実装していることに気付きませんBaseFunction: 関数は から派生したクラスで明示的に実装する必要がありますInterface。このように変更します。

class Interface
{
    public:
        virtual void BaseFunction() = 0;
        virtual void DerivedFunction() = 0;
};

class Base : public Interface
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public Base
{
    public: 
        virtual void DerivedFunction(){}
};

int main()
{
    Derived derived;
}

そのうちの 1 つだけを実装するだけで済ませたい場合は、Interface2 つのインターフェイスに分割します。

class DerivedInterface
{
    public:
        virtual void DerivedFunction() = 0;
};

class BaseInterface
{
    public:
        virtual void BaseFunction() = 0;
};

class Base : public BaseInterface
{
    public:
        virtual void BaseFunction(){}
};

class Derived : public DerivedInterface
{
    public: 
        virtual void DerivedFunction(){}
};  

class Both : public DerivedInterface, public Base {
    public: 
        virtual void DerivedFunction(){}
};

int main()
{
    Derived derived;
    Base base;
    Both both;
}

注: main は int を返さなければなりません。注:厳密に必須ではない場合でも、ベースでは仮想であった派生のメンバー関数の前
に置くことをお勧めします。virtual

于 2008-11-14T18:14:03.437 に答える
4

Derived が "is-a" Base であるとは限らないようです。これは、包含が継承よりも優れた実装である可能性があることを示唆しています。

また、Derived メンバー関数も仮想として宣言する必要があります。

class Contained
{
    public:
        void containedFunction() {}
};

class Derived
{
    public:
        virtual void derivedFunction() {}
        virtual void containedFunction() {return contained.containedFunction();}
    private:
        Containted contained;
};

実装の詳細を非表示にする場合は、含まれているメンバーを参照またはスマート ポインターにすることができます。

于 2008-11-14T18:40:38.157 に答える
1

I agree with the answer by litb. However, there's an opportunity here to understand something of how virtual functions and multiple inheritance work.

When a class has multiple base classes, it has separate vtables for each base class. Derived will have a vtable structure that looks like this:

Derived
 vtable: Interface
   BaseFunction*
   DerivedFunction*
 vtable: Base
   BaseFunction*

Additionally, each base class will only be able to see its own vtable. When Base is instantiated, it fills in the Base::BaseFunction pointer in the vtable, but can't see the vtable for Interface.

If the code you provided could compile, the resulting vtable structure of an instance of Derived would look like this:

Derived
 vtable: Interface
   BaseFunction* = 0
   DerivedFunction* = Derived::DerivedFunction
 vtable: Base
   BaseFunction* = Base::BaseFunction
于 2008-11-14T20:53:22.947 に答える
1

問題は、あなたの例では、 からのものとInterfaceからのものの 2 つの実装があることです。これは、C++ 言語の仕様です。すでに指摘したように、 の定義で基本クラスを削除するだけです。BaseDerivedInterfaceDerived

于 2008-11-14T18:16:12.997 に答える
0

litbの回答に欠けていることが1つ見つかりました。Derivedインスタンスがあれば、DerivedInterfaceとを取得できますBaseInterface。しかし、 a しかない場合は、派生元が機能しDerivedInterfaceないため、 a を取得できません。BaseInterfaceDerivedInterfaceBaseInterface

しかし、これまでずっと、何らかの理由でコンパイル時間のチェックに限定してきました。これDerivedInterfaceはうまく機能します:

class DerivedInterface
{
    public:
        virtual void DerivedFunction() = 0;
        BaseInterface* GetBaseInterface()
            {return dynamic_cast<BaseInterface*>(this);}
};

void main()
{
    Derived derived;

    DerivedInterface* derivedInterface = &derived;
    derivedInterface->GetBaseInterface()->BaseFunction();
}

Derived では必要なパススルー関数がなく、みんな幸せです。確かに、もはや厳密にはインターフェースではありませんが、それで問題ありません。なんでもっと早く思いつかなかったの?:)

于 2008-11-15T01:57:41.910 に答える