28 29 为“异常安全”而努力是值得的

本文探讨了如何通过提供不同级别的异常安全保证(nothrow、强烈保证和基本承诺)来编写稳健的代码,强调了在新代码中应用基本承诺的重要性,并举例说明了nothrow操作、copyandswap策略的应用。同时解释了避免返回handles的必要性,以维护对象封装和数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一、异常安全函数提供以下三个保证

  • 基本承诺如果异常被抛出,程序内的任何事务仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态(例如所有的class约束条件都继续获得满足)。如果含函数出现异常,程序有可能处于任何状态——只要那是个合法状态
  • 强烈保证如果异常抛出,程序状态不改变。调用这样的函数需要有这样的认知:如果函数成功,就是完全成功,如果函数失败,程序会回复到“调用函数之前”的状态
  • 不抛异常保证承诺不抛出异常,因为他们总是能够完成他们原先承诺的功能。作用于内置类型(如指针等等)身上的所有操作都提供nothrow保证

二、应该提供何种异常安全函数

  • 尽可能的实施最高等级的异常安全保证。只有当调用没有异常安全的传统代码,才可被迫”无任何保证“。
  • 强烈保证并非对所有函数都可实现或具备实现意义
  • 函数的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者
  • 通常在写新代码是应提供异常安全基本承诺。

2.1 nothrow

作用于内置类型身上的所有操作都提供nothrow保证。

2.2 强烈保证

  • 使用对象管理资源。如智能指针管理裸指针、使用Lock对象管理互斥锁
  • 调整语句词语
  • copy and swap 策略 为你打算修改的对象(原件)做出一个副本,然后在那副本身上做一些必要修改。若有任何修改动作抛出异常,原对象仍保持未改变状态。待所有改变都成功后,再将修改过的那个副本和原对象在一个不抛异常的操作中置换(swap)。
    copy and swap 策略是对对象状态做出“全有或全无”改变的一个很好办法,但是并不保证整个函数具有强烈的异常安全性。

2.3 基本承诺

强烈保证的前两条。

2.4 示例

  • 基本承诺 示例
class PrettyMenu{
public:
    ...
    void changeBackground(std::istream& imgSrc);
    ...
private:
    Mutex mutex;
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges;
};
void PrettyMenu::changeBackground(std::istream& imgSrc){
    Lock ml(&mutex);
    bgImage.reset(new Image(imgsrc)); //如果Image构造函数抛出异常,有可能输入流的读取记号已被移走,而这样的搬移对程序其余部分是一种可见状态改变。所以该函数只提供了基本的异常安全保证。
    ++imageChanges;                  
}
  • copy and swap示例
struct PMImpl{
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges;
};
class PrettyMenu{
private:
    Mutex mutex;
    std::tr1::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc){
    using std::swap;
    Lock ml(&mutex);
    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); //创建pImpl的副本
    pNew->bgImage.reset(new Image(imgsrc));                //修改pImpl的副本
    ++pNew->imageChanges;                                  //修改pImpl的副本
    swap(pImpl,pNew);                                      //交换副本和原对象
}

28 避免返回handles对象内部成分

  • reference、指针或迭代器就是所谓的handles
  • 成员变量的封装性最多只等于“返回其reference”的函数的访问级别。
  • 内部对象不光包括成员变量,还包括private和protected访问级别的成员函数。
  • 避免返回handles对象内部成分的原因
    • 使const成员函数的行为更像一个const
      const成员函数传出一个reference,后者所指数据与对象自身有关联,而它有被存储于对象之外,那么这个函数的调用者可以修改那笔数据。
    • 返回const 的handles虽然外接不能更改,但却可能导致空悬
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值