读书笔记_Effective_C++_条款四十九:了解new_handler的行为
本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete。在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常。有的时候,我们希望能够调用自己定制的异常处理函数,这就是本条款要说的。
在声明于<new>的一个标准程序库中,有如下的接口:
namespace std
{
typedef void (*new_handler)();
new_handler set_new_handler(new handler p) throw();
}
注意这里面typedef了一个函数指针new_handler,它指向一个函数,这个函数的返回值为void,形参也是void。set_new_handler就是将new_handler指向具体的函数,在这个函数里面处理out of memory异常(函数末尾的throw()表示它不抛出任务异常),如果这个new_handler为空,那么这个函数没有执行,就会抛出out of memory异常。
void MyOutOfMemory()
{
cout << "Out of memory error!" << endl;
abort();
} int main()
{
set_new_handler(MyOutOfMemory);
int *verybigmemory = new int[0x1fffffff];
delete verybigmemory;
}
这里预先设定好new异常时调用的函数为MyOutOfMemory,然后故意申请一个很大的内存,就会走到MyOutOfMemory中来了。
好,我们更进一步,现在想要在不同的类里面定制不同的new_handler处理机制,一种想法是在类内部定义set_new_handler函数,将new_handler作为私有的成员变量,具体的new_handler函数可以由构造函数传入,但编译器要求set_new_handler是静态的,所以通过构造函数传入new_handler不被编译器支持,只能将set_new_handler与operator new都写成静态的,同时定义一个静态的new_handler变量,像下面这样:
class Widget
{
private:
static new_handler CurrentHandler; public:
void set_new_handler(new_handler h) throw()
{
CurrentHandler = h;
}
static void* operator new(size_t size)
{
Widget::set_new_handler(CurrentHandler);
return ::operator new(size);
}
}; new_handler Widget::CurrentHandler = ;
属于类的静态变量CurrentHandler用于保存当前环境下的new_handler函数,在operator_new中,先设置成当前的new异常处理函数,再去调用std的operator new,执行内存分配操作。但这里就存在问题了,set_new_handler到下一次设置它为止,一直都是生效的,我们只想在处理这个类对象的分配时用自定义的new_handler函数,但是类似于new int,new char这些基本类型,还是希望走默认的new_handler(就是null,就是什么也不执行,如我们期望,这样会抛出异常)。
一种自然的想法,就是在调用operator new末尾处还原new_handler,这就需要保存之前的new_handler,为此,我们构造一个NewHandlerHolder类,像下面这样:
class NewHandlerHolder
{
private:
new_handler SavedHandler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder& operator= (const NewHandlerHolder&); public:
explicit NewHandlerHolder(new_handler h) :SavedHandler(h){}
~NewHandlerHolder()
{
set_new_handler(SavedHandler);
}
};
这里有一个SavedHandler成员变量,它在NewHandlerHolder构造时确定具体的指向(其实就是指向系统默认的new_handler函数(即null),将拷贝构造函数与赋值运算符重载设置为private是为了防止出现拷贝的行为(在编译期就可以阻止),这点可以参照之前的条款。还要特别注意这里的析构函数,它调用了set_new_handler,将new异常的处理恢复成SavedHandler(其实就是null)。
这样我们重新整理一下Widget,如下:
class Widget
{
private:
static new_handler CurrentHandler; public:
static new_handler set_new_handler(new_handler h) throw()
{
new_handler OldHandler = CurrentHandler;
CurrentHandler = h;
return OldHandler;
}
static void* operator new(size_t size)
{
NewHandlerHolder h(Widget::set_new_handler(CurrentHandler));
return ::operator new(size);
}
}; new_handler Widget::CurrentHandler = ;
为了返回系统默认的new_handler,我们在set_new_handler处理完之后,进行了旧handler的返回,同时在operator new的调用中进行了NewHandlerHolder的包装,这样在return之后,h会自动调用析构函数,恢复成默认的new_handler。
到这一步,本条款的重要内容已经说完了,但为了避免重复劳动,即为每一个需要重定义new_handler的类都写一份set_new_handler和operator new,书上在最后对之进行了封装,其实就是将Widget专门作为这两个函数(set_new_handler和operator new)的类,然后将需要自行处理new_handler的类public继承于Widget即可。
最后总结一下:
set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。
读书笔记_Effective_C++_条款四十九:了解new_handler的行为的更多相关文章
- 读书笔记_Effective_C++_条款二十九:为“异常安全”而努力是值得的
还是举书上的例子: void PrettyMenu::changeBackground(std::istream& imgSrc) { lock(&mutex); delete bgI ...
- 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承
private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...
- 读书笔记_Effective_C++_条款四十八:了解模板元编程
作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...
- 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数
这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...
- 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型
比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...
- 读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template
标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况. 假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对 ...
- 读书笔记_Effective_C++_条款四十二:了解typename的双重意义
顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...
- 读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承
多重继承是一种比较复杂的继承关系,它意味着如果用户想要使用这个类,那么就要对它的父类也了如指掌,所以在项目中会带来可读性的问题,一般我们都会尽量选择用单继承去替代它. 使用多重继承过程容易碰到的问题就 ...
- 读书笔记_Effective_C++_条款三十:了解inline的里里外外
学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...
随机推荐
- Atitit.实现反向代理(1)----url rewrite 配置and内容改写 and -绝对路径链接改写 java php
Atitit.实现反向代理(1)----url rewrite 配置and内容改写 and -绝对路径链接改写 java php 1. 代理的实现:::普通代理and反向代理?? 1 2. url ...
- Python之Web框架们
Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块. pip i ...
- Head First Design Patterns
From Head First Design Patterns. Design Principle: Idnetify the aspects of your application that var ...
- 解决安卓SDK更新连不通问题
http://wenku.baidu.com/link?url=d7t81OFF4_o2YF9iBne-azyovROGPGOozMgWKNyAIQK8vtI0mIjvzpfdOXg7KOobu202 ...
- 澳洲最大的华资快递公司ACE 签约动软微信商城系统!
ACE-平安立达快递是澳洲最大的华资快递公司ACE平安立达,总部设在Boxhill,同时在中国成立了进口食品专营连锁加盟店“澳莱优品”,目前已经有近20家加盟店,14年底国内计划开到60家店. 201 ...
- 销傲销售过程GSP管理系统功能概述
1 公司介绍 西安海思威软件有限公司于2009年2月注册成立,海思威软件公司隶属于海思威集团,位于交通十分便利的西安经济技术开发区.公司致力于中国本土式销售管理的研究与管理软件产品的开发,是 ...
- Linux环境安装jdk
Linux中JDK1.6的安装和配置方法 一.安装 创建安装目录,在/usr/java下建立安装路径,并将文件考到该路径下: # mkdir /usr/java 1.jdk-6u11-linux-i5 ...
- AtomicInteger简介
这个类真的非常实用,更重要的是 它确实非常简单: 附上自己的代码,可以自己试试: AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的 ...
- 【转】ASP.NET WEB API系列教程
from: 西瓜小强 http://www.cnblogs.com/risk/category/406988.html ASP.NET Web API教程(六) 安全与身份认证 摘要: 在实际的项目应 ...
- OsmocomBB 编译安装
工具: sudo apt-get install libtool shtool autoconf git-core pkg-config make gcc gnuarm: ## 32 bit wget ...