您的位置:首页 > 编程语言 > C语言/C++

Effective C++ 总结2 内存管理 (条款5 - 10)

2010-12-06 14:51 381 查看
5. 对应的new和delete要采用相同的形式

用new的时候会发生两件事。首先,内存被分配(通过operator new 函数),然后,为被分配的内存调用一个或多个构造函数。

用delete的时候,也有两件事发生:首先,为将被释放的内存调用一个或多个析构函数,然后,释放内存(通过operator delete 函数)。

对于 delete来说会有这样一个重要的问题:内存中有多少个对象要被删除?答案决定了将有多少个析构函数会被调用。

string *stringptr1 = new string;
string *stringptr2 = new string[100];

...

delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组

6. 析构函数里对指针成员调用delete

增加一个指针成员意味着几乎都要进行下面的工作:
·在每个构造函数里对指针进行初始化。对于一些构造函数,如果没有内存要分配给指针的话,指针要被初始化为0(即空指针)。(构造函数  拷贝构造函数)
·删除现有的内存,通过赋值操作符分配给指针新的内存。 (赋值函数)
·在析构函数里删除指针。  (析构函数)

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

1) operator new在无法完成内存分配请求时会抛出异常 std::bad_alloc

2) 当内存分配请求不能满足时,调用你预先指定的一个出错处理函数。这个方法基于一个常规,即当operator new不能满足请求时,会在抛出异常之前调用客户指定的一个出错处理函数——一般称为new-handler函数

3) 指定出错处理函数时要用到set_new_handler函数, operator new不能满足内存分配请求时,new-handler函数不只调用一次,而是不断重复,直至找到足够的内存。

// function to call if operator new can't allocate enough memory
void nomorememory()
{
cerr << "unable to satisfy request for memory/n";
abort();
}

int main()
{
set_new_handler(nomorememory);
int *pbigdataarray = new int[100000000];

...

}
[/code]
4) 一个设计得好的new-handler函数必须实现下面功能中的一种。
·产生更多的可用内存。

·安装另一个不同的new-handler函数。

·卸除new-handler。

·抛出std::bad_alloc或从std::bad_alloc继承的其他类型的异常。

正确写法

class x {
public:
static new_handler set_new_handler(new_handler p);
static void * operator new(size_t size);

private:
static new_handler currenthandler;
};

new_handler x::currenthandler;	//缺省设置currenthandler为0(即null)

new_handler x::set_new_handler(new_handler p)
{
new_handler oldhandler = currenthandler;
currenthandler = p;
return oldhandler;
}

void * x::operator new(size_t size)
{
new_handler globalhandler =		// 安装x的new_handler
std::set_new_handler(currenthandler);

void *memory;

try { 	// 尝试分配内存
memory = ::operator new(size);
}

catch (std::bad_alloc&) { 	// 恢复旧的new_handler
std::set_new_handler(globalhandler);
throw;	// 抛出异常
}
std::set_new_handler(globalhandler);	// 恢复旧的new_handler
return memory;
}

int main()
{
x::set_new_handler(nomorememory);

x *px1 = new x;

string *ps = new string;

x::set_new_handler(0);

x *px2 = new x;

return 0;
}
[/code]
6) operator new 不抛出异常

class widget { ... };

widget *pw2 = new (nothrow) widget;

8. 写operator new和operator delete时要遵循常规

1)  有关返回值的部分很简单。如果内存分配请求成功,就返回指向内存的指针;如果失败,则遵循条款7的规定抛出一个std::bad_alloc类型的异常。2)  operator new实际上会不只一次地尝试着去分配内存,它要在每次失败后调用出错处理函数,还期望出错处理函数能想办法释放别处的内存。只有在指向出错处理函数的指针为空的情况下,operator new才抛出异常。

这样,非类成员形式的operator new的伪代码看起来会象下面这样:
void * operator new(size_t size)        // operator new还可能有其它参数
{

if (size == 0) {                      // 处理0字节请求时,
size = 1;                           // 把它当作1个字节请求来处理
}
while (1) {
分配size字节内存;

if (分配成功)
return (指向内存的指针);

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

if (globalhandler) (*globalhandler)();
else throw std::bad_alloc();
}
}


条款7提到operator new内部包含一个无限循环,上面的代码清楚地说明了这一点——while (1)将导致无限循环。
跳出循环的唯一办法是
内存分配成功或
出错处理函数完成了条款7所描述的事件中的一种:
得到了更多的可用内存;
安装了一个新的new-handler(出错处理函数);
卸除了new-handler;
抛出了一个std::bad_alloc或其派生类型的异常;
或者返回失败。

9. 避免隐藏标准形式的new
1) 为class重载operator new必须是static

正确写法
class x {
public:
void f();

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

static void * operator new(size_t size)
{ return ::operator new(size); }
};

x *px1 =
new (specialerrorhandler) x;      // 调用 x::operator
// new(size_t, new_handler)

x* px2 = new x;                     // 调用 x::operator
// new(size_t)


另外一个正确写法
class x {
public:
void f();

static
void * operator new(size_t size,                // p缺省值为0
new_handler p = 0);         //
};

x *px1 = new (specialerrorhandler) x;               // 正确

x* px2 = new x;                                     // 也正确


10. 如果写了operator new就要同时写operator delete
1) operator delete想弄清它要释放的内存有多大,就必须知道当初operator new分配的内存有多大。有一种常用的方法可以让operator new来告诉operator delete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。也就是说,当你写了下面的语句,
airplane *pa = new airplane;
你不会得到一块看起来象这样的内存块:
pa——> airplane对象的内存
而是得到象这样的内存块:
pa——> 内存块大小数据 + airplane对象的内存

2) 如果只写operator new 但是不写operator delete,那么会出现调用类自己写的operator new,但是却调用了全局的operator delete

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: