c++ダイナミックバインドと静的バインド&&effective 37本

9777 ワード

マルチステート(polymorphism)とは、異なるオブジェクトが同じメッセージを受信すると異なる操作を実行することを意味する.一般的には、同じ名前で多くの異なる関数を定義し、これらの関数は異なるデータ型に対して同じまたは類似の機能、すなわち「1つのインタフェース、複数の実装」を実現することができる.. C++における多態性は結合という概念と密接に関連している.実行可能ファイルを形成するには、ソースプログラムをコンパイル、接続する必要があります.このプロセスでは、呼び出し関数名を対応する関数に関連付けます.このプロセスはバインディング(binding)であり、結合とも呼ばれます.バインディングは静的バインディングと動的バインディングに分けられます.
静的バインドは静的コンパイルとも呼ばれ、コンパイラ時に呼び出し関数から提供される情報に基づいて、それに対応する具体的な関数を決定し、コンパイル時に呼び出し関数名を具体的な関数にバインドすることを指す.動的バインドは動的アセンブリとも呼ばれ、プログラムをコンパイルする際に関数呼び出しに対応する具体的な関数を確定することができず、プログラムの実行中にのみ関数呼び出しに対応する具体的な関数を確定することができ、すなわちプログラムの実行時に呼び出し関数名を具体的な関数にバインドすることができる.静的バインディング静的バインディング:コンパイル時バインディング、オブジェクト呼び出しによる動的バインディング:実行時バインディング、アドレスによるC++の多態性静的多態性の実現:関数の多態性-関数の再ロードと演算子の再ロードテンプレートの多態性-C++テンプレート(クラステンプレート、関数テンプレート)の動的多態性:継承と虚関数、虚関数(アドレスのみで動的多態性を実現)は「ポインタ->関数()」または動的バインドは、「参照変数.関数()」でC++クラスのダミー関数を呼び出すことによって実行されます.C++の非虚関数については,動的バインディングの特徴を持たないため,どのように呼び出しても動的バインディングは実行されない.
  1. ≪オブジェクトの静的タイプ|Object Static Type|emdw≫:宣言時にオブジェクトが使用するタイプ.コンパイル期間中に決定されました.  2. ≪オブジェクトのダイナミック・タイプ|Object Dynamic Type|emdw≫:現在指定されているオブジェクトのタイプ.運行期間で決められています.
オブジェクトのダイナミックタイプは変更できますが、静的タイプは変更できません.オブジェクトの静的タイプと動的タイプについては、次の例を参照してください.
class B
{
}
class C : public B
{
}
class D : public B
{
}
D* pD = new D();//pD            D*,      D*
B* pB = pD;//pB            B*,     pB      pD   D*
C* pC = new C();
pB = pC;//pB           ,         C*

  3.静的バインド:バインドされているのはオブジェクトの静的タイプで、関数などの特性はオブジェクトの静的タイプに依存し、コンパイル期間に発生します.
  4.≪動的バインド|Dynamic Binding|emdw≫:オブジェクトの動的タイプをバインドします.関数などの特性は、実行期間中に発生するオブジェクトの動的タイプに依存します.
class B
{
    void DoSomething();
    virtual void vfun();
}
class C : public B
{
    void DoSomething();//      ,            no-virtual  ,         ,       ;                      。
    virtual void vfun();
}
class D : public B
{
    void DoSomething();
    virtual void vfun();
}
D* pD = new D();
B* pB = pD;

pD->DoSomething()とpB->DoSomething()は同じ関数を呼び出していますか?
いいえ、pDとpBは同じオブジェクトを指していますが.関数DoSomethingは、静的にバインドされたno-virtual関数であるため、コンパイラはコンパイル時にオブジェクトの静的タイプに応じて関数を選択します.pDの静的タイプはD*であり、コンパイラはpD->DoSomething()を処理するときにD::DoSomething()を指します.同様に、pBの静的タイプはB*であり、pB->DoSomething()がB::DoSomething()を呼び出す.pD->vfun()とpB->vfun()は同じ関数を呼び出していますか?はい.vfunは虚関数であり、動的にバインドされているため、すなわちオブジェクトの動的タイプがバインドされている.pBとpDは静的タイプは異なるが、同時にオブジェクトを指している.彼らの動的タイプは同じで、D*であるため、彼らの呼び出しは同じ関数である:D::vfun()である.
上記はいずれもオブジェクトポインタの場合であり,参照(reference)の場合にも同様に適用される.
ポインタと参照のダイナミックタイプと静的タイプは一致しない場合がありますが、オブジェクトのダイナミックタイプと静的タイプは一致します.
D D;
D.DoSomething() D.vfun()       D::DoSomething() D::vfun()。

