【单继承】:一个子类只有一个直接的父类的继承关系
【多继承】:一个子类有两个或者两个以上的直接父类的继承关系
【菱形继承】:
class A
{
public:
void fun()
{
cout << "A::fun()" << endl;
}
int _a;//sizeof(A) = 4
};
class B :public A
{
public:
void fun1()
{
cout << "B::fun()" << endl;
}
int _b;//sizeof(B) =4(A成员_a)+4+(成员_b)= 8
};
class C:public A
{
public:
void fun2()
{
cout << "C::fun()" << endl;
}
int _c;//sizeof(C) =4(A成员_a)+4+(成员_c)= 8
};
class D :public B, public C
{
public:
void fun3()
{
cout << "D::fun()" << endl;
}
int _d;//sizeof(D)=8(szeof(B))+8(sizeof(C))+4(_d)=20
};
int main()
{
D d1;
d1.B::_a = 1;
d1.C::_a = 2;
d1._b = 3;
d1._c = 4;
d1._d = 5;
cout << sizeof(D) << endl;
system("pause");
return 0;
}
【菱形的二义性问题】
我们看到,子类D继承了父类B的成员 _a,同时也继承了父类C的成员_a;那么问题来了,当我们给D类中的_a赋值时到底是给那个_a赋值呢?
相当于A在类中有两个,这可能不是我们想要的结果,增加调用的困难,同时也会浪费内存资源。
当然,我们可以声明类域解决调用这个问题。
可以看到A指向的虚函数表的位置是不一样的!即有两个实例!
【虚函数表】
虚函数表就是通过一块连续的内存来存储虚函数是我地址。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。
那么如何解决菱形二义性问题呢??——使用虚拟继承
class A
{
public:
virtual void fun()//fun是虚函数
{
cout << "A::fun()" << endl;
}
public:
int _a;//sizeof(A) = 4(成员变量_a)+4(虚表指针) = 8
};
class B :virtual public A//B类虚继承A
{
public:
void fun1()
{
cout << "B::fun()" << endl;
}
public:
int _b;//sizeof(B) = 8(A副本)+4(虚表指针)+4(成员变量_b) = 16
};
class C:virtual public A//C虚继承A
{
public:
void fun2()
{
cout << "C::fun()" << endl;
}
public:
int _c;
};
class D :public B, public C
{
public:
void fun3()
{
cout << "::fun()" << endl;
}
public:
int _d;//sizeof(D)=8(B副本)+8(c副本)+4(虚表指针)+4(成员_d)+4(公共的A的虚表指针) = 28
};
int main()
{
D d1;
system("pause");
return 0;
}
我们打开调试,可以看到如下:
B类和C类中的A类是同一份,既在B类中改变A,那么C类中的A也改变,说明虚继承消除了菱形的二义性问题。
小结:
可以通过虚拟继承消除二义性,但是虚拟继承的开销是增加虚函数指针
我们发现一个问题;既然B类和C类虚继承A类。同时他自己也有虚函数,那么B类和C类自己的虚表指针_vfptr呢??
这是因为在vs编译器下,他把子类本身的虚表指针没有显示出来。但是他是真真实实存在的,我们在vs的监视窗口中看不到而已。