1、虚基类的概念
之前,我们了解了抽象类是一种有纯虚函数的类。而虚基类又是什么呢?
首先,我们可以明确的是vietual可修饰于两个地方。一是修饰成员方法是虚函数,二是修饰继承方式是虚继承。被虚继承的类叫做虚基类
所以,我们可以给出虚基类的定义:被虚继承的类称为虚基类 vbptr和vbtable
例如如下代码,A类就是一个虚基类
class A
{
public:
private:
int ma;
};
class B:virtual public A
{
public:
private:
int mb;
};
在这里面,我们画一下,类的内存结构图如下。
根据之前绘画虚函数的内存结构认知,我们首先画出没有虚基类时的内存结构,再做调整,得出有基类时的内存图。
那其中的vbptr又指向的是什么呢?如下图所示:
2、虚基类和虚函数共存的情况
上述代码只是一个简单的虚基类,那如果我们在虚基类里面加一个虚函数,内存结构又会有什么变化呢?
代码如下:
class A
{
public:
virtual void func() { cout << "call A::func" << endl; }
private:
int ma;
};
class B :virtual public A
{
public:
virtual void func() { cout << "call B::func" << endl; }
private:
int mb;
};
int main()
{
A* p = new B();
p->func();
delete p;
return 0;
}
运行结果如下图:
为什么会出现上述错误呢?我们来分析一下
首先,我们还是画出对应的内存结构图如下:
接下来思考指针p指的是哪儿呢?
基类指针指向派生类对象,永远指向的是派生类基类部分数据的起始位置如下图所示:
错误原因,就在于内存回收的时候出错。如下图:
在开辟内存的时候是从013855D0也就是vbptr处开辟,但是回收内存的时候却是在虚基类里面开始回收,存在了内存泄露的问题。
改进方法:在主函数里面开辟一个栈上的对象就可,不用析构,出了对象会自己回收内存,如下代码:
int main()
{
B b;
A* p = &b;
cout << "main p:" << p << endl;
p->func();
return 0;
}
运行结果如下:
3、虚基类解决的菱形继承问题
关于菱形继承,还有间接继承等有问题的继承方式如下图所示:
这就存在设计的问题,派生类有多份间接基类的数据。例如图中的ma。对应一个具体的类,如果ma为一个name属性,那么到了D对象就会存在两个name产生异议。
对于上图的菱形继承,代码实现如下:
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl;}
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
class B :public A
{
public:
B(int data) :A(data),mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
class D :public B,public C
{
public:
}
~D() { cout << "~D()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
运行结果如下:
由此,我们可以画出D的内存布局如下:
我们可以明显看见多份间接基类的问题。因此,我们可以用虚基类的方式来解决
这样就可以通过虚指针表示的偏移量来访问ma的数据。
在这里,我们一定要注意,在D类里面就一定要对类A进行一个初始化,不然的话就出现找不到默认的构造函数A的错误。代码实现如下:
class A
{
public:
A(int data) :ma(data) { cout << "A()" << endl;}
~A() { cout << "~A()" << endl; }
protected:
int ma;
};
class B :virtual public A
{
public:
B(int data) :A(data),mb(data) { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
protected:
int mb;
};
class C : virtual public A
{
public:
C(int data) :A(data), mc(data) { cout << "C()" << endl; }
~C() { cout << "~C()" << endl; }
protected:
int mc;
};
class D :public B,public C
{
public:
D(int data) :A(data),B(data), C(data),md(data) { cout << "B()" << endl; }
D(int data) : B(data), C(data), md(data) { cout << "B()" << endl; }
protected:
int md;
};
int main()
{
D d(10);
return 0;
}
运行结果如下:
用虚基类的方式使问题得到解决。