C++派生クラスのコンストラクション関数(コンストラクション関数の実行順序)


1、単一継承の場合、派生クラス構築関数は常にベースクラス構築関数を呼び出してから他のコードを実行する
1.1派生クラス呼び出しベースクラスの構築関数
クラスのコンストラクション関数は継承できません.コンストラクション関数は継承できないのは理にかなっています.継承しても、その名前と派生クラスの名前は異なり、派生クラスのコンストラクション関数にはなれません.もちろん、普通のメンバー関数にはなれません.派生クラスを設計する場合、継承されたメンバー変数の初期化作業も派生クラスの構造関数によって行われますが、ほとんどのベースクラスにはprivate属性のメンバー変数があり、派生クラスではアクセスできません.派生クラスの構造関数を使用して初期化することはできません.この矛盾はC++継承において普遍的に存在し,この問題を解決する構想は,派生クラスの構造関数においてベースクラスの構造関数を呼び出すことである.
#include
using namespace std;

//  People
class People{
protected:
    char *m_name;
    int m_age;
public:
    People(char*, int);
};
People::People(char *name, int age): m_name(name), m_age(age){}

//   Student
class Student: public People{
private:
    float m_score;
public:
    Student(char *name, int age, float score);
    void display();
};
//People(name, age)           
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }
void Student::display(){
    cout<"    "<",   "<"。"<int main(){
    Student stu("  ", 16, 90.5);
    stu.display();

    return 0;
}

23行目のコードに注意してください.
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }

People(name,age)はベースクラスを呼び出すコンストラクション関数であり,nameとageを実パラメータとして渡し,m_score(score)は、派生クラスのパラメータ初期化テーブルであり、カンマで区切られています.
ベースクラス構築関数の呼び出しは、パラメータ初期化テーブルの後に置くこともできます.
Student::Student(char *name, int age, float score): m_score(score), People(name, age){ }

しかし、それらの順序にかかわらず、派生クラス構築関数は、パラメータ初期化テーブルおよび関数ボディのコードを含む他のコードを実行するために、ベースクラス構築関数を呼び出してから実行されます.
この点をまとめると、ベースクラスコンストラクション関数は常に優先的に呼び出されます.これは、派生クラスオブジェクトを作成するときに、ベースクラスコンストラクション関数を呼び出してから派生クラスコンストラクション関数を呼び出します.継承関係がいくつかある場合、例えば、A->B->C
では、Cクラスオブジェクトを作成するときのコンストラクタの実行順序は、Aクラスコンストラクタ->Bクラスコンストラクタ->Cクラスコンストラクタです.
コンストラクション関数の呼び出し順序は、継承された階層に従って上から下へ、ベースクラスから派生クラスに移動します.
1.2派生クラスの構築関数は直接ベースクラスの構築関数のみを呼び出すことができる
また、派生クラスコンストラクタでは直接ベースクラスのコンストラクタのみが呼び出され、間接ベースクラスは呼び出されないことに注意してください.上記のA,B,Cクラスを例にとると,Cは最終的な派生クラス,BはCの直接ベースクラス,AはCの間接ベースクラスである.
C++という規定は理にかなっている.CではBのコンストラクション関数を呼び出し、BではAのコンストラクション関数を呼び出し、Cが間接的に(あるいは暗黙的に)Aのコンストラクション関数を呼び出し、CではAのコンストラクション関数を明示的に呼び出すと、Aのコンストラクション関数が2回呼び出され、それに応じて初期化作業も2回行われるからである.これは、余計なだけでなく、CPUの時間やメモリを無駄にしてしまうため、C++は、CでAのコンストラクタを明示的に呼び出すことを禁止します.参照先:http://c.biancheng.net/cpp/biancheng/view/231.html
2、多継承下の構造関数
2.1複数継承の宣言
複数継承された構文も簡単で、複数のベースクラスをカンマで区切ることができます.例えば、クラスA、クラスB、およびクラスCが宣言された場合、派生クラスDを宣言することができる.
class D: public A, private B, protected C{
    // D      
}

2.2継承されたコンストラクション関数の呼び出し順序
Dはマルチ継承形式の派生クラスであり、Aクラスを公有的に継承し、Bクラスを私有的に継承し、Cクラスを保護的に継承する.Dは、異なる継承方式に従ってA、B、Cのメンバーを取得し、派生クラスにおけるアクセス権を決定する.マルチ継承形式のコンストラクション関数と単一継承形式は基本的に同じですが、派生クラスのコンストラクション関数で複数のベースクラスのコンストラクション関数を呼び出すだけです.上記のA,B,C,Dクラスを例にとると,Dクラス構築関数の書き方は次のようになる.
D(    ): A(    ), B(    ), C(    ){
    //    
}

ベースクラスコンストラクション関数の呼び出し順序は、派生クラスコンストラクション関数に現れる順序とは関係なく、派生クラスを宣言するときにベースクラスが現れる順序と同じです.なお、上記のA、B、C、Dクラスを例に、Dクラス構築関数を以下の形式で記述してもよい.
D(    ): B(    ), C(    ), A(    ){
    //    
}

Aクラスのコンストラクタを呼び出してからBクラスコンストラクタを呼び出し,最後にCクラスコンストラクタを呼び出す.参照先:http://c.biancheng.net/cpp/biancheng/view/236.html
3、虚継承時のコンストラクション関数
3.1 Aをダミーベースクラスとして宣言する方法
class A//    A
{…};
class B :virtual public A//   B  A      ,A B    
{…};
class C :virtual public A//   C  A      ,A C    
{…};

3.2(派生ダミーベースクラスの)派生クラスの構築関数
class A//    A
{
   A(int i){ } //      ,     };
class B : virtual public A //A  B    
{
   B(int n):A(n){ } //B     ,             
};
class C : virtual public A //A  C    
{
   C(int n):A(n){ } //C     ,             
};
class D :public B,public C 
{
   D(int n):A(n),B(n),C(n){ } // D     ,              
};

注意:クラスDのコンストラクション関数を定義する場合、従来の方法とは異なります.最後の派生クラスでは,直接ベースクラスの初期化だけでなく,虚ベースクラスの初期化も担当することを規定している.C++コンパイルシステムは、最後の派生クラスによるダミーベースクラスのコンストラクション関数の呼び出しのみを実行し、ダミーベースクラスの他の派生クラス(クラスBやクラスCなど)によるダミーベースクラスのコンストラクション関数の呼び出しを無視し、ダミーベースクラスのデータメンバーが複数回初期化されないことを保証する.参照先:https://www.cnblogs.com/yiranlaobaitu/p/3764422.html