C++基础学习之2 - 内存对象模型
来源:互联网 发布:ubuntu 16.04分区方案 编辑:程序博客网 时间:2024/05/02 22:24
内存对象模型 是同学们在面试过程中经常会被问到的问题,一堆的 Sizeof 求答案,怎么破?听作者讲完本节,也许你会有个简单的认识,先说对于C++,其内存是如何存放的:
先说说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为1class 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++基础学习之2 - 内存对象模型
- C++学习之C++对象内存模型(上)
- C++学习之C++对象内存模型(下)
- 【C++】C++基础学习之面向对象
- 【C++】C++对象内存模型简介
- 漫谈C++:对象内存模型分析
- C++-对象继承内存模型配图
- NDK开发学习之C学习基础篇-2(函数指针,字符串,动态分配内存)
- C语言提高之技术模型层次、学习标准、特点、内存四区、函数调用模型
- C++对象内存模型2
- Java面向对象基础__方法、对象的内存模型
- C++ - 对象模型之 内存布局
- vlc之vlc_object_t对象的内存模型
- JVM基础 之图解JVM内存模型
- java基础之java内存模型
- objective-c基础语法学习之--(7)拷贝对象
- java学习——java基础(十一)之JVM内存模型
- C学习之------内存相关操作(2)
- Paint.setXfermode终极解析
- React Native的Navigator详解
- 包装类之装箱、拆箱
- 线性表-顺序存储结构(C语言实现)
- MySQL创建用户与授权方法
- C++基础学习之2 - 内存对象模型
- opencv学习(十九)之均值滤波blur
- Linux内核驱动(四)——内核制作
- curl 常用命令总结
- JZOJ4937. 与运算
- 1924. Four Imps-博弈论
- 【Redis学习】:Windows环境下的Redis安装与配置
- Android---事件分发机制原理讲述
- ActiveMQ 发布订阅(Topic)