それらがダイナミックバインドである場合、それらは静的バインドです.虚関数だけがダイナミックバインドを使用し、他のすべては静的バインドを使用します.今のところ私はまだこの言葉が適用されないことを発見していません.もし間違いがあれば、指摘してほしいです.特に注意すべき点は、デフォルトのパラメータと虚関数が一緒に現れると、状況が少し複雑で、エラーが発生しやすいことです.虚関数は動的にバインドされることを知っているが,効率を実行するためにデフォルトパラメータは静的にバインドされる.
#include
using namespace std;
class Base
{
public:
    virtual int getVal(int i = 0)
    {
        cout << "    " << endl;
        return i;
    }
};

class Derived :public Base
{
public:
    int getVal(int i = 1)
    {
        cout << "     " << endl;
        return i;
    }
};

int main()
{   
    Derived d;
    Base* pb = &d;
    Derived *pd = &d;
    cout << pb->getVal() << endl;
    cout << pd->getVal() << endl;
    system("pause");
    return 0;
    /*  resault:
             
        0
             
        1
    */
}

以上の解析から、pD->vfun()とpB->vfun()呼び出しは関数D::vfun()であることがわかりますが、デフォルトパラメータはいくらですか?解析すると、デフォルトパラメータは静的バインドであり、pD->vfun()の場合、pDの静的タイプはD*であるため、デフォルトパラメータは20であるべきである.同様に、pB->vfun()のデフォルトパラメータは10であるべきである.コードを作成して検証しました.正しいです.この特性については、誰も好きではないと思います.したがって、「継承されたデフォルトパラメータは再定義されません.
effect c++条項37は、継承されたデフォルトパラメータを再定義しない
class Base
{
public:
    virtual int getVal(int i = 0)
    {
        cout << "    " << endl;
        return i;
    }
};

class Derived :public Base
{
public:
    int getVal(int i = 1)
    {
        cout << "     " << endl;
        return i;
    }
};

int main()
{   
    Derived d;
    Base* pb = &d;
    cout << pb->getVal() << endl;
    system("pause");
    return 0;
    /*  resault:
             
        0
    */
}

不思議に思ったことはありませんが、ダイナミックバインドの原則に基づいて、ここでは確かに派生クラスのgetVal関数が呼び出されていますが、なぜ0が出力されるのでしょうか.ここのポインタをリファレンスに置き換えると、同じ結果になります.なぜなら、virtual関数は動的にバインドされていますが、関数のデフォルトパラメータは静的にバインドされています.したがって、コンパイラはpbがBase*のタイプであることを見ると、関数を呼び出すときにベースクラスのデフォルトパラメータを選択します.これはどんなに間違いやすいことだろう.派生クラスの関数を呼び出しますが、彼のパラメータのデフォルト値はベースクラスで提供されます.しかし、コンパイラにも独自の苦衷がある:運行期間の効率.派生クラスのデフォルトパラメータをベースクラスと同じに変更する必要があるようですが、最も深刻なのは、ベースクラスのデフォルトパラメータを変更する必要がある場合、すべての派生クラスのデフォルトパラメータを変更しなければなりません.
1つの比較的良い代替案は、前に紹介したNVI手法です.
class Base
{
public:
    int getVal(int i = 200)
    {
        doGetVal(i);
        return i;
    }
private:
    virtual int doGetVal(int i)
    {
        cout << "    " << endl;
        return i;
    }
};

class Derived :public Base
{
private:
    virtual int doGetVal(int i=10)
    {
        cout << "     " << endl;
        return i;
    }
};
int main()
{   
    Derived d;
    Derived *pd = &d;
    Base* pb = &d;
    cout << pb->getVal() << endl;
    cout << pd->getVal() << endl;
    system("pause");
    /*  resault:
                 
            200
                 
            200
    */
    return 0;

}

この場合、ベースクラスと派生クラスには共通のデフォルトパラメータが使用されます.要するにvirtual関数は動的バインドであるが,関数のデフォルトパラメータは静的バインドである.したがって派生クラスの関数のデフォルトパラメータ値を再定義する必要はなく,NVI手法を用いることが望ましい.