effective c++里面的一些知识
假设构造函数里面包含virtual函数的情况
class Transaction {// 所有交易的基类
public:
Transaction();
virtual void logTransaction() const = 0;//建立依赖于具体交易类型的登录项
};
Transaction::Transaction() //实现基类的构造函数
{
logTransaction(); //最后,登录该交易
}
class BuyTransaction : public Transaction {
// 派生类
public:
virtual void logTransaction() const; //怎样实现这种类型交易的登录?
};
class SellTransaction : public Transaction {
//派生类
public:
virtual void logTransaction() const; //怎样实现这种类型交易的登录?
};
这段代码来自《Effecitive C++》条款09,当声明一个BuyTransaction对象的时候,首先Transaction的构造函数会被调用,从而其virtual函数也被调动,这里就是引发惊奇的起点。这时候被调用的logTransaction是Transaction的版本,而不是派生类BuyTransaction的版本。
我们再看一段代码。
class Base
{
public:
Base()
{
Fuction();
}
~Base()
{
Fuction1();
}
virtual void Fuction()
{
cout << "Base::Fuction" << endl;
}
virtual void Fuction1()
{
cout << "Base::Fuction1" << endl;
}
};
class A : public Base
{
public:
A()
{
Fuction();
}
~A()
{
Fuction1();
}
virtual void Fuction()
{
cout << "A::Fuction" << endl;
}
virtual void Fuction1()
{
cout << "A::Fuction1" << endl;
}
};
#include<iostream>
using namespace std;
int main()
{
A a;
cout<<1<<endl;
}
当声明一个派生类对象,会发生什么呢?
程序输出为:
Base::Fuction
A::Fuction
1
A::Fuction1
Base::Fuction1
A类继承自Base,在Base中有虚函数Function,并且有代码,同时构造(析构)函数调用了虚函数,此时对象a先调用基类Base的构造函数,从而调用了Function函数,此时调用的还是基类的Functio函数,所以并没有实现多态的功能,析构函数先调用了A类的析构函数,从而调用了自己的function1,再调用了基类的function1。
总结
在基类的构造过程中,虚函数调用从不会被传递到派生类中。派生类对象表现出来的行为好象其本身就是基类型。不规范地说,在基类的构造过程中,虚函数并没有被”构造”。也就是说,在派生类对象的基类子对象构造期间,调用的虚函数的版本是基类的而不是子类的。
对上面这种看上去有点违背直觉的行为可以用一个理由来解释-因为基类构造器是在派生类之前执行的,所以在基类构造器运行的时候派生类的数据成员还没有被初始化。如果在基类的构造过程中对虚函数的调用传递到了派生类, 派生类对象当然可以参照引用局部的数据成员,但是这些数据成员其时尚未被初始化。这将会导致无休止的未定义行为和彻夜的代码调试。沿类层次往下调用尚未初始化的对象的某些部分本来就是危险的,所以C++干脆不让你这样做。
在对象的析构期间,存在与上面同样的逻辑。一旦一个派生类的析构器运行起来,该对象的派生类数据成员就被假设为是未定义的值,这样以来,C++ 就把它们当做是不存在一样。一旦进入到基类的析构器中,该对象即变为一个基类对象,C++中各个部分(虚函数,dynamic_cast运算符等等)都这样处理。
所以,析构函数和构造函数不能调用虚函数,从语法上讲,调用完全没有问题, 但是从效果上看,往往不能达到需要的目的。 派生类对象构造期间进入基类的构造函数时,对象类型变成了基类类型,而不是派生类类型。 同样,进入基类析构函数时,对象也是基类类型。