2012-01-19 7 views
70

これは簡単な質問のように思えるかもしれませんが、私はどこでも答えを見つけることができません。すべての仮想関数は派生クラスで実装する必要がありますか?

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

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

はそれが派生クラスがバー()関数を実装していないことをOKです:

は、私は、次のしていると仮定しますか? 派生したすべてのクラスにbar()関数が必要なわけではありませんが、いくつかはそうです。 抽象基本クラスのすべての仮想関数は、派生クラス内に実装する必要がありますか、純粋仮想クラスだけを実装する必要がありますか? おかげ

答えて

59

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


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

+2

(純粋な仮想関数の実装)もインスタンス化を意図している場合のみ(抽象基本クラス自体とは対照的に)。 –

+0

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

+0

@pixelpusherもちろん 'Derived :: bar'は' Abstract :: bar'の関数本体を持っています。つまり、それが定義されている翻訳単位(どこにでも定義されていますか?)は、それが呼び出される翻訳単位にリンクされていないようです。 –

3

はい、いいです...抽象基本クラスから派生したクラスをインスタンス化するために純粋な仮想関数を実装すればよいだけです。

31

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

だから、ちょうどあなたのオプションの仮想メソッドの後{}を置くことはあなたの空のデフォルトの実装を提供します:

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(); 
}; 

より複雑なデフォルトの実装では、しかし、別のソースファイルに行くだろう。

+2

。これは私の問題だった、ありがとう。 :) – mikestaub

+0

構文の入力ミス? – Goldname

7

ISO C++標準では、純粋仮想ではないクラスのすべての仮想メソッドを定義する必要があります。
あなたの派生クラスが基底クラスの仮想メソッドをoveriddes場合、それは、その後、基底クラスがそのメソッドの定義を提供する必要があり、同様の定義を提供しない場合必要があります。

単にルールがある置きます。

コード例では、virtual void bar();には、Baseクラスの定義が必要です。

リファレンス:

C++ 03標準:[class.virtual]

10.3仮想関数は、クラス内で宣言された仮想関数が定義され、又は(10.4)は、純粋な宣言でなければなりませんそのクラス、またはその両方診断は必要ありません(3.2)。

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

gcc faq doccuments同様にそれ:

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

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

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

0

はい、派生クラスが親クラスのPure Virtualである関数をオーバーライドする必要があることは間違いありません。純粋仮想関数を持つ親クラスは抽象クラスと呼ばれます。これは、子クラスが純粋仮想関数の本体を与えなければならないためです。

ノーマルバーチャルファンクションの場合: - いくつかの子クラスはその機能を持つかもしれないので、それ以上は無効にする必要はありません。

仮想関数メカニズムの主な目的は、実行時多型です.Pure Virtual Function(抽象クラ​​ス)の主な目的は、自分の身体と同じ名前の機能を持つことを必須とすることです。

関連する問題