すべての仮想関数を派生クラスに実装する必要がありますか?


91

これは簡単な質問のように思えるかもしれませんが、他のどこにも答えが見つかりません。

私が次のものを持っているとしましょう:

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

class Derived : Abstract {
public:
    virtual void foo();
}

Derivedクラスがbar()関数を実装していなくても大丈夫ですか?すべての派生クラスがbar()関数を必要としない場合はどうなりますが、一部は必要です。抽象基本クラスのすべての仮想関数を派生クラスに実装する必要がありますか、それとも純粋仮想関数だけを実装する必要がありますか?ありがとう

回答:


82

派生クラスは、すべての仮想関数自体を実装する必要ありませ。彼らは純粋なものを実装する必要があるだけです。1これは、問題のクラスが正しいことを意味します。それは継承し、その上位クラスから実装を。(これは、それがどこかに実装されていることを前提としています。質問のコードはメソッドを宣言していますが、定義していません。Trenkiの回答が示すようにインラインで定義することも、個別に定義することもできます。)DerivedbarAbstractAbstract::bar


1それでも、派生クラスがインスタンス化される場合のみ。派生クラスが直接インスタンス化されず、より多くの派生クラスの基本クラスとしてのみ存在する場合、すべての純粋仮想メソッドを実装する責任があるのはそれらのクラスです。階層内の「中間」クラスでは、基本クラスと同様に、一部の純粋仮想メソッドを実装しないでおくことができます。「中間」クラス純粋仮想メソッドを実装している場合、その子孫はその実装を継承するため、自分で再実装する必要はありません。


3
そして、これ(純粋仮想関数の実装)でさえ、それらが(それ自体が抽象基本クラスであるのとは対照的に)インスタンス化されることを意図している場合に限ります。
クリスチャンラウ2012年

1
それが私が思ったことです。しかし、私のプロジェクトでこれを行っていると、Derived :: bar();に「未解決の外部シンボル」があるというリンクエラーが発生します。しかし、Derived内でbarを宣言したことはありません。それでは、なぜリンカーが関数本体を探しているのでしょうか。
mikestaub 2012年

1
@pixelpusherもちろんDerived::bar、関数本体がありAbstract::barます。したがって、それが定義されている(どこでも定義されている)翻訳ユニットは、それが呼び出されている翻訳ユニットにリンクされていないようです。
クリスチャンラウ2012年

2
@Rob:They only need to implement the pure ones.誤解を招く恐れがあります。派生クラスも、必ずしも純粋仮想関数を実装する必要はありません。
nawaz 2012年

私は助けに感謝しますが、@ trenkiは頭に釘を打ちました。あなたも正しいクリスチャン・ラウでしたが、それは定義されていませんでした。
mikestaub 2012年

47

派生クラスに実装する必要があるのは純粋仮想メソッドのみですが、それでも他の仮想メソッドの定義(宣言だけでなく)が必要です。提供しない場合、リンカは非常によく文句を言うかもしれません。

したがって、{}オプションの仮想メソッドの後に置くだけで、空のデフォルト実装が得られます。

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

ただし、より複雑なデフォルトの実装は、別のソースファイルに入ります。


7

ISO C ++標準では、純粋仮想ではないクラスのすべての仮想メソッドを定義する必要があると規定されています。

簡単に言えば、ルールは次のとおりです。
、派生クラスが基本クラスの仮想メソッドをオーバーライドする場合、定義も提供する必要があります。そうでない場合は、基本クラスがそのメソッドの定義を提供する必要があります。

コード例の上記のルールに従って、virtual void bar();Baseクラスに定義が必要です。

参照:

C ++ 03標準:10.3仮想関数[class.virtual]

クラスで宣言された仮想関数は、そのクラスで定義されるか、純粋(10.4)と宣言されるか、またはその両方である必要があります。ただし、診断は必要ありません(3.2)。

したがって、関数を純粋仮想にするか、関数の定義を提供する必要があります。

gccのよくある質問doccumentsそれにも:

ISO C ++標準では、純粋仮想ではないクラスのすべての仮想メソッドを定義する必要があると指定されていますが、このルールの違反に対する診断は必要ありません。 [class.virtual]/8。この仮定に基づいて、GCCは、暗黙的に定義されたコンストラクター、代入演算子、デストラクタ、および最初のそのような非インラインメソッドを定義する変換ユニット内のクラスの仮想テーブルのみを出力します。

したがって、この特定のメソッドを定義しなかった場合、リンカは明らかに無関係なシンボルの定義がないことについて不平を言う可能性があります。残念ながら、このエラーメッセージを改善するには、リンカーを変更する必要がある場合がありますが、これを常に実行できるとは限りません。

解決策は、純粋ではないすべての仮想メソッドが定義されていることを確認することです。純粋仮想として宣言されている場合でも、デストラクタを定義する必要があることに注意してください[class.dtor]/7


3

はい、それで問題ありません...抽象基本クラスから派生したクラスをインスタンス化するには、純粋仮想関数を実装するだけで済みます。


1

はい、Derivedクラスが親クラスのPureVirtualである関数をオーバーライドする必要があるのは正しいことです。純粋仮想関数を持つ親クラスは、その子クラスが純粋仮想関数の独自の本体を提供する必要があるため、抽象クラスと呼ばれます。

通常の仮想関数の場合:-子クラスの中にはその関数を持っているものもあれば、持っていないものもあるため、それらをさらにオーバーライドする必要はありません。

仮想関数メカニズムの主な目的は実行時ポリモーフィズムです。純粋仮想関数(抽象クラ​​ス)の主な目的が、自分の体と同じ名前の関数を持つことを必須にすることであるかどうかに関係なく。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.