C++基础学习之2 - 内存对象模型
2017-01-12 22:13
246 查看
内存对象模型 是同学们在面试过程中经常会被问到的问题,一堆的 Sizeof 求答案,怎么破?听作者讲完本节,也许你会有个简单的认识,先说对于C++,其内存是如何存放的:
先说说C++的存储区,有4种类型,堆、栈、全局存储区、常量存储区。
堆: 通过new来初始化,delete释放,一般是由程序员来创建和管理;
栈: 栈 是指临时或局部变量的存放位置,一般由操作系统分配和释放,比如我们常说的压栈、出栈;
全局存储区: 全局存储区 是指 程序的 全局变量、静态变量 的存放位置,独立于类对象,程序结束时释放;
常量存储区: 和全局存储区类似,用于常量数据的存放;
那么上面代码中的 sizeof 是多大呢?我们运行的结果是 8,也就是sizeof(int)+sizeof(char*),为什么呢?根据我们前面的描述我们知道,静态变量和函数放在全局区里面,不占用对象存储空间,占用存储空间的只有前两个。
我们再来看下面几种情况:
我们来总结一下:
1. 空类的 Sizeof等于1;
2. 带有虚函数的 Sizeof 需要额外计算虚函数表的指针;
3. 继承模式下 父子变量相加,虚函数表也计算在内。
说到这里,基本已经说清楚了,接下来我们来看两个专题,可重入函数 和 菱形继承问题。
** 可重入函数:
当出现多个任务调用同一个函数的时候,如果能保证
每次调用得到一样的结果,我们认为该函数是可重入的,反之,函数是不可重入的,不可重入函数一般也称为不安全函数。
那么导致函数不可重入的因素有哪些呢?
1. 函数内部使用了静态或者全局变量,而这些变量的值有可能每次并不一致;
2. 使用堆导致分配位置不同,或者IO带来的不一致输入;
3. 调用了其他不可重入的函数;
事实上,我们对可重入函数的定位,通常仅仅用于计算,我们给定了input,里面所有用的临时变量都在栈内分配。
可重入函数 与 线程安全 没有必然的联系,可重入只是针对单线程反复调用来讲,线程安全的保证方式在于加锁,即:
1. 可重入的函数一定是线程安全的;
2. 线程安全的函数也不一定是可重入的;
** 菱形继承问题:
菱形继承问题也叫钻石继承问题,描述为 两个类继承自同一父类,同时又有子类同时继承这两个类,图示如下:
通过一段代码来看:
先来看一个Key Problem,编译不过,Base的成员变量在 Derived 里面有几份Copy? 没错,是两份,每个父类保存了一份,这种二义性显然是不允许的。
解决方案1:尽量避免使用双继承,确实这并不是一个清晰的集成体系;
解决方案2:感谢C++给出了一个解决方案,那就是
虚继承,专门针对这个问题给出的(姑且算个补丁吧,出来这摊就废了),我们把上面的类改造一下:
好简单,就这样,Derived 类就只保留一个 a 对象,解决了二义性。
最后再来看,虚继承 的sizeof,与虚函数类似,虚继承添加了一个 虚基类表 指针(4字节)。
很明显 Sizeof(Parent1) = Sizeof(a) + Sizeof(b) + 虚基类指针 = 12
Sizeof(Derived) = Sizeof(a) + Sizeof(b) + Sizeof(c) + Sizeof(d) + 虚基类指针 * 2 = 24
详细的内容描述也可以参考(这篇作者认为写的还是不错):
http://www.cnblogs.com/QG-whz/p/4909359.html
先说说C++的存储区,有4种类型,堆、栈、全局存储区、常量存储区。
堆: 通过new来初始化,delete释放,一般是由程序员来创建和管理;
栈: 栈 是指临时或局部变量的存放位置,一般由操作系统分配和释放,比如我们常说的压栈、出栈;
全局存储区: 全局存储区 是指 程序的 全局变量、静态变量 的存放位置,独立于类对象,程序结束时释放;
常量存储区: 和全局存储区类似,用于常量数据的存放;
class BaseClass; // 前向声明 void main() { int i = 16; // 栈 char* str = new char[16]; // 堆 BaseClass b; // 类对象分配到栈 } class BaseClass { public: int ID; char* m_strName; Static int m_nCountry; // 全局存储区 public: void setID(int _id) { ID=_id; } static void Show() {……} }; const int nCountryNum = 224; // 常量存储区从上面代码可以清晰的看到内存的分布情况,这里需要展开的是,当类对象分配到堆或者栈上的时候,其Sizeof的情况。
那么上面代码中的 sizeof 是多大呢?我们运行的结果是 8,也就是sizeof(int)+sizeof(char*),为什么呢?根据我们前面的描述我们知道,静态变量和函数放在全局区里面,不占用对象存储空间,占用存储空间的只有前两个。
我们再来看下面几种情况:
class BaseClass1 { }; cout << sizeof(BaseClass1); // 输出为1 - 空类的sizeof为1 class BaseClass2 { public: BaseClass2(); virtual ~BaseClass2(); // 虚函数,访问虚函数表 protected: virtual void init(); }; cout << sizeof(BaseClass2); // 输出为4 - 增加了虚函数表地址,但与虚函数个数无关 class DeriveClass : public BaseClass2 { public: int m_nType; }; cout << sizeof(DeriveClass); // 输出为8 - 父子变量相加
我们来总结一下:
1. 空类的 Sizeof等于1;
2. 带有虚函数的 Sizeof 需要额外计算虚函数表的指针;
3. 继承模式下 父子变量相加,虚函数表也计算在内。
说到这里,基本已经说清楚了,接下来我们来看两个专题,可重入函数 和 菱形继承问题。
** 可重入函数:
当出现多个任务调用同一个函数的时候,如果能保证
每次调用得到一样的结果,我们认为该函数是可重入的,反之,函数是不可重入的,不可重入函数一般也称为不安全函数。
那么导致函数不可重入的因素有哪些呢?
1. 函数内部使用了静态或者全局变量,而这些变量的值有可能每次并不一致;
2. 使用堆导致分配位置不同,或者IO带来的不一致输入;
3. 调用了其他不可重入的函数;
事实上,我们对可重入函数的定位,通常仅仅用于计算,我们给定了input,里面所有用的临时变量都在栈内分配。
可重入函数 与 线程安全 没有必然的联系,可重入只是针对单线程反复调用来讲,线程安全的保证方式在于加锁,即:
1. 可重入的函数一定是线程安全的;
2. 线程安全的函数也不一定是可重入的;
** 菱形继承问题:
菱形继承问题也叫钻石继承问题,描述为 两个类继承自同一父类,同时又有子类同时继承这两个类,图示如下:
通过一段代码来看:
class Base { int a; }; class Parent1 : public Base { int b; }; class Parent2 : public Base { int c; }; class Derived : public Parent1, public Parent2 { int d; };
先来看一个Key Problem,编译不过,Base的成员变量在 Derived 里面有几份Copy? 没错,是两份,每个父类保存了一份,这种二义性显然是不允许的。
解决方案1:尽量避免使用双继承,确实这并不是一个清晰的集成体系;
解决方案2:感谢C++给出了一个解决方案,那就是
虚继承,专门针对这个问题给出的(姑且算个补丁吧,出来这摊就废了),我们把上面的类改造一下:
class Base { int a; }; class Parent1 : virtual public Base { int b; }; class Parent2 : virtual public Base { int c; }; class Derived : public Parent1, public Parent2 { int d; };
好简单,就这样,Derived 类就只保留一个 a 对象,解决了二义性。
最后再来看,虚继承 的sizeof,与虚函数类似,虚继承添加了一个 虚基类表 指针(4字节)。
很明显 Sizeof(Parent1) = Sizeof(a) + Sizeof(b) + 虚基类指针 = 12
Sizeof(Derived) = Sizeof(a) + Sizeof(b) + Sizeof(c) + Sizeof(d) + 虚基类指针 * 2 = 24
详细的内容描述也可以参考(这篇作者认为写的还是不错):
http://www.cnblogs.com/QG-whz/p/4909359.html
相关文章推荐
- C++基础学习笔记----第十一课(类的静态成员、对象模型初步认识)
- C++学习之C++对象内存模型(上)
- C++学习之C++对象内存模型(下)
- C++学习笔记4:面向对象的基础之类与对象
- C++学习笔记5:从方法到属性(面向对象基础)
- Data语意学之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)
- 虚继承内存布局@c++对象模型
- c++基础学习6-c++面向对象基本概念
- C++内存对象模型
- c++对象内存模型之虚析构函数篇(3)
- 深度探索C++对象内存模型
- c++ 继承 33 虚继承对c++ 对象内存模型造成的影响
- C++对象模型学习笔记
- [学习笔记]Java面向对象思想和内存模型
- C++学习笔记(第9章->内存模型和名称空间)
- 三十二、C++内存布局,对象大小计算、虚函数虚继承对类内存模型的影响
- 深度探索c++对象模型学习笔记
- 学习《深入理解C++对象模型》小结
- [C++对象模型][6]sizeof与对象内存布局
- [原创]C++虚继承内存对象模型探讨