C++の虚関数について


時間:2014.04.10
場所:基地2階
----------------------------------------------------------------------
一、動的バインド;
  動的バインドの考え方により、同じコードの中でベースクラスと派生クラスオブジェクトをそれぞれ処理することができ、パラメータがベースクラスの参照である場合、ベースクラスオブジェクトを使用して対応関数を呼び出すことも、派生クラスオブジェクトを使用して対応関数を呼び出すこともできます.実際に入力されたオブジェクトは、そのバージョンの対応する関数を実行するかどうかを決定します.すなわち、上記の手順では、関数の実行バージョンが実パラメータによって決定されるため、実行時に関数のバージョンが選択されるため、動的バインドは実行時バインドとも呼ばれることがある.総じて、C++では、ベースクラスの参照またはポインタを使用して虚関数を呼び出すと、動的バインドが発生します.
----------------------------------------------------------------------
二、派生クラスの虚関数
  派生クラスは継承された虚関数を常に上書きします.派生クラスがベースクラスの虚関数を上書きしていない場合、その虚関数の動作は通常のメンバーと同様で、派生クラスはベースクラスのバージョンを直接継承します.C++11では、関数の最後にキーワードoverrideを付けることで、メンバー関数を使用して継承された虚関数を上書きしたことを表示できます.
----------------------------------------------------------------------
三、虚関数の呼び出し
  ベースクラスの参照またはポインタを使用して虚メンバー関数を呼び出すと、動的バインドが実行されます.実行時になってから、どのバージョンの虚関数が呼び出されたのか分からないため、すべての虚関数を定義する必要があります.通常、関数を使用する必要がない場合は、定義する必要はありませんが、各虚関数に定義する必要があります.使用するかどうかにかかわらず、コンパイラはどの虚関数を使用するか分からないためです.虚関数の呼び出しは、実行時に解析される可能性があります.すなわち、ある虚関数がポインタまたは参照によって呼び出されると、コンパイラが生成したコードは、実行時になってからどのバージョンの関数を呼び出すべきかを決定します.呼び出された関数は、ポインタまたは参照にバインドされたオブジェクトのダイナミックタイプと一致します.動的バインドは、ポインタまたは参照によって虚関数を呼び出す場合にのみ発生することを決定する必要があります.
  通常のタイプ(参照されていないポインタではない)の式で虚関数を呼び出すと、コンパイル時に呼び出しバージョンが決定されます.たとえば、次のようになります.
base =derived;    //                  base
base.function();  //             
ここではbaseが表すオブジェクトの値を変更できますが、オブジェクトのタイプは変更されません.したがって、コンパイラは呼び出しベースクラスメンバー関数を解析します.
----------------------------------------------------------------------
四、C++における多態
  OOPの核心思想は多態であり、意味は「多様な形式」であり、私たちは関係を継承する複数のタイプを多様なタイプにしている.私たちはこれらのタイプの「多様な形式」を使用することができ、彼らの違いを気にする必要はないからだ.
  ベースクラスの参照またはポインタを使用してベースクラスで定義された関数を呼び出すと、その関数が実際に作用するオブジェクトのタイプはわかりません.ベースクラスオブジェクトである可能性があり、派生クラスオブジェクトである可能性があります.関数が虚関数である場合、実行時になってからどのバージョンを実行するかが決定され、参照またはポインタがバインドされているかが判断されます.を選択します.
  一方、非虚関数の呼び出しはコンパイル時にバインドされます.オブジェクトによる関数(虚または非虚の関数)呼び出しもコンパイル時にバインドされます.オブジェクトのタイプは一定であるため、オブジェクトのダイナミックタイプを静的タイプと一致させることはできません.したがって、オブジェクトによる関数呼び出しは、コンパイル時にオブジェクトが属するクラスの関数バージョンにバインドされます.
