MST

星途 面试题库

面试题:C++ 父类虚函数与多态实现对代码扩展性的深度剖析

假设你正在开发一个图形绘制库,有一个基类 Shape,包含一个虚函数 draw。从 Shape 派生出 Circle、Rectangle 等子类,各自重写 draw 函数。阐述在这种场景下,父类虚函数如何在长期的开发过程中,比如不断添加新的图形类型时,保障代码的扩展性。同时,分析虚函数表和动态绑定机制在此过程中是如何发挥作用以支持扩展性的。
23.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 保障代码扩展性方面
    • 开闭原则:通过将 draw 函数定义为虚函数,使得在添加新的图形类型(如 Triangle 等新子类)时,无需修改已有代码(如 Shape 基类及其他已存在的子类代码)。新的图形子类只需继承 Shape 基类,并实现 draw 虚函数即可。例如:
    class Triangle : public Shape {
    public:
        void draw() override {
            // 绘制三角形的具体实现
        }
    };
    
    • 多态性:父类 Shape 的指针或引用可以指向不同子类的对象,在调用 draw 函数时,根据实际对象的类型调用相应子类的 draw 实现。这使得在使用图形绘制库时,代码可以统一处理各种图形,而不必为每种图形类型编写单独的处理逻辑。例如:
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();
    shape1->draw(); // 调用 Circle 的 draw 函数
    shape2->draw(); // 调用 Rectangle 的 draw 函数
    
  2. 虚函数表和动态绑定机制的作用
    • 虚函数表:每个包含虚函数的类(如 Shape 及其子类)都有一个虚函数表(vtable)。当创建一个对象时,对象的内存布局中会包含一个指向其所属类虚函数表的指针(vptr)。虚函数表中存储了类中虚函数的地址。对于 Shape 基类,虚函数表中存储了 Shape::draw 的地址。当子类(如 Circle)重写 draw 函数时,子类的虚函数表中对应的 draw 函数地址被替换为 Circle::draw 的地址。这使得通过对象的 vptr 可以快速找到对应类的虚函数实现。
    • 动态绑定机制:在运行时,当通过父类指针或引用调用虚函数(如 shape1->draw())时,程序会根据对象实际的类型(即对象 vptr 指向的虚函数表)来确定调用哪个版本的虚函数。这就是动态绑定。在添加新图形类型时,新子类会有自己的虚函数表,通过动态绑定机制,即使在编译时不知道具体对象类型(只知道是 Shape 指针或引用),运行时也能正确调用新子类的 draw 函数,从而支持了代码的扩展性。