C++多态是如何实现
📄问题
C++多态是如何实现
📝我的回答
C++中的多态主要分为两种:编译时多态(静态多态)和运行时多态(动态多态)。其中运行时多态是我们通常讨论的重点,它是通过虚函数机制实现的。
运行时多态的核心机制是虚函数表(vtable)和虚表指针(vptr)。当一个类定义了虚函数,编译器会为这个类创建一个虚函数表,表中存储了该类所有虚函数的地址。同时,编译器会在每个包含虚函数的类的对象中插入一个隐藏的指针(vptr),指向该类的虚函数表。
具体实现过程如下:
当我们定义一个包含虚函数的类时,编译器会为这个类创建一个虚函数表(vtable)。
类的每个对象都会包含一个虚表指针(vptr),通常位于对象内存布局的开始位置。
当派生类重写基类的虚函数时,派生类的虚函数表中对应的函数指针会被更新为派生类的函数实现。
当我们通过基类指针或引用调用虚函数时,程序会先找到对象的vptr,通过vptr找到虚函数表,然后在表中查找对应的函数地址,最后调用该函数。
这个过程可以用代码和内存布局来说明:
class Base {
public:virtual void func1() { std::cout << "Base::func1" << std::endl; }virtual void func2() { std::cout << "Base::func2" << std::endl; }void nonVirtual() { std::cout << "Base::nonVirtual" << std::endl; }
};class Derived : public Base {
public:void func1() override { std::cout << "Derived::func1" << std::endl; }// func2未重写,使用Base::func2
};int main() {Base* ptr = new Derived();ptr->func1(); // 调用Derived::func1ptr->func2(); // 调用Base::func2delete ptr;
}
内存布局大致如下:
+--------+
| vptr | --> Base vtable: [&Base::func1, &Base::func2]
+--------+
| 其他成员 |
+--------+
Derived对象的内存布局:
+--------+
| vptr | --> Derived vtable: [&Derived::func1, &Base::func2]
+--------+
| 继承的成员|
+--------+
| 新增成员 |
+--------+
当执行ptr->func1()时,实际的调用过程是:获取ptr指向的对象的vptr,通过vptr找到对应的虚函数表,在表中查找func1的地址(通常是表中的第一个条目),调用该地址处的函数,这就是为什么即使ptr是Base*类型,调用的仍然是Derived::func1。
值得注意的是,虚函数机制会带来一些运行时开销:每个对象增加了vptr的内存开销(通常是一个指针的大小,32位系统4字节,64位系统8字节),每个类需要维护一个虚函数表,调用虚函数时需要额外的间接寻址,此外,C++的多重继承会使虚函数的实现更复杂,可能涉及到多个虚表和复杂的指针调整。