【c++】——虚基类

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;
}

运行结果如下:
在这里插入图片描述
用虚基类的方式使问题得到解决。

发布了98 篇原创文章 · 获赞 9 · 访问量 3662

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105240064
今日推荐