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++知识库 -> 继承、虚继承、虚函数内存分布(MSVC下) -> 正文阅读

[C++知识库]继承、虚继承、虚函数内存分布(MSVC下)

前提知识:

对象的内存中只包含成员变量,存储在栈区或堆区(使用 new 创建对象);

成员函数与对象内存分离,存储在代码区

对象的大小,可以自己分析,int 四个字节,指针也是四个字节。(在x86中)可用sizeof运算符查看对象的大小。

1、普通继承?

代码示例

class A {
public:
	int ma;
	void func(){
		cout << "A :: func" << endl;
	}
};

class B : public A {
public:
	int mb;
	void func() {
		cout << "B :: func" << endl;
	}
};

内存分布

class A size(4):
        +---
 0      | ma
        +---

class B size(8):
        +---
 0      | +--- (base class A)
 0      | | ma
        | +---
 4      | mb
        +---

?2、普通多继承

代码示例

????????派生类都只有一个基类,称为单继承。C++ 也支持多继承,即一个派生类可以有两个或多个基类。

class A {
public:
	int ma;
	void func(){
		cout << "A :: func" << endl;
	}
};

class B : public A {
public:
	int mb;
	void func() {
		cout << "B :: func" << endl;
	}
};

class C : public A {
public:
	int mc;
	void func() {
		cout << "C :: func" << endl;
	}
};

class D : public B, public C {
public:
	int md;
	void func() {
		cout << "D :: func" << endl;
	}
};

?内存分布

????????从多继承的内存分布中,我们可以看到存在一些缺点:在一个派生类中保留间接基类的多份同名成员;菱形继承中,会有命名冲突的出现

????????类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量继承到类 D 中变成了两份,一份来自 A-->B-->D 这条路径,另一份来自 A-->C-->D 这条路径。此时,在D中对A中的变量赋值,就会有命名冲突。并且在使用基类A的公开成员函数时,也会指向不明确报错。

class A size(4):
        +---
 0      | ma
        +---

class B size(8):
        +---
 0      | +--- (base class A)
 0      | | ma
        | +---
 4      | mb
        +---

class C size(8):
        +---
 0      | +--- (base class A)
 0      | | ma
        | +---
 4      | mc
        +---

class D size(20):
        +---
 0      | +--- (base class B)
 0      | | +--- (base class A)
 0      | | | ma
        | | +---
 4      | | mb
        | +---
 8      | +--- (base class C)
 8      | | +--- (base class A)
 8      | | | ma
        | | +---
12      | | mc
        | +---
16      | md
        +---

?3、虚继承

????????为了解决多继承时的命名冲突和冗余数据问题,C++?提出了虚继承,使得在派生类中只保留一份间接基类的成员。

????????在继承方式前面加上?virtual?关键字就是虚继承;

????????虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类,本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

?代码示例

class A {
public:
	int ma;
	void func(){
		cout << "A :: func" << endl;
	}
};

class B :virtual public A {
public:
	int mb;
	void func() {
		cout << "B :: func" << endl;
	}
};

class C : virtual public A {
public:
	int mc;
	void func() {
		cout << "C :: func" << endl;
	}
};

class D : public B, public C {
public:
	int md;
	void func() {
		cout << "D :: func" << endl;
	}
};

?内存分布

在MSVC编译器中,引入虚基类表,如果某个派生类有一个或者多个虚基类,编译器会在派生类对象中安插一个指针(vbptr)指向虚基类表(vbtable)。虚基类表放的是偏移量。

class A size(4):
        +---
 0      | ma
        +---



class B size(12):
        +---
 0      | {vbptr}
 4      | mb
        +---
        +--- (virtual base A)
 8      | ma
        +---

B::$vbtable@:
 0      | 0
 1      | 8 (Bd(B+0)A)



class C size(12):
        +---
 0      | {vbptr}
 4      | mc
        +---
        +--- (virtual base A)
 8      | ma
        +---

