MST
星途 面试题库

面试题:C++ 继承中的多态与虚函数机制

请阐述 C++ 中虚函数和多态的概念及实现原理。假设存在一个基类 Shape,派生类 Circle 和 Rectangle 都继承自 Shape,Shape 类中有一个虚函数 draw()。请写出代码示例展示如何通过基类指针或引用来调用派生类中重写的 draw() 函数,以实现多态行为,并解释背后的动态绑定过程。
14.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚函数和多态的概念

  1. 虚函数:在基类中使用 virtual 关键字声明的成员函数。虚函数的目的是为了让派生类可以重写(override)这个函数,从而提供不同的实现。
  2. 多态:多态是指同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在 C++ 中,多态主要通过虚函数和指针或引用的动态绑定来实现。通过基类的指针或引用调用虚函数时,会根据指针或引用实际指向的对象类型来决定调用哪个类的虚函数实现,这就是多态性的体现。

实现原理

  1. 虚函数表:当一个类中包含虚函数时,编译器会为这个类生成一个虚函数表(vtable)。虚函数表是一个存储类成员虚函数地址的数组。每个包含虚函数的类或从包含虚函数的类派生而来的类都有自己的虚函数表。
  2. 虚指针:每个对象在内存布局中会包含一个指向其所属类虚函数表的指针,称为虚指针(vptr)。当通过基类指针或引用调用虚函数时,程序会首先根据对象的虚指针找到对应的虚函数表,然后在虚函数表中找到实际要调用的虚函数地址,最后调用该函数。这就是动态绑定的过程,它发生在运行时,根据对象的实际类型决定调用哪个函数。

代码示例

#include <iostream>

// 基类 Shape
class Shape {
public:
    // 虚函数 draw
    virtual void draw() const {
        std::cout << "Drawing a Shape." << std::endl;
    }
};

// 派生类 Circle
class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a Circle." << std::endl;
    }
};

// 派生类 Rectangle
class Rectangle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing a Rectangle." << std::endl;
    }
};

int main() {
    Shape* shapePtr1 = new Circle();
    Shape* shapePtr2 = new Rectangle();

    shapePtr1->draw(); // 调用 Circle 的 draw 函数
    shapePtr2->draw(); // 调用 Rectangle 的 draw 函数

    delete shapePtr1;
    delete shapePtr2;

    return 0;
}

动态绑定过程解释

  1. main 函数中,创建了 CircleRectangle 对象,并分别用 Shape 类型的指针 shapePtr1shapePtr2 指向它们。
  2. 当调用 shapePtr1->draw() 时,由于 draw 是虚函数,程序首先通过 shapePtr1 指向对象的虚指针找到 Circle 类的虚函数表。
  3. Circle 类的虚函数表中找到 draw 函数的地址,并调用 Circle 类中重写的 draw 函数。
  4. 同理,当调用 shapePtr2->draw() 时,程序通过 shapePtr2 指向对象的虚指针找到 Rectangle 类的虚函数表,然后调用 Rectangle 类中重写的 draw 函数。这就是通过基类指针实现多态的动态绑定过程。