1. new/delete
1. 对于简单数据类型
这里的简单数据类型指的是内置类型和不需要构造函数的自定义类型。
例如:int* a = new int;
这里我们可以查看new的源代码:
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
_THROW_NCEE(_XSTD bad_alloc, );
}
return (p);
}
此处调用相应的 operator new(size_t)
函数,动态分配内存,本质还是调用malloc()函数
。如果 operator new(size_t)
不能成功获得内存,则调用 new_handler()
函数用于处理new失败问题。如果没有设置new_handler()
函数或者 new_handler()
未能分配足够内存,则抛出 std::bad_alloc
异常。
new一个简单数据类型时还可以直接对其进行初始化,例如:
int* a = new int(0);
注意:这里初始化用的是()
而不是[]
。
- new简单数据类型调用
operator new()
,如果开辟空间失败,直接抛出异常,而不是想malloc一样返回空,所以使用new无需对其返回值进行判空操作。
对于delete,我们先看它的源代码
void operator delete( void * p )
{
RTCCALLBACK(_RTC_Free_hook, (p, 0));
free( p );
}
- 从上可以看出,当使用delete销毁一个简单数据类型对象时,只是调用free()函数。
2. 对于复杂数据类型
这时候new和delete的调用过程便有所不同。
- new:先开辟空间,然后再调用构造函数对这块空间进行初始化。
- delete:先调用析构函数清理这块空间,再释放这块空间。
所以此时如果new开辟的空间用free()来释放,就无法调用构造函数。
class T
{
public:
T()
{
cout << "调用构造函数" << endl;
}
void* operator new(size_t size)
{
T* p = (T*)malloc(sizeof(T));
cout << "开辟空间" << endl;
return p;
}
~T()
{
cout << "调用析构函数" << endl;
}
void operator delete(void* p)
{
cout << "释放空间" << endl;
free(p);
}
};
int main()
{
T* t = new T();
delete t;
system("pause");
return 0;
}
运行结果:
2. new[ ] / delete[ ]
1. 对于自定义类型
int* arr = new int[10]();
delete[] arr;
第一句代码:创建一个10个int类型大小的数据空间,这里在后面加()可以把数组元素全初始化为0。
第二句代码:这里使用delete[]
释放arr,如果粗心的小伙伴写成了delete,我们发现代码也是可以编过的,但大部分C++书籍肯定都提到new[]和delete[]必须匹配使用,那这样写是对是错呢?要解释这点还得从自定义类型说起。
2. 自定义类型
class A
{
public:
A()
{}
~A()
{}
private:
int _a;
};
int main()
{
A* p = new A[10];
delete[] p;
return 0;
}
这里我们申请了10个A类型大小的空间,我们知道对于自定义类型是先申请空间,然后调用构造函数初始化这块空间;在delete[]时,是先调用析构函数清理空间,再释放空间。这里问题就出来了,delete[]是如何知道要调用几次析构函数呢?给出的解决方案是在申请的空间前面多申请4字节空间,里面记录了申请的空间的个数。
这样在delete[]就知道调用几次析构函数,就不会出现资源泄漏问题。但要强调一点的是:使用new[]时,只对显示地实现了析构函数的自定义类型才会多申请4字节空间,没有写析构函数的自定义类型和内置类型(int、char等)是不会多申请这4字节空间的。
对于这点,我的个人理解是:多申请的这4字节只是为了知道应该调用几次析构函数。至于释放空间,其实它的本质是把这块空间的使用权还给操作系统,但delete[]本身是不知道要还多大空间给操作系统的,那谁知道呢?操作系统。
简而言之就是:欠钱的人不必记得自己借了多少钱(欠钱的都是大爷),但是债主必须记得自己借了多少钱出去。 所以对于上面简单类型,new[]出来的对象用delete释放是不会报错的,但最好是配对使用!