C::$vbtable@:
 0      | 0
 1      | 8 (Cd(C+0)A)



class D size(24):
        +---
 0      | +--- (base class B)
 0      | | {vbptr}
 4      | | mb
        | +---
 8      | +--- (base class C)
 8      | | {vbptr}
12      | | mc
        | +---
16      | md
        +---
        +--- (virtual base A)
20      | ma
        +---

D::$vbtable@B@:
 0      | 0
 1      | 20 (Dd(B+0)A)

D::$vbtable@C@:
 0      | 0
 1      | 12 (Dd(C+0)A)

?4、虚函数

????????如果一个类包含了虚函数,那么在创建该类的对象时就会额外地增加一个数组,数组中的每一个元素都是虚函数的入口地址。每定义的任何一个对象,都会有vfptr,vfptr指向的是同一块vftable。

? ? ? ? 子类继承了父类之后,同名函数(包括返回值,函数名,参数列表)也会自动是virtual类型。子类的vftable中的函数,只有 virtual类型,不是virtual类型的,不会在vftable中。

代码示例

class A {
public:
	int ma;
	virtual void func() {
		cout << "A" << endl;
	}
};
class B : public A {
public:
	int mb;
	void func() {
		cout << "B" << endl;
	}
	void func2() {
		cout << "B" << endl;
	}
	virtual void func3() {

	}
};

内存分布

class A size(8):
        +---
 0      | {vfptr}
 4      | ma
        +---

A::$vftable@:
        | &A_meta
        |  0
 0      | &A::func


class B size(12):
        +---
 0      | +--- (base class A)
 0      | | {vfptr}
 4      | | ma
        | +---
 8      | mb
        +---

B::$vftable@:
        | &B_meta
        |  0
 0      | &B::func
 1      | &B::func3

5、虚继承(多)、虚函数

代码示例

class A {
public:
	int ma;
	virtual void func(){
		cout << "A :: func" << endl;
	}
};

class B :virtual public A {
public:
	int mb;
	void func() {
		cout << "B :: func" << endl;
	}
};

class C : virtual public A {
public:
	int mc;
	void func() {
		cout << "C :: func" << endl;
	}
};

class D : public B, public C {
public:
	int md;
	void func() {
		cout << "D :: func" << endl;
	}
};

?内存分布

class A size(8):
        +---
 0      | {vfptr}
 4      | ma
        +---

A::$vftable@:
        | &A_meta
        |  0
 0      | &A::func


class B size(16):
        +---
 0      | {vbptr}
 4      | mb
        +---
        +--- (virtual base A)
 8      | {vfptr}
12      | ma
        +---

B::$vbtable@:
 0      | 0
 1      | 8 (Bd(B+0)A)

B::$vftable@:
        | -8
 0      | &B::func

class C size(16):
        +---
 0      | {vbptr}
 4      | mc
        +---
        +--- (virtual base A)
 8      | {vfptr}
12      | ma
        +---

C::$vbtable@:
 0      | 0
 1      | 8 (Cd(C+0)A)

C::$vftable@:
        | -8
 0      | &C::func


class D size(28):
        +---
 0      | +--- (base class B)
 0      | | {vbptr}
 4      | | mb
        | +---
 8      | +--- (base class C)
 8      | | {vbptr}
12      | | mc
        | +---
16      | md
        +---
        +--- (virtual base A)
20      | {vfptr}
24      | ma
        +---

D::$vbtable@B@:
 0      | 0
 1      | 20 (Dd(B+0)A)

D::$vbtable@C@:
 0      | 0
 1      | 12 (Dd(C+0)A)

D::$vftable@:
        | -20
 0      | &D::func

?6、多态的指向

在多态中,基类指针指向派生类对象时候,永远指向的是派生类基类部分数据的起始地址

父类并不能访问子类中在父类未出现的函数和变量。只能访问虚函数表中的函数。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:08:08  更:2022-04-01 23:08:34 
 
开发: 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 3:07:19-

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