C++中的多重继承

多重继承:一个派生类有多个基类
虚基类
class A {
public:
protected:
	int ma;
};
class B :virtual public A {//虚继承,类A称为虚基类
public:
private:
	int mb;
};

通过命令提示输入cpp文件路径/cl cpp文件名.cpp/dlreportSingleClassB 来查看当前类B内存状况

在这里插入图片描述

虚基类数据保存在了派生类的最末尾,类的起始部分生成一个vbptr,指向vbtable。vbtable第一行是vbptr向类内存顶部的偏移量为0,第二行是类起始到虚基类数据的偏移量为8个字节

一个派生类对应一个虚基类表,编译时期生成,运行时期放到.rodata只读数据段

当需要访问派生类中虚基类的数据时,在原本普通基类数据位置找到vbptr,再通过偏移量找到虚基类数据

虚基类和虚函数同时存在的情况
class A {
public:
	virtual void func() {
		cout << "call A::func()" << endl;
	}
protected:
	int ma;
};
class B :virtual public A {//虚继承,类A称为虚基类
public:
	void func() {
		cout << "call B::func()" << endl;
	}
private:
	int mb;
};
int main() {
	A* pa = new B();
	pa->func();
	delete pa;//调用没问题,delete会出错
	return 0;
}

基类指针指向派生类对象,永远指向派生类中基类数据的起始地址

在基类有虚函数并且同时使用虚继承时,派生类内存中基类部分数据移到末尾,vfptr在基类数据的头部,也跟着移动了,再在派生类内存顶部生成vbptr

所以pa指向的是vfptr所在的内存(vfptr属于基类数据是因为虚函数在基类中,如果虚函数只属于派生类那么vfptr不会跟着移动)

因为new的内存是从最顶部开辟的,但是delete的地址变为了中间vfptr的地方,上面的内存不能释放

Linux不会出现该问题,会自动偏移到开辟的堆内存顶部

在这里插入图片描述

菱形继承:

​ A

B C

​ D

A可以称作D的间接的基类

#include<iostream>
using namespace std;
class A {
public:
	A(int data) :ma(data) {
		cout << "call A()" << endl;
	}
	~A() {
		cout << "call ~A()" << endl;
	}
protected:
	int ma;
 };

class B :public A {
public:
	B(int data) :A(data), mb(data) {
		cout << "call B()" << endl;
	}
	~B() {
		cout << "call ~B()" << endl;
	}
protected:
	int mb;
};
class C :public A {
public:
	C(int data) :A(data), mc(data) {
		cout << "call C()" << endl;
	}
	~C() {
		cout << "call ~C()" << endl;
	}
protected:
	int mc;
};
class D :public B, public C {
public:
	D(int data) :B(data), C(data), md(data) {
		cout << "call D()" << endl;
	}
	~D() {
		cout << "call ~D()" << endl;
	}
protected:
	int md;
};
int main() {
	D d(11);
	return 0;
}
/*
call A()
call B()
call A()
call C()
call D()
call ~D()
call ~C()
call ~A()
call ~B()
call ~A()
*/

此时会构造、析构两次相同的A数据,需要杜绝这种情况

class B :virtual public A {
public:
	B(int data) :A(data), mb(data) {
		cout << "call B()" << endl;
	}
	~B() {
		cout << "call ~B()" << endl;
	}
protected:
	int mb;
};
class C :virtual public A {
public:
	C(int data) :A(data), mc(data) {
		cout << "call C()" << endl;
	}
	~C() {
		cout << "call ~C()" << endl;
	}
protected:
	int mc;
};

将所有直接继承A的继承写为虚继承

class D :public B, public C {
public:
	D(int data) :B(data), C(data), md(data) {
		cout << "call D()" << endl;
	}
	~D() {
		cout << "call ~D()" << endl;
	}
protected:
	int md;
};
int main() {
	D d(11);
	return 0;
}

再运行会报错:A没有合适的默认构造函数可用,因为类A的数据移到了D内存的末尾,原先B、C的作用域中只留下了vbtable,A的数据属于了D的作用域,并且只会有一份(所以需要把所有直接继承A的操作写为虚继承,不然还是会有多份A数据)

所以需要D中亲自构造A

class D :public B, public C {
public:
	D(int data) :A(data), B(data), C(data), md(data) {
		cout << "call D()" << endl;
	}
	~D() {
		cout << "call ~D()" << endl;
	}
protected:
	int md;
};
/*
call A()
call B()
call C()
call D()
call ~D()
call ~C()
call ~B()
call ~A()
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值