MoreEffect[8] 理解不同含义的new和delete
C++使用new操作符(new operator / new)来完成动态内存的分配,其的实现分为两步:通过operator new分配足够的堆内存,调用构造函数初始化内存对象。类似的,delete首先调用析构函数,然后通过operator delete释放内存。
new、delete操作符和sizeof、typeid一样是语言内置的关键字,不能重载以改变它的含义,但可以重载new操作(operator new)、delete操作来改变内存的分配方式。必须要明确,这些操作符所完成的功能被明确固定,定制函数绝不能改变它们完成的功能,只能改变实现功能的方法。
operator new
new调用operator new完成必要的内存分配,其的函数形式为:void * operator new(size_t size);。operator new返回一块未经处理的堆内存的首地址,参数size决定的内存块的大小。可见,其和malloc非常接近。
编译器提供了6种operator new的全局重载形式,并允许用户提供自定义的operator new,参数可以和已有形式完全相同(但不能在已有参数形式上增加含默认值的参数,避免引起歧义)。
void *operator new(std::size_t count) throw(std::bad_alloc);//标准版本
void *operator new(std::size_t count, const std::nothrow_t&) throw(); //兼容早版本的new,不会抛出异常
void *operator new(std::size_t count, void *ptr) throw();//placement new
void *operator new[](std::size_t count) throw(std::bad_alloc);//new[]版本
void *operator new[](std::size_t count, const std::nothrow_t&) throw();
void *operator new[](std::size_t count, void *ptr) throw();
在调用new时提供对应的参数以调用合适的operator new。
void * memory = operator new(sizeof(Awesome));
Awesome * pObj = new (memory) Awesome;//调用placement new
重载operator new
重载operator new函数的返回值必须是void *,可以添加新的形参,但第一个参数必须是size_t类型。全局重载函数的参数不需要自定义类型,参数可以和已有的完全一致。
void * operator new(size_t size);//new [type]
void * operator new(size_t size, int arg);//new(int) [type]
void * operator new(size_t size, int arg = 0);//error: 歧义
class Awesome
{
public:
void * operator new(size_t size)//new Awesome
{
cout << "Awesome::operator new" << endl;
return malloc(size);
}
};
通过重载可以自定义内存分配的过程,并通过提供合适的参数让new自动调用。当我们设计的类需要更细致的内存管理时,就应该考虑重载类的operator new、operator delete
placement new
placement new是一个特殊的operator new,用于可以在一个未经处理的内存上构造对象。
void *operator new(std::size_t count, void *ptr) throw();//placement new
void * memory = operator new(sizeof(Awesome));
Awesome * pObj = new (memory) Awesome;
参数ptr执行未经处理的内存,作为new的额外参数。
operator delete
operator delete与new的关系类似于operator new与new的关系。delete会调用析构函数析构对象,然后释放对象占有的内存。换言之,析构函数本身不会释放内存,必须借由operator delete完成。
delete pObj;
//相当于
pObj->~Awesome();
operator delete(pObj);
直接调用operator delete可以释放对象的内存,但对象关联的其它内存就不会被释放,必须在调用operator delete前调用析构函数。
void operator delete(void *);
void operator delete[](void *);
暴露的operator delete只有两个版本。可以重载operator delete。
placement delete
placement delete并不能直接调用,编译器只会在placement new构造失败时调用placement delete清理内存。
手动释放placement new生成的对象时应显式调用对象的析构函数,并在需要时在析构之后把内存释放掉。
参考:
https://developer.aliyun.com/article/640510
https://www.zhihu.com/question/22947192

浙公网安备 33010602011771号