new / delete / new[ ] / delete[ ]
与C语言不通,C++更多使用new和delete进行内存管理
int *p=new int;//开辟1个整型大小的空间,并由p指向它。
int *p1=new int(3);//开辟1个整型大小的空间并初始化为3,由p1指向它。
int *p2=new int[2];//开辟2个整型大小的空间,并由p2指向它。
delete p;
delete p1; //释放p,p1,p2指向的空间。
delete[] p2;
能够发现,new/delete对应单个变量的内存开辟,new[ ]/delete[ ]对应数组的开辟。使用时一定要记得对应,不然可能会出现内存泄漏甚至崩溃的问题。
C/C++内存管理
- 栈又称堆栈,非静态局部变量,函数参数,返回值等等都存在栈上,栈是向下增长的。
- 内存映射是高效的i/o映射方式,用于装载一个动态的动态内存库,用户可以使用系统接口创建共享内存,做进程间通信。
- 堆用于程序执行时的动态内存分配,堆是向上增长的。
- 数据段:存储全局数据和静态变量。
- 代码段:可执行的代码/只读常量。
我们发现,C++是兼容C的,在C++程序里使用malloc/ free一样可以开辟空间,那为什么还要使用new/delete呢?
new/delete 和 malloc/free 的区别和联系
- 他们都是用于动态内存管理的接口
- malloc/free是C/C++标准库函数,而new/delete是C/C++的操作符
- new/delete在开辟/释放空间的同时,会对应调用构造函数和析构函数。而malloc/free不会。
- 用malloc开辟空间时,需要自己计算开辟的大小,且默认指针类型是void*,而new不用计算加调整指针类型,编译器会自己搞定。
- 使用malloc开辟内存,倘若失败,会返回0,而new失败会抛异常,更早的被发现。
上述的区别与联系中,需要重点了解的是第三条,看看下面的代码:
#include<iostream>
#include<stdlib.h>
using namespace std;
class Array
{
public:
Array(size_t size = 10)
: _size(size)
, _a(0)
{
cout << "Array(size_t size)" << endl;
if (_size > 0)
{
_a = new int[size];
}
}
~Array()
{
cout << "~Array()" << endl;
if (_a)
{
delete[] _a;
_a = 0;
_size = 0;
}
}
private:
int*_a;
size_t _size;
};
int main()
{
Array* p1 = (Array*)malloc(sizeof(Array));
Array* p2 = new Array;
Array* p3 = new Array(20);
Array* p4 = new Array[10];
free(p1);
delete p2;
delete p3;
delete[] p4;
system("pause");
}
可以看出new在创建对象的同时已经调用了析构函数进行初始化。
operator new和operator delete
- operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一样。
- 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
- 实际operator new和operator delete只是malloc和free的一层封装。
new做了两件事
- 调用operator new分配空间。
- 调用构造函数初始化对象。
delete也做了两件事 - 调用析构函数清理对象
- 调用operator delete释放空间
new[N] - 调用operator new分配空间。
- 调用N次构造函数分别初始化每个对象。
delete[ ] - 调用N次析构函数清理对象。(思考这里怎么N是怎么来的?)
- 调用operator delete释放空间。
这里就有一个问题了:在使用delete [ ]释放空间的时候,编译器怎么知道该调用多少次析构函数呢?
原来在用new[ ] 开辟多个对象的空间时,系统在该开辟内存大小前多分配了4个字节的内存,用来存对象的个数,这样在delete[ ]时,就能取到对象的个数了。
但是在C++编译器中,在如下情况下就不会开辟:
- 开辟的对象是系统的内置类型
- 开辟的对象数量是1个
- 在释放内存是不会调用析构函数
- 没有自定义析构函数
以下情况均有定义析构函数:
new 和delete[ ]:
new开辟了1个对象的空间,不会多分配4个字节存对象个数,而delete[ ] 会误认为开辟了多个对象,会去访问前面的那4个字节,这就属于非法访问,系统会报错
new[ ]和 delete
new[ ]开辟了多个对象的空间,而delete误认为只开辟了1个对象的空间,所以,在free时首地址出现错误,无法正常释放空间。