C++的多重继承与虚基类
多重继承(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