Effective Modern C++ 条款14:如果函数不抛出异常请使用noexcept

作为C++开发者,我们都知道异常处理是语言中一个重要但复杂的特性。今天我想和大家分享一个能显著提升代码质量和性能的特性——noexcept

从C++98的异常说明说起

在C++98时代,异常说明是个让人又爱又恨的特性。我们需要明确列出函数可能抛出的所有异常类型:

void foo() throw(std::runtime_error, std::logic_error);

这种设计带来了很多问题:

  1. 函数实现改变时,异常说明也需要跟着改
  2. 影响客户端代码,因为调用者可能依赖原有的异常说明
  3. 编译器不保证函数实现与异常说明的一致性

最终,大多数开发者都放弃了这种繁琐的异常说明方式。

C++11带来的革新:noexcept

C++11引入的noexcept从根本上改变了异常说明的方式,它只关心一个简单的问题:这个函数会不会抛出异常?

void foo() noexcept;  // 保证不抛异常
void bar();           // 可能抛异常

为什么noexcept如此重要?

  1. 它是接口设计的一部分

noexceptconst一样,是函数接口的重要组成部分。调用者会根据这个信息决定如何调用你的函数。

  1. 性能优化

编译器会对noexcept函数进行特殊优化:

  • 不需要保证栈的可展开状态
  • 不需要保证对象按构造顺序的反序析构
  • 生成的代码更高效

比较以下三种声明方式:

RetType function(params) noexcept;  // 极尽所能优化
RetType function(params) throw();   // 较少优化
RetType function(params);           // 较少优化
  1. 移动语义和swap的关键

标准库容器(如std::vector)在需要扩容时,会根据元素的移动操作是否noexcept来决定使用移动还是复制:

class Widget {
public:
    Widget(Widget&& rhs) noexcept;  // 移动构造函数
    Widget& operator=(Widget&& rhs) noexcept;  // 移动赋值
    
    void swap(Widget& other) noexcept {
        using std::swap;
        swap(data, other.data);
    }
private:
    SomeType data;
};

如果移动操作不是noexcept,容器会保守地使用复制操作,导致性能损失。

何时使用noexcept?

  1. 移动操作:移动构造函数和移动赋值运算符
  2. swap函数:成员和非成员swap函数
  3. 简单工具函数:确定不会抛出异常的小函数
  4. 内存管理:自定义的operator delete

何时避免noexcept?

  1. 可能间接抛异常的函数:即使函数自己不抛异常,但调用的函数可能抛异常
  2. 未来可能修改的函数:如果实现可能改变并开始抛异常
  3. 有前置条件的函数:违反前置条件时可能需要抛异常

实际应用示例

  1. 标准库风格的swap
class MyType {
public:
    void swap(MyType& other) noexcept {
        using std::swap;
        swap(data1, other.data1);
        swap(data2, other.data2);
    }
private:
    int data1;
    std::string data2;
};

void swap(MyType& a, MyType& b) noexcept {
    a.swap(b);
}
  1. 带条件的noexcept
template <typename T>
void swap(T& a, T& b) noexcept(noexcept(a.swap(b))) {
    a.swap(b);
}

总结

noexcept是C++11引入的一个强大特性,正确使用它可以:

  • 明确表达设计意图
  • 实现更好的性能优化
  • 使标准库容器更高效地使用移动语义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值