静态多态靠编译期绑定,本质是函数重载;动态多态需虚函数、继承和指针/引用调用三要素,通过vtable实现运行时分发,override/final可避免误用。

静态多态靠编译期绑定,本质是函数名相同但签名不同
函数重载(overload)不是真正意义上的“多态”语义,而是编译器根据实参类型在编译时选择具体函数版本。它不涉及继承或运行时决策,只是名字空间内多个同名函数的共存。
常见错误现象:void foo(int) 和 void foo(double) 在调用 foo(5) 时选 int 版本,但若只声明了 foo(double),foo(5) 会隐式转换并调用它——这容易掩盖预期缺失的重载。
- 重载解析发生在编译期,与对象动态类型无关
- 参数个数、类型、const 限定符都会影响重载决议
- 基类和派生类中的重载不构成覆盖关系;派生类中新增重载不会自动继承基类重载,需用
using Base::func;显式引入
动态多态必须有虚函数、继承和指针/引用调用三要素
只有当三个条件同时满足时,virtual 才能触发运行时多态:基类声明虚函数、派生类重写(override)该函数、通过基类指针或引用调用。缺一不可。
典型错误:用值传递对象(如 void call(Base b)),导致对象被切片(slicing),虚函数表丢失,退化为静态绑定。
立即学习“C++免费学习笔记(深入)”;
citySHOP是一款集CMS、网店、商品、分类信息、论坛等为一体的城市多用户商城系统,已完美整合目前流行的Discuz! 6.0论坛,采用最新的5.0版PHP+MYSQL技术。面向对象的数据库连接机制,缓存及80%静态化处理,使它能最大程度减轻服务器负担,为您节约建设成本。多级店铺区分及联盟商户地图标注,实体店与虚拟完美结合。个性化的店铺系统,会员后台一体化管理。后台登陆初始网站密匙:LOVES
-
virtual关键字必须出现在基类函数声明中,派生类重写时可省略(但建议加上override) - 析构函数若可能被多态删除(如
delete ptr),必须声明为virtual - 构造函数不能是虚函数;静态成员函数也不能是虚函数
虚函数表(vtable)是实现动态多态的底层机制
每个含虚函数的类(或其子类)在编译后都有一个对应的虚函数表,表中按声明顺序存放函数指针。对象实例头部隐式存储一个指向 vtable 的指针(vptr)。调用虚函数时,实际执行的是 obj->vptr[索引]()。
性能影响:虚函数调用比普通函数多一次内存读取(查 vtable)和一次间接跳转,现代 CPU 分支预测通常能缓解,但高频小函数仍可能成为瓶颈。
- 纯虚函数(
= 0)使类变为抽象类,vtable 中对应项为 nullptr,强制派生类实现 - 虚函数内联几乎不可能,编译器无法在编译期确定目标地址
- 多重继承下 vptr 可能不止一个,布局更复杂,但标准未规定细节,依赖具体 ABI
override 和 final 是 C++11 后避免多态误用的关键工具
没加 override 的派生类函数,即使签名看似匹配,也可能因 const / 引用限定符或参数类型细微差异(如 int vs int&)而变成新重载,而非重写——此时虚调用仍走基类实现,bug 隐蔽。
class Base {
public:
virtual void func(int x) const;
};
class Derived : public Base {
public:
void func(int x) override; // ✅ 正确:签名完全匹配
void func(int& x) override; // ❌ 编译错误:不能重写,因为参数类型不同
};
-
override让编译器检查是否真正在重写虚函数,否则报错 -
final可用于类(禁止继承)或虚函数(禁止进一步重写),防止意外覆盖 - 虚函数默认不继承:派生类若不重写,调用时仍沿用基类实现(包括基类的虚函数体)
memcpy、序列化反序列化)、跨 DLL 边界或手动管理 vptr,就极易破坏多态行为——这些地方没有编译器保护,出错时往往表现为随机 crash 或静默调用错误函数。








