【问题标题】:C++ virtual method not workingC ++虚拟方法不起作用
【发布时间】:2012-06-04 23:09:35
【问题描述】:

举个例子:

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;
}

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

  1. Level2DerivedClass 析构函数被执行。
  2. 因为 Level1DerivedClass 析构函数是虚拟的,所以会被执行。
  3. 因为 BaseClass 析构函数是虚拟的,所以会被执行。
  4. 因为 BaseClass::CleanupLevel1DerivedClass::Cleanup 都是虚拟的,所以从 BaseClass 中的“this”指针调用em>BaseClass 析构函数将执行最派生类的实现 - Level2DerivedClass::Cleanup
  5. Level2DerivedClass::Cleanup 调用其父级的 Cleanup 实现。
  6. Level1DerivedClass::Cleanup 调用其父级的 Cleanup 实现。

正在发生的事情是,它以我期望的方式为每个继承级别 (1 - 3) 调用析构函数。但是当 this->Cleanup()BaseClass 析构函数中调用时,它只执行自己的实现。我不明白为什么会发生这种情况,因为通常当您实例化派生类指针,将其转换为基类指针并从基类指针调用虚拟方法(在本例中为“this”)时,它仍然运行派生类实现(“虚拟”的全部意义,是吗?)。在我的示例中,Level2DerivedClass::CleanupLevel1DerivedClass::Cleanup 永远不会被调用。

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

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

提前感谢您的宝贵时间。

【问题讨论】:

  • 这可以编译吗?错误:'BaseClass::BaseClass()' 是私有的
  • 亚历山德罗,对不起。这不是真正的代码,只是代表我在我的程序中尝试做的一个示例。

标签: c++ function inheritance methods virtual


【解决方案1】:

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

他们的行为不像你想象的那样;随着每个析构函数的完成,this 的动态类型被有效地修改。来自 C++ 标准中的 [class.cdtor]:

当从构造函数(包括 mem-initializer 或 非静态数据成员的大括号或相等初始化器)或来自析构函数,以及 call apply 是正在构造或销毁的对象,被调用的函数是在 构造函数或析构函数自己的类或其基类之一,但不是在派生类中覆盖它的函数 来自构造函数或析构函数的类,或在派生最多的其他基类之一中覆盖它 对象。

【讨论】:

  • 谢谢,奥利!这就是我所缺少的。非常感谢您的解释。因此,一种可能的解决方案是在每个类中放置一个非虚拟 Cleanup() 方法,并从每个类自己的析构函数中调用它。虚拟析构函数仍将确保调用所有析构函数 - Cleanup() 不会像我现在这样由虚拟实现处理。听起来对吗?
【解决方案2】:

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

如果你想从析构函数中清理 not 的东西,你做错了。在 C++ 中,我们有一个叫做 RAII 的小东西,资源获取就是初始化。但也有它的对偶,它似乎没有一个正式的名称,但这里有一些可以工作的东西:RDID,资源处置是破坏。

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

【讨论】:

  • 谢谢 n.m.我一直在学习有关 C++ 的新事物,而这不是我以前遇到过的。关于最佳做法的好提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多