-
三种形式:
throwing (1)
void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2)
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3)
void* operator new (std::size_t size, void* ptr) throw();
(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。
A* a = new A; //调用throwing(1)
A* a = new(std::nothrow) A; //调用nothrow(2)
(3)是placement new,它也是对operator new的一个重载,定义于#include <new>中,它多接收一个ptr参数,但它只是简单地返回ptr。其在new.h下的源代码如下:
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
inline void *__cdecl operator new(size_t, void *_P)
{return (_P); }
#if _MSC_VER >= 1200
inline void __cdecl operator delete(void *, void *)
{return; }
#endif
#endif
那么它究竟有什么用呢?事实上,它可以实现在ptr所指地址上构建一个对象(通过调用其构造函数),这在内存池技术上有广泛应用。
它的调用形式为:
new(p) A(); //也可用A(5)等有参构造函数
placement new本身只是返回指针p,new(p) A()调用placement new之后,还会在p上调用A:A(),这里的p可以是动态分配的内存,也可以是栈中缓冲,如char buf[100]; new(buf) A();
我们仍然可以通过一个例子来验证:
#include <iostream>
class A
{
public:
A()
{
std::cout<<"call A constructor"<<std::endl;
}
~A()
{
std::cout<<"call A destructor"<<std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A* p = (A*)::operator new(sizeof(A)); //分配
new(p) A(); //构造
p->~A(); //析构
::operator delete(p); //释放
system("pause");
return 0;
}
上面的代码将对象的分配,构造,析构和释放分离开来,这也是new和delete关键字两句就能完成的操作。
先直接运行可以看到程序输出:
call A constructor
call A destructor
再分别注释掉new(a) A();和a->~A();两句,可以看到对应的构造和析构函数将不会被调用。
原文链接:https://blog.csdn.net/WUDAIJUN/article/details/9273339