荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Dreamer (我与萤火虫), 信区: SoftDev
标  题: 【C++】[FAQ]我能从构造函数调用虚函数吗?
发信站: 荔园晨风BBS站 (Wed Jun  9 13:13:46 2004), 站内信件


Q: 我能从构造函数调用虚函数吗?
A: 可以。不过你得悠着点。当你这样做时,也许你自己都不知道自己在干什么!
在构造函数中,虚拟机制尚未发生作用,因为此时overriding尚未发生。万丈高楼
平地起,总得先打地基吧?对象的建立也是这样——先把基类构造完毕,然后在此
基础上构造派生类。

看看这个例子:
#include<string>
#include<iostream>
using namespace std;

class B {
public:
B(const string& ss) { cout << "B constructor\n"; f(ss); }
virtual void f(const string&) { cout << "B::f\n";}
};

class D : public B {
public:
D(const string & ss) :B(ss) { cout << "D constructor\n";}
void f(const string& ss) { cout << "D::f\n"; s = ss; }
private:
string s;
};

int main()
{
D d("Hello");
}


这段程序经编译运行,得到这样的结果:
B constructor B::f D constructor
注意,输出不是D::f 。 究竟发生了什么?f()是在B::B()中调用的。如果构造函
数中调用虚函数的规则不是如前文所述那样,而是如一些人希望的那样去调用D::
f()。那么因为构造函数D::D()尚未运行,字符串s还未初始化,所以当D::f()试图
将参数赋给s时,结果多半是——立马当机。
析构则正相反,遵循从继承类到基类的顺序(拆房子总得从上往下拆吧?),所以
其调用虚函数的行为和在构造函数中一样:虚函数此时此刻被绑定到哪里(当然应
该是基类啦——因为继承类已经被“拆”了——析构了!),调用的就是哪个函数


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

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




--
如果你真的爱萤火虫,你就不应该将她困在瓶子里;
如果你真的爱萤火虫,你应该放开她,让她在天空中自由自在地飞!
虽然你会不舍得她,但是最终你就会明白这样你才真正拥有了她!

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.0.223]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店