new的操作:
(1)调用operator new函数获取内存空间
(2)在分配的内存空间中调用构造函数构造对象
delete操作:
(1)调用析构函数销毁对象
(2)调用operator delete函数释放对象所占的空间。
其实operator new和operator delete的底层实现是调用了malloc和free。这两个函数定义在<cstdlib>头文件中。
malloc函数接受一个表示待分配字节数的size_t,返回指向分配空间的指针或者返回0以表示分配失败。
free函数接受一个void*,它是malloc返回的指针的副本。free将相关内存返回给系统。调用free(0)没有任何意义。
以下是简单版本的malloc和free的实现
void* operator new (size_t size)
{
if(void* mem = malloc(size))
return mem;
else
throw bad_alloc();
}
void operator delete(void* mem) noexcept
{
free(mem);
}
如果应用程序希望控制内存分配的过程,则他们需要定义自己的operator new函数和operator delete函数。
当编译器发现一条new表达式或delete表达式后,将在程序中查找可供调用的operator函数。如果被分配(释放)的对象时类类型,则编译器首先在类及其基类的作用域中查找。如果此时该类中含有operator new成员或operator delete成员,则相应的表达式将调用这些成员。否则,编译器在全局作用域中查找匹配的函数。此时如果编译器找到了用户自定义的版本,则使用该版本执行new表达式或delete表达式,如果没有找到,则使用标准库定义的版本。
operator new接口和operator delete接口:
一共有8个重载版本,前面四个可能抛出bad_alloc异常,后面四个不会抛出异常。
//这些版本可能抛出异常
void* operator new(size_t); //分配一个对象
void* operator new[](size_t); //分配一个数组
void* operator delete(void*) noexcept; //释放一个对象
void* operator delete[](void*) noexcept; //释放一个数组
//这些版本承诺不会抛出异常
void* operator new(size_t, nothrow_t&) noexcept;
void* operator new[](size_t, nothrow_t&) noexcept; //分配一个数组
void* operator delete(void*, nothrow_t&) noexcept; //释放一个对象
void* operator delete[](void*, nothrow_t&) noexcept; //释放一个数组
应用程序可以自定义上面函数版本中的任何一个,前提是自定义的版本必须位于全局作用域或者类作用域中。
当我们将上述运算符函数定义为成员类的成员时,他们是饮食静态的。因为operator new用在构造对象之前,而operator delete用在对象销毁之后。所以这两个成员必须是静态的。而且它们不能操纵类的任何数据成员。
对于operator new和operator new[]函数来说,它们的返回类型必须是void*。第一个形参的类型必须是size_t且该形参不能含有默认实参。如果我们想要自定义operator new函数,则可以为它提供额外的形参。不过此时,用到这些自定义函数的new表达式必须使用new的定位形式。
有一种operator new无论如可也不能被用户重载
void *operator new(size_t, void*); //不允许重新定义这个版本
这种形式只供标准库使用,不能被用户重新定义。
对于operator delete和operator delete[]函数来说,他们的返回类型必须是void,第一个形参的类型必须是void*。