MST

星途 面试题库

面试题:C++单继承中的虚函数与多态实现

在C++单继承体系中,描述虚函数和多态的概念及实现原理。假设基类有一个虚函数 `void print()`,派生类重写了该函数。当通过基类指针指向派生类对象并调用 `print()` 函数时,程序是如何确定调用哪个 `print()` 函数的?请阐述其底层机制,并给出相应的代码示例。
44.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚函数和多态的概念

  1. 虚函数:在基类中使用 virtual 关键字声明的成员函数。虚函数允许在派生类中被重写(override),以便实现不同的行为。
  2. 多态:多态是指同一个函数调用在不同的对象上会产生不同的行为。在C++中,多态通过虚函数和指针或引用实现。通过基类指针或引用调用虚函数时,会根据指针或引用实际指向的对象类型来决定调用哪个类的虚函数版本,从而实现运行时多态。

实现原理

  1. 虚函数表(vtable):当类中包含虚函数时,编译器会为该类生成一个虚函数表。虚函数表是一个存储类的虚函数地址的数组。每个包含虚函数的类都有一个对应的虚函数表。
  2. 虚函数表指针(vptr):每个包含虚函数的对象都包含一个指向其所属类的虚函数表的指针,即虚函数表指针。这个指针通常在对象的内存布局的开头部分。
  3. 动态绑定:当通过基类指针或引用调用虚函数时,程序会首先通过对象的虚函数表指针找到对应的虚函数表,然后在虚函数表中查找被调用虚函数的地址,并调用该地址处的函数。这就是动态绑定的过程,它使得程序能够在运行时根据对象的实际类型来决定调用哪个虚函数版本。

底层机制

  1. 对象创建:当创建一个派生类对象时,对象的内存布局中会包含一个指向派生类虚函数表的虚函数表指针。派生类的虚函数表继承自基类的虚函数表,并根据需要重写其中的虚函数地址。
  2. 函数调用:当通过基类指针或引用调用虚函数时,程序会首先获取对象的虚函数表指针,然后通过虚函数表指针找到对应的虚函数表。在虚函数表中,根据虚函数的索引找到被调用虚函数的地址,并调用该函数。

代码示例

#include <iostream>

class Base {
public:
    virtual void print() {
        std::cout << "Base::print()" << std::endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        std::cout << "Derived::print()" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    basePtr->print(); // 调用 Derived::print()

    delete basePtr;
    return 0;
}

在上述代码中,Base 类定义了一个虚函数 printDerived 类重写了这个虚函数。在 main 函数中,通过基类指针 basePtr 指向派生类对象,并调用 print 函数,此时实际调用的是 Derived::print(),这体现了多态性。