函数后关键字

学习定位:现代 C++ 核心知识。本页只要求先掌握日常最常用、最影响正确性的声明:const 成员函数、override= default= delete 和常见 noexcept。引用限定符、尾置返回类型等可以作为补充查阅。

以下是按照重要性排序的 C++ 函数后/特殊语法笔记。重要性依据:日常使用频率 + 安全性/正确性影响 + 现代 C++ 推荐度

1. const(成员函数后)

语法void func() const;

作用:承诺该成员函数不会修改对象成员(mutable 除外)。
关键点

  • const 对象只能调用 const 成员函数。
  • 编译器会帮你检查是否意外修改了成员。
  • const 正确性的核心。

示例

class A {
    int data;
public:
    int get() const { return data; }   // 正确
    void set(int x) const { data = x; } // 错误:不能修改成员
};

重要性:⭐⭐⭐⭐⭐
建议:所有“只读”成员函数后都应加上 const


2. override

语法void func() override;

作用:显式声明该函数重写了基类的虚函数。
关键点

  • 如果基类没有匹配的 virtual 函数,编译错误(防止写错)。
  • 极大提升继承代码的可维护性。

示例

struct Base { virtual void f(int); };
struct Derived : Base { void f(int) override; };   // 正确
// struct Derived2 : Base { void f(double) override; }; // 错误:并未重写

重要性:⭐⭐⭐⭐⭐(现代 C++ 强制要求写)
建议:只要有重写意图,就必须加上 override


3. = default

语法A() = default;

作用:显式要求编译器生成默认版本的特殊成员函数(构造/析构/拷贝/移动)。
关键点

  • 当你声明了其他构造函数后,默认构造函数被抑制,用 = default 可恢复。
  • 比手动空实现更高效(可能为 trivial)。

示例

class A {
public:
    A(int x) : val(x) {}
    A() = default;   // 依然保留默认构造函数
private:
    int val;
};

重要性:⭐⭐⭐⭐
建议:需要编译器默认行为时优先用 = default,不要写 A() {}


4. = delete

语法A(const A&) = delete;

作用:禁止调用该函数(包括禁止拷贝、禁止赋值等)。
关键点

  • 比声明为 private 且不定义更清晰、错误信息更早。
  • 常用于禁止拷贝:NonCopyable(const NonCopyable&) = delete;

示例

class Singleton {
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

重要性:⭐⭐⭐⭐
建议:明确禁止某函数时使用 = delete


5. noexcept(注意拼写为 noexcept)

语法void func() noexcept;void func() noexcept(true/false);

作用:声明函数不会抛出异常。
关键点

  • 如果 noexcept 函数真的抛出异常,程序直接 std::terminate
  • 标准库容器(如 vector)在移动元素时,只有 noexcept 移动构造才会使用移动语义,否则用拷贝(保证强异常安全)。

示例

class A {
public:
    A(A&&) noexcept;   // 移动构造不抛异常
};

重要性:⭐⭐⭐(高层库/移动语义优化必备,普通业务代码可选)
建议:移动构造/移动赋值/swap 等应该加上 noexcept


6. final

语法class B final { };void func() final;

作用

  • 修饰类:禁止被继承。
  • 修饰虚函数:禁止子类进一步重写。

示例

class Base final { };      // 不能有派生类
class Base2 { virtual void f() final; };  // 派生类不能重写 f()

重要性:⭐⭐⭐(用于明确设计意图,不是常用)
建议:仅在设计明确需要“禁止继承/重写”时才用。


7. &&&(引用限定符)

语法void func() &; void func() &&;

作用:限制成员函数只能被左值对象调用(&),或只能被右值对象(临时对象)调用(&&)。
关键点

  • 常用于区分“对临时对象的优化”或“禁止对临时对象调用危险操作”。

示例

class Widget {
public:
    void work() &   { /* 只能对左值 Widget 调用 */ }
    void work() &&  { /* 只能对临时 Widget 调用 */ }
};
Widget a; a.work();      // 调用 &
Widget().work();         // 调用 &&

重要性:⭐⭐(库作者/性能关键场景,普通代码很少用)
建议:除非需要精准控制重载,否则不必要。


8. 尾置返回类型 -> type

语法auto func(params) -> type;

作用:将返回类型写在函数名之后。
关键点

  • 必须场景:模板中返回类型依赖参数时。
  • 也可用于简化复杂返回类型(如函数指针)。

示例

// 模板场景
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) { return t + u; }
 
// 函数指针
auto getFunc() -> int(*)(int) { return nullptr; }

重要性:⭐⭐(一般开发很少用,写库/模板时必需)
建议:仅在无法通过 decltype(expr) 提前写出返回类型,或返回类型极其复杂影响可读性时使用。


特别说明(用户提到的一句建议)

“建议优先把父类设成纯虚函数而不是依赖 override 关键字检查”

这个建议不完全准确。纯虚函数override 是两件不同的事:

  • 纯虚函数用于定义接口,强制子类实现。
  • override 是语法检查,防止你写错。

正确做法:二者结合使用。
如果你希望子类必须实现某个函数,写纯虚函数;同时子类实现时加上 override 来确保签名匹配。
不建议放弃 override,它是现代 C++ 的最佳实践之一。


总结速查表

关键字最常用场景重要性
const(函数后)只读成员函数🔥🔥🔥🔥🔥
override重写虚函数🔥🔥🔥🔥🔥
= default恢复默认构造函数🔥🔥🔥🔥
= delete禁止拷贝/移动🔥🔥🔥🔥
noexcept移动构造/赋值、swap🔥🔥🔥
final禁止继承/重写🔥🔥🔥
& / &&区分左值/右值调用🔥🔥
-> type模板返回类型推导🔥🔥

建议日常编码时,前四个(constoverride=default=delete)作为基础习惯,noexcept 作为进阶优化。