虚基类
1.概念
当在多条继承路径上有一个公共的基类< 菱形继承>,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。所以虚基类的作用是为了保证虚基类在派生类中只被继承一次。
如图:A为公共基类
类D中有两个int a,这不仅没有意义,还浪费空间,显然是不合理的,所以引入虚基类,来解决这个问题。
2.声明格式
class A
{
int a;
public:
A(int x = 0) :a(x) {}
};
class B : virtual public A
{
int b;
public:
B(int x = 1) :A(x + 10), b(x) {}
};
class C : virtual public A
{
int c;
public:
C(int x = 2) :A(x + 10), c(x) {}
};
class D : public B, public C
{
int d;
public:
D(int x = 3) :B(x + 10), C(x + 5), d(x) {}
};
这样类D中就只有一份int a,可以有效的避免二义性,空间浪费。
虚继承与继承的区别:
多了一个虚基指针。
虚基类位于派生类存储空间的最末尾。
不会共用虚函数指针。
3.菱形虚继承的对象的内存分布图
虚指针所指向的虚基表的内容:
- 虚基指针的第一条内容表示的是该虚基指针距离所在的子对象的首地址的偏移
- 虚基指针的第二条内容表示的是该虚基指针距离虚基类子对象的首地址的偏移
b的内存分布图
c的内存分布图
d的内存分配图
2.
4虚拟继承中派生类对象的构造过程:
在派生类对象的创建中,首先是虚基类的构造函数并按它们声明的顺序构造。第二批是非虚基类的构
造函数按它们声明的顺序调用。第三批是成员对象的构造函数。最后是派生类自己的构造函数被调用。
//测试代码
class A
{
int a;
public:
void display();
A(int x = 5) :a(x) { cout << "Creat A " << endl; }
~A() { cout << "Destroy A" << endl; }
};
class B : virtual public A
{
int b;
public:
B(int x = 1) :A(x) { cout << "Creat B " << endl; }
~B() { cout << "Destroy B" << endl; }
};
class C : virtual public A
{
int c;
public:
C(int x = 0) :A(x) {cout << "Creat C " << endl;}
~C() { cout << "Destroy C" << endl; }
};
class D : public B, public C
{
int d;
public:
D() :A(10), B(), C() { cout << "Creat D " << endl; }
~D() { cout << "Destroy D" << endl; }
};
int main()
{
D d;
return 0;
}
结果如图:
可以看到析构顺序和构建顺序相反,这是因为系统给函数分配的栈空间遵循后进先出规则。