首页 > 代码库 > C++ new/new operator、operator new、placement new初识

C++ new/new operator、operator new、placement new初识

简要释义

1.operator new是内存分配函数(同malloc),C++在全局作用域(global scope)内提供了3份默认的operator new实现,并且用户可以重载operator new。

1 void* operator new(std::size_t) throw(std::bad_alloc);//normal new2 void* operator new(std::size_t,const std::nothrow_t&) throw();//nothrow new3 void* operator new(std::size_t,void*) throw();//placement new

下面这两行代码是等价的,都是分配一块大小为sizeof(widget)的内存并返回指针,没有执行构造函数

1 widget *a=(widget*) ::operator new(sizeof(widget));2 widget *b=(widget*)malloc(sizeof(widget));

2.new/new operator即C++内置的new操作符。

//这里new一个widget对象分成两步//1.运行期系统调用operator new开辟sizeof(widget)大小的内存//2.在该内存地址上构造一个widget对象widget *c=new widget();

我们平常的new操作由运行期系统调用operator new,然后调用构造函数初始化。这个过程是不可重定义的。即程序员不能重载C++内建的new操作符。我们能重载的仅是其中的 operator new/operator new[],即分配内存的部分。

3.placement new是operator new在全局作用域上的一个重载版本,即如上我们看到的

1 void* operator new(std::size_t,void*) throw();//placement new

 placement new并不分配内存,而是返回已分配的内存的指针,这个指针正是函数参数列表中的void *,即“返回一个你刚传入的已分配的内存的指针”。

std::中该函数的实现:

1 inline _LIBCPP_INLINE_VISIBILITY void* operator new  (std::size_t, void* __p) _NOEXCEPT {return __p;}

 那么为什么需要这个placement new呢?

答案是:当你需要在一段已分配的内存中构造对象时,调用寻常的new widget()会开辟另外一个内存空间,而非在已知地址上构造widget()对象。

//这里先申请了一段内存空间,由指针widget*a持有,并未调用构造函数//然后用placement new在a地址上构造了widget对象//这里::表示调用在global scope中的匹配函数widget *a=(widget*) ::operator new(sizeof(widget));::new(a) widget();

 

进一步的讨论

1.placement new实质上是有额外实参之operator new,一般情况下我们指的placement new是那个额外实参为void*的重载版本(已被纳入C++标准程序库),这些叫法对我们的讨论没有影响,只要知道placement new同时也是一种operator new即可。但是不要忘了我们可以重载其它版本的placement new,例如额外实参为std::ostream&,提供log功能。

 1 #include <iostream> 2 class widget 3 { 4 public: 5     static void * operator new(std::size_t _size,std::ostream& o) 6     { 7         o<<"void * operator new(std::size_t _size,ostream& o)"<<std::endl; 8         return ::operator new(_size); 9     }10     widget()11     {12         std::cout<<"widget()"<<std::endl;13     };14     ~widget()15     {16         std::cout<<"~widget()"<<std::endl;17     };18 19 };20 int main()21 {22     //下面两种构造方法是等价的23     //构造一个widget对象,并且使用有log功能的operator new24     widget* a=(widget*) widget::operator new(sizeof(widget), std::cout);25     ::new(a) widget();26     27     //同样构造一个widget对象,并且使用有log功能的operator new28     widget* b=new(std::cout) widget();29     return 0;30 }

 

这份示例代码中,我在类中重载了placement new,附带的额外参数是ostream&。

 

2.注意作用域遮掩问题。如果你在类内重载了一个operator new,当你对这个类及其子类使用new操作符的时候,会掩盖全局作用域中的operator new,编译器发现里层作用域(类内)有operator new声明,就不会查找全局作用域是否有其它operator new声明,而是直接进入参数匹配阶段,如果你重载的operator new参数和调用的不匹配,便会抛出一个编译错误。

解决方法就是:如果类内重载了operator new,并且你仍有可能使用到全局作用域中的operator new,请同时也重载和全局作用域同型的operator new,确保调用成功。

为你的类建立一个base class,内含全局作用域同型的operator new,使其调用全局作用域内的operator new即可。

 1 #include <iostream> 2 class globalScopeNew 3 { 4 public: 5     static void* operator new(std::size_t size) throw(std::bad_alloc) 6     { 7         return ::operator new(size); 8     } 9     static void* operator new(std::size_t size,const std::nothrow_t& t) throw()10     {11         return ::operator new(size, t);12     }13     static void* operator new(std::size_t size,void* p) throw()14     {15         return ::operator new(size, p);16     }17     18 };19 class widget:public globalScopeNew20 {21 public:22     using globalScopeNew::operator new;23     static void * operator new(std::size_t _size,std::ostream& o)24     {25         o<<"void * operator new(std::size_t _size,ostream& o)"<<std::endl;26         return ::operator new(_size);27     }28     widget()29     {30         std::cout<<"widget()"<<std::endl;31     };32     ~widget()33     {34         std::cout<<"~widget()"<<std::endl;35     };36    37 };38 int main()39 {40     widget* w2=new(std::cout) widget();41     //这句调用原来不能通过编译42     widget* w1=new widget();43     widget* w3=(widget*)operator new(sizeof(widget));44     //这句调用原来不能通过编译45     new(w3) widget();46     return 0;47 }

 

注意,这边需要在子类中using globalScopeNew::operator new;即在子类中使基类的operator new可见。

这样,各种形式的new操作符调用都能通过编译了。

C++ new/new operator、operator new、placement new初识