C++之operator new 和new operator区别
在C++中,operator new和new operator还是很有区别。new operator是c++内建的,无法改变其行为;而operator new 是可以根据自己的内存分配策略去重载的。
1. operator new
operator new和operator delete有两个重载版本,每个版本支持相关的new表达式和delete表达式:
void *operator new(size_t);
void *operator new[](size_t);
void *operator delete(void*);
void *operatordelete[](void*);
对于operator new你可以对其进行重载,但是必须注意:
(1) 只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则如果有new_handler,则调用new_handler;否则如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常;否则返回0。(这个调用顺序要记住,可以通过set_new_handler()函数设置new_handler)
(2) 重载时,返回类型必须声明为void*
(3) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(4) 重载时,可以带其它参数。
注:当然重载了operator new,你就要重载对应的operator delete。
operator new实际上总是以标准的C malloc()完成,虽然并没有规定非得这么做不可。同样,operator delete也总是以标准得C free()来实现,不考虑异常处理的话他们类似下面的样子:
[code lang="cpp"]
extern void* operator new( size_t size )
{
if( size == 0 )
size = 1; // 这里保证像 new T[0] 这样得语句也是可行的
void *last_alloc;
while( !(last_alloc = malloc( size )) )
{
if( _new_handler )
( *_new_handler )(); //调用handler函数
else
return 0;
}
return last_alloc;
}
extern void operator delete( void *ptr )
{
if(ptr) // 从这里可以看出,删除一个空指针是安全的
free( (char*)ptr );
}
[/code]
2. new operator
当你写string *ps = new string("Hands up!")时,你所使用的new是所谓的new operator,它其实干了两件事:一、分配足够的内存(实际大小是大于所创建的对象大小)二、调用对象构造函数,new operator永远干这两件事。上面的那段代码大约反映以下的行为:
void *mem = operator new(sizeof(string));
call string::string("Hands up!") on *mem;//只能由编译器完成,用户是不允许这样操作的,也就是说如果你想建立一个堆对象就必须用new操作符,不能直接像上面一样调用构造函数来初始化堆对象。
string *ps = static_cast<string*>(mem);
也就是说operator new仅仅分配内存(就像malloc一样),我们能够做的仅仅是重载operator new,为自己的类创建一个定制的内存管理方案,这也让我有点明白为什么在重载operator new的时候并没有写调用构造函数的代码,但它确实被调用了,原来都是new operator搞的鬼。
编译器看到类类型的new或者delete表达式的时候,首先查看该类是否是有operator new或者operator delete成员,如果类定义了自己的new和delete函数,则使用这些函数为对象分配和释放内存,否则调用标准库版本。如果你想定制自己独有的内存分配过程,你应该重载全局的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。当然你可以显示的调用:: operator new和:: operator delete强制使用全局的库函数。下面:
Type *p = :: new Type;//该new operator会调用全局的operator new
::delete p;//该delete operator会调用全局的operator delete
建立数组时new操作符(new[])的行为与单个对象建立(new)有少许不同:第一是内存不再调用用operator new函数进行分配,代替以operator new[]函数(常称作array new)。它与operator new一样能被重载,允许定制数组的内存分配,就象定制单个对象内存分配一样。
同理:
[code lang="cpp"]
delete ps
[/code]
上述语句实际上有两步,第一步,对ps所指的对象调用适当的析构函数;第二步,调用名为operator delete的标准库函数释放该对象所用的内存;即delete ps,就相当于delete operator,它的动作分为以下两步:调用析构函数,释放内存空间;即
[code lang="cpp"]
ps->~string();
operator delete(ps);
[/code]
[code lang="java"]
总结: operator new 唯一的任务就是分配内存,new operator的任务是取得 operator new 分配的内存,并将之转换为一个对象,返回一个指向它的指针;同理operator delete的唯一任务是释放内存空间,它不能调用析构函数;
因此,如果你只打算处理原始的、未设初值的内存,应该完全回避new operator 和delete operator,改调用operator new 取得内存并以operator delete归还给系统;
[/code]
3. 定位new表达式
类似于constructor成员,有第三种new表达式,称为定位new(placement new)。定位new表达式在已分配的与原始内存中初始化一个对象,他与new的其他版本不同,它不分配内存。相反,它接受指向已分配好但未构造的内存指针,并在该内存中初始化一个对象。实际上,定位new表达式使我们在特定的、预分配的内存地址构造一个对象。它可以定义类的任何构造函数。
new(place_address) type
new(place_address) type(initializer_list)
place_address必须是一个指针,而initializer_list提供了初始化列表(可能是空的),以便在构造新分配的对象中使用。
4. 使用注意点
如果只考虑分配和释放,内存管理基本要求是“不重不漏”:既不重复 delete,也不漏掉 delete。也就说我们常说的 new/delete 要配对,“配对”不仅是个数相等,还隐含了 new 和 delete 的调用本身要匹配,不要“东家借的东西西家还”。例如:
(1)用系统默认的 malloc() 分配的内存要交给系统默认的 free() 去释放;
(2)用系统默认的 new 表达式创建的对象要交给系统默认的 delete 表达式去析构并释放;
(3) 用系统默认的 new[] 表达式创建的对象要交给系统默认的 delete[] 表达式去析构并释放;
(4) 用系统默认的 ::operator new() 分配的的内存要交给系统默认的 ::operator delete() 去释放;
(5) 用 placement new 创建的对象要用 placement delete (为了表述方便,姑且这么说吧)去析构(其实就是直接调用析构函数);
(6)从某个内存池 A 分配的内存要还给这个内存池。
(7) 如果定制 new/delete,那么要按规矩来。见 Effective C++ 相关条款。
参考:
https://kelvinh.github.io/blog/2014/04/19/research-on-operator-new-and-delete/
作者|分布式资源与管理小组
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:hwclouds.bbs@huawei.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
- 点赞
- 收藏
- 关注作者
评论(0)