多态指通过基类接口操作不同派生类对象,运行时根据对象的动态类型调用对应实现。C++ 以虚函数实现动态绑定:经基类指针或引用调用虚函数时,解析目标由实际对象类型决定。本节在继承基础上说明 virtual、override、纯虚函数与虚析构。
虚函数与动态绑定
#include <iostream>
class Shape {
public:
virtual double area() const { return 0.0; }
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
explicit Circle(double r) : r_(r) {}
double area() const override { return 3.14159 * r_ * r_; }
private:
double r_;
};
class Rect : public Shape {
public:
Rect(double w, double h) : w_(w), h_(h) {}
double area() const override { return w_ * h_; }
private:
double w_, h_;
};
void print_area(const Shape& s) {
std::cout << s.area() << "\n";
}
int main() {
Circle c(2.0);
Rect r(3, 4);
print_area(c);
print_area(r);
return 0;
}典型输出:
12.5664
12override 标明派生类重写虚函数;若基类无匹配虚函数则编译失败。经 Shape& 调用 area 时执行 Circle 或 Rect 的实现。
非虚与虚的对比
| 调用方式 | 绑定时机 | 经基类引用/指针 |
|---|---|---|
| 非虚成员函数 | 编译期 | 调用基类版本(静态绑定) |
| 虚成员函数 | 运行期 | 调用对象动态类型对应版本 |
纯虚函数与抽象类
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};= 0 表示纯虚函数;含纯虚函数的类不能实例化,派生类必须实现全部纯虚函数方可创建对象。接口类(仅含纯虚函数与虚析构)常用于插件与框架扩展。
final 与 override
override:派生类显式重写,防止签名错误写成重载。final:标记虚函数不可再被派生类重写,或标记类不可被继承。
虚析构
若可能通过基类指针 delete 派生对象,基类析构函数须为 virtual,否则只运行基类析构,派生部分资源可能泄漏。详见析构函数。
#include <memory>
std::unique_ptr<Shape> p = std::make_unique<Circle>(1.0);
// unique_ptr 析构时正确调用 Circle 与 Shape 析构链vtable 概念
含虚函数的类通常带有虚表指针,运行期通过表间接调用目标函数;有少量空间与时间开销。非虚成员函数、static 成员与模板函数不参与虚派发。将不需要多态的函数保持非虚,是常见性能与清晰性权衡。
多态与设计
多态适合扩展点固定、实现多样的层次(图形、设备驱动)。若行为组合更灵活,可考虑 std::variant、策略对象或模板静态多态,避免过度虚函数层次。
本节说明了虚函数与动态绑定。下一节介绍析构函数:对象销毁时的清理与继承链中的调用顺序。