先看一段在构造函数中直接调用虚函数的代码:
#include <iostream>
class Base
{
public:
Base() { Foo(); } ///< 打印 1
virtual void Foo()
{
std::cout << 1 << std::endl;
}
};
class Derive : public Base
{
public:
Derive() : Base(), m_pData(new int(2)) {}
~Derive() { delete m_pData; }
virtual void Foo()
{
std::cout << *m_pData << std::endl;
}
private:
int* m_pData;
};
int main()
{
Base* p = new Derive();
delete p;
return 0;
}
这里的结果将打印:1。
这表明第6行执行的的是Base::Foo()而不是Derive::Foo(),也就是说:虚函数在构造函数中“不起作用”。为什么?
当实例化一个派生类对象时,首先进行基类部分的构造,然后再进行派生类部分的构造。即创建Derive对象时,会先调用Base的构造函数,再调用Derive的构造函数。
当在构造基类部分时,派生类还没被完全创建,从某种意义上讲此时它只是个基类对象。即当Base::Base()执行时Derive对象还没被完全创建,此时它被当成一个Base对象,而不是Derive对象,因此Foo绑定的是Base的Foo。
C++之所以这样设计是为了减少错误和Bug的出现。假设在构造函数中虚函数仍然“生效”,即Base::Base()中的Foo();所调用的是Derive::Foo()。当Base::Base()被调用时派生类中的数据m_pData还未被正确初始化,这时执行Derive::Foo()将导致程序对一个未初始化的地址解引用,得到的结果是不可预料的,甚至是程序崩溃(访问非法内存)。
总结来说:基类部分在派生类部分之前被构造,当基类构造函数执行时派生类中的数据成员还没被初始化。如果基类构造函数中的虚函数调用被解析成调用派生类的虚函数,而派生类的虚函数中又访问到未初始化的派生类数据,将导致程序出现一些未定义行为和bug。
转自:http://www.cnblogs.com/carter2000/archive/2012/04/28/2474960.html