問題の歴史:
C++は、現代の言語にとって基本的なオブジェクト指向プログラミングのサポートを提供します。ポリモーフィズムを実現するために仮想関数が使用されました。これにより、コンパイル時だけでなく、実行時に必要なメソッドの実装を呼び出すことができ、継承を持つアーキテクチャにとっては重要です。
問題:
一般的な誤りは、メソッドの静的および動的呼び出しの混乱、忘れられた仮想デストラクタ、誤った継承(例えば、オブジェクトスライス、オーバーライドされたバージョンの代わりにベースバージョンを呼び出す)に関連しています。実際にポリモーフィズムが機能するのはいつかを誤解することがよくあります。
解決:
仮想関数は、基底クラスでvirtualキーワードを使用して宣言され、派生クラスでオーバーライドできます。基底クラスへのポインタまたは参照で関数を呼び出すと、派生クラスのバージョンが実行されます。
コード例:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Derived::fooが出力されます }
主な特徴:
overrideキーワードが必要です(C++11以降使用可能)。オブジェクトを値として渡す際にポリモーフィズムは機能しますか?
いいえ。値渡しは"スライス"を引き起こします。パラメータの型に対応する部分(通常は基底クラス)のみがコピーされ、ポリモーフィズムは無効になります。
コード例:
void call(Base b) { b.foo(); } // 常にBase::fooが呼び出されます
基底クラスでデストラクタを仮想として宣言する必要がありますか?
はい、基底クラスへのポインタを通じて派生オブジェクトを削除することを想定している場合は必要です。さもないと、メモリリークやリソースが解放されない問題が発生します。
コード例:
struct Base { virtual ~Base() {} };
派生クラスでoverrideキーワードを使用しないとどうなりますか?
派生クラスでoverrideを指定しない場合、誤って関数のシグネチャを変更した場合(例えば、constをスキップする、またはパラメータを間違える)、関数は仮想関数をオーバーライドせず、新しい関数が作成され、期待通りにポリモーフィズムが機能しなくなります。
プログラマーが基底クラスのデストラクタを仮想として宣言しなかった。基底ポインタを通じてオブジェクトの配列を削除した結果、メモリリークが発生した。
利点:
欠点:
仮想デストラクタが宣言され、基底型への参照/ポインタのみが使用された。ポリモーフィズムが正しく機能した。
利点:
欠点: