Effective C++ 7

7.预先准备好内存不够的情况。

new在无法完成内存分配请求时,会抛出异常,异常了要怎么办,这是一个很现实且以后绝对要碰到的问题。

在c中一般使用宏来分配内存并检测分配是否成功,c++中类似以下函数:

#define NEW(PTR,TYPE)    try { (PTR) = new TYPE;}  	catch (std::bad_alloc& ){assert(0);}

catch 的std::bad_alloc为new 操作符中 不能满足内存分配请求时抛出的异常类型。

assert是在<assert.h>中宏,或者带命名空间的<cassert> 。宏检测传给它的表达式的值是否为0,如果是0,就会发出一条出错信息,并调用abort。assert在没有定义标准宏NDEBUG时,即只在调试状态中这么做,当产品发布后,即定义了NDEBUG后,assert什么也不做。

但使用宏是不够的,因为其只能进行简单的一个基本数据类型的内存分配,而对于数组以及有构造函数的数据类型无法使用。可以考虑重载operator new 。

而常用的简单的出错处理方法是,通过设置一个出错处理函数,使内存分配请求不能满足时,调用这个处理函数。因为operator new 操作符在不能满足请求时,会在抛出异常前调用客户指定的一个出错处理函数,这个函数称为new-handler函数。

指定出错处理函数要用到set_new_handler 函数,该函数位于头文件<new>中,其定义如下:

new_handler __cdecl set_new_handler(_In_opt_ new_handler)
	_THROW0();

而new_handler的定义如下:

typedef void (__cdecl * new_handler) ();

new_handler为指向一个没有参数没有返回值的函数,而set_new_handler函数是一个输入参数为 operator new 分配内存失败时调用的出错处理函数的指针,返回值为set_new_handler函数没调用之前的已经在起作用的旧的函数处理函数的指针。使用方法如下:

