まず:一つの概念を強調します
関数を仮想関数として定義しても、その関数が実装されない関数であることを意味するわけではありません。
仮想関数として定義するのは、基底クラスのポインタを使って子クラスのこの関数を呼び出せるようにするためです。
関数を純粋仮想関数として定義して初めて、関数が実装されていないことを意味します。
純粋仮想関数を定義するのはインターフェースを実装するためであり、規範としての役割を果たします。このクラスを継承するプログラマーはこの関数を必ず実装しなければならないという規範です。
1、概要
次のようなクラス階層があると仮定します:
例
この例は仮想関数の典型的な応用です。この例を通して、仮想関数について少し概念が掴めたかもしれません。仮想関数が「仮想」であるのは、いわゆる「遅延結合」または「動的結合」のためです。クラス関数の呼び出しはコンパイル時に決定されるのではなく、実行時に決定されます。コードを書く時点では、呼び出されるのが基底クラスの関数なのか、どの派生クラスの関数なのかを確定できないため、「仮想」関数と呼ばれます。
仮想関数はポインタまたは参照を介してのみ多態性の効果を達成できます。
C++ 純粋仮想関数
一、定義
純粋仮想関数は基底クラスで宣言される仮想関数であり、基底クラスでは定義されませんが、すべての派生クラスが独自の実装方法を定義することを要求します。基底クラスで純粋仮想関数を実装する方法は、関数プロトタイプの後に=0:
virtual void funtion1()=0
二、導入の理由
1、多態性の特性を簡単に利用するために、基底クラスで仮想関数を定義する必要がよくあります。
2、多くの場合、基底クラス自体がオブジェクトを生成するのは不合理です。例えば、動物は基底クラスとして虎、孔雀などの派生クラスを派生させることができますが、動物自体がオブジェクトを生成するのは明らかに不合理です。
上記の問題を解決するために、純粋仮想関数の概念が導入され、関数を純粋仮想関数として定義します(方法:virtual ReturnType Function()= 0;)、コンパイラは派生クラスで多態性を実現するためにこれを必ずオーバーライドすることを要求します。同時に、純粋仮想関数を含むクラスは抽象クラスと呼ばれ、オブジェクトを生成できません。これにより上記の2つの問題がうまく解決されます。
純粋仮想関数を宣言したクラスは抽象クラスです。したがって、ユーザーはクラスのインスタンスを作成できず、その派生クラスのインスタンスのみを作成できます。
純粋仮想関数の最も顕著な特徴は:それらは継承クラスで関数を再宣言しなければなりません(後ろの=0を付けないでください。付けないとその派生クラスもインスタンス化できません)。また、それらは抽象クラスでは多くの場合定義されていません。
純粋仮想関数を定義する目的は、派生クラスに関数のインターフェースのみを継承させることにあります。
純粋仮想関数の意味は、すべてのクラスオブジェクト(主に派生クラスオブジェクト)が純粋仮想関数の動作を実行できるようにすることですが、クラスは純粋仮想関数に合理的なデフォルト実装を提供できません。したがって、クラスの純粋仮想関数の宣言は、子クラスの設計者に「あなたは純粋仮想関数の実装を提供しなければなりませんが、私(クラス)はあなたがどのように実装するか知りません」と伝えているのです。
抽象クラスの紹介
抽象クラスは特殊なクラスであり、抽象化と設計の目的のために確立されたもので、継承階層構造の比較的上位に位置します。
(1)抽象クラスの定義:純粋仮想関数を持つクラスを抽象クラスと呼びます。
(2)抽象クラスの役割:抽象クラスの主な役割は、関連する操作を結果インターフェースとして継承階層構造内に組織化し、派生クラスに共通のルートを提供することです。派生クラスは、基底クラスでインターフェースとして定義された操作を具体的に実装します。したがって、派生クラスは実際には一連のサブクラスの操作インターフェースの共通セマンティクスを描写しており、これらのセマンティクスはサブクラスにも伝えられ、サブクラスはこれらのセマンティクスを具体的に実装することも、さらに自分のサブクラスに伝えることもできます。
(3)抽象クラスを使用する際の注意:
- 抽象クラスは基底クラスとしてのみ使用でき、その純粋仮想関数の実装は派生クラスによって提供されます。派生クラスが純粋仮想関数を再定義せず、基底クラスの純粋仮想関数を継承するだけの場合、その派生クラスは依然として抽象クラスです。派生クラスが基底クラスの純粋仮想関数の実装を提供した場合、その派生クラスはもはや抽象クラスではなく、オブジェクトを生成できる具体的なクラスです。 抽象クラスはオブジェクトを定義できません。
まとめ:
-
1、純粋仮想関数の宣言は以下の通りです:virtual void funtion1()=0;純粋仮想関数は必ず未定義であり、純粋仮想関数は派生クラスの動作、すなわちインターフェースを規範化するために使用されます。純粋仮想関数を含むクラスは抽象クラスであり、抽象クラスはインスタンスを定義できませんが、その抽象クラスを実装する具体的なクラスを指すポインタまたは参照を宣言することはできます。
-
2、仮想関数の宣言は以下の通りです:virtual ReturnType FunctionName(Parameter)仮想関数は実装しなければなりません。実装しない場合、コンパイラはエラーを報告します。エラーメッセージは次の通りです:
error LNK****: unresolved external symbol "public: virtual void __thiscall ClassName::virtualFunctionName(void)"
-
3、仮想関数に関して、親クラスと子クラスはそれぞれ独自のバージョンを持ちます。多態的な方法で呼び出される場合、動的にバインドされます。
-
4、純粋仮想関数を実装した子クラスでは、その純粋仮想関数は子クラスの中で仮想関数になります。子クラスの子クラス、つまり孫クラスはその仮想関数をオーバーライドでき、多態的な方法で呼び出される場合、動的にバインドされます。
-
5、仮想関数はC++で多態性(polymorphism)を実装するためのメカニズムです。核となる理念は、基底クラスを通じて派生クラスで定義された関数にアクセスすることです。
-
6、ヒープ上にメモリを動的割り当てする場合、デストラクタは仮想関数でなければなりませんが、純粋仮想関数である必要はありません。
-
7、フレンドはメンバ関数ではなく、メンバ関数のみが仮想にできます。したがって、フレンドは仮想関数にできません。しかし、フレンド関数に仮想メンバ関数を呼び出させることで、フレンドの仮想問題を解決できます。
-
8、デストラクタは仮想関数であるべきです。そうすれば、対応するオブジェクト型のデストラクタが呼び出されます。したがって、ポインタが子クラスオブジェクトを指している場合、子クラスのデストラクタが呼び出され、その後自動的に基底クラスのデストラクタが呼び出されます。
純粋仮想関数を持つクラスは抽象クラスであり、オブジェクトを生成できず、派生のみ可能です。派生したクラスで純粋仮想関数が書き換えられていない場合、その派生クラスもまだ抽象クラスです。
純粋仮想関数を定義するのは、基底クラスをインスタンス化できないようにするためです。なぜなら、そのような抽象データ構造をインスタンス化すること自体に意味がなく、実装を提供することにも意味がないからです。
実際、私個人としては、純粋仮想関数の導入には2つの目的があると考えています:
- 1、安全性のため。明確にすべきなのに不注意によって引き起こされる未知の結果を避け、子クラスにすべき実装を行うよう促すためです。
- 2、効率のため。プログラム実行の効率ではなく、コーディングの効率のためです。
原文アドレス:https://blog.csdn.net/hackbuteer1/article/details/7558868