你真的了解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));
}

浙公网安备 33010602011771号