多态必须通过基类指针或引用触发,值传递导致对象切片和静态绑定;接口类须全纯虚函数+虚析构;override强制编译器检查重写正确性;虚表带来轻微内存开销。

多态必须靠指针或引用才能触发
直接用对象值传递或局部变量声明,编译器会在编译期就绑定函数(静态绑定),virtual 和 override 完全无效。只有通过基类指针或引用调用虚函数时,才会查虚表、走动态绑定。
- ✅ 正确写法:
Base* p = new Derived(); p->func();或Base& ref = derived_obj; ref.func(); - ❌ 错误写法:
Base obj = Derived(); obj.func();—— 这里发生对象切片,Derived特有部分被丢弃,只剩Base子对象 - 注意:
std::unique_ptr和std::shared_ptr同样支持多态,且更安全
接口类必须只含纯虚函数和析构函数
所谓“接口类”,本质是契约定义,不提供实现、不维护状态。C++ 中没有 interface 关键字,靠全纯虚函数 + 显式虚析构实现。
- 纯虚函数写法统一用
= 0,不能带函数体:virtual void draw() = 0; - 必须声明虚析构函数:
virtual ~Shape() = default;或virtual ~Shape() {},否则delete派生类指针时析构不完整 - 禁止在接口类中定义非静态数据成员、普通函数、构造函数(除默认/虚析构外)
- 若需默认行为,应拆成抽象基类(含部分实现)+ 接口类(仅契约),不要混用
override 是强制检查,不是可选项
override 不只是语义提示,它让编译器验证:该函数是否真的重写了基类的虚函数。漏写或拼错签名会导致意外的重载而非重写,多态失效却无报错。
- 基类函数没加
virtual?子类加override直接编译失败 - 参数类型不一致(比如
const int*vsint*)、const修饰位置不同、返回类型协变没满足条件,都会触发错误 - 现代项目应开启编译器警告(如
-Woverloaded-virtual或/we4263),配合override形成双重保险
虚函数表开销小但不可忽视
每个含虚函数的类对象隐式携带一个指向虚表的指针(通常 8 字节,64 位平台),虚表本身存于只读段,大小取决于虚函数个数。对内存极度敏感的场景(如嵌入式、高频小对象池)要权衡。
立即学习“C++免费学习笔记(深入)”;
- 空接口类(全纯虚 + 虚析构)的对象大小仍是
sizeof(void*),不是 0 - 多重继承下虚表更复杂,可能产生多个虚表指针,
sizeof可能翻倍 - 若确定无需运行时多态,优先用模板 +
concept(C++20)或策略模式替代虚函数,零成本抽象
虚析构函数的写法、override 的强制性、以及值语义导致的切片问题,这三点在真实项目里最容易被跳过或误判——尤其是当测试用例只覆盖了“看起来能跑通”的路径时。









