Effective C++学习笔记 第二弹 5-10

 

条款5 使用相同形式的new和delete

简而言之 new对应delete

               new[]对应delete[]

因为一个构造函数对应一个析构函数,new了多少个对象就需要在delete时虚构掉多少个对象

条款6 记得在destructor中以delete对付pointer members

对于类中的几乎所有指针成员变量,都需要配合以下每一件事:

1、在构造函数中将他们一一初始化或者初始化为NULL指针

2、在重载赋值运算符(=)中将指针原有的内存删除,重新配置一块

3、虚构函数中该delete的都delete掉(不然会出现内存泄露的问题,不易察觉,但很致命)

条款7 为内存不足的状况预做准备

当Operator New无法满足需求时,会在抛出Exception之前先反复调用new_handler,不断调用直到找到足够内存为止

也可以用set_new_handler指定一个无返回值、无参数的函数代替new_handler

#include <new>

void NoMemory(){ cout<<"内存不足"<<endl; abort(); }

void main()

{

   set_new_handler(NoMemory);

   int *p = new int[100000000000000];

   ..........

}

1、显然上列的NoMemory()不能很好的工作,一个良好设计的new_handler需要完成以下功能

(1)让更多内存可用。

(2)更换new_handler,假如另一个new_handler更好用,更容易取得内存

(3)卸载这个new_handler,即set_new_handler(null),实在取不到内存了那就让他抛出bad_alloc异常吧。(在抛出异常前会一直调用new_handler,除非卸载)

(4)抛出一个类别为bad_alloc或派生类别

(5)无返回值,直接退出程序,abort() exit()

 

2、可重用的base class

#include <new>
#include <iostream>

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;
}

template<class T>
new_handler NewHandlerSupport<T>::currentHandler = NULL;

       之所以用模板类是为了确保每一个继承而来的class拥有一个不同的currentHandler,如果不用template那么每一个继承类的对象都共用一个static成员变量currentHandler,不能实现多态。

当需要类加上set_new_handler支持能力就只要继续自NewHandlerSupport

class Example:public NewHandlerSupport<Example>{

    ...

}

3、当内存配置失败时会抛出bad_alloc异常,longlongago的C++是不抛出异常而只是返回0,

     当我们还需要这个机制时,可以使用nothrow

如:

class Widget { ...  };

Widget *pw1 = new Widget;//配置失败 会抛出异常

if(pw1 == 0)  ....                 //这条永远为假 因为失败时不会运行到这一步

 

class Widget { ...  };

Widget *pw2 = new (nothrow) Widget;//配置失败 返回0

if(pw2 == 0)  ....                 // if条件可能为真

条款8 撰写operator new和operator delete时应遵行的公约

1、C++ standard要求,即使用户要求的是0bytes内存,operator new也应该传回一个合法指针。

2、operator new的伪码

     void * operator new (size_t size)

    {

         if(size == 0 ){

               size = 1;

         }

         while(true){

               attempt to allocate size bytes;

               if(success)

                  return (a pointer to the memory);

 

        //配置不成功 找出目前的错误处理函数

       new_handler globalHandler = set_new_handler(0);

       set_new_handler(globalHandler);

       if(globalHandler)

            (*globalHandler)();

       else

            throw std::bad_alloc();

      }

    }

3、Base class的operator new可以被继承,为了应付子类调用的局面,最好的办法是将“不正确数量”之内存索求动作丢给标准的

     operator new 去执行,

如:

void *Base::operator new(size_t size)

{

    if(size ! = sizeof(Base))                  //sizeof(Base)不可能为0,即使没有任何member 当申请0size内存时,会掉过这句

          return ::operator new(size);

     .......

}

4operator delete需要保证删除一个null指针永远是安全

于是operator delete的伪码可以这么写

void Base::operator delete(void *rawMemory,size_t size)

{

    if(rawMemory == 0)  return;

    if(size != sizeof(Base)){

         ::operator delete(rawMemory);

         return;

     }

     deallocate the memory pointed to by rawMemory;

     return;

}

条款9 避免遮掩了new的正规形式

当operator new接受额外的参数时,会出现不能调用global operator new的问题

如;

class Example

{

public:

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

};

void specialErrorHandler();  //定义于它处

Example *pe1 = new (specialErrorHandler) Example;     //调用Example::operator new

Example *pe2 = new Example; //Error 预调用global版本,却不行

有两种解决办法

1、

class X{

public:

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

    static void *operator new(size_t size)

    {   return ::operator new(size);   }

};

2、

class X{

public:

     static void *operator new(size_size, new_handler p = 0);         //设置其余参数的默认值

};

条款10 如果你写了一个operator new,请对应写一个operator delete

通常new操作之后会获得一块这样的内存(加挂于前的小量内存有时被称为cookie)


如果所需内存不大的话,cookie所占的内存就显得很浪费。所以要定制new和delete。

方法一:

#include <iostream>

class AirplaneRep
{
};

class Airplane{
public:
 static void *operator new(size_t size);
private:
 union{
  AirplaneRep *rep;//指向使用中的对象                      两者取一
  Airplane *next;     //指向未使用待分配的对象
 };
 static const int BLOCK_SIZE;
 static Airplane *headofFreeList;
};

void *Airplane::operator new(size_t size)
{
 if(size != sizeof(Airplane))
  return ::operator new(size);

 Airplane *p =
  headofFreeList;
 if(p)
  headofFreeList = p->next;//已分配 指向FreeList头一块空间
 else{//新分配一大块内存
  Airplane *newBlock =
   static_cast<Airplane*>(::operator new(BLOCK_SIZE*sizeof(Airplane)));
  for(int i = 1;i<BLOCK_SIZE - 1;++i)
   newBlock[i].next = &newBlock[i+1];

  newBlock[BLOCK_SIZE-1].next = 0;

  p = newBlock;
  headofFreeList = &newBlock[1];
 }
 return p;
}

Airplane *Airplane::headofFreeList;           //默认情况下static member会被初始化为0
const int Airplane::BLOCK_SIZE = 512;

 

当delete方法一中的多个对象会出现问题,因为delete找不到任何表头信息(cookie),于是乎,不知道释放多少大小的空间。

所以需要重载delete运算符

void Airplane::operator delete(void *deadObject,
                            size_t size)
{
    if(deadObject == 0)  return;

    if(size != sizeof(Airplane)){
         ::operator delete(deadObject);
   return;
 }
 Airplane *carcass =
  static_cast<Airplane*>(deadObject);
 carcass->next = headofFreeList;
 headofFreeList = carcass;
}

方法二:

方法一中反复对一个对象声明和释放会产生多余的操作。方法二用内存池的机制来解决这个问题,而且降低Airplane的耦合度。

class Pool{

public:

   Pool(size_t n);                         //预先分配大小为n的objects

   void *alloc(size_t n);                //为一个object配置足够的内存

   void free(void *p,size_t n);      //将p所指的内存送回给Pool

   ~Pool();                                   //释放Pool中的所有内存

};

 

class Airplane{

public:

   static void *operator new(size_t size);

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

private:

   AirplaneRep *rep;                       //指向实际对象

   static Pool memPool;

}

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);}

Pool Airplane::memPool(sizeof(Airplane));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值