从operator=中“自我赋值”看new的抛出异常

在Effective C++中看到这个问题时才发现以前写的代码完全没有注意过这个问题

“自我赋值”发生在对象被赋值给对象本身时,例如:

class Weight
{...};
...
w=w;

虽然这看上去确实非常的蠢,但是谁也不能保证这不会发生,毕竟这是合法的,但是问题出来了,我们写的显示赋值函数一般是这样的,假设我们在类中使用了动态分配。

class Bitmap{...};
class Weight
{
...
private:
Bitmap * _pb;
};
Weight & Weight::operatot=(const Weight & rhs)
{
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

好的,现在面临的问题是,*this和rhs这两个指向了同一个对象,在执行delete _pb 时同时也将rhs的Bitmap销毁了。

对此提出了两种有效的解决方法

①传统做法在operator=之前做一个“证同测试(identity test)”

Weight & Weight::operatot=(const Weight & rhs)
{
if(this == &rhs)
    return *this;
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

这样做完全行得通,但是为一件很少发生的事情花费额外的判断操作似乎不是很划算。

②依靠精心周到的语句顺序解决
调整最初的源代码

Weight & Weight::operatot=(const Weight & rhs)
{
Bitmap *pOrig=_pb;                    //记住原先的_pb
_pb=new Bitmap(rhs._pb);              //令_pb指向*_pb的一个副本
delete pOrig;                         //删除原先的_pb
return *this;
}

再调整顺序后,在重新new之后再摧毁原来的_pb,这样的操作具有很高的异常安全性。这里有一个问题,如果new失败了怎么办?

我相信所有人一定都写过这样一段代码

int *p=new int[5];
if(p==NULL)
    return -1;

当我们使用malloc/calloc分配内存时,检测返回值是否为”空指针”是一个良好的习惯,可惜的是new在默认状态下,分配失败并不会返回一个空指针,而是抛出(throw)一个异常!!

对此正确的操作有如下两个方法:

①捕获异常

try
{
    _pb=new Bitmap(rhs._pb);  
    ...                     //其他操作
}
catch(const bad_alloc& e )
{
    return -1;
}

②标准 C++ 亦提供了一个方法来抑制 new 抛出异常,而返回空指针

   int* p = new (std::nothrow) int; 
   // 这样如果 new 失败了,就不会抛出异常,而是返回空指针
   if ( p == 0 ) // 如此这般,这个判断就有意义了
       return -1;
   ...           // 其它代码
   delete p;

你可能感兴趣的:(异常,C语言,new)