web-dev-qa-db-ja.com

実装された純粋な仮想関数

私の基本的な理解は、純粋仮想関数の実装はないということですが、純粋仮想関数の実装があるかもしれないと言われました。

class A {
public:
    virtual void f() = 0;
};

void A::f() {
    cout<<"Test"<<endl;
}

上記のコードは大丈夫ですか?

実装のある純粋な仮想関数にする目的は何ですか?

159
skydoor

純粋なvirtual関数は、直接インスタンス化される派生型で実装する必要がありますが、基本型は引き続き実装を定義できます。派生クラスは、完全にスコープされた名前を使用して(アクセス許可で許可されている場合)基本クラスの実装を明示的に呼び出すことができます(例でA::f()を呼び出すことにより-A::f()publicまたはprotectedの場合)。何かのようなもの:

class B : public A {

    virtual void f() {
        // class B doesn't have anything special to do for f()
        //  so we'll call A's

        // note that A's declaration of f() would have to be public 
        //  or protected to avoid a compile time problem

        A::f();
    }

};

私が頭の中で思いつくことができるユースケースは、多かれ少なかれ妥当なデフォルトの振る舞いがありますが、クラス設計者はそのデフォルトの振る舞いが明示的にのみ呼び出されることを望んでいます。また、派生クラスが常に独自の作業を実行するだけでなく、共通の機能セットを呼び出すことができるようにすることもできます。

言語で許可されていても、一般的に使用されるものではないことに注意してください(そして、それができるという事実は、経験豊富なC++プログラマーをも驚かせるようです)。

194
Michael Burr

明確にするために、あなたはwhat = 0を誤解しています。仮想関数が意味する後。

= 0は、派生クラスが実装を提供する必要があり、基本クラスが実装を提供できないことを意味します。

実際には、仮想関数を純粋(= 0)としてマークすると、誰かがBase :: Function(...)を介して明示的にそうしない場合、または基本クラスコンストラクターは、問題の仮想関数を呼び出します。

70
Terry Mahaffey

この方法の利点は、派生型にメソッドのオーバーライドを強制するだけでなく、デフォルトまたは追加の実装を提供することです。

20
JaredPar

派生クラスによって実行されるべきコードがあるが、直接実行されたくない場合、および強制的にオーバーライドされるようにする場合。

あなたのコードは正しいですが、これはすべて頻繁に使用される機能ではありませんが、通常は純粋な仮想デストラクタを定義しようとしたときにのみ見られます-その場合must実装を提供します。面白いのは、そのクラスから派生した後は、デストラクタをオーバーライドする必要がないということです。

したがって、純粋仮想関数の賢明な使用法の1つは、「非最終」キーワードとして純粋仮想デストラクターを指定することです。

次のコードは驚くほど正しいです:

class Base {
public:
  virtual ~Base() = 0;
};

Base::~Base() {}

class Derived : public Base {};

int main() { 
  // Base b; -- compile error
  Derived d; 
}
17

たとえば、純粋な仮想デストラクタにボディを渡す必要があります:)

読む: http://cplusplus.co.il/2009/08/22/pure-virtual-destructor/

(リンク切れ、アーカイブ使用)

5
rmn

本体の有無にかかわらず純粋な仮想関数は、派生型が独自の実装を提供する必要があることを意味します。

基本クラスの純粋な仮想関数本体は、派生クラスが基本クラスの実装を呼び出す場合に役立ちます。

4
Brian R. Bondy

はい、これは正しいです。この例では、Aから派生するクラスは、インターフェースf()とデフォルトの実装の両方を継承します。ただし、派生クラスにメソッドf()を実装するように強制します(Aが提供するデフォルトの実装を呼び出すだけの場合でも)。

Scott MeyersはこれについてEffective C++(2nd Edition) Item#36で説明しています。インターフェースの継承と実装の継承を区別します。最新版ではアイテム番号が変更されている場合があります。

4
Yukiko

「仮想ボイドfoo()= 0;」構文は、現在のクラスにfoo()を実装できないことを意味するわけではありません。 また、派生クラスで実装する必要があるという意味ではありません。私を平手打ちする前に、ダイヤモンドの問題を観察しましょう:(暗黙のコード、気にしないでください)。

class A
{
public: 
    virtual void foo()=0;
    virtual void bar();
}

class B : public virtual A
{
public:
    void foo() { bar(); }
}

class C : public virtual A
{
public:
    void bar();
}

class D : public B, public C
{}

int main(int argc, const char* argv[])
{
    A* obj = new D();
    **obj->foo();**
    return 0;
}

これで、obj-> foo()を呼び出すと、B :: foo()になり、次にC :: bar()になります。

参照してください...純粋な仮想メソッドを派生クラスに実装する必要はありません(foo()にはクラスCに実装がありません-コンパイラはコンパイルします)C++には多くの抜け穴があります。

私が助けることができることを願っています:-)

2
Nir Hedvat