考虑到构造函数调用的顺序,不要在构造函数调用虚函数。原因如下:
①因为派生类对象的虚函数是由派生类给出实现的,而派生类的构造函数是在基类的构造函数调用结束之后再调用的,因此在构造函数中调用虚函数相当于是要求访问对象内部尚未初始化的成分(派生类的构造函数还没执行),这在C++中是不允许的。
②当定义一个派生类对象时,派生类构造函数会被调用,但基类的构造函数一定会在之前调用,也就是说派生类对象的基类成分会在派生类自身成分被构造前先构造完毕。这时调用的虚函数不会下降到派生类的层次,而是基类的版本,亦即在基类构造期间,虚函数不是虚函数(派生类对象调用虚函数本该调用派生类的版本却调用了基类的版本,相当于失去了多态的功能)。
③根本原因:派生类对象在派生类构造函数开始执行前不会成为一个派生类对象,基类构造期间它只是一个基类对象,它的派生类部分由于未初始化所以被无视。
相同道理也适用于析构函数。一旦派生类析构函数执行,派生类的析构函数析构了派生类, 然后调用基类的析构函数, 如果这时基类析构函数里面调用一个虚函数, 本该调用到派生类的函数的, 但由于此时派生类部分已被析构,所以也只能调用基类版本,相当于失去了多态功能。
综上,不要在构造函数和析构函数中调用虚函数,因为构造函数/析构函数中的虚机制会被无视,可能运行时不会报错(有警告),但不能实现多态的虚函数应该并不符合你的预期。
虽然无法通过虚函数从基类向下调用,但构造期间令派生类将必要的构造信息向上传递至基类构造函数是可行的。
class base{
public:
base();
void func(const string& s)=0; //非虚
...
}
base::base(const string& s){
...
func(s);
}
class derived:public base{
public:
derived(parameters):base(createInfo()); //传递构造信息
...
private:
string createInfo(...);
}