上面的代码说明了异常规格说明的基本语法,以及unexpected函数的作用,以及如何自定义自己的unexpected函数,还讨论了在unexpected函数中继续抛出异常的情况下,该如何处理抛出的异常.C++11中取消了这种异常规格说明.引入了一个noexcept函数,用于表明这个函数是否会抛出异常
void recoup(int) noexecpt(true); //recoup不会抛出异常
void recoup(int) noexecpt(false); //recoup可能会抛出异常
此外还提供了noexecpt用来检测一个函数是否不抛出异常.
异常安全
异常安全我觉得是一个挺复杂的点,不光光需要实现函数的功能,还要保存函数不会在抛出异常的情况下,出现不一致的状态.这里举一个例子,大家在实现堆栈的时候经常看到书中的例子都是定义了一个top函数用来获得栈顶元素,还有一个返回值是void的pop函数仅仅只是把栈顶元素弹出,那么为什么没有一个pop函数可以 即弹出栈顶元素,并且还可以获得栈顶元素呢?
template<typename T> T stack<T>::pop()
{
if(count == 0)
throw logic_error("stack underflow");
else
return data[--count];
}
如果函数在最后一行抛出了一个异常,那么这导致了函数没有将退栈的元素返回,但是Count已经减1了,所以函数希望得到的栈顶元素丢失了.本质原因是因为这个函数试图一次做两件事,1.返回值,2.改变堆栈的状态.最好将这两个独立的动作放到两个独立的函数中,遵守内聚设计的原则,每一个函数只做一件事.我们 再来讨论另外一个异常安全的问题,就是很常见的赋值操作符的写法,如何保证赋值操作是异常安全的.
class Bitmap {...};
class Widget {
...
private:
Bitmap *pb;
};
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
上面的代码不具备自我赋值安全性,倘若rhs就是对象本身,那么将会导致*rhs.pb指向一个被删除了的对象.那么就绪改进下.加入证同性测试.
Widget& Widget::operator=(const Widget& rhs)
{
If(this == rhs) return *this; //证同性测试
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
但是现在上面的代码依旧不符合异常安全性,因为如果delete pb执行完成后在执行new Bitmap的时候出现了异常,则会导致最终指向一块被删除的内存.现在只要稍微改变一下,就可以让上面的代码具备异常安全性.
Widget& Widget::operator=(const Widget& rhs)
{
If(this == rhs) return *this; //证同性测试
Bitmap *pOrig = pb;
pb = new Bitmap(*rhs.pb); //现在这里即使发生了异常,也不会影响this指向的对象
delete pOrig;
return *this;
}










