malloc()负责动态配置内存,大小由size决定,分配成功时返回值为任意类型指针,指向一段可用内存(虚拟内存)的起始地址。分配失败时为NULL。
void * malloc(size_t size)
free()负责释放动态申请的内存空间,调用free( )后ptr所指向的内存空间被收回,如果ptr指向未知地方或者指向的空间已被收回,则会发生不可预知的错误,如果ptr为NULL,free不会有任何作用。
void free(void *ptr)
malloc函数动态申请的内存空间是在堆里(而一般局部变量存于栈里),并且该段内存不会被初始化,与全局变量不一样,如果不采用手动free()加以释放,则该段内存一直存在,直到程序退出才被系统,所以为了合理使用内存,在不适用该段内存时,应该调用free()。另外,如果在一个函数里面使用过malloc,最好要配对使用free,否则容易造成内存泄露。
从操作系统角度来看,malloc的实现有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。
这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
申请小于128k的内存时,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:
申请大于128k的内存时,使用mmap分配内存,在堆和栈之间找一块空闲内存分配,如下图:
默认情况下:当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。
new和delete是C++中的运算符,不是库函数,不需要库的支持,同时,他们是封装好的重载运算符,并且可以再次进行重载。
new是动态分配内存的运算符,自动计算需要分配的空间,在C++中,它属于重载运算符,可以对多种数据类型形式进行分配内存空间,比如int型、char型、结构体型和类等的动态申请的内存分配,分配类的内存空间时,同时调用类的构造函数,对内存空间进行初始化,即完成类的初始化工作。new运算符的使用示例:
new int //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址
new int(100) //同上,并指定该整数的初值为100
new char[100] //开辟一个存放字符数组(100个元素)的空间,返回首地址
new int[4][5]//开辟一个存放二维数组的空间,返回首元素的地址
float *p=new float(3.14157) //开辟一个存放单精度的空间,并指定该数的初值为3.14157,将返回的该空间的地址赋给指针变量p
注意:用new分配数组空间不能指定初值,若无法正常分配,则new会返回一个空指针NULL或者抛出bad_alloc异常。
delete是撤销动态申请的内存运算符。delete与new通常配对使用,与new的功能相反,可以对多种数据类型形式的内存进行撤销,包括类,撤销类的内存空间时,它要调用其析构函数,完成相应的清理工作,收回相应的内存资源。delete运算符的使用示例:
//注意,指针p存于栈中,p所指向的内存空间却是在堆中。
int *p = new int; delete p;
char *p = new char; delete p;
//注意,new申请数组,delete删除的形式需要加括号“[ ]”,表示对数组空间的操作,总之,申请形式如何,释放的形式就如何。
Obj * p = new Obj[100]; delete [ ]p;
new申请的内存也是存于堆中,所以在不需要使用时,需要delete手动收回。
在new一个对象的时候,首先会调用operator new() 为对象分配内存空间,然后调用对象的构造函数。
delete会调用对象的析构函数,然后调用free回收内存。
Windows下32位程序如果单纯看地址空间能有4G左右的内存可用,不过实际上系统会把其中2G的地址留给内核使用,32位Linux是用户3G+内核1G。所以你的程序最大能用2G(Windows)或者3G(Linux)的内存。除去其他开销,你能用malloc申请到的内存只有1.9G或者2.9G左右。
new的执行过程:先给定需要的内存大小,调用operator new,在那里面获得制定大小的内存并返回;然后才以刚才返回的内存为基础调用类的构造函数。如果使用的是new[]来生成对象数组,需要多申请sizeof(int)(即4个字节)的空间来存储对象个数,以确定析构的次数。
delete的执行过程:如果需要删除的是对象数组,首先要根据数组最开头的int数值来调用若干次析构函数;然后才释放存储空间。
这告诉我们,可以认为new就是malloc的封装。并且也解释了为什么new[]分配的空间用free()释放会出错(因为new[]分配空间返回的地址并不是它里面malloc分配空间的首地址,系统预留了sizeof(int)个字节)。
可以,但是一般不这么用。malloc/free是c语言中的函数,c++为了兼容c保留下来这一对函数。简单来说,new 可以理解为,先执行malloc来申请内存,后调用构造函数来初始化对象;delete是先执行析构函数,后使用free来释放内存。
不可以,因为new[]分配空间返回的地址并不是它里面malloc分配空间的首地址,系统预留了sizeof(int)个字节用来确定调用析构函数的次数。
#include
#include
using namespace std;
int main() {
int *pint = new int(5);
delete[] pint;
int *pinta = new int[4];
delete pinta;
cout << "success" << endl;
return 0;
}
程序输出:
success
这段代码即使不配对使用也会正常运行,因为int是内置类型,调用delete[]时不需要析构函数,所以也就不需要多4个字节来存放数组长度,只需要直接操作内存即可。
这是因为虽然你告诉了malloc你要多少空间,但malloc真正分配了多少只有它自己知道。例如,你向malloc要了999字节,但某人写的malloc分配的最小粒度是1024字节,那么你会得到一个1024字节的空间。
在malloc时,所分配的不仅是你请求的那点空间,还加了一个信息块来记录额外信息,这个信息块位于你请求的空间前面。而malloc返回指针的指向的是你请求的空间,如果你想看看那个信息块的话,把malloc返回的指针往前走几步就能看到了。free所需的信息可以直接在信息块中取。信息块和空间都会被释放。
最直观的思想:避免直接调用类的构造函数,因为对象静态建立时,会调用类的构造函数创建对象。但是直接将类的构造函数设为私有并不可行,因为当构造函数设置为私有后,不能在类的外部调用构造函数来构造对象。但是由于 new 创建对象时,底层也会调用类的构造函数,将构造函数设置为私有后,那就无法在类的外部使用 new 创建对象了。
首先我们想到的是将析构函数设置为私有。这是因为静态对象建立在栈上,是由编译器分配和释放内存空间,当析构函数设为私有时,编译器创建的对象就无法通过访问析构函数来释放对象的内存空间,因此,编译器不会在栈上为对象分配内存。
但是该方法存在两个问题:
因此有了下面这个解决方法:将构造函数和析构函数设置为 protected,并提供一个 public 的静态函数来完成构造,而不是在类的外部使用 new 构造。
将 operator new() 设置为私有。原因:当对象建立在堆上时,是采用 new 的方式进行建立,其底层会调用 operator new() 函数,因此只要对该函数加以限制,就能够防止对象建立在堆上。