C++ 中函数后面的 constnoexcept 的含义

在 C++ 中,函数声明或定义后添加的 constnoexcept 是函数修饰符,用于指定函数的行为和属性。


1. 函数后面的 const

含义

  • 成员函数限定符const 出现在类成员函数的声明或定义后,表示该函数不会修改对象的非 mutable 成员变量。
  • 承诺:调用该函数时,对象的状态(除 mutable 成员外)保持不变。
  • 语义:用于声明“只读”成员函数,增强代码安全性。

实现机制

  • 编译器将 const 成员函数的 this 指针视为 const T*(而非 T*),禁止修改非 mutable 成员。
  • 可被 const 对象、引用或指针调用,而非 const 成员函数只能被非 const 对象调用。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

class MyClass {
int x = 0;
mutable int y = 0; // mutable 允许在 const 函数中修改
public:
void setX(int val) { x = val; } // 非 const 函数
int getX() const { // const 函数
// x = 42; // 错误:不能修改非 mutable 成员
y = 42; // 正确:mutable 成员可修改
return x;
}
};

int main() {
const MyClass obj;
// obj.setX(10); // 错误:const 对象不能调用非 const 函数
std::cout << obj.getX() << "\n"; // 正确:调用 const 函数
}

应用场景

  • 只读访问:用于访问对象状态而不修改(如 getter 函数)。
  • const 正确性:确保 const 对象或引用能安全调用函数。
  • 接口设计:提高类接口的语义清晰性和安全性。

注意事项

  • mutable 成员可在 const 函数中修改,需谨慎使用。
  • 重载时,const 和非 const 版本可共存,编译器根据调用对象的 const 属性选择。

2. 函数后面的 noexcept

含义

  • 异常规范noexcept 表示函数承诺不抛出异常(或有条件不抛出)。
  • 性能优化:编译器可优化不抛异常的函数(如减少异常处理开销)。
  • C++11 引入:位于 <exception>,用于增强异常安全性和性能。

实现机制

  • noexcept 分为两种:
    • **无条件 noexcept**:noexceptnoexcept(true),保证不抛异常。
    • **条件 noexcept**:noexcept(expression),根据表达式决定是否抛异常。
  • noexcept 函数抛出异常,程序调用 std::terminate() 终止。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

void func1() noexcept { // 无条件不抛异常
std::cout << "func1 called\n";
}

void func2() noexcept(false) { // 可能抛异常
throw std::runtime_error("Error");
}

void func3(int x) noexcept(x > 0) { // 条件 noexcept
std::cout << "func3 called with x = " << x << "\n";
}

int main() {
func1(); // 安全调用
try {
func2(); // 可能抛异常
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << "\n";
}
func3(1); // 不抛异常
func3(-1); // 可能抛异常
}

应用场景

  • 性能优化:在标准库容器(如 std::vector)的移动操作中,noexcept 函数可启用更高效的实现(强异常保证)。
  • 异常安全性:明确函数的异常行为,方便调用者处理。
  • 模板编程:配合 noexcept 运算符(如 std::is_nothrow_move_constructible)优化代码。

注意事项

  • 错误使用 noexcept(抛出异常)会导致程序终止,需确保函数逻辑符合承诺。
  • 使用 noexcept 运算符检查函数是否为 noexcept
    1
    static_assert(noexcept(func1()), "func1 should be noexcept");

3. 结合使用 constnoexcept

  • constnoexcept 可同时用于成员函数,互不冲突。
  • 语法:ReturnType func() const noexcept;
  • 示例:
    1
    2
    3
    4
    5
    class MyClass {
    int x;
    public:
    int getX() const noexcept { return x; } // 只读且不抛异常
    };

应用场景

  • 高性能只读函数:如标准库中的访问器函数,需高效且安全。
  • 严格接口:在要求高可靠性的系统中,明确函数行为。

4. 对比总结

特性 const noexcept
作用 禁止修改对象状态 承诺不抛异常
适用范围 成员函数 任何函数(成员或非成员)
编译器行为 限制 thisconst 优化代码,异常抛出调用 terminate
性能影响 无直接影响 减少异常处理开销
语义 保证状态不变 保证异常安全

5. 注意事项

  • 误用后果
    • const 函数修改非 mutable 成员会导致编译错误。
    • noexcept 函数抛出异常会导致程序终止。
  • 与智能指针结合(结合上下文):
    • 智能指针(如 unique_ptrshared_ptr)的析构函数通常是 noexcept,确保异常安全。
    • 成员函数操作智能指针时,const 保证不修改指针状态,noexcept 优化析构或移动操作。
  • C++ 标准演进
    • C++11 引入 noexceptalignof,C++17 增强 noexcept 推导。
    • 优先使用 std::make_unique/make_shared(如上下文所述),它们通常是 noexcept 的。

6. 总结

  • **const**:用于成员函数,保证不修改对象状态,适合只读操作。
  • **noexcept**:承诺函数不抛异常,提升性能和异常安全性,适合移动操作或关键路径。
  • 结合使用:在高性能、强安全场景中,const noexcept 提供清晰语义和优化。