理解了虚表虚置针,动态绑定就自然理解了

840 阅读2分钟

一、继承与多态

#include <iostream>

// 基类 A
class A {
public:
    virtual void draw() {
        std::cout << "A::draw()" << std::endl;
    }
};

// 继承自 A 的子类 B
class B : public A {
public:
    virtual void draw() {
        std::cout << "B::draw()" << std::endl;
    }
};

// 继承自 B 的子类 C
class C : public B {
public:
    virtual void draw() {
        std::cout << "C::draw()" << std::endl;
    }
};

  • A 是基类,B 继承 A,C 继承 B,A、B、C 都有各自的虚函数 draw
  • 对于数据部分,继承则是把数据直接继承下来,继承了数据的大小
  • 对于函数来说,继承的则是调用权
  • 父类有虚函数,子类一定有虚函数
int main() {
    // 创建一个 C 对象
    C c;

    // 调用 C 的虚函数 draw
    c.draw();

    return 0;
}

二、动态绑定

2.1 动态绑定方式调用

int main() {
    // 创建一个 C 对象指针 
    C* pc = new C(); 
    // 通过指针调用 C 的虚函数 draw 
    pc->draw(); 
    // 释放内存 
    delete pc;
}

2.2 什么是动态绑定

动态绑定是 C++ 中实现多态性的一种机制,也称为运行时多态(Run-time Polymorphism)。它可以在运行时根据对象的实际类型来动态绑定相应的函数,以实现不同对象之间的多态性。

具体来说,当使用一个指向基类的指针或引用调用一个虚函数时,程序会在运行时根据指针或引用所指向的对象的实际类型(即派生类类型)来调用相应的虚函数。这种机制可以让程序在不知道对象实际类型的情况下,仍然能够正确地调用相应的函数。

动态绑定是通过虚函数和虚表来实现的。每个类都有一个虚表,其中存放着该类的虚函数的地址。当程序调用一个虚函数时,会根据对象的实际类型在虚表中查找相应的函数地址,并进行调用。这就是动态绑定的实现原理。

总之,动态绑定是 C++ 中实现多态性的一个重要机制,它让程序可以更加灵活和可扩展,使得代码具有更好的可维护性和可重用性。

三、虚指针和虚表

虚指针和虚表是实现动态多态的两个重要机制。

3.1. 虚指针(virtual pointer,简记:vptr)

虚指针是一个指向虚表的指针,它是一个对象(通常是类的对象)中的隐藏成员,用于支持动态多态。当一个类被声明为具有虚函数时,每个对象都会在其布局中包含一个虚指针。虚指针是在对象的构造函数中初始化的,它指向一个虚表。

3.2. 虚表(virtual table,简记:vtbl)

虚表是一个存储了该类中所有虚函数地址的数组。每个类都有自己的虚表,当一个类被声明为具有虚函数时,编译器会自动生成一个虚表。在使用虚函数时,实际调用的函数地址是通过虚表查找得到的。虚表通常是只读的,而且是全局唯一的。

通过使用虚指针和虚表,C++ 可以支持运行时多态,即可以根据对象的实际类型在运行时确定要调用哪个函数,这是 C++ 面向对象编程的重要特性之一。

虚表的实现机制是编译器将虚函数地址存储在一个数组中,然后将该数组的地址存储在虚表指针中。当调用虚函数时,程序会通过虚指针找到对象所属类的虚表,然后根据函数在虚表中的位置找到实际要调用的函数。