C++之构造函数和析构函数中不要调用virtual函数(9)---《Effective C++》

3 篇文章 0 订阅
2 篇文章 0 订阅

条款9:绝不要在构造函数和析构函数中调用virtual函数

为什么不要在构造函数和析构函数中调用virtual函数呢?下面请大家带着上述问题来看如下代码:

class Base{
public:
    Base();
        virtual void hello();
        ...
    }
private:
    int x;
    int y;
};

Base::Base(){
    ...
    hello();
}

class ABase:public Base{
public:
    virtual void hello();//这儿可以声明为virtual,也可以不用声明为virtual,因为在基类中已经声明为virtual,那么子类中相应的所有函数都就变味virtual
    ...
};

class BBase::public Base{
public:
    virtual void hello();
    ...
}

ABase a;

试想上面代码运行时候会发生什么问题?ABase的构造函数被调用,ABase的基类Base构造函数一定更早调用,即ABase中从Base中继承的x,y变量通过Base的构造函数被初始化,ABase中的z变量尚未被初始化,此时好戏上演。
1)Base类的构造函数中调用的虚函数hello()函数是基类Base的,还是子类ABase的呢?答案当然是基类Base的,因为在基类Base的构造函数执行期间,virtual函数绝对不会下降到子类ABase阶层,如果这样比较枯燥的话,我们可以反向解释:2)基类Base的构造函数执行期间,子类ABase中的成员变量z并没有被初始化,如果在基类Base的构造函数中调用的virtual函数已经下降到了子类ABase阶层,那么Base中调用的这个virtual函数一定要取取用子类ABase中的成员变量,而这些成员变量并没有被初始化,别挣扎了,C++不允许这样,可以这样理解:在Base类构造期间,virtual函数不是virutal函数,3)其实最主要的原因是:在子类ABase对象的基类构造函数执行期间,对象的类型是基类Base的,而不是子类ABase的,不止virtual函数会被解至基类Base中,也会把对象视为基类Base类型,在本例中,当子类ABase的构造函数执行起来时,一定先调用基类Base的构造函数来初始化子类ABase中的基类成分,这时候对象的类型是基类Base的,同时这时子类ABase中的专属成分如z尚未被初始化,对象在子类构造函数(即子类中的基类构造函数执行结束后)开始执行之前一定不会成为一个子类对象。
同样对着适用于析构函数,C++的析构函数先执行子类的析构函数,后执行基类的析构函数,执行和构造函数执行的顺序刚好相反,一旦子类析构函数开始执行,对象中的子类成员变量呈现未定义值,C++视它们不存在,进入基类析构函数之后就成为一个基类对象,C++的任何部分包括virtual函数,dynamic_casts也同样这样看待它。
**

需要注意的是,有的编译器对这种情况报错,而有的并不报错,很坑的!

**

由于这样并没有执行virtual函数,达到相应的效果,因此,C++明确规定不能在构造函数和析构函数中调用virtual函数。

其他方法可以解决这个问题,其中之一是在基类Base中virtual函数hello声明为non-virtual,然后要求子类ABase构造函数传递必要的信息给Base构造函数,因为基类Base中调用了Base的成员函数hello,不可能下降至子类ABase中,因此安全,为了双重安全,我们也可以选择利用static函数提供数据给基类Base数据,这样在子类ABase中一定不会存在未被初始化的数据,而后这个构造函数就可以安全的调用non-virtual函数hello,如:

class Base{
public:
    Base(int x,int y);
    void hello();//现在是个non-virtual函数
    ...
private:
    int x;
    int y;
};
Base::Base(int x,int y):x(x),y(y){
    hello();//现在是个non-virtual调用
}
class ABase:public Base{
public:
    ABase(params):Base(createParams(params)){
        ...
    }
    ...
private:
    static *** createParams(parameters);
};

这种方式是怎样的呢?你无法使用virtual函数从基类Base向下调用,在构造期间,你可以籍由“令子类将必要的构造信息向上传递值基类构造函数”替换加以弥补,这里令createParams函数为static,也就不可能意外指向“初期未成熟之子类ABase对象中尚未初始化的成员变量”。

总结如下:在构造函数中不要调用virtual函数,因为这类调用从不下降至子类derived class这层。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值