C++ のダイヤモンド継承における継承の曖昧さの問題

C++ のダイヤモンド継承における継承の曖昧さの問題

class A
{
public:
	virtual void func1()
	{
		cout << "A::func1()" << endl;
	}

	int _a;
};

class B:virtual public A 
{
public:
	virtual void func1()
	{
		cout << "B::func1()" << endl;
	}

	int _b;
};

class C:virtual public A
{
public:
	virtual void func1()
	{
		cout << "C::func1()" << endl;
	}

	int _c;
};

class D:public B,public C
{
public:
	int _d;
};

int main(void)
{
	D d;
	return 0;
}

このコードはエラーを直接報告します。

なぜ?

B と C は両方とも A からの仮想継承であるため、主にデータの冗長性を解決するために使用されますが、これにより、D が作成されるときに A のインスタンスは 1 つだけになります。また、D は多重継承であり、B と C を継承するため、D ではそこには2 つの仮想関数テーブルがありますが、B と C の両方が A の func1 を書き換えているため、D が func1() を呼び出してどちらを呼び出すべきか分からない場合、最終的にあいまいさが生じます。

仮想継承により、サブクラスが基本クラスのインスタンスを共有できるようになり、サブクラス内に基本クラスのコピーが複数存在することが回避されます。

A の 3 つのクラス ABC、B、C の仮想継承がある場合、B と C がそれぞれ A の test() を書き換えた場合、このテストは書き換え後に共有されなくなりますが、他のテストは引き続き共有されます。

クラス B とクラス C がクラス A の仮想関数 test() をそれぞれ書き換えると、この関数は共有されなくなりますが、クラス B とクラス C の仮想関数テーブルのそれぞれの実装に存在します。このとき、クラスDでこの関数を呼び出す場合、必要に応じてクラスBとクラスCの実装をそれぞれ呼び出す必要がありますが、クラスAの実装を直接呼び出すことはできません。

仮想継承により、クラス B とクラス C がクラス A の他の仮想関数を含め、クラス A の同じインスタンスを共有するようになるため、他の仮想関数は引き続き共有されます。仮想継承は、派生クラス内の基本クラスのレイアウトと格納にのみ影響し、関数の書き換え動作には影響しません。関数の書き換え動作は、関数名とパラメーター リストの一致に基づいており、仮想継承とは関係ありません。したがって、クラス B とクラス C がクラス A 内の他の仮想関数をオーバーライドしない場合でも、それらの関数は共有されます。

class A
{
public:
	virtual void func1()
	{
		cout << "A::func1()" << endl;
	}

	int _a;
};

class B:virtual public A 
{
public:
	virtual void func1()
	{
		cout << "B::func1()" << endl;
	}

	int _b;
};

class C:virtual public A
{
public:
	virtual void func1()
	{
		cout << "C::func1()" << endl;
	}

	int _c;
};

class D:public B,public C
{
public:
	virtual void func1()
	{
		cout << "D::func1()" << endl;
	}
	
	int _d;
};

正しいコードは次のようになりますが、ダイヤモンド継承の使用は一般的に推奨されておらず、特に面倒なので、できる限り使用しないでください。

おすすめ

転載: blog.csdn.net/AkieMo/article/details/131742525