条款29:为”异常安全“而努力是值得的

Strive for exception-safe code.

异常安全的两个条件

void PrettyMenu::changeBackground(std::istream& imgSrc) {
    lock(&mutex);
    delete bgImage;
    ++imageChanges;
    bgImage = new Image(imgSrc);
    unlock(&mutex);
}

异常安全有两个条件:

  • 不泄露任何资源:当 new Image(imgSrc) 导致异常,unlock 则绝的不会被执行,导致了锁资源的泄漏;

  • 不允许数据败坏:当 new Image(imgSrc) 导致异常,bgImage 就是指向一个已被删除的对象,并且 imageChanges 也被累加;

而 C++ 提供一些机制来保证异常安全:

class PrettyMenu {
    ...
    std::tr2::shared_ptr<Image> bgImage;
    ...
};

void PrettyMenu::changeBackground(std::istream& imgSrc) {
    Lock m1(&mutex);                    // 使用资源管理类管理锁
    bgImage.reset(new Image(imgSrc);    // 智能指针
    ++imageChanges;
}

异常安全的三个保证等级

异常安全函数(Exception-safe functions)提供以下三个保证之一:

  • 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态;

  • 强烈保证:如果异常被抛出,程序状态不变,即函数成功就是完全成功,函数失败程序就会回复到”调用函数之前的状态“;

  • 不抛掷(nothrow)保证:承诺绝对不抛出异常,因为它们总是能够完成它们原先承诺的功能;

copy and swap

在修改一个对象时,可以先创建一个副本,然后在副本身上做一切必要的修改;若有任何修改动作抛出异常,原对象仍保持未改变的状态;待所有改变成功后,再将修改过的那个副本和原对象在一个不抛出异常的操作中置换。

”copy and swap“ 策略是对对象状态做出”全有或全无“改变的一个很好的办法,但一般而言它并不保证整个函数有强烈的异常安全性。

"copy and swap" 策略有两个需要注意的方面:

  • ”连带影响“(side effect),如果函数值操作局部性状态(local state),便相对容易地提供强烈保证;但如果函数对”非局部性数据“(non-local data)有连带影响时,提供强烈保证就很困难;

  • 效率,创建副本和交换操作将带来时间和空间的开销;

Last updated

Was this helpful?