你真的了解new操作符吗
如果你能回答出以下几个问题,请跳过本博客,以免浪费你的时间
1:new操作符能重载吗?
2:new操作符可以在一个指定内存处开僻空间不?
3:new操作符的底层实现究竟是什么样的?
对于第一个问题不作过多解释,new是一个操作符,而且是一个可以重载的操作符,C++中不能重载的操作符最为典型的有四种(.、::、.*、?:)
对于2、3两个问题以实现源码作为剖析
一个简单的例子
#include<iostream> using namespace std; int main() { int *tmp=new int(2); cout<<*tmp<<endl; char buf[20]; int *bfT=new (buf)int(5); cout<<*bfT<<endl; return 0; }
注解:int *tmp=new int(2)表示的是开设一个int空间,同时tmp指向它,*bfT=new (buf)int(5)表示的是在当前存储空间buf中取出4Bytes作为一个整形变量,并将其地址赋给bfT,因此此条语句执行后,实际上在内存中并未分配空间,内存池就是基于此实现的:)
跟踪代码,进入其汇编语言实现,可以发现如下调用:
5: int *tmp=new int(2); 00401578 push 4 0040157A call operator new (004205e0)
从这里我们可以发现了new操作符实际上调用的是底层的operator new,前面的operator这个关键字,也说明了new操作符是可以重载的。如果有兴趣,可以继续对其进行跟踪后,可以发现operator new又调用了更底层的C语言函数malloc.
在C++的安装目录中,可以找到new或者new.h文件,打开它,可以找到真正需要的operator new函数,其部份源码:
// new AND delete DECLARATIONS void __cdecl operator delete(void *) _THROW0(); void *__cdecl operator new(size_t) _THROW1(std::bad_alloc);//方式一 void *__cdecl operator new(size_t, const std::nothrow_t&)_THROW0();//方式一 #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
从上面的源码中,可以看出operator new实际上有两种形式,一种是直接从内存空间中分配内存的方式一( int *tmp=new int(2)),另一种是方式二,placement new(*bfT=new (buf)int(5)),方式一中的_THROW1与_THROW0是两上宏定义
nothrow是一个空结构体,如下定义:
struct nothrow_t {};
从源码中可以发现的是 placement new 只返回内存地址,而忽略了 size_t 参数,其结果是允许用户把一个对像放到一个特定的地方,达到调用构造函数的目的。
至于为什么可以对申请的空间赋一初值,可以跳入汇编,发现如下结果
00401578 push 4 0040157A call operator new (004205e0) 0040157F add esp,4 00401582 mov dword ptr [ebp-20h],eax 00401585 cmp dword ptr [ebp-20h],0 00401589 je main+3Ch (0040159c) 0040158B mov eax,dword ptr [ebp-20h] 0040158E mov dword ptr [eax],2 00401594 mov ecx,dword ptr [ebp-20h]
即申请完空间后,编译器自动生成代码将初始值放入刚申请的空间中
最后以一个例子结束,注释已经写的很清楚了,就不作解释
#include<iostream> #include<new> using namespace std; //重载new操作符,注意区分new.h中定义的两个operator new void * operator new(size_t size,int data); int main() { //调用重载的operator new int *p=(int *)operator new(sizeof(int),10); *p=2; cout<<*p<<endl; //调用重载的operator new,并且传入第二个参数为10,第一参数不需要传入 int *tmp=new(10) int(5); cout<<*tmp<<endl; //声明一个空结构体,表示不抛出异常 nothrow_t exp; //调用new或者new.h中定义的void *__cdecl operator new(size_t, const std::nothrow_t&) _THROW0(); int *tmpS=new (exp)int(20); cout<<*tmpS<<endl; return 0; } //重载的new函数 void * operator new(size_t size,int data) { cout<<data<<endl; return operator new(sizeof(int)); }