构造函数中调用虚函数不触发多态,只会调用当前类的版本。例如Base构造函数调用print()时,即使Derived重写了该函数,仍执行Base::print()。因为在基类构造期间,对象被视为基类类型,vptr指向基类虚表,派生类成员未初始化,无法支持动态绑定。为避免未定义行为,C++禁止此阶段多态。最佳实践是避免在构造函数中调用虚函数,可改用init()方法或工厂模式实现延迟调用。
在C++中,构造函数里调用虚
函数不会触发多态行为。这是很多开发者容易误解的地方。即使函数是虚函数,也不会调用派生类的重写版本,而是只调用当前正在构造的对象所属类型的版本。
当一个派生类对象被创建时,构造过程从基类开始,逐步向派生类推进。在基类构造函数执行期间,派生类的成员尚未初始化,整个对象在运行时被视为“基类对象”。
因此,此时即使调用了虚函数,动态绑定机制也会被暂时禁用,系统只会调用当前构造函数所在类定义的版本。
来看一段代码:
#includeclass Base { public: Base() { print(); // 调用虚函数 } virtual void print() const { std::cout << "Base::print()\n"; } }; class Derived : public Base { public: Derived() : Base() {} // 先调用 Base 构造函数 void print() const override { std::cout << "Derived::print()\n"; } }; int main() { Derived d; // 输出:Base::print() return 0; }
输出结果是:Base::print(),而不是你可能期望的 Derived::print()。
原因是在 Base 的构造函数中,Derived 部分还没有构建完成,所以虚函数表(vtable)指向的是 Base 类的实现。
避免在构造函数中调用虚函数。如果确实需要类似“虚行为”的功能,可以考虑以下替代方案:
init() 或 setup() 方法,在构造完成后手动调用基本上就这些。记住一句话:构造函数中的虚函数调用是静态解析的,不走多态。理解这一点,能帮你避开不少隐蔽的bug。