`
shi5jin
  • 浏览: 37017 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

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

阅读更多
可以。不过你得悠着点。当你这样做时,也许你自己都不知道自己在干什么!在构造函数中,虚拟机制尚未发生作用,因为此时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实际上是在告诉你,不是“先有实作再有规则”,而是“如此实作,因为规则如此”。]

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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics