多态指通过基类接口操作不同派生类对象,运行时根据对象的动态类型调用对应实现。C++ 以虚函数实现动态绑定:经基类指针或引用调用虚函数时,解析目标由实际对象类型决定。本节在继承基础上说明 virtual、override、纯虚函数与虚析构。

虚函数与动态绑定

C++ 多态与虚函数运行
#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
12

override 标明派生类重写虚函数;若基类无匹配虚函数则编译失败。经 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、策略对象或模板静态多态,避免过度虚函数层次。

本节说明了虚函数与动态绑定。下一节介绍析构函数:对象销毁时的清理与继承链中的调用顺序。