合計点:ポインタまたは参照によって虚関数が呼び出された場合にのみ、実行時に呼び出しが解析され、この場合のオブジェクトのダイナミックタイプが静的タイプと異なる可能性があります.
----------------------------------------------------------------------
五、虚関数の使用
派生クラスで虚関数を上書きする場合は、virtualキーワードをもう一度使用して関数の性質を指定できます.これは必須ではありません.関数が虚関数として宣言されると、すべての派生クラスで虚関数になります.派生クラスの関数が継承された虚関数を上書きすると、そのパラメータタイプは上書きされたベースクラス関数と一致する必要があります.完全に一致します.戻りタイプもベースクラス関数と一致する必要があります.例外として、クラスの虚関数戻りタイプがクラス自体のポインタまたは参照である場合、ベースクラス虚関数はベースクラスポインタまたは参照を返し、派生クラス対応関数は派生クラスポインタまたは参照を返します.
----------------------------------------------------------------------
六、虚構造関数
継承関係がベース・クラスのコピー制御に最も直接的な影響を及ぼすのは、ベース・クラスが通常、継承システム内のオブジェクトを動的に割り当てる仮想構造関数を定義する必要があることです.deleteが動的に割り当てられたオブジェクトのポインタをdeleteすると、継承システム内のタイプを指すポインタの静的タイプと削除されたオブジェクトの静的タイプが現れる可能性があります.動的タイプが一致しない場合.コンパイラは、この操作がベースクラスの解析関数を実行するか派生クラスの解析関数を実行するかを知る必要があります.これにより、他の関数と同様に、ベースクラスで解析関数を虚関数として定義することで、正しい解析関数バージョンが実行されることを確認します.たとえば、次のようにします.
class MyClass
{
  public:
    //                      ,        
  virtual ~MyClass()=default;  //        
};
クラスの他の虚関数と同様に、構造関数の虚属性も継承されます.これは、合成された構造を使用してもカスタムの構造を使用しても虚構造であり、ベースクラスの構造が虚構造である限り、deleteベースクラスポインタが正しい構造関数バージョンを実行することを保証します.ベースクラスの構造が虚構造でない場合、deleteは派生クラスオブジェクトを指すベースです.クラスポインタは、未定義の動作を生成します.
経験的ガイドライン:ベースクラスには常に構造関数が必要であり、構造関数を虚関数に設定できます.また、構造関数は組織合成移動操作を行います.
----------------------------------------------------------------------
七、派生クラス構造関数
構造関数ボリュームの実行が完了すると、オブジェクトのメンバーは暗黙的に破棄され、類似するオブジェクトのベースクラス部分も暗黙的に破棄されます.派生クラス構造関数は、派生クラスによって割り当てられたリソースのみを破棄します.
class D:public Base
{ 
  public:
    //Base::~Base           
    ~D(){/*     */}
};

オブジェクトの破棄の順序は、作成とは逆に、派生クラスの構造関数が最初に実行され、次にベースクラスの構造関数が実行されます.
----------------------------------------------------------------------
八、コンストラクション関数とコンストラクション関数で虚関数を呼び出す
  ベースクラスコンストラクション関数が虚関数の派生クラスバージョンを呼び出す場合、この虚関数は派生クラスメンバーにアクセスする可能性があり、派生クラスメンバーにアクセスする必要がなければ、派生クラスはベースクラスの虚関数バージョンを直接使用すればよいが、ベースクラスの構造関数を実行する際に使用する派生クラスメンバーはまだ初期化されていないため、このようなアクセスを許可すると、プログラムがクラッシュする可能性があります.コンストラクション関数またはコンストラクション関数が虚関数を呼び出した場合、コンストラクション関数またはコンストラクション関数が属するタイプに対応する虚関数バージョンで実行する必要があります.つまり、コンストラクション関数およびコンストラクション関数では虚関数を呼び出すことができますが、自分のタイプに属するバージョンのみを呼び出すことに注意してください.