“伊凡 C普”第一式-内存管理

<条款5>对应的new和delete要采用相同的形式。

这一条没什么可说的,注意new的是不是数组就可以了。其中有两点需要注意:

1.在写一个包含指针数据成员,并且提供多个构造函数的类时,构造中new的形势要相同,否则析构中的delete便"为难"了。

2.喜欢用typedef的人需要注意,最好杜绝对数组类型用typedefs。

=====================================

<条款6>析构函数里对指针成员调用delete

增加一个指针成员意味着几乎都要进行下面的工作:

  • 在每个构造函数里对指针进行初始化。对于一些构造函数,如果没有内存要分配给指针的话,指针要被初始化为0(即空指针)。
  • 删除现有的内存,通过赋值操作符分配给指针新的内存。
  • 在析构函数里删除指针。

=====================================

<条款7>预先准备好内存不够的情况

这一条款说的是:new可能调用失败,你要做好准备工作。

一、与C中类似的方法-宏:

#definenew(ptr, type)           /

try {(ptr) = new type; }       /

catch(std::bad_alloc&) { assert(0); }

此种方法缺点有2:

(1) 只是在没定义标准宏ndebug的时候,即在调试状态下才有效。

(2) 没有考虑到new有各种各样的使用方式。

二、当内存分配请求不能满足时,调用你预先指定的一个出错处理函数。

此方法基于一种机制:即当operator new不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数。

指定出错处理函数时要用到set_new_handler函数,它的输入参数是operator new分配内存失败时要调用的出错处理函数的指针,返回值是set_new_handler没调用之前就已经在起作用的旧的出错处理函数的指针(有点类似GDI中设置dc的画笔)。下面是一个使用set_new_handler的例子。

//function to call if operator new can't allocateenough memory

void nomorememory()

{

cerr<< "unable to satisfy request for memory/n";

abort();

}

int main()
{

set_new_handler(nomorememory);

int*pbigdataarray = new int[100000000];

...... ...

}

operator new不能满足内存分配请求时,new-handler函数不只调用一次,而是不断重复,直至找到足够的内存(里面有一个while(1)循环)。

一个设计得好的new-handler函数必须实现下面功能中的一种:

<1>产生更多的可用内存。这将使operator new下一次分配内存的尝试有可能获得成功。实施这一策略的一个方法是:在程序启动时分配一个大的内存块,然后在第一次调用new-handler时释放。释放时伴随着一些对用户的警告信息,如内存数量太少,下次请求可能会失败,除非又有更多的可用空间。

<2>安装另一个不同的new-handler函数。如果当前的new-handler函数不能产生更多的可用内存,可能它会知道另一个new-handler函数可以提供更多的资源。这样的话,当前的new-handler可以安装另一个new-handler来取代它(通过调用set_new_handler)。下一次operator new调用new-handler时,会使用最近安装的那个。(这一策略的另一个变通办法是让new-handler可以改变它自己的运行行为,那么下次调用时,它将做不同的事。方法是使new-handler可以修改那些影响它自身行为的静态或全局数据。)

<3>卸除new-handler,抛出标准异常。也就是传递空指针给set_new_handler。没有安装new-handler,operator new分配内存不成功时就会抛出一个标准的std::bad_alloc类型的异常。

<4>抛出std::bad_alloc或从std::bad_alloc继承的其他类型的异常。这样的异常不会被operator new捕捉,所以它们会被送到最初进行内存请求的地方。(抛出别的不同类型的异常会违反operator new异常规范。规范中的缺省行为是调用abort,所以new-handler要抛出一个异常时,一定要确信它是从std::bad_alloc继承来的。想更多地了解异常规范,参见条款m14。)

<5>没有返回,直接中断退出。典型做法是调用abort或exit。abort/exit可以在标准c库中找到(还有标准c++库,参见条款49)。

二、写类自己的new和set_new_handler

简言之,分三步:1.class里面的"三样"要齐全。

(1) set_new_handler

(2) operator new

(3) static new_handler currenthandler;

2.实现set_new_handler。

(1) 保存传给它的任何指针,并返回在调用它之前所保存的任何指针。

