C++言語学習(十一)——多態


C++言語学習(十一)——多態
一、多態の紹介
C++のマルチステート(polymorphism)とは、継承によって生成される関連する異なるクラスを指し、そのオブジェクトが同じメッセージに対して異なる応答を行う.マルチステート性はオブジェクト向けプログラム設計の重要な特徴であり,プログラムの柔軟性を高めることができる.システムのアップグレード、メンテナンス、デバッグの作業量と複雑さを軽減できます.マルチステートは異なる階層分類の下で重要なつながりであり、階層間操作である.
二、多態実現の前提
付与互換性ルールとは、ベースクラスオブジェクトが必要な場所で、共通の派生クラスのオブジェクトを使用して置き換えることです.付与互換性はデフォルトの動作であり、明示的な変換ステップは不要であり、public継承方式でのみ発生し、マルチステート実装の前提条件である.代入互換性ルールでは、A、子オブジェクトが親オブジェクトBに直接代入できる場合、子オブジェクトが親オブジェクトCを直接初期化できる場合、親参照が子オブジェクトDを直接参照できる場合、親ポインタが子オブジェクトを直接指し示す場合、子オブジェクトは親オブジェクトに退化し、親クラスで定義されたメンバーのみにアクセスでき、布団クラスで上書きされた同名のメンバーに直接アクセスできます.
#include 
using namespace std;

class Parent
{
public:
    int m;
    Parent(int a)
    {
        m = a;
    }
    void print()
    {
        cout << "Parent m = " << m << endl;
    }
};

class Child : public Parent
{
public:
    int m;
    Child(int a):Parent(a)
    {
        m = a;
    }
    void print()
    {
        cout << "Child m = " << m << endl;
    }
};
int main()
{
    Parent p(0);
    Child c(0);
    Parent p1(c);
    Parent* pp = &c;//                  
    Parent& rp = c;//                  
    pp->print(); //Parent m = 0
    rp.print(); //Parent m = 0
    //Child cc = static_cast(p);//          
    Child* pc = static_cast(&p);
    pc->print();//Child m = xxxx
    return 0;
}

代替として、派生クラスオブジェクトはベースクラスのオブジェクトとして使用できますが、ベースクラスから継承されたメンバーのみが使用できます.親クラスは、強い回転で子クラスに変換することもできますが、境界を越えたアクセスのリスクがあります.子クラスでは、親クラスにすでに存在するメンバー関数、すなわち関数の書き換えを再定義できます.関数の書き換えが付与互換性に遭遇した場合、コンパイラはポインタのタイプに基づいて指向するオブジェクトを判断するしかなく、付与互換性の原則に基づいて、コンパイラは親ポインタが親オブジェクトを指していると考え、親で定義された同名の関数しか呼び出せない.オブジェクト向けプログラミングでは、通常、実際のオブジェクトタイプに基づいて書き換え関数を呼び出す方法を判断する必要があります.親ポインタが親オブジェクトを指すと、親定義の関数が呼び出されます.親ポインタがサブクラスオブジェクトを指している場合は、サブクラス定義の関数を呼び出す必要があります.親参照が親オブジェクトを参照すると、親オブジェクト定義の関数が呼び出されます.親参照が子オブジェクトを参照すると、子定義の関数が呼び出されます.
三、多態形成の条件
1、多態形成の条件
呼び出した関数、すなわちマルチステートは、親ポインタが指す実際のオブジェクトタイプに基づいて決定されます.マルチステートでは、親ポインタ(参照)が親オブジェクトを指すと、親オブジェクトで定義された関数が呼び出され、親ポインタ(参照)が子オブジェクトを指すと、子オブジェクトを呼び出す関数が呼び出されます.C++はvirtualキーワードでマルチステートをサポートし、virtualで宣言された関数が書き換えられた後にマルチステート属性を持つ.多態形成の条件:A、親に虚関数がある.B、サブクラスoverride(上書き)親クラスの虚関数.C、掛け布団類オブジェクトから付与された親類ポインタにより、共通インタフェースを呼び出す.
2、虚関数
虚関数の宣言構文は次のとおりです.
virtual     ;

