C++ 类的实例中 内存分配详解
一个类,有成员变量:静态与非静态之分;而成员函数有三种:静态的、非静态的、虚的。
那么这些个东西在内存中到底是如何分配的呢?
以一个例子来说明:
#include"iostream.h"
class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
int m_count;
int m_index;
};
VoidCObject::Fun(){ cout<<"Fun\n"<<endl;}
CObject::CObject(){ cout<<"Construct!\n";}
CObject::~CObject(){ cout<<"Destruct!\n";}
int CObject::a=1;
void main(){
cout<<"Sizeof(CObject):"<<sizeof(CObject)<<endl; cout<<"CObject::a="<<CObject::a<<endl;
CObject myObject;cout<<"sizeof(myObject):"<<sizeof(myObject)<<endl;
cout<<"sizeof(int)"<<sizeof(int)<<endl;
}
这是我的一段测试代码, 运行结果是:
Sizeof(CObject):8
CObject::a=1
Construct!
sizeof(myObject):8
sizeof(int)4
Destruct!
我有疑问如下:
(1)C++中,应该是对象才会被分配内存空间吧??为什么CObject内存大小是8,刚好和两个成员变量的大小之和一致!难道还没实例化的时候,类就已经有了内存空间了?
(2)当对象生成了之后,算出的内存大小怎么还是8,函数难道不占用内存空间吗?至少应该放个函数指针在里面的吧?内存是怎样布局的?
(3)静态成员应该是属于类的,怎么类的大小中没有包含静态成员的大小?
下面分别解答如下:
1)Sizeof(CObject)是在编译时就计算了的,一个类定义了,它所占的内存编译器就已经知道了,这时只是得到它占用的大小,并没有分配内存操作 。也可以这样想:编译器肯定知道大小了,这与分配内存空间无关,知道大小了,以后实例化了才能知道要分配多大。
2)类的普通成员、静态成员函数是不占类内存的,至于你说的函数指针在你的类中有虚函数的时候存在一个虚函数表指针,也就是说如果你的类里有虚函数则sizeof(CObject)的值会增加4个字节。
其实类的成员函数实际上与普通的全局函数一样。
只不过编译器在编译的时候,会在成员函数上加一个参数,传入这个对象的指针。
成员函数地址是全局已知的,对象的内存空间里根本无须保存成员函数地址。
对成员函数(非虚函数)的调用在编译时就确定了。
像 myObject.Fun() 这样的调用会被编译成形如 _CObject_Fun( &myObject ) 的样子。
函数是不算到sizeof中的,因为函数是代码,被各个对象共用,跟数据处理方式不同。对象中不必有函数指针,因为对象没必要知道它的各个函数的地址(调用函数的是其他代码而不是该对象)。
类的属性是指类的数据成员,他们是实例化一个对象时就为数据成员分配内存了,而且每个对象的数据成员是对立的,而成员函数是共有的~
静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。总之,程序中的所有函数都是位于代码区的。
3)静态成员并不属于某个对象,sizeof取的是对象大小。
知道了上面的时候,就可以改一下来看看:
我也补充一些:
class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
double m_count; //这里改成了double
int m_index;
};
这个类用sizeof()测出来的大小是 2*sizeof(double)=16 class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
char m_count; //这里改成了char
int m_index;
};
大小是2*sizeof(int)=8
class CObject
{
public:
static int a;
CObject();
~CObject();
void Fun();
private:
double m_count; //这里改成了double
int m_index;
char c;
};
sizeof(char)+sizeof(int) <sizeof(double) 所以大小是2*sizeof(double)
其实这里还有一个是内存对齐的问题。
空类大小是1。
另外要注意的一些问题:
先看一个空的类占多少空间?
class Base
{
public:
Base();
~Base();
};
class Base {
public:
Base();
~Base();
};
注意到我这里显示声明了构造跟析构,但是sizeof(Base)的结果是1.
因为一个空类也要实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含 的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。
而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例,而普通成员函数,是针对类体的,一个类的成 员函数,多个实例也共用相同的函数指针,所以自然不能归为实例的大小,这在我的另一篇博文有提到。
接着看下面一段代码
class Base
{
public:
Base();
virtual ~Base(); //每个实例都有虚函数表
void set_num(int num) // 普通成员函数,为各实例公有,不归入sizeof统计
{
a=num;
}
private:
int a; //占4字节
char *p; //4字节指针
}; class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st; //非实例独占
int d; //占4字节
char *p; //4字节指针
};
int main()
{
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
return 0;
}
class Base {
public:
Base();
virtual ~Base(); //每个实例都有虚函数表
void set_num(int num) { a=num; } //普通成员函数,为各实例公有,不归入sizeof统计
private:
int a; //占4字节
char *p; //4字节指针
};
class Derive:public Base {
public:
Derive():Base(){};
~Derive(){};
private:
static int st; //非实例独占
int d; //占4字节
char *p; //4字节指针
};
int main() {
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
return 0;
}
结果自然是
12
20
Base类里的int a;char *p;占8个字节。
而虚析构函数virtual ~Base();的指针占4子字节。
其他成员函数不归入sizeof统计。
Derive类首先要具有Base类的部分,也就是占12字节。
int d;char *p;占8字节
static int st;不归入sizeof统计
所以一共是20字节。
在考虑在Derive里加一个成员char c;
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st;
int d;
char *p;
char c;
};
class Derive:public Base {
public:
Derive():Base(){};
~Derive(){};
private:
static int st;
int d;
char *p;
char c;
};
这个时候,结果就变成了
12
24
一个char c;增加了4字节,说明类的大小也遵守类似class字节对齐,补齐规则。
至此,我们可以归纳以下几个原则:
1.类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
2.普通成员函数与sizeof无关。
C++ 类的实例中 内存分配详解的更多相关文章
- C++中内存分配详解
转载自51CTO.com http://developer.51cto.com/art/201107/276154.htm 我们都知道,内存基本上分为静态存储区.堆区和栈区三大部分 ...
- java程序运行时内存分配详解
java程序运行时内存分配详解 这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个 ...
- Java学习之二维数组定义与内存分配详解
二维数组:就是元素为一维数组的一个数组. 格式1: 数据类型[][] 数组名 = new 数据类型[m][n]; m:表示这个二维数组有多少个一维数组. n:表示每一个一维数组的元素有多少个. 注意: ...
- [Java]Java类和对象内存分配详解
描述 代码说明: 一.当Person p1 = new Person();第一次被调用时需要做两件事: 1.先判断类加载器是否加载过Person类,如果没有则加载到Person类型到方法区 2.在堆中 ...
- JAVA中堆栈和内存分配详解(摘抄)
在Java中,有六个不同的地方可以存储数据: 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存 ...
- java程序运行时内存分配详解 (转)
转自:http://www.tuicool.com/articles/uU77v2 一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Ja ...
- java运行时内存分配详解
一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私 ...
- Java虚拟机内存分配详解
简介 了解Java虚拟机内存分布的好处 1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序.比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈 (stack),其栈是先进后出的数 ...
- 内存分配详解 malloc, new, HeapAlloc, VirtualAlloc,GlobalAlloc
很多地方都会使用内存,内存使用过程中操作不当就容易崩溃,无法运行程序,上网Google学习一下,了解整理下他们之间的区别以及使用 ,获益匪浅 0x01 各自的定义和理解 (1)先看GlobalAllo ...
随机推荐
- Android:手把手带你深入剖析 Retrofit 2.0 源码
前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将手把手带你深入剖析Retrofit v2.0的源码,希望你们会喜 ...
- 一次delete速度异常慢的处理过程
InnoDB delete from xxx速度暴慢原因 博客分类: database MySQLPythonMobile多线程SQL step1,一个简单的联系人表 CREATE TABLE `c ...
- C++内存管理学习堆和栈
来源:http://c.chinaitlab.com/basic/936306_2.html 一 C++内存管理 1.内存分配方式 在讲解内存分配之前,首先,要了解程序在内存中都有什么区域,然后再详细 ...
- iOS:UI简单的总结
UI简单总结: 一.常用单例: NSBundle *bundel = [NSBundle mainBundle]; //加载资源 NSFileManager *fm = [NSFileManager ...
- iOS: sqlite数据库的基本操作
介绍: sqlite3(3是版本)是本地系统中的一个小型数据库,因为它没有在数据维护和安全上做过多的操作,所以它存储处理数据时,非常简单方便,但是它是不安全和不可靠的,如果一旦误操作删除了数据,是没有 ...
- [转]nginx折腾记(HTTP性能能测试,与Apache对比)
话说nginx在大压力的环境中比apache的表现要好,于是下载了一个来折腾一下. 下载并编译安装,我的编译过程有点特别: 1.去除调试信息,修改$nginx_setup_path/auto/cc ...
- Powerdesigner数据库建模--概念模型--ER图
目标: 本文主要介绍PowerDesigner中概念数据模型 CDM的基本概念. 一.概念数据模型概述 数据模型是现实世界中数据特征的抽象.数据模型应该满足三个方面的要求: 1)能够比较真实地模拟现实 ...
- 使用OctreeQuantizer提高gdi+绘图质量
.net中gdi+绘制的图形质量很少,原因是gdi+使用的是256色的. 为了提高绘制图片的质量,可以使用是“Octree“ 算法.“Octree“ 算法允许我们插入自己的算法来量子化我们的图像. 一 ...
- SQL insert失败时也会造成自增长字段加1
CREATE TABLE #test(id INT IDENTITY(1,1), NAME varchar(30)) INSERT #test(name)SELECT '41545' SELECT ...
- 使用HTML5的两个api,前端js完成图片压缩
主要用了两个html5的 API,一个file,一个canvas,压缩主要使用cnavas做的,file是读取文件,之后把压缩好的照片放入内存,最后内存转入表单下img.src,随着表单提交. 照片是 ...