199

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

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

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

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

実装を備えた純粋仮想関数にする目的は何ですか?

4

10 に答える 10

236

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

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++ プログラマーでさえ、ほとんどの C++ プログラマーを驚かせるようです)。

于 2010-01-18T21:00:13.257 に答える
85

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

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

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

于 2010-01-18T21:09:49.673 に答える
20

派生クラスで実行する必要があるコードがあるが、直接実行したくない場合 - 強制的にオーバーライドしたい場合。

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

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

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

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

Base::~Base() {}

class Derived : public Base {};

int main() { 
  // Base b; -- compile error
  Derived d; 
}
于 2010-01-18T20:49:02.470 に答える
20

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

于 2010-01-18T20:49:31.857 に答える
7

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

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

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

于 2010-01-18T21:24:23.180 に答える
4

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

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

于 2010-04-09T18:12:42.227 に答える
4

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

Scott Meyers はこれについて、Effective C++ (第 2 版)の項目 #36 で説明しています。インターフェースの継承と実装の継承を区別してください。最新版では品番が変わっている可能性があります。

于 2010-01-18T21:01:49.863 に答える
3

「仮想 void foo() =0;」構文は、現在のクラスに foo() を実装できないという意味ではありません。実装できます。また、派生クラスで実装する必要があるという意味でもありません。私を平手打ちする前に、Diamond Problem を見てみましょう: (暗黙のコード、気をつけてください)。

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 ++には多くの抜け穴があります。

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

于 2015-03-24T20:55:57.020 に答える