这篇博文,讲一下类的多继承有可能造成的二义性及解决方案。
话不多说,直接上代码
#include <iostream>
using namespace std;
class A
{
public:
int a;
public:
A(){
cout << "A类构造函数" << endl;
}
~A(){
cout << "A类析构函数" << endl;
}
};
class B: public A
{
public:
int b;
public:
B(){
cout << "B类构造函数" << endl;
}
~B(){
cout << "B类析构函数" << endl;
}
};
class C : public A
{
public:
int c;
public:
C(){
cout << "C类构造函数" << endl;
}
~C(){
cout << "C类析构函数" << endl;
}
};
class D : public B,public C
{
public:
int d;
public:
D(){
cout << "D类构造函数" << endl;
}
~D(){
cout << "D类析构函数" << endl;
}
};
void display()
{
D d;
d.a = 100; //error,对a的访问不明确
}
int main(void)
{
display();
return 0;
}
上面的代码编译不通过,报错对a的访问不明确,为什么会造成这种问题呢,因为B类和C类都继承了A类,也就是B类和C类内部都有a成员变量,D类继承了B类和C类,这个时候访问继承下来的a成员变量时,编译器不知道我们要访问的是哪个类的a成员变量。这种问题多发生在派生类的多个基类拥有共同的基类,或者派生类的基类中有同名的成员变量。
解决方法是我们在访问有可能存在二义性的成员变量是加上域作用符,如下
d.B::a = 100; //指明访问B类的a成员变量
d.C::a = 200; //指明访问C类的a成员变量
针对上述代码修改之后可以编译通过,并且可以运行。运行结果如下:
仔细查看上述运行结果我们发现,我们在调用d.B::a = 100或者d.C::a = 200之后,A类的构造函数被调用了两次,多继承的构造函数和析构函数的调运规则,下一篇博文再详细分析,出现上述这种情况,可以通过C++提高的虚继承解决。就是在B类和C类继承A类的时候加上virtual参数,这样就可以解决A类构造函数被调用多次的问题,代码如下:
class B : virtual public A
{
public:
int b;
public:
B(){
cout << "B类构造函数" << endl;
}
~B(){
cout << "B类析构函数" << endl;
}
};
class C : virtual public A
{
public:
int c;
public:
C(){
cout << "C类构造函数" << endl;
}
~C(){
cout << "C类析构函数" << endl;
}
};
在添加了virtual参数之后,其实是在类里面添加了一些属性,可以标记让基类的构造函数调用一次。具体我们我可通过如下方法验证
让B类虚继承A类,让C类正常继承A类,然后在main函数中添加一句代码,输出B类和C类对象的大小:
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
编译运行,结果如下:
发现A类的大小是4个字节,但是B类虚继承A类之后大小变成了12个字节,C类正常继承A类之后大小为8个字节,也就是说虚继承之后在类对象的内部被添加了4个字节的信息。这样就可以做到上述的结果。虚继承和添加域作用符都可以解决多继承二义性的问题,但是应用场景不同。