new/delete的认识
回顾:C语言的内存管理
malloc/calloc/realloc/free
- 函数原型
//返回值:成功:空间首地址,失败:NULL
//malloc
void * malloc(size_t size);
//size---申请空间的字节数
//calloc(初始化为0)
void *calloc( size_t num, size_t size );
//num---元素个数
//size---每个元素所占字节数
//realloc(对memblock所指向空间进行调整到size个字节)
void *realloc( void *memblock, size_t size );
//memblock---以前分配的内存块的指针
//memblock-->NULL-----与malloc功能类似
//memblock-->!NULL-----newSize
//size---新空间的字节数
//free
void free( void *memblock );
int main()
{
//malloc,calloc,realloc必须和free成对使用
int * p1 = (int *)malloc(sizeof(int));
if(p1 == NULL) //利用返回值可以进行检测
{
printf("malloc error\n");
}
free(p1);
int * p2 = (int *)calloc(4,sizeof(int));
int * p3 = (int *)realloc(p2,sizeof(int)*4);
free(p3);
//p2,p3指向的是同一块空间,所以释放p3后不用再释放p2
return 0;
}
new/delete操作符
- new/delete和new []/delete []
//new/delete 单个元素空间的申请与释放
//new []/delete [] 连续空间的申请与释放
void Test()
{
int *p1 = new int; //动态申请一个int类型的空间
int *p2 = new int(3); //申请一个int类型的空间并且初始化为3
int *p3 = new int[5]; //申请5个int类型的空间
delete p1;
p1 = nullptr;
delete p2;
delete[] p3;
}
- new 与 malloc 的区别
特征 | new/delete | malloc/free |
---|---|---|
分配内存的位置 | 自由存储区 | 堆 |
内存分配成功的返回值 | 完整类型指针 | void* |
内存分配失败的返回值 | 默认抛出异常 | 返回NULL |
分配内存的大小 | 由编译器根据类型计算得出 | 必须显式指定字节数 |
处理数组 | 有处理数组的new版本new[] | 需要用户计算数组的大小后进行内存分配 |
已分配内存的扩充 | 无法直观地处理 | 使用realloc简单完成 |
是否相互调用 | 可以,看具体的operator new/delete实现 | 不可调用new |
分配内存时内存不足 | 客户能够指定处理函数或重新制定分配器 | 无法通过用户代码进行处理 |
函数重载 | 允许 | 不允许 |
构造函数与析构函数 | 调用 | 不调用 |
要注意的地方
- malloc/new/new[] | free/delete/delete[]
class Test
{
public:
Test()
{
cout << "Test():" << this << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
int _data;
};
int main()
{
printf("malloc!--------\n");
Test* p1 = (Test*)malloc(sizeof(Test));
free(p1);
p1 = NULL;
printf("new!--------\n");
Test* p2 = new Test();
delete p2;
printf("new[]!--------\n");
Test* p3 = new Test[10];
delete[] p3;
return 0;
}
- malloc 不会调用构造函数,free不会调用析构函数
- new 回调用类型的构造函数,delete释放堆内存时会调用析构函数(清理对象中资源,空间释放)
- new[] 申请空间N*sizeof(T)+调用N次构造函数,delete[]将N个对象从后往前清理,调用N次析构函数,释放空间
operator new/operator delete
new T:申请空间时new是C++提供的操作符(关键字),不是真的函数
operator new 和operator delete是系统提供的 全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
new:
1.申请空间
void* operator new(size_t size)
//size申请空间字节数
{
while(NULL == (p = malloc(size)))
{
//检测是否提供空间不足的应对措施
//提供:执行应对措施
//未提供:抛出异常
}
return p;
}
2.调用构造函数T()
delete:
1.调用析构函数:清理对象中的资源~T()
2.释放空间:
void operator delete(void* p);
//该函数是通过free来释放空间的
new[]:
1.申请空间(必须是缺省的构造函数)
void* operator new[](size_t size = N*sizeof(T)+4);
//4:用于存放N这个数字,取决于T类是否提供析构函数
//operator new[]中实际调用operator new函数完成N个对象空间的申请
2.构造N个对象
在申请的空间上执行N次构造函数
delete[]:
1.调用N次析构函数:到p空间的前四个字节中取对象个数N,调用N次析构函数,倒着释放
2.释放空间
p向前4个字节
- 这是底层的operator new 的实现
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试
执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
格式:new (place_address) type(initializer-list),place_address必须是一个指针,initializer-list是类型的初始化列表
class Test
{
public:
Test()
: _data(0)
{
cout<<"Test():"<<this<<endl;
}
~Test()
{
cout<<"~Test():"<<this<<endl;
}
private:
int _data;
};
void Test()
{
Test* pt = (Test*)malloc(sizeof(Test));
new(pt) Test;
//注意:如果Test类的构造函数有参数时,此处需要传参
}