zoukankan      html  css  js  c++  java
  • 条款9:不要在构造和析构过程中调用virtual函数

    如下是一个股票交易的例子:

     1 class Transaction                               // 交易的基类
     2 {
     3 public:
     4     Transaction();
     5     virtual void logTransaction() const = 0;    // 用于记录交易日志
     6 };
     7 Transaction::Transaction()
     8 {
     9     logTransaction();              // 调用虚函数
    10 }
    11 
    12 class BuyTransaction : public Transaction       // 买进股票
    13 {
    14     virtual void logTransaction() const;
    15 };
    16 void BuyTransaction::logTransaction() const{ }
    17 
    18 class SellTransaction : public Transaction      // 卖出股票
    19 {
    20     virtual void logTransaction() const;
    21 };
    22 void SellTransaction::logTransaction() const{ }
    23 
    24 int main()
    25 {
    26 BuyTransaction b;
    27 
    28 return 0;
    29 }

    上述代码中执行BuyTransaction b时,会调用基类的构造函数,而基类的构造函数会调用一个虚函数来完成工作。但是该虚函数是否会调用派生类的对应函数呢?答案是否定的,解释如下:

    1>  基类的构造是先于派生类的构造的。当基类的构造函数执行时,派生类中的成员尚未被初始化,如果允许基类构造期间调用派生类的函数,可能也会调用了没有初始化的“垃圾值”,导致不确定行为发生。

    2>  基类构造期间,virtual函数的行为绝对不会伸到派生类中,因为此时构造的是基类,即此时构造的对象的类型是基类而非派生类,这点很重要。

     

    但是有时我们在构造基类的时候又需要派生类中才有的参数信息,如何解决:

    在基类中将logTransaction函数改为非虚函数,然后在构造派生类时将必要的参数传递到基类的构造函数中去,像如下这样:

     1 #include <string>
     2 
     3 class Transaction                                             // 交易的基类
     4 {
     5 public:
     6     explicit Transaction(const std::string& logInfo);
     7     void logTransaction(const std::string& logInfo) const;    // 用于记录交易日志
     8 };
     9 Transaction::Transaction(const std::string& logInfo)
    10 {
    11     logTransaction(logInfo);                  // 调用非虚函数
    12 } 13 void Transaction::logTransaction(const std::string& logInfo) const{ } 14 15 class BuyTransaction : public Transaction // 买进股票 16 { 17 public: 18 BuyTransaction(const std::string& parameter) : Transaction(parameter){ } 19 }; 20 21 22 int main() 23 { 24 BuyTransaction b("BT"); 25 26 return 0; 27 }

    由上可知,如果基类构造时要使用派生类的信息,可以通过派生类构造函数的初始化列表传入相关参数,切不可调妄图调用虚函数实现,因为在构造基类期间其对象的类型是基类类型。

    对于析构函数也是同样的道理,基类的析构总是在派生类析构后才进行,此时派生类的对象已经不存在了,而调用虚函数必然会引发一个不确定行为。当然你也可以将基类的虚函数实现后在析构函数中进行调用,但是这样的话,为什么不定义为非虚函数呢,虚函数的意义将不复存在了。

  • 相关阅读:
    rsync
    zabbix一键部署
    MYSQL高可用——MHA(概述与安装)
    正向代理的简单概括和应用
    kvm的简介与安装桥接
    linux下常用的五个查找命令
    简述MVC框架模式以及在你(Android)项目中的应用
    ThreadLocal源码分析
    Handler、Looper、MessageQueue、Thread源码分析
    HashMap总结
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4527948.html
Copyright © 2011-2022 走看看