C++における虚関数と純虚関数の使い方

4059 ワード

本論文では,C++における虚関数と純虚関数の用法をより深く解析し,対象向けプログラム設計の学習と把握にとって極めて重要である.具体的な内容は以下の通りです.
まず,オブジェクト向けプログラム設計(object-oriented programming)の核心思想はデータ抽象,継承,動的バインドである.データ抽象化により、クラスのインタフェースを実装から分離し、継承を使用することで、他のクラスと似ているが完全に同じではない新しいクラスをより容易に定義することができ、ダイナミックバインドを使用することで、類似クラスの違いをある程度無視し、それらのオブジェクトを統一的に使用することができます.
虚関数の役割は多態性(Polymorphism)を実現することであり,多態性はインタフェースと実装を分離し,共通の方法を採用するが,個体差により異なる戦略を採用する.純粋な虚関数は特殊な虚関数である.虚関数はマルチステートに関連付けられ、マルチステートは継承に関連付けられます.だから本文では継承レベルで文章を作っています.継承がなければ、何も話さない.
一、虚関数
1 . 定義#テイギ#
C++では、ベースクラスはその2つのメンバー関数を区別する必要があります.1つは、ベースクラスが派生クラスを上書きすることを望んでいる関数です.もう1つは、ベースクラスが派生クラスを変更せずに直接継承したい関数です.前者の場合、ベースクラスは、関数の前にvirtualキーワードを付けることによって虚関数(virtual)として定義される.

class Base{ //    
public: 
  virtual int func(int n) const; 
}; 
 
class Derive_Class : public Base{ 
public: 
  int func(int n) const; //         
}; 

派生クラスで関数を上書きすると、関数の前にvirtualキーワードを追加できます.ただし、関数が虚関数として宣言されると、すべての派生クラスが虚関数であるため、これは必須ではありません.任意のコンストラクション関数以外の非静的関数は虚関数であってもよい.派生クラスは継承された虚関数を常に上書きしますが、必ずしも上書きしません.派生クラスがベースクラスの虚関数を上書きしていない場合、その虚関数の動作は他の一般メンバーと同様で、派生クラスはベースクラスのバージョンを直接継承します.
2 . ダイナミックバインド
ベースクラスの参照(またはポインタ)を使用して虚関数を呼び出すと、動的バインド(dynamicbinding)が発生します.どのバージョンの虚関数が呼び出されたのか、ベースクラスのバージョンであるか、派生クラスのバージョンであるかを実行するまで知ることができないため、参照(またはポインタ)にバインドされたオブジェクトの真のタイプに基づいて判断されます.非虚関数がコンパイル時にバインドされるのとは異なり、虚関数は実行時に関数を選択するバージョンであるため、動的バインドは実行時バインドとも呼ばれる.
3 . 静的タイプと動的タイプ
静的タイプとは、変数宣言時のタイプまたは式によって生成されるタイプを指し、コンパイル時に常に既知である.動的タイプとは、実行時になってから分かる変数または式で表されるメモリ内のオブジェクトのタイプです.ベースクラスのポインタまたは参照によって虚関数が呼び出される場合にのみ、実行時に呼び出しが解析され、この場合のオブジェクトのダイナミックタイプが静的タイプと異なる可能性があります.式が参照でもポインタでもない場合、そのダイナミックタイプは常に静的タイプと一致します.
4 . 义齿
派生クラスで、ベースクラスの虚関数と同じ名前の関数が定義されているが、パラメータリストが異なる場合、コンパイラは派生クラスが新しく定義した関数だと考えます.我々の意図が虚関数を上書きすることであれば,この誤りは発見しにくい.派生クラスの虚関数にoverrideキーワードを最後に追加することで、意図がより明確になります.overrideを使用して関数をタグ付けしたが、既存の虚関数を上書きしていない場合、コンパイラはエラーを報告します.

class Base{ //    
public: 
  virtual int func(int a, int b) const; 
}; 
 
class Derive_Class : public Base{ 
public: 
  int func(int a) const override; //   ,        
}; 

クラスを定義すると、継承されたくない.あるいは、関数が上書きされないことを望む場合は、クラスまたは関数をfinalとして指定できます.その後、クラスを継承したり、関数を上書きしたりしようとする操作はエラーを引き起こします.

class Base final { /*  */ };   //         
class Derive_Class : public Base { /* */ };   //    
 
void func(int) const final;  //            func(int)


5 . 虚関数を回避するメカニズム
場合によっては、虚関数の呼び出しを動的にバインドするのではなく、虚関数の特定のバージョンを実行するように強制したい場合があります.この目的は、役割ドメイン演算子を使用して実現できます.

//                  baseP         
int a = baseP->Base::func(42); 


派生クラス虚関数がベースクラスバージョンを呼び出す必要があるが、役割ドメイン演算子が使用されていない場合、実行時に派生クラスバージョン自体の呼び出しとして解析され、無限再帰を招きます.
二、純虚関数
1 . 定義#テイギ#
マルチステート特性の使用を容易にするために,基底クラスで虚関数を定義することがしばしば必要である.多くの場合、ベースクラスでは虚関数に有意義な実装は与えられない.虚関数をベースクラスで何もしないために,「純虚関数」の概念を導入し,関数を定義する必要がない.関数体の位置(すなわち、文のセミコロンを宣言する前に)=0と書くことで、1つの虚関数を純虚関数(pure virtual)として説明することができます.ただし、=0はクラス内の虚関数宣言文にのみ表示されます.

class Base{ //      
public: 
  virtual int func(int n) const =0; 
}; 

純粋な虚関数を定義することもできますが、関数体はクラスの外部で定義する必要があります.
2 . 抽象ベースクラス
純粋な虚関数を含む(または上書きされていない)クラスを抽象ベースクラス(abstract base class)と呼びます.抽象ベースクラスはインタフェースを定義し、後続の他のクラスはインタフェースを上書きすることができます.派生クラスに純虚関数が再定義されず、ベースクラスの純虚関数のみが継承されている場合、この派生クラスは抽象的なベースクラスです.抽象ベースクラスには純粋な虚関数(定義されていない)が含まれているため、抽象ベースクラスのオブジェクトを作成することはできませんが、抽象ベースクラスへのポインタまたは参照を宣言できます.

Base base;  //   ,          


まとめ:
①.虚関数は実装されなければなりません.実装しないとコンパイラがエラーを報告します.②.親クラスと子クラスには、それぞれの虚関数バージョンがあります.実行時にマルチステート方式で動的にバインドされます.③.役割ドメイン演算子を使用すると、指定した虚関数バージョンを強制的に呼び出すことができます.④.純虚関数は以下のように宣言される:virtual void funtion()=0;純粋な虚関数は定義する必要はありません.純粋な虚関数を含むクラスは抽象ベースクラスであり、抽象ベースクラスはオブジェクトを作成できませんが、抽象ベースクラスへのポインタまたは参照を宣言できます.⑤.派生クラスが純虚関数を実現すると,この純虚関数は派生クラスで虚関数となり,そのサブクラスはこの関数を上書きすることができる.⑥.構造関数は通常虚関数であるべきであり、構造を解析するときに正しい構造関数バージョンが呼び出されることを保証します.