C++多態性虚関数、抽象クラス(一)

3690 ワード

注意:本明細書のコードはQtを使用してコンパイル環境を開発する
虚関数は動的アセンブリの基礎である.虚関数は、protectedまたはpublicのアクセス権を持つベースクラスの非静的メンバー関数である必要があります.ベースクラスのクラス定義で虚関数の一般的な形式を定義します.
virtual returnType functionName (param...) { 
        // some codes
}

タイプ互換規則に従って、ベースクラスオブジェクトの代わりに派生クラスのオブジェクトを使用できることを知っています.ベース・クラス・タイプのポインタで派生クラス・オブジェクトを指す場合、このポインタでオブジェクトにアクセスできます.問題は、ベース・クラスから継承された同名のメンバーのみがアクセスされることです.この問題を解決する方法は、ベースクラスのポインタで派生クラスのオブジェクトを指し、ベースクラスと同じ名前のメンバーにアクセスする必要がある場合、まずベースクラスでこの同名関数を虚関数として説明することです.これにより、ベースクラスタイプのポインタにより、異なる派生クラスに属する異なるオブジェクトに異なる動作を生じさせ、実行プロセスのマルチステートを実現することができる.
動的アセンブリでは、次の形式で、ベースクラスへのポインタまたはベースクラスオブジェクトへの参照のみで虚関数を呼び出すことができます.
ptrToBaseClass->virtualFunctionName(param...);

または
quoteToBaseClass.virtualFunctionName(param...);

虚関数はC++多態の表現である.例えば、子は親の関数(メソッド)を継承し、親のポインタを子に向けると、親の関数(メソッド)をvirtual(虚関数)に設定しなければならない.
虚関数を使用すると,動的結合を柔軟に行うことができ,もちろん一定のオーバーヘッドを代価とする.
親クラスの関数(メソッド)が必要でないか、実装できない場合、完全に子クラスに依存して実装する場合は、この関数(メソッド)を次のように設定できます.
virtual returnType functionName(params) = 0;

このような関数(方法)を純虚関数と呼び,クラスに純虚関数が含まれている場合,抽象クラスと呼ぶ.
ダミー関数の例:
#include 
#include 

class B{
public:
    virtual void display(){ qDebug() << "B::display()"; }
};

class C: public B{
public:
    void display(){ qDebug() << "C::display()"; }
};

class D: public C{
public:
    void display(){ qDebug() << "D::display()"; }
};

void fun(B *ptr){
    ptr->display();
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B b,*p;
    C c;
    D d;

    p = &b; fun(p);
    p = &c; fun(p);
    p = &d; fun(p);

    return a.exec();
}

出力の実行:
B::display()
C::display()
D::display()

この例では、派生クラスは明示的に虚関数宣言を与えていません.この場合、派生クラスの関数メンバーが虚関数であるかどうかを判断するには、次のルールに従います.
(1)この関数がベースクラスの虚関数と同じ名前であるか,(2)この関数がベースクラスの虚関数と同じパラメータ個数と同じ対応パラメータタイプであるか,(3)この関数がベースクラスの虚関数と同じ戻り値であるか,あるいはタイプ互換規則を満たすポインタ,参照型の戻り値であるか.
名前、パラメータ、戻り値の3つの側面からチェックした後、派生クラスの関数が上記の条件を満たすと、自動的に虚関数として決定されます.このとき,派生クラスの虚関数はベースクラスの虚関数を上書きする.それだけでなく、派生クラスの虚関数は、ベースクラスの同名関数の他のすべてのリロード形式を非表示にします.
ベースクラスのコンストラクション関数が虚関数を呼び出すと、派生クラスの虚関数は呼び出されません.これは、ベースクラスが構築されると、オブジェクトが派生クラスオブジェクトではないためです.同様に、ベースクラスが解析されると、オブジェクトは派生クラスオブジェクトではなくなります.したがって、ベースクラスは、ベースクラスの虚関数を呼び出します.
虚関数だけが動的に結合され、派生クラスがベースクラスと同じ名前の関数を書き換える必要がある場合は、対応する関数をベースクラスで虚関数として宣言する必要があります.ベースクラスで宣言される非虚関数は、通常、派生クラスによって変更されたくない機能を表し、マルチステートを実現することはできません.したがって、継承された非虚関数は書き換えられません.(文法的には強引な制限はありませんが).
継承された虚関数を書き換える場合、関数にデフォルトのパラメータ値がある場合は、異なる値を再定義しないでください.なぜなら、虚関数は動的に結合されていますが、デフォルトのパラメータ値は静的にバインドされています.すなわち、派生クラスオブジェクトを指すベースクラスポインタを使用すると、派生クラスの虚関数にアクセスできますが、デフォルトのパラメータ値はベースクラスの定義からのみ使用できます.
動的アセンブリを実現するには、3つの条件が必要です.1、動的アセンブリの動作をクラスの虚関数として定義する必要があります.2、クラス間にはサブタイプ関係があり、一般的に一つのクラスが別のクラス公有から派生したと表現される.3、ベースクラスポインタを使用してサブタイプのオブジェクトを指し、ベースクラスポインタを直接または間接的に使用して虚関数を呼び出す必要があります.
虚関数の制限を定義します.(1)クラス以外のメンバー関数を虚関数として定義することはできません.クラスのメンバー関数では静的メンバー関数やコンストラクション関数を虚関数として定義することはできませんが、コンストラクション関数を虚関数として定義することはできます.実際には、優れたプログラマーは、ベースクラスのコンストラクション関数を虚関数として定義することがよくあります.なぜなら、ベースクラスのコンストラクション関数を虚関数として定義した後、deleteを使用して削除すると派生クラス定義のオブジェクトポインタを指すと、対応するクラスの構造関数が呼び出されます.解析関数を虚関数として定義しない場合は、ベースクラスの解析関数のみが呼び出されます.
(2)関数を宣言するクラスでキーワード「virtual」を使用して関数を虚関数として宣言するだけで、関数を定義するときにキーワード「virtual」を使用する必要はありません.
(3)ベースクラスのあるメンバー関数を虚関数として宣言すると,派生クラスの同名関数は自動的に虚関数となる.
(4)あるメンバー関数を虚関数として宣言した場合、そのクラスには、そのメンバー関数と同じ名前で戻り値、パラメータ個数、タイプが同じ非虚関数は現れません.このクラスをベースとした派生クラスにも、このような同名関数は現れません.
虚関数はマルチステートに関連付けられ、マルチステートは継承に関連付けられます.だから本文では継承レベルで文章を作っています.継承がなければ、何も話さない.