条款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);
.......
}
4、operator 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));