MST

星途 面试题库

面试题:C++抽象类构造函数中的虚函数行为及设计考量

在C++中,当在抽象类的构造函数中调用虚函数时会发生什么?请通过代码示例说明,并阐述在设计抽象类构造函数时,如何避免因调用虚函数可能带来的问题,以及这样设计背后的原理。
14.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 在抽象类构造函数中调用虚函数的情况
    • 当在抽象类的构造函数中调用虚函数时,该调用不会表现出多态性,而是调用的当前类的版本。这是因为在构造函数执行期间,对象还未完全构造完成,此时虚函数表指针可能还未正确初始化,所以无法实现动态绑定。
    • 代码示例:
#include <iostream>

class AbstractClass {
public:
    AbstractClass() {
        std::cout << "AbstractClass constructor" << std::endl;
        virtualFunction();
    }
    virtual void virtualFunction() {
        std::cout << "AbstractClass virtualFunction" << std::endl;
    }
};

class ConcreteClass : public AbstractClass {
public:
    ConcreteClass() {
        std::cout << "ConcreteClass constructor" << std::endl;
    }
    void virtualFunction() override {
        std::cout << "ConcreteClass virtualFunction" << std::endl;
    }
};

int main() {
    ConcreteClass obj;
    return 0;
}
  • 在上述代码中,AbstractClass 是抽象类(这里为了演示方便,实际抽象类通常至少有一个纯虚函数),ConcreteClass 继承自 AbstractClass 并覆盖了 virtualFunction。当创建 ConcreteClass 对象时,先调用 AbstractClass 的构造函数,在这个构造函数中调用 virtualFunction,输出的是 AbstractClass virtualFunction,而不是 ConcreteClass virtualFunction,这证明了没有体现多态性。
  1. 避免因调用虚函数带来问题的设计方法
    • 避免在构造函数中调用虚函数:将需要的初始化逻辑封装到一个单独的非虚函数中,并在构造函数完成后调用该函数。
    • 代码示例
#include <iostream>

class AbstractClass {
public:
    AbstractClass() {
        std::cout << "AbstractClass constructor" << std::endl;
        initialize();
    }
    void initialize() {
        nonVirtualFunction();
    }
    virtual void nonVirtualFunction() {
        std::cout << "AbstractClass nonVirtualFunction" << std::endl;
    }
};

class ConcreteClass : public AbstractClass {
public:
    ConcreteClass() {
        std::cout << "ConcreteClass constructor" << std::endl;
    }
    void nonVirtualFunction() override {
        std::cout << "ConcreteClass nonVirtualFunction" << std::endl;
    }
};

int main() {
    ConcreteClass obj;
    return 0;
}
  • 在这个示例中,AbstractClass 的构造函数调用 initialize 函数,initialize 函数再调用 nonVirtualFunction。在 ConcreteClass 中覆盖了 nonVirtualFunction,由于构造函数完成后调用 initialize,此时对象已构造完成,虚函数表指针已正确初始化,所以 nonVirtualFunction 的调用体现了多态性,输出 ConcreteClass nonVirtualFunction
  1. 原理阐述
    • 构造函数的任务是初始化对象的成员变量,并为对象准备好运行时的状态。在构造函数执行期间,对象处于部分构造状态,虚函数表指针可能还未完全正确设置。因此,在构造函数中调用虚函数,C++ 标准规定调用的是当前类的版本,而不是派生类覆盖的版本,以避免未定义行为。通过将初始化逻辑移到构造函数之外的函数中,可以确保对象完全构造后再调用可能被覆盖的函数,从而实现多态性。