C++ new操作符详解
一.new操作符的概念
我们通常讲的new是指的是new operator,其实还有另外两个概念,operator new 和 placement new。
1、new operator
我们在使用new operator的时候,实际上是执行了三个步骤:
1)调用operator new分配内存 ;2)调用构造函数生成类对象;3)返回相应指针。
2、operator new
所以说operator new做的事情是new operator的一部分。
operator new的原型是
Void* operator new(size_t size);
参数size指定待分配的内存大小,函数内部调用malloc初始化内存,返回指向这个内存的指针。
你可以重载这个函数(注意是重载operator new,而不能重载new operator)。operator new默认情况下首先调用分配内存的代码,尝试得到一段堆上的空间,如果成功就返回,如果失败,则转而去调用一个new_hander(异常处理函数),若没有定义new_hander,则抛出异常,否则执行new_hander,然后继续重复前面过程。你可以在重载的时候加上额外的参数,但是第一个参数类型必须是size_t.例如:
class A
{
public:
void* operator new(size_t size)
{
printf("operator new calledn");
return ::operator new(size);
}
};
A* a = new A();
这里通过::operator new调用了原有的全局的new,实现了在分配内存之前输出一句话。全局的operator new也是可以重载的,但这样一来就不能再递归的使用new来分配内存,而只能使用malloc了:
void* operator new(size_t size)
{
printf("global newn");
return malloc(size);
}
相应的,delete也有delete operator和operator delete之分,后者也是可以重载的。并且,如果重载了operator new,就应该也相应的重载operator delete,这是良好的编程习惯。
3、placement new
placement new是用来实现定位构造的,因此可以实现new operator三步操作中的第二步。
其实它也只是operator new的一个重载的版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new时行不通的。也就是说placement new允许你在一个已经分配好的内存中(栈或者堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。
我 们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。 placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途 出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。
使用方法如下:
1. 缓冲区提前分配
可以使用堆的空间,也可以使用栈的空间,所以分配方式有如下两种:
class MyClass {…};
char *buf=new char[N*sizeof(MyClass)+sizeof(int)];或者char buf[N*sizeof(MyClass)+sizeof(int)];
2. 对象的构造
MyClass * pClass=new(buf) MyClass;
3. 对象的销毁
一旦这个对象使用完毕,你必须显式的调用类的析构函数进行销毁对象。但此时内存空间不会被释放,以便其他的对象的构造。
pClass->~MyClass();
4. 内存的释放
如果缓冲区在堆中,那么调用delete[] buf;进行内存的释放;如果在栈中,那么在其作用域内有效,跳出作用域,内存自动释放。
注意:
- 在C++标准中,对于placement
operator new []有如下的说明: placement operator new[] needs
implementation-defined amount of additional storage to save a size of
array. 所以我们必须申请比原始对象大小多出sizeof(int)个字节来存放对象的个数,或者说数组的大小。 - 使用方法第二步中的new才是placement new,其实是没有申请内存的,只是调用了构造函数,返回一个指向已经分配好的内存的一个指针,所以对象销毁的时候不需要调用delete释放空间,但必须调用析构函数销毁对象。
placement new 是重载operator new 的一个标准、全局的版本,它不能够被自定义的版本代替(不像普通版本的operator new 和 operator delete能够被替换)。
void *operator new( size_t, void *p ) throw() { return p; }
placement new的执行忽略了size_t参数,只使用第二个参数。其结果是允许用户把一个对象放到一个特定的地方,达到调用构造函数的效果。
和其他普通的new不同的是,它在括号里多了另外一个参数。比如:
Widget * p = new Widget; - - - - - - - - - //ordinary new
pi = new (ptr) int; pi = new (ptr) int; //placement new
括号里的参数ptr是一个指针,它指向一个内存缓冲器,placement new将在这个缓冲器上分配一个对象。Placement new的返回值是这个被构造对象的地址(比如括号中的传递参数)。placement new主要适用于:在对时间要求非常高的应用程序中,因为这些程序分配的时间是确定的;长时间运行而不被打断的程序;
三、处理内存分配异常
正如前面所说,operator new的默认行为是请求分配内存,如果成功就返回,如果失败,则转而去调用一个new_hander(异常处理函数),若没有定义new_hander,则抛出异常,否则执行new_hander,然后继续重复前面过程。于是,想要从operator new的执行过程中返回,则必然需要满足下列条件之一:
1)分配内存成功
2)new_handler中抛出bad_alloc异常
3)new_handler中调用exit()或类似的函数,使程序结束
于是,我们可以假设默认情况下operator new的行为是这样的:
void* operator new(size_t size)
{
void* p = null
while(!(p = malloc(size)))
{
if(null == new_handler)
throw bad_alloc();
try
{
new_handler();
}
catch(bad_alloc e)
{
throw e;
}
catch(…)
{}
}
return p;
}
当operator new不能满足一个内存分配请求的时候,默认会抛出一个异常,我们可以通过设置new_handler定义处置策略。
new_handler的模型为:void (*new_handler)()
可以通过“void set_new_handler( void(*new_handler)()) throw();”设置这个处理函数(new_handler),它定义在<new>标准函数库中:
namespace std
{
void (*new_handler)();
void set_new_handler( new_handler )throw();
}
//error-handling function
void MemErrorHandling()
{
std::cerr << "Failed to allocate memory.\n";
std::abort();
}
... ...
std::set_new_handler(MemErrorHandling);
现在我们知道了new操作失败后,系统地大概处理流程,以及怎么设置用户自定义处理函数,但是我们究竟可以在new_handler中做些什么处理呢?
1、删除其它无用的内存,使系统具有可以更多的内存可以使用,为下一步的内存申请作准备。
2、设置另外一个new_handler。如果当前的new_handler不能够做到更多的内存申请操作,或者它知道另外一个new_handler可
以做到,则可以调用set_new_handler函数设置另外一个new_handler,这样在operator
new下一次调用的时候,可以使用这个新的new_handler。
3、卸载new_handler(通过set_new_handler(0)),使operator new在下一次调用的时候,因为new_handler为空抛出内存申请异常。
4、抛出自定义异常。
5、不再返回,调用abort或者exit退出程序。
参考:
1、http://www.bc-cn.net/Article/kfyy/cjj/jszl/200604/4002.html
2、http://blog.csdn.net/youdianmengxiangba/article/details/8233651
C++ new操作符详解的更多相关文章
- SQL Server 执行计划操作符详解(3)——计算标量(Compute Scalar)
接上文:SQL Server 执行计划操作符详解(2)--串联(Concatenation ) 前言: 前面两篇文章介绍了关于串联(Concatenation)和断言(Assert)操作符,本文介绍第 ...
- SQL Server 执行计划操作符详解(2)——串联(Concatenation )
本文接上文:SQL Server 执行计划操作符详解(1)--断言(Assert) 前言: 根据计划,本文开始讲述另外一个操作符串联(Concatenation),读者可以根据这个词(中英文均可)先幻 ...
- C#基础操作符详解(上)
本节内容: 1.操作符概览: 2.操作符的本质: 3.操作符与运算顺序 4.操作符详解. 1.操作符概览: 操作符(Operator)也译为”运算符” 操作符是用来操作数据的,被操作符操作的数据称为操 ...
- javascript中的操作符详解1
好久没有写点什么了,根据博主的技术,仍然写一点javascript新手入门文章,接下来我们一起来探讨javascript的操作符. 一.前言 javascript中有许多操作符,但是许多初学者并不理解 ...
- PHP比较操作符详解(转自hack58)
php的比较操作符有==(等于)松散比较,===(完全等于)严格比较,这里面就会引入很多有意思的问题. 在松散比较的时候,php会将他们的类型统一,比如说字符到数字,非bool类型转换成bool类型, ...
- LINQ标准查询操作符详解(转)
一. 关于LINQ LINQ 英文全称是“Language-Integrated Query”,中文为“语言集成查询”,它是微软首席架构师.Delphi 之父和C# 之父——Anders ...
- SQL Server 执行计划操作符详解(1)——断言(Assert)
前言: 很多很多地方对于语句的优化,一般比较靠谱的回复即使--把执行计划发出来看看.当然那些只看语句就说如何如何改代码,我一直都是拒绝的,因为这种算是纯蒙.根据本人经验,大量的性能问题单纯从语句来看很 ...
- JavaScript 中的相等操作符 ( 详解 [] == []、[] == ![]、{} == !{} )
ECMAScript 中的相等操作符由两个等于号 ( == ) 表示,如果两个操作数相等,则返回 true. 相等操作符会先转换操作数(通常称为强制转型),然后比较它们的相等性. 在转换不同的数据类型 ...
- Java一元操作符++详解
废话不多说,直接上代码. package com.coshaho.learn; /** * * OperatorLearn.java Create on 2016-11-13 下午8:38:15 * ...
随机推荐
- Windows注意目录
win7: c:\windows\tracing%windir%\system32\logfiles\C:\Users\Administrator\AppData\Roaming\softC:\Use ...
- 〖Eclipse〗Eclipse实现类似于YouCompleteMe补全插件的tab选择,shift+tab反向选择,空格、回车、点号等结束选择。
1.增加Eclipse的提示功能 在Eclipse中,从Window -> preferences -> Java -> Editor -> Content assist -& ...
- Windows下安装Redmine 2.5.2不全然指南
我决定在项目中引入Redmine来管理开发任务和计划,至于Redmine的优点,请问度娘或者脑补一下. 互联网搜索到的.基本上都是旧版本号的. 1.2.1 的最多,我要新的啊,所以仅仅好自己来啦. 本 ...
- 如何通过from语句调用模块的变量名?
# -*- coding: utf-8 -*- #python 27 #xiaodeng #如何通过from语句调用模块的变量名? #my.py def printer(x): print x #如何 ...
- iOS微信支付集成
概述 iOS微信支付集成 详细 代码下载:http://www.demodashi.com/demo/10735.html 支付宝和微信都是业界的老大哥,相信大家都有所觉得文档.SDK都是各种坑吧(纯 ...
- OpenStack 网络:Neutron 初探
OpenStack Neutron 网络模型 OpenStack nova-network 独立成为单独的组件 Neutron 后,形象的网络模型的多平面网络.混合平面私有网络.如图 3,图 4,图 ...
- 微博混合云DCP:极端流量下的峰值应对与架构挑战
摘要: 在2016杭州云栖大会的“开发者技术峰会”上,来自新浪微博的资深运维架构师王关胜带来题为<微博混合云DCP:极端流量下的峰值应对与架构挑战>的精彩分享,分享中他从微博业务背景及峰值 ...
- java多线程(二)之实现Runnable接口
一.java多线程方式2: 实现Runnable接口 好处:a. 可以避免由于java单继承带来的局限性. b. 适合多个相同的程序的代码去处理同一个资源的情况, 把线程与程序的代码, 数据有效分离, ...
- JQuery包装集size,length,index,slice,find,filter,is,children,next,nextAll,parent,parents,closest,siblings,add,end,andSelf的用法
在使用Jquery包装集的知识之前首先要注意三个概念(当前包装集.新包装集.包装集内部元素)的区别. <!DOCTYPE html> <html xmlns="http:/ ...
- 转multicast vs broadcast
转自:http://blog.csdn.net/bloghome/article/details/4682984 一.multicast概述: 多媒体应用集成了声音.图形.动画.文本以及视频,这种 ...