虚関数の使用規則は以下の通りである:A、親クラスでvirtualでメンバー関数が虚関数であることを宣言する.クラス外で虚関数を実現する場合はvirtualを追加する必要はありません.B、派生クラスで親クラスにすでに存在するメンバー関数を再定義することを関数上書きと呼び、関数名、戻り値タイプ、関数パラメータの個数およびタイプをすべて一致させ、派生クラスの必要に応じて関数体を再定義する.C、1つのメンバー関数が虚関数として宣言された後、その派生クラスの中で完全に同じ関数も虚関数であり、派生クラスの虚関数はvirtualキーワードを加えてもよいし、加えなくてもよい.D、親オブジェクトポインタを定義して、その子クラスのオブジェクトを指し、親ポインタで虚関数を呼び出します.このとき、子クラスの同名関数が呼び出されます.E、コンストラクション関数は虚関数ではなく、コンストラクション関数の実行が完了した後、虚関数テーブルポインタが正しく初期化される.構造関数は虚関数で、親ポインタを定義しnewで作成したサブクラスオブジェクトを使用して初期化できます.deleteを使用して親ポインタのスタック空間を解放すると、親の構造関数のみが呼び出され、サブクラスの構造関数は呼び出されず、メモリが漏洩します.親構造関数が虚関数と宣言されると、この問題を回避できます.一般に、構造関数を虚関数として宣言する必要があります.コンストラクション関数が実行されると、虚関数テーブルポインタが正しく初期化されないため、コンストラクション関数にマルチステートが発生することはできません.構造関数関数が実行されると、虚関数テーブルポインタが破棄されるため、構造関数にマルチステートが発生することはありません.コンストラクション関数とコンストラクション関数では、現在のクラスで定義されている関数バージョンのみが呼び出されます.
#include 

using namespace std;

class Parent
{
public:
    int mi;
    void add(int i)
    {
        mi += i;
    }
    void add(int a, int b)
    {
        mi += (a + b);
    }
    virtual void print()
    {
        cout << "Parent" << endl;
    }
};

class Child : public Parent
{
public:
    int mi;
    //    
    void add(int x, int y)
    {
        mi += (x + y);
    }
    //    
    void print()
    {
        cout << "Child" << endl;
    }
};

int main(int argc, char *argv[])
{
    Child child;
    child.add(1,2);//      
    child.Parent::add(1);//      
    child.Parent::add(1,2);//      
    Parent p = child;
    p.add(1);
    p.add(1,2);
    Parent& rp = child;
    rp.add(1);
    rp.add(1,2);
    rp.print();//Child

    Parent* pp = &child;
    pp->add(1);
    pp->add(1,2);
    pp->print();//Child

    return 0;
}

3、純虚関数
純虚関数の宣言構文は次のとおりです.
virtual      = 0;

純虚関数の使用規則は以下の通りである:A、純虚関数を含むクラスは抽象ベースクラスと呼ばれ、インスタンス化できない、すなわちオブジェクトを作成できない、存在する意味は継承され、クラスファミリーの共通インタフェースを提供し、Javaではinterfaceと呼ばれる.B、純虚関数は宣言のみで実現していない.C、あるクラスに純虚関数が宣言され、派生クラスに純虚関数が実装されていない場合、この虚関数は派生クラスにおいて依然として純虚関数であり、派生クラスは依然として抽象ベースクラスである.
4、虚関数の使用規則
虚関数の使用規則は以下の通りである:A、クラスのメンバー関数のみが虚関数として宣言できる.ダミー関数は継承関係のあるクラスオブジェクトにのみ適用されるため、通常の関数はダミー関数として宣言できません.B、静的メンバー関数は虚関数ではありません.静的メンバー関数はオブジェクトにバンドルされず、クラスの情報のみです.C、インライン関数は虚関数ではありません.D、コンストラクション関数は虚関数ではありません.構築時にオブジェクトの作成が完了していません.構造が完了してから、名実ともにオブジェクトを計算できます.E、構造関数は虚関数であってもよく、通常は虚関数F、虚関数を含むクラスとして宣言され、構造関数も虚関数として宣言されなければならない.delete親ポインタの場合、子クラスの構造関数が呼び出されます.
四、多態の意味
マルチステートの意味は以下の通りである:A、プログラムの実行中に現れる動的特性B、関数の書き換えはマルチステートで実現しなければならない.そうしないと、意味がないC、マルチステートはオブジェクトコンポーネント化プログラム設計向けの基礎特性Dであり、マルチステートは階層間操作静的コンパイルであり、プログラムのコンパイル中に関数の再ロードなどの具体的な関数呼び出しを確定することができる.動的アセンブリは、プログラムが実際に実行されたときに、関数の書き換えなどの特定の関数呼び出しを決定することができます.
#include 

using namespace std;

class Parent
{
public:
    int mi;
    virtual void add(int i)
    {
        mi += i;
    }
    virtual void add(int a, int b)
    {
        mi += (a + b);
    }
    virtual void print()
    {
        cout << "Parent" << endl;
    }
};

class Child : public Parent
{
public:
    int mi;
    //    
    void add(int x, int y)
    {
        mi += (x + y);
    }
    //    
    void print()
    {
        cout << "Child" << endl;
    }
};

int main(int argc, char *argv[])
{
    Child child;
    child.add(1,2);//    
    child.Parent::add(1);//    
    child.Parent::add(1,2);//    
    Parent p = child;
    p.add(1);//    
    p.add(1,2);//    
    p.print();//    
    cout << endl;
    Parent& rp = child;
    rp.add(1);//    
    rp.add(1,2);//    
    rp.print();//    

    Parent* pp = &child;
    pp->add(1);//    
    pp->add(1,2);//    
    pp->print();//    

    return 0;
}