C ++虚拟方法不起作用

时间:2012-05-30 17:08:35

标签: c++ function inheritance methods virtual

给出以下示例:

class BaseClass
{
  BaseClass()
  {
  };

  virtual ~BaseClass()
  {
    this->Cleanup();
  };

  virtual void Cleanup()
  {
    // Do cleanup here.
  };
};

class Level1DerivedClass : public BaseClass
{
  Level1DerivedClass()
  {
  };

  virtual ~Level1DerivedClass()
  {
  };

  virtual void Cleanup()
  {
    // Call my base cleanup.
    BaseClass::Cleanup();

    // Do additional cleanup here.
  };
};

class Level2DerivedClass : public Level1DerivedClass
{
  Level2DerivedClass()
  {
  };

  ~Level2DerivedClass()
  {
  };

  void Cleanup()
  {
    // Call my base cleanup.
    Level1DerivedClass::Cleanup();

    // Do additional cleanup here.
  };  
};


main()
{
  Level2DerivedClass * derived2 = new Level2DerivedClass();
  delete derived2;
  return 0;
}

当我删除派生类引用时,我会 EXPECT 流程如下:

  1. Level2DerivedClass 析构函数已执行。
  2. 因为 Level1DerivedClass 析构函数是虚拟的,所以它会被执行。
  3. 因为 BaseClass 析构函数是虚拟的,所以它将被执行。
  4. 因为 BaseClass :: Cleanup Level1DerivedClass :: Cleanup 都是虚拟的,所以来自 BaseClass '此'指针的调用< em> BaseClass 析构函数将执行最派生类的实现 - Level2DerivedClass :: Cleanup
  5. Level2DerivedClass :: Cleanup 调用其父 Cleanup 实现。
  6. Level1DerivedClass :: Cleanup 调用其父 Cleanup 实现。
  7. 正在发生的是,它正在调用每个继承级别(1 - 3)的析构函数,超出了我期望的方式。但是当从 BaseClass 析构函数调用 this-&gt; Cleanup()时,它只执行自己的实现。我不明白为什么会发生这种情况,因为通常当您实例化派生类指针,将其转换为基类指针,并从基类指针调用虚方法(在本例中为'this')时,它仍会运行派生类实现('虚拟'的整点,是吗?)。在我的示例中, Level2DerivedClass :: Cleanup Level1DerivedClass :: Cleanup 永远不会被调用。

    我这样设置的原因是我希望能够调用我的清理代码而不必销毁我的对象,这就是为什么我从实际的析构函数体中抽象它。

    如果您对更合适的方法有建议,我会全力以赴。但我还想解释为什么我的设置不起作用 - 我误解了什么?

    提前感谢您的时间。

2 个答案:

答案 0 :(得分:7)

经验法则是:Never Call Virtual Functions during Construction or Destruction

他们没有你所期望的那样行事;当每个析构函数完成时,动态类型this被有效修改。来自C ++标准中的[class.cdtor]:

  

直接或间接从构造函数(包括mem-initializer或。)调用虚函数时   用于非静态数据成员的brace-or-equal-initializer)或来自析构函数,以及对象的对象   call apply是正在构造或销毁的对象,被调用的函数是在中定义的函数   构造函数或析构函数自己的类或其基础之一,但不是在派生类中重写它的函数   来自构造函数或析构函数的类,或者将其覆盖在派生最多的其他基类之一中   对象

答案 1 :(得分:2)

正确的做事方式是:自我清理,只有你自己,在析构函数中。不要在孩子或父母之后清理。

如果你想从析构函数中清除而不是的东西,你就是在做错了。在C ++中,我们有一个名为RAII的小东西,资源获取是初始化。但也有它的双重,似乎没有正式的名称,但这里有一些可行的东西:RDID,资源处置是毁灭。

当然,您不必遵守RAII / RDID理念,但这不是C ++方式。