C++虚函数与多态详解

一、虚函数(Virtual Function)

1. 基本概念
  • 虚函数是 C++ 中实现 动态多态(运行时多态)的核心机制。
  • 通过 virtual 关键字声明,允许派生类 重写(override) 基类的函数实现。
  • 虚函数的存在使得基类指针或引用可以调用 实际对象类型 的函数。
2. 语法与使用
class Base {
public:
    virtual void func() { 
        cout << "Base::func()" << endl; 
    }
};

class Derived : public Base {
public:
    void func() override {  // 使用 override 明确表示重写(C++11 起支持)
        cout << "Derived::func()" << endl;
    }
};

关键点

  • 基类中声明虚函数时使用 virtual
  • 派生类重写虚函数时,建议使用 override 关键字(避免拼写错误或签名不匹配)。
  • 虚函数可以是成员函数,但不能是静态成员函数或构造函数(析构函数可以是虚函数)。

二、多态(Polymorphism)

1. 多态的类型
  • 静态多态(编译时多态):

    • 通过 函数重载模板运算符重载 实现。
    • 函数调用在编译时确定。
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
    
  • 动态多态(运行时多态):

    • 通过 虚函数继承 实现。
    • 函数调用在运行时根据对象的实际类型决定。
    Base* ptr = new Derived();
    ptr->func();  // 调用 Derived::func()
    
2. 动态多态的实现原理
  • 虚函数表(vtable)
    • 每个包含虚函数的类都有一个虚函数表,表中存储该类所有虚函数的地址。
    • 例如,Base 类有虚函数 func(),则其虚函数表包含 Base::func 的地址。
  • 虚函数指针(vptr)
    • 每个对象内部包含一个指向其所属类的虚函数表的指针(vptr)。
    • 当派生类重写虚函数时,其虚函数表中对应的函数地址会被替换为派生类的实现。

内存模型示例

Derived 对象内存布局:
+-------------------+
| vptr (指向 Derived 的 vtable) |
| 其他成员变量...          |
+-------------------+

Derived 的 vtable:
+-------------------+
| &Derived::func    |
+-------------------+
3. 动态绑定的过程
Base* ptr = new Derived();
ptr->func();  // 运行时根据 ptr 指向的对象的 vptr 找到 vtable,再调用 func()

三、虚函数与多态的核心规则

1. 虚析构函数
  • 若类可能被继承,且需要通过基类指针释放派生类对象,基类析构函数必须为虚函数
    否则会导致派生类析构函数未被调用,引发内存泄漏。
    示例

    class Base {
    public:
        virtual ~Base() { cout << "Base destroyed"; }
    };
    class Derived : public Base {
    public:
        ~Derived() { cout << "Derived destroyed"; }
    };
    
    Base* ptr = new Derived();
    delete ptr;  // 输出:Derived destroyed → Base destroyed
    
2. 纯虚函数与抽象类
  • 纯虚函数:没有默认实现的虚函数,语法为 virtual void func() = 0;

  • 抽象类:包含纯虚函数的类,不能实例化,仅作为接口定义。
    示例

    class Shape {
    public:
        virtual double area() const = 0;  // 纯虚函数
    };
    
    class Circle : public Shape {
    public:
        double area() const override { return 3.14 * r * r; }
    };
    
3. override 与 final 关键字(C++11)
  • override:明确表示派生类函数重写基类虚函数,编译器会检查签名是否匹配。

  • final:禁止派生类重写某个虚函数,或禁止某个类被继承。
    示例

    class Base {
    public:
        virtual void func() {}
    };
    
    class Derived : public Base {
    public:
        void func() override final {}  // Derived::func 不可被进一步重写
    };
    

四、多态的应用场景

1. 统一接口,多样实现
  • 定义基类接口,不同派生类实现具体逻辑(如不同图形计算面积)。
    示例

    Shape* shapes[] = { new Circle(), new Square() };
    for (auto shape : shapes) {
        cout << shape->area() << endl;  // 调用不同子类的 area()
    }
    
2. 回调机制
  • 通过基类指针传递回调函数,实现事件处理、插件系统等。
3. 工厂模式
  • 基类定义创建接口,派生类实现具体对象的创建。

五、注意事项与性能

1. 虚函数开销
  • 时间开销:虚函数调用需要间接寻址(通过 vptr 和 vtable),比普通函数调用稍慢。
  • 空间开销:每个对象需要存储 vptr,每个类需要存储 vtable。
  • 优化建议:避免在性能关键路径中过度使用虚函数。
2. 设计原则
  • 优先使用组合而非继承:避免复杂的继承层次。
  • 接口隔离:抽象类应定义最小必要接口(参考接口隔离原则)。

六、总结

特性虚函数多态
目的实现运行时动态绑定提供统一的接口,支持多种具体实现
核心机制虚函数表(vtable)、虚函数指针(vptr)继承 + 虚函数
使用场景需要根据对象类型调用不同函数时接口抽象、回调、工厂模式等
性能影响有间接调用开销灵活性优先,牺牲少量性能

关键点

  • 虚函数是实现动态多态的基础,依赖虚函数表和虚函数指针。
  • 多态的核心是 “一个接口,多种实现”,通过基类指针或引用操作派生类对象。
  • 合理使用 overridefinal 和虚析构函数,是写出健壮代码的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值