我能从构造函数调用虚函数吗?

本文探讨了C++中虚函数的调用机制,特别是在构造函数和析构函数中的行为。文章解释了为何在构造函数中调用虚函数会绑定到基类,以及析构函数如何遵循从派生类到基类的调用顺序。
可以。不过你得悠着点。当你这样做时,也许你自己都不知道自己在干什么!在构造函数中,虚拟机制尚未发生作用,因为此时overriding尚未发生。万丈高楼平地起,总得先打地基吧?对象的建立也是这样——先把基类构造完毕,然后在此基础上构造派生类。
 
看看这个例子:
 
 
这段程序经编译运行,得到这样的结果:
 
 

析构则正相反,遵循从继承类到基类的顺序(拆房子总得从上往下拆吧?),所以其调用虚函数的行为和在构造函数中一样:虚函数此时此刻被绑定到哪里(当然应该是基类啦——因为继承类已经被“拆”了——析构了!),调用的就是哪个函数。

更多细节请见《The Design and Evolution of C++》,13.2.4.2 或者《The C++ Programming Language》第三版,15.4.3 。

有时,这条规则被解释为是由于编译器的实作造成的。[译注:从实作角度可以这样解释:在许多编译器中,直到构造函数调用完毕,vtable才被建立,此时虚函数才被动态绑定至继承类的同名函数。] 但事实上不是这么一回事——让编译器实作成“构造函数中调用虚函数也和从其他函数中调用一样”是很简单的[译注:只要把vtable的建立移至构造函数调用之前即可]。关键还在于语言设计时的考量——让虚函数可以求助于基类提供的通用代码。[译注:先有鸡还是先有蛋?Bjarne实际上是在告诉你,不是“先有实作再有规则”,而是“如此实作,因为规则如此”。]

原文地址:http://www.research.att.com/~bs/bs_faq2.html#vcall

<think>我们正在讨论C++中在构造函数调用虚函数的行为。根据引用[2]和引用[3],在构造函数调用虚函数时,调用的是当前类的版本,而不是派生类的版本。这是因为在基类构造函数执行期间,派生类的对象尚未完全构造,虚函数表(vtable)指向的是当前类的虚函数表。因此,不会调用派生类重写的虚函数。根据引用[2]中的代码示例:Base*ptr =new Derived();在构造Base部分时,调用Base构造函数,此时如果调用虚函数,则调用Base::show(),而不是Derived::show()。同样,在析构函数中调用虚函数时(引用[1]),当执行基类析构函数时,派生类部分已经被销毁,所以虚函数调用的是基类的版本。因此,在构造函数或析构函数中调用虚函数不会导致未定义行为(undefinedbehavior),但它的行为是确定的:调用当前类(构造函数或析构函数所在类)的版本,而不是派生类的版本。然而,这种行为可能不符合程序员的预期(他们可能期望调用派生类的版本),因此通常被视为一种不良实践,应避免。总结:在构造函数或析构函数中调用虚函数是安全的(不会导致未定义行为),但可能不是你想要的行为。如果你需要多态行为,应该避免在构造/析构期间调用虚函数,或者使用其他设计模式(如工厂模式,如引用[3]所示)。因此,回答用户的问题:在C++构造函数调用虚函数不会导致未定义行为,但只会调用当前类的实现,不会调用派生类的重写版本。根据引用[3]的总结,这种设计是C++为了保证对象构造的安全性而做出的规定。下面我们按照要求生成相关问题。</think>### C++构造函数调用虚函数的行为分析 在C++中,**构造函数调用虚函数不会导致未定义行为**,但其行为与常规多态调用有本质区别。根据C++对象构造机制: 1. **调用的是当前类版本** 在构造函数执行期间,虚函数调用会绑定到**当前正在构造的类**的版本,而非派生类的重写版本。这是因为: - 基类构造时派生类部分尚未初始化 - 虚函数表(vtable)此时指向当前类的虚表 ```cpp class Base { public: Base() { this->virtualFunc(); // 调用Base::virtualFunc } virtual void virtualFunc() { /* 基类实现 */ } }; ``` 2. **行为安全性** 该行为是**语言规范明确定义**的(非未定义行为),但需注意: - ✅ 语法完全合法 - ⚠️ 可能违反开发者预期(期待多态行为) - ⚠️ 若函数依赖派生类未初始化的成员会导致运行时错误 3. **设计建议** 应避免在构造/析构函数中调用虚函数: - 使用初始化函数:在构造完成后显式调用 ```cpp class Derived : public Base { public: Derived() { init(); } // 构造完成后手动初始化 void init() override { /* 安全调用虚函数 */ } }; ``` - 采用工厂模式(如引用[3]所示):对象构造与初始化分离[^3] > **关键原理**:对象构造遵循**自顶向下**顺序(基类→派生类),析构则是**自底向上**(派生类→基类)。在基类构造期间,对象类型被视为基类类型,虚函数分派基于当前构造阶段[^2]。 --- ### 相关问题 1. 为什么C++析构函数中调用虚函数同样无法实现多态?[^1] 2. 如何通过工厂模式解决构造期间需要多态调用的问题? 3. C++对象构造过程中虚函数表(vtable)的建立机制是怎样的? 4. 在构造函数调用非虚成员函数是否存在风险? 5. 现代C++中哪些设计模式可以替代构造期间的虚函数调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值