C++ 内存详解(二)
来源:互联网 发布:sql怎么用别名查询 编辑:程序博客网 时间:2024/06/05 23:22
在本文中主要是介绍C++中类在实例化过程中在内存中的分配。
很多人都知道C++类是由结构体发展而来,所以他们的成员变量(C语言的结构体只有成员变量,C++的结构体和类基本相同,除了默认成员变量的属性,类成员变量默认私有,结构体成员变量默认公有,C++保留结构体主要是兼容C程序)的内存分配机制是一样的。下面我们以类来说明问题,如果类搞清楚了,结构体也就明白了。类分为成员变量和成员函数,我们先讨论成员变量。一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一成员变量地址。(在定义成员对象的同时,这些成员变量也就被定义了)我们一段代码说明问题:
类定义:
class K{ public: K(){k = 12;} ~K(){} int k;};类的使用:
K kTemp; printf("%d--%d\n",&kTemp,&kTemp.k); printf("%d--%d\n",sizeof(K),sizeof(kTemp.k)); int *i = (int*)(&kTemp); int w = *i; printf("%d\n",w);上面代码运行的结果为:
1310588--1310588
4--4
12
很明显,类的内存大小和其唯一的成员变量的内存大小是一致的。内存地址也是一致的。他们甚至是可以相互转换的。换成结构体结果也是一样的。这个时候问题来了:成员函数呢?上面的代码就好像没有成员函数一样,那是因为所有的成员函数都是存放在代码区的,不管全局函数,还是成员函数。要是成员函数占用类的对象空间,那将是多么可怕的一件事:定义类对象就有成员函数占用一段空间。我们再来补充一下静态成员函数的存放问题:静态成员函数与一般的成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员,就像我们前面提到的,所有函数都存放在代码区,静态函数也不例外。
C++是一种面向对象的编程语言,它向下保存了对C的兼容,同时也允许程序员能够自由的操控内存,虽然会带来一些问题,但是这不是本文讨论的问题。类是对某种对象的定义,包括变量和方法,也可以理解为现实生活中一类具有共同特征的事物的抽象,它是面向对象语言的基础。所以类是不占有内存的。但是如果类生成实例,那么将会在内存中分配一块内存来存储类对象。
类的实例化对象在内存中是如何分配内存的,请看下面的类:
class A {};
从形式上看,它似乎什么也没有,事实上它不知隐含了一个构造函数和一个析构函数,hi阿尤一些操作重载运算函数,比如”=“。如果A被实例化,例如:A a;在内存会占据多大的空间?有人可能说是4,也有说是0,但是正确的答案是:1,为什么是1?原因很多,比如说如我定义一个数组A[10];如果按上面说的是0,这样的局面会很尴尬,所以A这样一个空类,编译器给它一个字节来填充。
如果我们增加一个变量,(字节对齐默认为4):
class A { public: int i; }类A的实例将占据4个字节的内存,sizeof(A)=4,变量i的初值被编译器指定为0xcdcdcdcd。
如果再增加一个变量:
class A { public: int i; int l; }此时,按章变量生命的先后顺序,i被放在了低地址上,l紧跟其后。实例对象占用8个字节,sizeof(A)=4+4=8。
如果类里面包含函数:
class A { public: int i; int l; int add(int x,int y){return (x+y);} };这时,有人会可能会说类的大小为12,事实上sizeof(A)=8;
为什么会这样?这是因为sizeof访问程序的数据段,而函数地址责备保存在代码段内,所以最后结果是8。
再看下面这个情况:
class A { public: int i; int l; static int s; int add(int x,int y){return (x+y)}; };此时sizeof(A)大小仍是8,这是因为static变量在全局区(静态区),而非数据段
当然,类里面含有虚函数时,情况就另当别论了!例如:
class A { public: int i; int l; static int s; virtual void Say(){}; int add(int x,int y){return (x+y)}; };因为含有虚函数,所以类里面将含有一个虚指针vptr,指向该类的虚表vtbl,一个指针占用四字节的地址,所以sizeof(A)=12,虚指针放在类实例地址的最低位置,比如:A *a=new A;我们可以这样给变量i赋值:
int *p = (int *)a; p++; *p = 1;//把i的值赋为1.
这种情况虽然有些复杂,但并不是说不好理解,它有多少个父类每个父类的大小加起来再加上自身就是sizeof的大小。
总结C++类对象内存结构:
首先介绍一下C++中有继承关系的类对象内存的布局:
在C++中,如果类中有虚函数,那么它就有一个虚函数表的指针——vfptr,在类对象最开始的内存数据中,之后是类中的成员变量的内存数据。
对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量),之后是子类自己的成员变量数据。
对于子类的子类,同样的原理,但是无论继承多少个子类,对象中始终只有一个虚函数表指针。
- c/c++指针详解(二)----内存分配
- C++ 内存详解(二)
- Obj-C内存管理(二)
- C 内存管理详解 (转载)
- C/C++ 内存布局详解(经典)
- C语言内存分配 详解(附图)
- C/C++ 内存布局详解(经典)
- C语言内存对齐详解(1)
- C语言内存对齐详解(2)
- C语言内存对齐详解(3)
- C/C++内存管理详解(大神)
- C 内存管理详解
- C内存对齐详解
- C 内存管理详解
- C 内存管理详解
- C 内存管理详解
- C 内存管理详解
- c 内存分配详解
- 00-自测4. Have Fun with Numbers (20)
- 进程与端口映射
- std::nothrow
- 克鲁斯卡尔算法求解最小生成树
- Java定时任务
- C++ 内存详解(二)
- 【文件压缩】 Android Jar、Zip文件压缩和解压缩处理
- 删除重复代码--提升代码质量
- AspNet MVC4 教学-19:Asp.Net MVC4 利用Linq技术的搜索应用快速Demo
- Hibernate继承映射
- 回归预测及R语言实现 Part2 回归R语言实现
- 七牛的ufop应用场景介绍
- 工作经历0
- leetcode Symmetric Tree