欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++的多重继承与虚基类

程序员文章站 2022-03-23 11:51:31
多重继承(MI)多重继承是指使用多个基类的继承,可以分为私有MI,保护MI,公有MI。这里要特别强调一下公有MI,因为它将导致一些问题。...

多重继承(MI)

多重继承是指使用多个基类的继承,可以分为私有MI,保护MI,公有MI。这里要特别强调一下公有MI,因为它将导致一些问题。
假设有一个基类A

class A
{
public:
	A(){a=0;}
	A(char a){this->a=a;}
	virtual ~A(){}
	virtual void Show()const{cout<<a<<endl;}
private:
	char a;
};

然后class A派生出两个类

class B:public A
{
public:
	B():A(){b=0;}
	B(char a, char b):A(a){this->b=b;}
	~B(){}
	void Show()const
	{
		A::Show();
		cout<<b<<endl;
	}
private:
	char b;
};
class C:public A
{
public:
	C():A(){c=0;}
	C(char a, char c):A(a){this->c=c;}
	~C(){}
	void Show()const
	{
		A::Show();
		cout<<c<<endl;
	}
private:
	char c;
};
B b1;
C c1;
B b2('a', 'b');
C c2('a', 'c');

A* p[4]={&b1, &c1, &b2, &c2};
for(int i=0;i<4;i++)
	p[i]->Show();

这样的设计看起来没问题,使用动态联编分别调用B::Show()和C::Show()。
接下来继续派生,让class B和class C再派生出一个类
class D:public B,public C{…};
这样会导致两个问题:

问题一
因为派生类对象的地址可以赋给基类指针,但在这里会出现二义性:
D d;
A* p=&d;
因为d继承自B,又继承自C,而B和C中都有A对象,到底要把哪个A对象赋给p?
可以将类型转换一下
A* p1=(B*)&d;
A* p2=(C*)&d;
但这不是重点,重点是d为什么要包含两个A对象?d应该和B、C对象一样,只包含一个A对象。

虚基类就可以实现这样的操作:使继承自同一个基类的多个类派生出的对象只继承一个基类对象(有点拗口,多读几遍)
在声明中可以这样写:
class B : virtual public A{…};//将A声明为虚基类
class C : virtual public A{…};//virtual和public的顺序不重要

然后使用新的构造函数规则
使用虚基类时,以前的构造函数传递值的方式将不起作用
例:
D(char a, char b, char c,) : B(a, b), C(a, c) { }
将a传递给A对象有两条途径(B和C),这将引起冲突,所以虚基类禁止通过中间类传值。参数a不会通过B和C传给A,但是构造派生对象之前又必须构造基类对象,所以编译器会调用A的默认构造函数,也可以显式调用。
新的构造函数应该是这样的:
D(char a, char b, char c) :A(a), B(a, b), C(a, c) { }

注:对于虚基类,这种用法是合法的,也必须这样做。对于非虚基类是非法的。

问题二
由于D没有新的数据成员,它继承了基类的所有方法,对于单继承来说并没有什么问题,例:
D d(‘a’, ‘b’, ‘c’);
d.Show();//D类没有重新定义Show(),所以会调用最近的基类中定义的Show()
但现在是多重继承,最近的基类有两个(B和C),这就导致了二义性。
可以使用作用域解析运算符来说明要使用哪个Show()
d.B::Show();
d.C::Show();
或者在D中重新定义Show(),并指出使用哪个Show()
void Show()const
{
B::Show();
}
一个小问题:如果要同时调用B类和C类的Show(),A::Show()会被调用两次。
怎么解决呢?
一种方法是,所有类都提供一个只显示自身数据成员的方法,然后在D类中将它们组合起来。
另一种方法是将所有数据都设为保护的。

总之,这种菱形结构的继承必须使用虚基类。

本文地址:https://blog.csdn.net/ajt1234/article/details/106891581