override与= default是 C++11 引入的两个关键字/修饰符,主要用于提高编译期类型安全以及显式控制编译器生成的特殊成员函数。
一、override的作用
override用于修饰派生类中的虚函数,它的核心作用是显式声明该方法必须重写基类的某个虚函数,并强制编译器进行签名一致性检查。
1. 解决的痛点:防止“隐蔽的多态失效”
在 C++11 之前,如果重写虚函数时发生哪怕极微小的疏忽(如打错字母、少写const、参数类型不匹配),编译器不会报错,而是将其默默视为一个全新的函数,导致运行时多态静默失效且极难排查
class Base { public: virtual void process(int x) const {} }; class Derived : public Base { public: // ❌ 痛点:少写了 const,或者把 int 误写成了 double // 编译器不会报错,但它不是重写,而是定义了一个新函数!多态失效! void process(int x) {} };2. 使用override后的效果
一旦加上override,编译器会在编译阶段严格校验基类中是否存在完全同名的虚函数。如果找不到匹配的原型,立即阻断编译并精准报错:
class Derived : public Base { public: // ✅ 加上 override 后: // 若写成 void process(int x) override; // 编译器直接报错:成员函数声明了 "override",但未重写任何基类成员函数! void process(int x) const override { // 签名完全一致,编译通过,安全实现多态 } };工程准则:在子类中重写虚函数时,始终加上
override(此时基类的virtual关键字在子类可以省略不写)。
二、= default的作用
= default专门用于 C++ 的 6 种特殊成员函数(默认构造、析构、拷贝构造、拷贝赋值、移动构造、移动赋值),核心作用是指示编译器显式生成该函数的默认实现。
常见应用场景与作用:
1. 找回被编译器“吞掉”的默认无参构造函数
一旦为一个类定义了带参数的构造函数,编译器就会自动取消生成无参构造函数。使用= default可以简洁地重新启用它:
class ImageBuffer { public: // 自定义了带参构造,编译器默认不会再生成 ImageBuffer() ImageBuffer(int size) {} // ✅ 显式要求编译器生成默认无参构造函数 ImageBuffer() = default; }; ImageBuffer buf; // 可以正常编译通过2. 编写基类的虚析构函数(最常见用法)
基类为了防止内存泄漏通常需要提供虚析构函数,但其内部往往不需要额外逻辑。使用= default既清晰又规范:
class Base { public: // ✅ 替代过去写空函数体 virtual ~Base() {} 的做法 virtual ~Base() = default; };3. 性能与类型特性优化(相比空花括号{})
许多人习惯写空函数体MyClass() {},但这与MyClass() = default;有本质区别:
MyClass() {}:属于用户自定义实现。即使里面什么都没写,编译器也会认为它包含自定义代码,从而使类丧失“平凡类型(Trivial Type)”特性,影响内存复制(memcpy/优化)和常量表达式(constexpr)支持。MyClass() = default;:属于编译器原生生成。它保留了类型的平凡性,编译器可以针对其进行最高级别的内联与优化。
对比清单
| 关键字 | 使用位置 | 核心价值 |
override | 派生类虚函数声明末尾 | 防呆机制:杜绝因函数签名不一致导致的多态失效,由编译器兜底报错。 |
= default | 6 种特殊成员函数声明/定义末尾 | 代码复用与性能:显式恢复或声明编译器默认生成的函数,保持类的平凡特性与高效优化。 |