IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 钻石继承重复继承解决方案,以及啥是vfptr和bfptr -> 正文阅读

[C++知识库]钻石继承重复继承解决方案,以及啥是vfptr和bfptr

重复继承 novirtual+novirtual

在这里插入图片描述
B继承A,而C也继承A。
然后D继承B和C。这样就称为重复继承,因为看着像个菱形,所以也叫钻石继承
那么这样继承会出现什么问题呢?我们先从内存分配的角度看。
在VS使用 / d1 reportSingleClassLayoutXXX就能查看内存分配。
具体如下:
在这里插入图片描述

在这里插入图片描述
运行之后就能看到XXX类这里是VirClass类的分布情况。
在这里插入图片描述
好的,切回正题。
假设我们有个这样的代码
正如刚刚那个继承图一样:B继承A,而C也继承A。然后D继承B和C。

#include<iostream>
#include<vector>
using namespace std;
class A {
public:
	int x;
	int y;
	virtual void f1() { cout << "A:f1" << endl; };
	virtual void f2() { cout << "A:f2" << endl; };
	virtual void f3() { cout << "A:f3" << endl; };
};

class B :public A
{
public:

private:

};
class C :public A
{
public:

private:

};
class DIS :public B, public C {
public:
	virtual void f2() { cout << "B:f2" << endl; };
	virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
	return 0;
}

别问我为什么D取名为DIS,问就是 / d1 reportSingleClassLayoutXXX的机制打咩
在这里插入图片描述
此时的布局是这样的。哦,这样看是不是有点乱,没关系,我给你可视化一波。
并且可以看到class DIS size(24) 说明这个对象有24字节。那么我们来分析一下为什么有24字节
在这里插入图片描述
是的,B中会建立一个A同时C也建立了一个A。
由于A对象有虚函数,所以头部会有一个4字节的vfptr[virtual function ptr 虚函数指针]
其次是,int x,y. 8字节。
故一个A对象就有12字节。
由于B创建了一个A对象,C也创建了一个A对象,所以一共是24字节。
在这里插入图片描述

由于创建了两个A所以打咩了!!!!这直接就会造成,访问冲突。
举个栗子,

#include<iostream>
#include<vector>
using namespace std;
class A {
public:
	int x;
	int y;
	virtual void f1() { cout << "A:f1" << endl; };
	virtual void f2() { cout << "A:f2" << endl; };
	virtual void f3() { cout << "A:f3" << endl; };
};

class B :public A
{
public:
	void SetX(int x)
	{
		this->x=x;
	}
	int GetX1()
	{
		return x;
	}
private:

};
class C :public A
{
public:
	int GetX() 
	{ return x; }
private:

};
class DIS :public B, public C {
public:
	virtual void f2() { cout << "B:f2" << endl; };
	virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
	DIS* dis = new DIS();
	dis->SetX(10);
	cout<<dis->GetX()<<endl;
	cout << dis->GetX1()<<endl;
	return 0;
}


运行结果如下:
在这里插入图片描述
啊咧,明明都是访问x,但是为什么一个是0,一个是10?
正是因为这两个x其实是不同的x,一个是B的基类A的x,一个是C的基类A的x。(证明了一波刚刚的内存分配情况合理!)

好的,那么如何化解?

虚继承 virtual+virtual

将继承改成关系改成virtual

#include<iostream>
#include<vector>
using namespace std;
class A {
public:
	int x;
	int y;
	virtual void f1() { cout << "A:f1" << endl; };
	virtual void f2() { cout << "A:f2" << endl; };
	virtual void f3() { cout << "A:f3" << endl; };
};

class B :virtual public A
{
public:
	void SetX(int x)
	{
		this->x=x;
	}
	int GetX1()
	{
		return x;
	}
private:

};
class C :virtual  public A
{
public:
	int GetX() 
	{ return x; }
private:

};
class DIS : public B,public C {
public:
	virtual void f2() { cout << "B:f2" << endl; };
	virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
	DIS dis;
	dis.SetX(10);
	cout<<dis.GetX()<<endl;
	cout << dis.GetX1()<<endl;
	return 0;
}


运行结果:

在这里插入图片描述
这次x都是一样的了
再看内存分配,size(20)字节,舒服,比之前少了。
在这里插入图片描述
虚基类指针vbptr和虚函数指针vfptr

仔细看,你会发现B和C里面存放的不是A了,而是vbptr(virtual base ptr 虚继承基类指针)
这个指针指向的就是A。此时B和C共享一个A。
并且这bvptr只占4字节。然后A占12字节。
故4+4+12=20字节
在这里插入图片描述

离谱继承 novirtual+virtual

来看个最离谱的。如果C是非virtual而B是virtual会发生什么

#include<iostream>
#include<vector>
using namespace std;
class A {
public:
	int x;
	int y;
	virtual void f1() { cout << "A:f1" << endl; };
	virtual void f2() { cout << "A:f2" << endl; };
	virtual void f3() { cout << "A:f3" << endl; };
};

class B :virtual public A
{
public:
private:

};
class C :public A
{
public:
private:

};
class DIS : public B,public C {
public:
	virtual void f2() { cout << "B:f2" << endl; };
	virtual void f3() { cout << "B:f3" << endl; };
};
int main()
{
	return 0;
}


在这里插入图片描述雾草,size(28),这波是反向优化了。
好的,那么我们点根烟冷静分析这28是怎么来的。
首先如果是非virtual继承,那么会创造一个A。即B里面会创造一个A
然后如果是virtual继承,则会创建一个vbptr,然后再最外面创建一个A,使得vbptr指向A
好家伙,那就相当于有两个A,并且还多一个vbptr。打咩!
12+12+4=28
在这里插入图片描述
所以使用钻石继承的各位务必要小心使用哦。当然像C#,Java这种语言并不支持多重继承所以无需担心,而且类似接口也完全不是那么一回事。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:30:29  更:2022-03-21 20:33:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:36:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码