函数后关键字
学习定位:现代 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 | 模板返回类型推导 | 🔥🔥 |
建议日常编码时,前四个(const、override、=default、=delete)作为基础习惯,noexcept 作为进阶优化。