void noMoreMemory(){
	cerr<<"123";
	abort();
}
int main(){
	set_new_handler(noMoreMemory);
	int *p = new int [ 0x1fffffff ];

真正在程序中,new_handler函数不是简单的检测出错然后停止程序,而是要去处理出错,即通过一些措施,使下次内存分配可以成功。一个好的new_handler函数从以下几个方向入手:

1.产生更多的可用内存。若在程序启动时预留一大块内存。然后失败时释放出这些内存供使用。

2.安装另一个不同版本的new_handler函数。通过一个新的可以获得资源的函数来获得更多资源,或者使用一些全局变量来改变自身的行为。

3.卸载new_handler。分配无法成功,直接返回抛出标准的std::bad_alloc异常。

4.抛出bad_alloc异常或bad_alloc的派生异常。

5.没有返回,即如上所示,调用abort或exit。

类中的内存分配出错可以有自己重载的类的new操作符和 set_new_handler来实现。而这些函数与重载都是类的静态成员。

首先,类中要有一个静态成员 currentHandler来保存当前使用的new_handler函数。

对于set_new_handler,它只是将类内的指针进行更换,以及返回旧的指针,而标准版本也是如此做的。

对于operator new ,类中的重载new操作符,只是简单的调用原有的new操作符操作,只是错误处理用上类中设置的新的出错处理函数,所以其做法只是用全局的set_new_handler()设置当前类中的currentHandler为处理函数,分配内存后或者异常报错前,恢复即可。

代码如下:

class A{
public:
	static void* operator new (size_t size);
	static new_handler set_new_handler(new_handler p);
private:
	static new_handler currentHandler;
};
new_handler A::currentHandler;
new_handler A::set_new_handler(new_handler p){
	new_handler t = currentHandler;
	currentHandler = p;
	return t;
}
void* A::operator new(size_t size){
	new_handler globalHandler = std::set_new_handler(currentHandler);
	void* memory;
	try{
		memory = ::operator new(size);//::operator new 表示为全局的new操作符。
	}
	catch(std::bad_alloc&){
		std::set_new_handler(globalHandler);
		throw;
	}
	std::set_new_handler(globalHandler);
	return memory;
}

而要做到很好的代码重用,即对任何一个类都能轻松的实现以上代码实现的内容,可以创建一个混合风格的基类,这种基类允许子类继承它的某一特定功能,如这里的new操作符的出错处理。

混合风格?43.

时间: 2024-08-04 12:39:02

Effective C++ 7的相关文章

《Effective C++》读书笔记汇总

我之前边读<Effective C++>边写下每个条款的读书笔记,这一版是C++11之前的版本.这里我将每个条款令我印象深刻的点小结一下. 1.C++包括:Plain C(面向过程).OOP(面向对象).模板(泛型和模板元编程).STL(C++标准库). 2.用inline.enum.const代替#define.#define定义的宏,一旦复杂起来,高手都很难掌控.不要带入C的习惯. 3.灵活使用const前缀.不需要进行改变的数据加上const前缀.指针的const前缀有两种形式,cons

Effective Java之内部类

Effective Java中对类的权限,接口等的要求,总结起来就是够用就行,不要赋予过多的访问权限,类的定义也是,如果某个类只会在类的内部使用,那就将该类定义为内部类吧. 内部类分为四种: 1.静态内部类:静态内部类就是在class前面多了static关键词的内部类,这种类和类的静态方法和静态变量一样,针对类本省进行操作,在静态内部类中可以随意访问其所在类的静态方法和静态变量. 2.非静态内部类:和静态内部类相对于,其实在类内部定义的所有东西只是受到访问修饰符的限制,所以非静态内部类和类的非静

学习日记之状态模式和Effective C++

状态模式(State):当一个对象内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类. (1),状态模式主要负责解决的是当控制一个对象转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. (2),状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来. (3),将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和

Effective Objective-C 2.0重读笔记---1

上次看这本书的时候匆匆走了一遍,最近不太忙,重温了一遍,把笔记写出来~.. 有兴趣的可以去买一本,感觉这本书还是挺不错的 由于大部分是在坐车的时候用手机写的,所以代码很少,图也很少 1. 尽量使用向前声明,延后引入头文件的时间,这样可以减少编译时间2. 使用arraywithobjects:....如果遇到为空的变量,就会自动终止,后面的变量便会添加不上,切不会报错,会造成数据不一致问题,所以尽量使用字面量语法创建相关对象,减少出错且减少代码量3. 使用字面量创建的对象都是不可变的,如果要获得可

Effective前端2:优化html标签

  借用Effective之名,开始写Effective系列,总结一些前端的心得. 有些人写页面会走向一个极端,几乎页面所有的标签都用div,究其原因,用div有很多好处,一个是div没有默认样式,不会有margin.background等初始化设置,另外可能会觉得不用div还能用啥.所以看他的页面,一展开是div,再展开还是div,展开四.五层都是div. 这样对用户来说虽然没什么区别,但是作为一名有追求的程序员,这种写法看起来是比较难受的.有些人虽然知道html5新增了很多标签,但也不怎么去

Effective前端1:能使用html/css解决的问题就不要使用JS

  借用Effective之名,开始写Effective系列,总结一些前端的心得. 为什么说能使用html/css解决的问题就不要使用JS呢?两个字,因为简单.简单就意味着更快的开发速度,更小的维护成本,同时往往具有更好的体验,下面介绍几个实例. 1. 导航高亮 导航高亮是一种很常见的问题,包括当前页面的导航在菜单里面高亮和hover时高亮.你可以用js控制,但是用一点CSS技巧就可以达到这个目的,不需要使用JS. 在正常态时,每个导航的默认样式为: 正常态透明度为0.5 CSS 1 2 3 n

【总结】Effective java经验之谈,类与接口

转载请注明出处:http://blog.csdn.NET/supera_li/article/details/44940563 Effective Java系列 1.Effective java经验之谈,创建和销毁对象 2.Effective java经验之谈,泛型 3.Effective java经验之谈,类与接口 4.Effective java经验之谈,通用方法 5.Effective java经验之谈,枚举,注解,方法,通用设计,异常 6.Effective java经验之谈,并发编程

阅读《effective java-第17条》遇到的问题解决与分享

问题背景 最近这2天准备重新看一遍<effective java>,发现这些经典的书籍真的是看一遍又有一遍的感受.也越来越觉的学习的过程是一个重复的过程.这次遇到的问题是在第17条中看到的,看了蛮久都没有看懂视例代码.第17条的内容是要么为继承而设计,并提供文档说明,要么就禁止继承.在其中有一段示例构造器决不能调用可被覆盖的方法代码如下: 父类代码 package com.sitech.test; /** * effect of java * @author liaowp * */ publi

《Effective C++》之条款34:区分接口继承和实现继承

<Effective C++> 条款34:区分接口继承和实现继承 Public继承由两部分组成 函数接口继承 函数实现继承 三种可能面对的继承 derived class 只继承成员函数的接口 derived class 同时继承函数的接口和实现,但又希望能够覆写它们所继承的实现 derived class 同时继承函数的接口和实现,但不允许覆写任何东西 总结: 接口继承和实现继承不同.在public继承下,derived classes 总是继承base class 的接口. Pure vi

EFFECTIVE JAVA 第十章 并发

EFFECTIVE  JAVA  第十章  并发 66.同步访问共享的可变数据 *java语言规范保证读或写一个变量是原子的(可以保证返回的值是某个线程保存在该变量中的),除非这个变量的类型为long或double.(但并不保证一个线程写入的值对于另一个线程是可见) *synchronized修饰方法.synchronized代码块可以实现同步 *volatile修饰的变量只保证读取的是主存里最新的值而不是内存中该值的拷贝,使用volatile变量必须遵循(即变量真正独立于其他变量和自己以前的值