(2) 保存传给它的任何指针,并返回在调用它之前所保存的任何指针。

3.实现operator new。

(1) 调用标准set_new_handler函数,输入参数为x的出错处理函数。这使得x的new-handler函数成为全局new-handler函数。注意下面的代码中,用了"::"符号显式地引用std空间(标准set_new_handler函数就存在于std空间)。

(2) 调用全局operatornew分配内存。如果第一次分配失败,全局operator new会调用x的new-handler,因为它刚刚(见1.)被安装成为全局new-handler。如果全局operator new最终未能分配到内存,它抛出std::bad_alloc异常,x的operator new会捕捉到它。x的operator new然后恢复最初被取代的全局new-handler函数,最后以抛出异常返回。

(3) 假设全局operatornew为类型x的对象分配内存成功,, x的operator new会再次调用标准set_new_handler来恢复最初的全局出错处理函数。最后返回分配成功的内存的指针。

c++是这么做的。

下面是一个"混合风格"(mixin-style)的基类,这种基类允许子类继承特定的功能:建立一个类的new-handler的功能。

template<classt> // 提供类set_new_handler支持的

classnewhandlersupport {     // 混合风格"的基类

public:

static new_handlerset_new_handler(new_handler p);

static void * operator new(size_tsize);

private:

static new_handler currenthandler;

};

template<classt>

new_handlernewhandlersupport<t>::set_new_handler(new_handler p)

{

new_handler oldhandler =currenthandler;

currenthandler = p;

return oldhandler;

}

template<classt>

void *newhandlersupport<t>::operator new(size_t size)

{

new_handler globalhandler =

std::set_new_handler(currenthandler);

void *memory;

try {

memory = ::operatornew(size);

}

catch (std::bad_alloc&) {

std::set_new_handler(globalhandler);

throw;

}

std::set_new_handler(globalhandler);

return memory;

}

// this sets eachcurrenthandler to 0

template<class t>

new_handlernewhandlersupport<t>::currenthandler;

=====================================

 

<条款8>写operator new和operator delete时要遵循常规

一句话,系统缺省怎么做,你就怎么做。

一、new所要做的

1)要有正确的返回值;

2)可用内存不够时要调用出错处理函数(见条款7);

3)处理好0字节内存请求的情况;

4)避免不小心隐藏了标准形式的new(不过这是条款9的话题)。

这样, operator new的伪代码看起来会象下面这样:

void * operator new(size_t size)          // operator new还可能有其它参数

{                                      

if (size != sizeof(base))                     // 如果数量"错误",让标准operator new

return ::operator new(size);            // 去处理这个请求                            

     while (1) {

分配size字节内存;

if (分配成功)

      return (指向内存的指针);

// 分配不成功,找出当前出错处理函数
    new_handler globalhandler = set_new_handler(0);

set_new_handler(globalhandler);

if (globalhandler) (*globalhandler)();

else throw std::bad_alloc();

  }

}

三、delete的任务

所要记住的只是,c++保证删除空指针永远是安全的,所以你要充分地应用这一保证。

伪代码如下:

void base::operator delete(void *rawmemory, size_t size)

{

  if (rawmemory == 0) return;                   // 检查空指针

  if (size != sizeof(base)) {                 // 如果size"错误",

::operator delete(rawmemory);        // 让标准operator来处理请求

return;                        

  }

  释放指向rawmemory的内存;

  return;

}

=====================================

<条款9>避免隐藏标准形式的new

在类里定义了一个称为"operator new"的函数后,会不经意地阻止了对标准new的访

问。条款50解释了为什么会这样,这里我们更关心的是如何想个办法避免这个问题。

一个办法是在类里写一个支持标准new调用方式的operator new,它和标准new做同样的事。这可以用一个高效的内联函数来封装实现。

另一种方法是为每一个增加到operator new的参数提供缺省值(见条款24)。

=====================================

<条款10>如果写了operator new就要同时写operator delete

问题1:为什么自己写operator new 和 operator delete.(答:为了效率)

问题2:为什么要为自己的new写自己的delete.(答:因为标准delete不知道自己new出了多大内存,见《深度探索c++对象模型》)

问题3:内存池.(详情见eff  c++)

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值