class Base
{
public:
Base();
virtual void oneFunction() = 0;
...
};
Base::Base()
{
...
oneFunction();
}
class Derived : public Base
{
public:
Derived(){};
virtual void oneFunction()
{
std::cout << "Derived oneFunction...";
}
}
当我们调用下面的代码时会出现什么情况了?
Derived d;
首先,编译器发出了一个警告,纯虚函数在构造函数中被调用。
[Warning] pure virtual 'virtual void Base::oneFunction()' called from constructor
接着,因为上面我们的函数oneFunction是纯虚函数,因此编译器会报错,undefined。
cppt.cpp:(.text+0x1f): undefined reference to `Base::oneFunction()'
然后,程序结束运行。
[Error] ld returned 1 exit status
我们试着理解一下上面的整个过程。
- 首先会调用Base的构造函数,并且在构造函数里面调用了
oneFunction
方法; - 调用Derived的构造函数
在调用Base构造函数的时候,末尾调用了一个virtual类型的函数,但是此时,Derived的对象 d 中属于Derived的部分并没有被创建出来,也就是Derived的构造函数还没有调用,那么,调用的oneFunction
方法就是属于Base的部分。
也就是说,在base class被构建的时候,virtual函数是不会下降到derived class 中的。更直接一点,在base class被构建的时候,virtual函数不是virtual函数。
同样的道理在析构函数的时候也是一样的,当derived函数的析构函数运行之后,derived class部分的变量等都已被析构,此时,当析构运行到base class的析构函数时,如果尝试调用了derived class的虚函数实现部分,则会出现不可预知的错误。
我们尝试将上面的纯虚函数修改为虚函数试试。
class Base
{
public:
virtual void oneFunction()
{
std::cout << "Base oneFunction...";
}
Base()
{
oneFunction();
}
};
class Derived : public Base
{
public:
Derived(){ };
virtual void oneFunction()
{
std::cout << "Derived oneFunction...";
}
};
修改后,在调用的时候,就会发现虽然程序能够正常运行,但是调用的版本是base class部分的虚函数。而这也不是我们所希望的版本。
所以,如果我们想要在 每一个 derived class 继承 base class的时候,都能调用适当版本的oneFunction
,那么,在构造函数中调用虚函数或者纯虚函数是不能实现的。
解决方法是将虚函数属性去掉,调用一个 non-virtual的函数。
#include<iostream>
#include<string>
class Base
{
public:
Base(const std::string& param);
void oneFunction(const std::string& param) const
{
std::cout << param;
}
};
Base::Base(const std::string& param)
{
oneFunction(param);
}
class Derived : public Base
{
public:
Derived(const std::string& param) : Base(createOneFunction(param))
{
}
private:
static std::string createOneFunction(const std::string& param);
};
std::string Derived::createOneFunction(const std::string& param)
{
return param + " Derived...";
}
int main()
{
Derived d("test");
return 0;
}
上面的例子中我们定义了一个非虚函数,并在base class的构造函数中调用,然后在derived class对象被创建的时候传入需要的参数。也就是借由derived class将必要的构造信息上传致base class构造函数来代替向下调用的虚函数。
在构造函数或者析构函数中调用virtual虚函数并不会满足我们需求。因为这类调用不会下降到派生类中。