为什么构造函数和析构函数中不应该调用虚函数

来源:互联网 发布:java小项目开发 编辑:程序博客网 时间:2024/05/18 21:47

在语法上,构造函数调用虚函数当然是没有问题的。但是这种用法不一定能得到想要的结果,还会引发一些小问题。

class Base {public:    Base() { this->func1(); this->func2(); };    virtual void func1() { cout << "base::func1" << endl; };    virtual void func2() { cout << "base::func2" << endl; };};class Derived:public Base {public:    Derived() { func1(); func2(); };    virtual void func1() { cout << "Derived::func1" << endl; };    virtual void func2() { cout << "Derived::func2" << endl; };};

以上有两个类
通过如下方式调用

void main(){    Base *a= new Derived();    system("pause");}

打印结果为:
base::func1
base::func2
Derived::func1
Derived::func2
当我们实例化一个派生类的时候,基类构造函数调用的virtual function定义是Base::func1和Base::func2,而Derived构造函数是正常的,这是因为虚函数通过虚指针来访问,一个对象维护一个虚指针,在对象构造函数开始执行之时,在对像执行初始化之前,虚指针生成,基类的这个虚指针只能访问基类定义的虚函数。
为什么C++要这么做呢,直接支持多态不好吗?因为派生类构造完成之前,基类构造之时,子类并没有形成,子类数据成员当然就没有初始化,也许是为了防止出现访问未定义Bug,C++只允许基数调用自己定义的虚函数。
所以构造函数中的多态特征失效了。
上面的问题如果对于CPP有一定的了解是可以完美的规避的,不会出现任何意料之外的情况。
但是一个C++高手却不一定能随时注意到一个冗余的函数调用的问题。
上面的类定义中,基类的两个虚函数被子类重写,当我只想实例化一个子类对象的时候,如果基类像上面的一样调用了两个被重载的函数,这显然不是一个好的决定,比如重复对成员进行赋值、拷贝、计算等等。所以我们不应该在构造函数中调用虚函数。
同样的,我们也不应该在析构函数中调用虚函数,基本上是和上面提到的同样的原因。析构是由内向外析构的过程,子类在前,父类在后,如果子类析构函数先通过一个虚函数把一个成员对象释放了,父类的析构函数再通过一个虚函数把这个成员释放一次,显然就出问题了。
当然这些问题都是对语法没吃透的原因,但是如果我们能够按照一定的规范去实现代码,规避这些问题,当然是最好的,因为谁也不能保证用危险的代码会不犯错。

原创粉丝点击