必须用虚函数、继承、基类指针或引用三者缺一不可;虚函数需严格匹配签名,推荐加override;仅通过基类指针或引用调用才触发动态绑定;抽象类含纯虚函数强制派生类实现;虚析构函数防止内存泄漏;多态用于消除类型分支,提升可扩展性。

必须用虚函数 + 继承 + 基类指针或引用,三者缺一不可。少一个,ptr->func() 就不会调用子类实现,而是静态绑定到基类版本。
虚函数声明和重写必须严格匹配
基类中用 virtual 声明,派生类里重写时函数签名(返回类型、函数名、参数列表)必须完全一致。C++11 起建议加 override 关键字,编译器会帮你检查是否真在重写——比如拼错函数名、参数类型不一致、const 修饰不匹配,都会报错。
-
void func() const和void func()是两个不同函数,后者不能重写前者 - 返回类型协变是唯一例外:若基类虚函数返回
Base*,派生类可返回Derived*(Derived继承自Base) - 不加
override时,看似重写了,实际可能是新函数,多态失效却不报错
调用方式决定是否触发动态绑定
只有通过基类指针或基类引用调用虚函数,才会查 vtable、走动态绑定。值传递、直接用对象名调用,一律静态绑定。
-
Base* ptr = new Derived; ptr->func();→ 多态生效 -
Base& ref = derived_obj; ref.func();→ 多态生效 -
Base obj = derived_obj;→ 发生对象切割(slicing),obj只剩基类部分,obj.func()永远调基类版 -
void f(Base b) { b.func(); }→ 值传参,等同于上一条,多态失效
抽象类和纯虚函数是强制多态设计的手段
如果基类本就不该被实例化(比如 Shape、Drinking),就把它变成抽象类:至少一个纯虚函数 virtual void draw() = 0;。这样编译器会阻止你写 Shape s;,也强制派生类必须实现 draw(),否则它自己也是抽象类、无法实例化。
立即学习“C++免费学习笔记(深入)”;
- 抽象类可以有构造函数、成员变量、非纯虚函数,甚至定义好的默认行为
- 析构函数如果是虚的(推荐写成
virtual ~Base() = default;),能确保delete ptr时正确调用派生类析构函数 - 别漏掉虚析构函数——这是新手最常踩的内存泄漏/未定义行为来源之一
真正难的不是语法怎么写,而是判断“这里该不该用多态”。比如一个函数内部十几个 if (type == X) 分支,就是典型的多态替代信号;而两个类只是碰巧有同名函数、但没有继承关系、也不共享接口意图,硬套虚函数反而增加耦合。多态的价值不在“能跑”,而在“加新类型不用动旧代码”。


















