如果global new-hander没有成功配置,会抛出一个std::bad_alloc的exception。

#include<iostream>
#include<new>
#include<climits>
using namespace std; template<class T>
class NewHandlerSupport{
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);
private:
static new_handler currentHandler;
}; template<class T>
new_handler NewHandlerSupport<T>::set_new_handler(new_handler p)
{
new_handler oldHandler=currentHandler;
currentHandler=p;
return oldHandler;
} template<class T>
void * NewHandlerSupport<T>::operator new(size_t size)
{
new_handler globalHandler=std::set_new_handler(currentHandler); void *memory;
try{
memory=::operator new(size);
}catch(std::bad_alloc&){
std::set_new_handler(globalHandler);
throw;
}
std::set_new_handler(globalHandler);
return memory;
} //以下使得每一个currentHandler为0
template<class T>
new_handler NewHandlerSupport<T>::currentHandler; class X:public NewHandlerSupport<X>
{
private:
int data;
};
void noMoreMemory()
{
cerr<<"unable to satify the memory"<<endl;
abort();
} int main()
{
X::set_new_handler(noMoreMemory);
X *px1=new X; //如果内存配置失败,则调用noMoreMemory string *ps=new string;//如果内存配置失败,则调用global new_handler (如果有的话) X::set_new_handler();//设定专属的new-hander为NULL
X *px2=new X;//如果内存配置失败,立刻抛出一个exception,因为此刻已没有登录“Class X专属的new-handler" }

说明:

知道1993年之前,c++还要求opertor new 在无法满足内存是传回0,现在的标准是抛出一个std::bad_alloc.为了以前的兼容,

c++继续提供”失败便传回0“的传统行为。这种形式称为nothrow形式。因为他们从不做任何抛出throw动作,而且他们在使用new的同时,使用了nothrow objects.(定义于标准头文件<new>之中)

class Widget { ... };
Widget *pw1 = new Widget;                 // throws bad_alloc if
                                          // allocation fails
if (pw1 == 0) ...                         // this test must fail 这个测试一定会失败
Widget *pw2 =new (std::nothrow) Widget;   // returns 0 if allocation for
                                          // the Widget fails
if (pw2 == 0) ...                         // this test may succeed

在条款10中,作者写了memory pool。

让我们回过头去看看这样一个基本问题:为什么有必要写自己的operator new和operator delete?

答案通常是:为了效率。缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。尤其在那些需要动态分配大量的但很小的对象的应用程序里,情况更是如此。

例如有这样一个表示飞机的类:类airplane只包含一个指针,它指向的是飞机对象的实际描述(此技术在条款34进行说明):

class airplanerep { ... }; // 表示一个飞机对象
//
class airplane
{
public:
...
private:
airplanerep *rep; //
指向实际描述
};

一个airplane对象并不大,它只包含一个指针(正如条款14和m24所说明的,如果airplane类声明了虚函数,会隐式包含第二个指针)。但当调用operator
new来分配一个airplane对象时,得到的内存可能要比存储这个指针(或一对指针)所需要的要多。之所以会产生这种看起来很奇怪的行为,在于operator
new和operator delete之间需要互相传递信息。

因为缺省版本的operator new是一种通用型的内存分配器,它必须可以分配任意大小的内存块。同样,operator
delete也要可以释放任意大小的内存块。operator delete想弄清它要释放的内存有多大,就必须知道当初operator
new分配的内存有多大。有一种常用的方法可以让operator new来告诉operator
delete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。也就是说,当你写了下面的语句,

airplane *pa = new airplane;

你不会得到一块看起来象这样的内存块:

pa——> airplane对象的内存

而是得到象这样的内存块:

pa——> 内存块大小数据 + airplane对象的内存

对于象airplane这样很小的对象来说,这些额外的数据信息会使得动态分配对象时所需要的的内存的大小翻番(特别是类里没有虚拟函数的时候)。

如果软件运行在一个内存很宝贵的环境中,就承受不起这种奢侈的内存分配方案了。为airplane类专门写一个operator new,就可以利用每个airplane的大小都相等的特点,不必在每个分配的内存块上加上附带信息了。

具体来说,有这样一个方法来实现你的自定义的operator new:先让缺省operator new分配一些大块的原始内存,每块的大小都足以容纳很多个airplane对象。airplane对象的内存块就取自这些大的内存块。当前没被使用的内存块被组织成链表——称为自由链表free list——以备未来airplane使用。听起来好象每个对象都要承担一个next域的开销(用于支持链表),但不会:rep域的空间也被用来存储next指针(因为只是作为airplane对象来使用的内存块才需要rep指针;同样,只有没作为airplane对象使用的内存块才需要next指针),这可以用union来实现。

具体实现时,就要修改airplane的定义,从而支持自定义的内存管理。可以这么做:

class airplane { // 修改后的类 — 支持自定义的内存管理
public: //

static void * operator new(size_t size);

...

private:
union {
airplanerep *rep; // 用于被使用的对象

airplane *next; // 用于没被使用的(在自由链表中)对象
};

// 类的常量,指定一个大的内存块中放多少个
// airplane对象,在后面初始化
static const int
block_size;

static airplane *headoffreelist;

};

上面的代码增加了的几个声明:一个operator
new函数,一个联合(使得rep和next域占用同样的空间),一个常量(指定大内存块的大小),一个静态指针(跟踪自由链表的表头)。表头指针声明为静态成员很重要,因为整个类只有一个自由链表,而不是每个airplane对象都有。static非常重要。

#include<iostream>
#include<new>
#include<climits>
#include<string>
using namespace std;
class AirPlaneRep{
public:
string name;
}; class AirPlane
{
public:
static void * operator new(size_t size);
static void operator delete(void *deadObject,size_t size); public:
union{
AirPlaneRep *rep;
AirPlane *next;//正对free list内的对象
}; static const int BLOCK_SIZE;
static AirPlane *headOfFreeList;
};
void * AirPlane::operator new(size_t size)
{
//如果大小错误,将内存配置申请转交给:operator new
//详见条款8
if(size!=sizeof(AirPlane))
return ::operator new(size); AirPlane *p=headOfFreeList;//p指向free list的头部
//如果p是有效的,就把list的头部移往free list的下一个元素
if(p)
headOfFreeList=p->next;
else
{
//free list已空,配置一块够大的内存
AirPlane *newBlock=static_cast<AirPlane*>(::operator new(BLOCK_SIZE*sizeof(AirPlane))); //组成一个新的free list:将小块内存串接在一起
//跳过第0个元素,因为你要将他传回给operator new的调用者
for(int i=;i<BLOCK_SIZE-;i++)
newBlock[i].next=&newBlock[i+]; //以null指针作为整个linked list的结束
newBlock[BLOCK_SIZE-].next=; //将p设为list的头部,将headOfFreeList设为下一个可被运用的小块内存
p=newBlock;
headOfFreeList=&newBlock[];
}
return p;
} void AirPlane::operator delete(void *deadObject,size_t size)
{
if(deadObject==) return;//见条款8
if(size!=sizeof(AirPlane))//见条款8
{
::operator delete(deadObject);
return;
}
AirPlane *carcass=static_cast<AirPlane*>(deadObject);
carcass->next=headOfFreeList;
headOfFreeList=carcass;
} AirPlane *AirPlane::headOfFreeList;
const int AirPlane::BLOCK_SIZE=; int main()
{
AirPlane *pa=new AirPlane();
AirPlaneRep *pr=pa->rep=new AirPlaneRep();
pr->name="jack"; AirPlane *pb=new AirPlane();
AirPlaneRep *pr2=pa->rep=new AirPlaneRep();
pr2->name="jack2"; cout<<"sizeof(AirPlane)"<<sizeof(AirPlane)<<endl;
cout<<pr->name<<ends<<pr2->name<<endl;
cout<<pa<<ends<<pb<<endl; }

输出:

sizeof(AirPlane)4
jack jack2

003C9588  003C958C

问:这里有memory leak吗?

答:不存在,memory leak问题是”在配置内存后,所有指向该内存的指针都遗失了“时才发生,如果缺乏垃圾收集系统(gc)或某种特殊的语言机制,这样的内存就没办法归还给系统。这里并没有内存泄露问题,因为并不会发生”所有指针都遗失“的情况。

如果将以上的内存配置管理方法,抽象出来成为一个独立的类,叫作 pool class,则对于不同的类与 pool 配置器搭配将获得这种内存配置功能:

接口如下:

class pool

{

public:

pool ( size_t n ) ;   //配置出一个大小为 n objects 的内存空间

void * alloc ( size_t n ) ;    //相当于上面的 operator new 的实现

void free ( void * p , size_t size ) ;   //相当于上面的 operator delete 的实现

~pool ( ) ;

} ;

pool 将负责与自定义内存管理相关的工作,而 Airplane 将不再需要增加一些与 Airplane 本身无关的代码。

如果一个 pool 对象被产生, 则有一大块内存被创建;如果这个对象被销毁,它会释放它配置的所有内存。如今,Airplane 类可以通过聚合一个 pool 得到自定义的内存管理能力:

class airplane {
public:

... // 普通airplane功能

static void * operator new(size_t size);
static void operator
delete(void *p, size_t size);

private:
airplanerep *rep; // 指向实际描述的指针
static pool
mempool; // airplanes的内存池

};

inline void * airplane::operator new(size_t size)
{ return
mempool.alloc(size); }

inline void airplane::operator delete(void
*p,
size_t size)
{ mempool.free(p,
size); }

// 为airplane对象创建一个内存池,
// 在类的实现文件里实现
pool
airplane::mempool(sizeof(airplane));

这个设计比前面的要清楚、干净得多,因为airplane类不再和非airplane的代码混在一起。union,自由链表头指针,定义原始内存块大小的常量都不见了,它们都隐藏在它们应该呆的地方——pool类里。让写pool的程序员去操心内存管理的细节吧,你的工作只是让airplane类正常工作。

现在应该明白了,自定义的内存管理程序可以很好地改善程序的性能,而且它们可以封装在象pool这样的类里。但请不要忘记主要的一点,operator
new和operator delete需要同时工作,那么你写了operator new,就也一定要写operator delete。

  • class Airplane

  • {

  • public:

  • static void * operator new ( size_t size ) ;

  • static void operator delete ( void * deadObject , size_t size ) ;

  • ……

  • private:

  • AirplaneRep * rep ; //指向一个真实的 Airplane object

  • static pool mempool ;

  • } ;

  • // cpp

  • void * Airplane : : operator new ( size_t size )

  • {   return mempool . alloc ( size ) ;   }

  • void Airplane : : operator delete ( void * deadObject , size_t size )

  • {   mempool . free ( deadObject , size ) ;   }

书中没说怎么实现Pool。可以自己写下。

http://blog.sina.com.cn/s/blog_4b02b8d001007wdi.html

http://bbs.csdn.net/topics/80038013

《Effective C++》内存管理的更多相关文章

  1. C++内存管理(effective c++ 04)

    阅读effective c++ 04 (30页) 提到的static对象和堆与栈对象.看了看侯老师的内存管理视频1~3.有点深. 了解一下. 目录 1 内存管理 1.1 C++内存管理详解 1.1.1 ...

  2. effective OC2.0 52阅读笔记(五 内存管理)

    第五章:内存管理 29 理解引用计数 30 以ARC简化引用计数 总结:ARC通过命名约定将内存管理规则标准化.其他编程语言很少像OC这样强调命名.ARC通过设置全局数据结构(此数据结构的具体内容因处 ...

  3. Objective-C:内存管理

    1 传统内存管理 Objective-C对象的生命周期可以分为:创建.存在.消亡. 1.1 引用计数 类似Java,Objective-C采用引用计算(reference counting)技术来管理 ...

  4. 揭开Java内存管理的面纱

    前言 相对于C.C++这些高性能语言,Java有着让此类程序员羡慕的功能:内存自动管理.似乎这样,Java程序员不用再关心内存,也不用去了解相关知识.但结果真的是这样吗?特别对于我们这种Android ...

  5. Linux内存管理 (13)回收页面

    专题:Linux内存管理专题 关键词:LRU.活跃/不活跃-文件缓存/匿名页面.Refault Distance. 页面回收.或者回收页面也即page reclaim,依赖于LRU链表对页面进行分类: ...

  6. <Linux内核源码>内存管理模型

    题外语:本人对linux内核的了解尚浅,如果有差池欢迎指正,也欢迎提问交流! 首先要理解一下每一个进程是如何维护自己独立的寻址空间的,我的电脑里呢是8G内存空间.了解过的朋友应该都知道这是虚拟内存技术 ...

  7. 进程,内存,管理 ps,pstree,top,free,vmstat,iftop,lsof,查看网速

    一些基础 不同进程之间,进行数据访问 同一主机:pipe 管道 socket   套接字文件 signal   信号 shm   shared memory semaphore 信号量,一种计数器 不 ...

  8. C++内存管理学习笔记(2)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  9. c++内存管理学习纲要

    本系列文章,主要是学习c++内存管理这一块的学习笔记. 时间:6.7-21 之下以技术内幕的开头语,带入到学习C++内存管理的技术中吧: 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题 ...

随机推荐

  1. Migration of ASP.NET app from IIS6 to IIS7 (7.5)

    For many of us familiar problem. You developing applications under IIS6 and you're about to move the ...

  2. ui-router的使用

    使用时需要ui中用ui-view指令指定 如: <div ui-view></div> 首先配置注册 ui-route var mainModule = angular.mod ...

  3. jQuery中的on()和click()的区别 分类: 前端 HTML jQuery 2014-11-06 10:26 96人阅读 评论(0) 收藏

    HTML页面代码 <div> <h1>Click</h1> <button class="add">Click me to add ...

  4. js基础知识之_流程控制语句

    javascript 流程控制 流程控制:就是程序代码执行顺序 流程控制:通过规定的语句让程序代码有条件的按照一定的方式执行 顺序结构 按照书写顺序来执行,是程序中最基本的流程结构 选择结构(分支结构 ...

  5. sql 嵌套事务学习笔记

    以下内容根据此官方文档修改:http://technet.microsoft.com/zh-cn/library/ms189336(v=sql.105).aspx 嵌套事务的使用场景或者说目的主要是为 ...

  6. iOS,长按图片保存实现方法,轻松搞定!

    1.添加手势识别: UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@s ...

  7. 邓白氏编码(duns number)申请入口的路径-苹果开发者申请必

    http://tieba.baidu.com/p/3861287522 这个网址有详细的介绍

  8. PAT_1018 锤子剪刀布

    问题描述 大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示: 现给出两人的交锋记录,请统计双方的胜.平.负次数,并且给出双方分别出什么手势的胜算最大. 输入格式: 输入第1行给出 ...

  9. [Introduction to programming in Java 笔记] 1.3.7 Converting to binary 十进制到二进制的转换

    public class Binary { public static void main(String[] args) { // Print binary representation of N. ...

  10. openssl移植

    一.下载openssl 1.下载网址http://www.openssl.org/source/ 2.下载版本openssl-1.0.0q.tar.gz 二.编译openssl为静态库(X86 lin ...