C ++仮想継承[詳細]

1つ、複数の継承

C ++言語は多重継承をサポートします。サブクラスは複数の親クラスを持つことができます。サブクラスは親クラスのすべてのメンバー変数を持ちます。サブクラスは親クラスのすべてのメンバー関数を継承します。サブクラスオブジェクトは、任意の親クラスオブジェクトとして使用できます。

class Derived : public BaseA,
                   public BaseB,
                   public BaseC   
{

};

2つの親クラスに同じ名前のメンバー関数がある場合、サブクラスで[クラス名::メンバー関数名]を使用して、どちらの親クラスの関数が呼び出されるかを確認できます。

第二に、仮想継承

多重継承は素晴らしいですが、次のような致命的なひし形の継承問題があります。

この時点でPeoplein the Doctorオブジェクトから継承されたメソッドを呼び出すと、コンパイルエラーが発生し、あいまいさが生じます。

#include <iostream>  

using namespace std;

class People {
public:
    void People_test() {
        cout << "People_test" << endl;
    }

};
class Teacher : public People {
public:

};
class Student : public People {
public:

};
class Doctor :public Teacher, public Student {
public:

};

int main()
{
    Doctor d;
    d.People_test();
    return 0;
}

実行結果は以下のとおりです。

現時点では、仮想継承が必要です。C++は、仮想基本クラスと仮想継承メカニズムを提供して、多重継承で保持される共通メンバーが1つだけであることを実現します。これにより、ダイヤモンド多重継承によって引き起こされるメンバーの冗長性の問題が完全に解決されます。
仮想継承では、中間レベルの親クラスは最上位の親クラスの初期化に注意を払わなくなり、最後の子クラスは最上位の親クラスのコンストラクターを直接呼び出す必要があります。
仮想継承の構文は次のとおりです。
class 派生类名:virtual 继承方式 基类名

现在只需要在上面Teacher以及Student继承People的前面加上virtual关键字声明为虚继承,则可以解决上述二义性问题。

これまで、各クラスのデフォルトコンストラクターを呼び出してきましたが、この時点でパラメーターを使用してそのパラメーターを呼び出したい場合、どうなるか、コードは次のとおりです。

#include <iostream>  

using namespace std;

class People {
public:
    void People_test() {
        cout << "People_test" << endl;
    }
    People(int a) {
        cout << "people have one param " << a << endl;
    }
    People() {
        cout << "people default" << endl;
    }
};
class Teacher :virtual public People {
public:
    Teacher(int a, int b) : People(a) {
        
    }
};
class Student :virtual public People {
public:
    Student(int a, int b) : People(a) {
        
    }
};
class Doctor :public Teacher, public Student {
public:
    Doctor(int a, int b) : Teacher(a, b), Student(a, b) {
     
    }
};

int main()
{
    Doctor d(1,2);
    
    return 0;
}

この時の手術の結果は?このときの操作の結果は次のとおりです。

Peopleのパラメーター化されたコンストラクターがTeacherクラスとStudentクラスの初期化リストで呼び出されるのはなぜですか?なぜ呼び出されないのですか?C ++言語では、基本クラスが仮想の場合、禁止されている情報が自動的に渡されると規定されているためです。中間クラス。基本クラスに渡されるため、上記のTeacherおよびStudentクラスのコンストラクターは、Peopleのパラメーター化されたコンストラクターを呼び出さず、デフォルトのコンストラクターを1回だけ呼び出しますが、デフォルトのコンストラクターを使用して構築したくない場合仮想基本クラスオブジェクトの場合、サブクラスで次のような初期化リストを使用できます。

Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
     
    }

この時点での実行結果は次のとおりです。

結論:クラスに仮想基本クラスがある場合、仮想基本クラスのデフォルトコンストラクターのみを使用する必要がない限り、仮想基本クラスのコンストラクターを明示的に呼び出す必要があります。

 

3、仮想基本クラスと優位性

非仮想基本クラスを使用する場合、サブクラスが異なる親クラスから同じ名前のメンバーを継承する場合、呼び出し時にクラス名を使用して修飾しないと、あいまいさが生じることがわかっています。ただし、現時点で仮想基本クラスであり、呼び出しがクラス名で修飾されていない場合、特定のクラスのメンバーが他のクラスのメンバーよりも優先される場合があるため、あいまいさを引き起こさない可能性があります。使用する場合、あいまいさは発生しません。優先順位を判断する方法は?派生クラスの名前は、直接または間接の祖先クラスの同じ名前よりも優先されます。

class People {
public:
    void test() {
        cout << "People test" << endl;
    }
    People(int a) {
        cout << "people have one param " << a << endl;
    }
    People() {
        cout << "people default" << endl;
    }
};
class Teacher :virtual public People {
public:
    Teacher(int a, int b) : People(a) {
        
    }
    void test() {
        cout << "Teacher test" << endl;
    }
};
class Student :virtual public People {
public:
    Student(int a, int b) : People(a) {
        
    }
};
class Doctor :public Teacher, public Student {
public:
    Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
     
    }
};

int main()
{
    Doctor d(1,2);
    d.test();
    return 0;
}

操作の結果は次のとおりです。

この時点でStudentでテスト関数が定義されていると、TeacherとStudentのどちらも互いの基本クラスではないため、あいまいさが生じます。

さらに、このあいまいさはアクセスルールとは関係ありません。学生のテストが非公開であっても、あいまいさは発生します。

 

 

 

 

おすすめ

転載: blog.csdn.net/Chiang2018/article/details/113787190