c++ - 如何确保虚方法调用一直传播到基类?

标签 c++ inheritance virtual

类层次结构的一个非常常见的错误是将基类中的方法指定为虚拟方法,以便继承链中的所有覆盖做一些工作,而忘记传播调用到基础实现。

示例场景

class Container
{
public:
  virtual void PrepareForInsertion(ObjectToInsert* pObject)
  {
    // Nothing to do here
  }
};

class SpecializedContainer : public Container
{
protected:
  virtual void PrepareForInsertion(ObjectToInsert* pObject)
  {
    // Set some property of pObject and pass on.
    Container::PrepareForInsertion(pObject);
  }
};

class MoreSpecializedContainer : public SpecializedContainer
{
protected:
  virtual void PrepareForInsertion(ObjectToInsert* pObject)
  {
    // Oops, forgot to propagate!
  }
};

我的问题是:有没有一种好的方法/模式来确保始终在调用链的末尾调用基本实现?

我知道有两种方法可以做到这一点。

方法一

您可以将成员变量用作标志,在虚方法的基本实现中将其设置为正确的值,并在调用后检查其值。这需要使用公共(public)的非虚拟方法作为客户端的接口(interface),并使虚拟方法受到保护(这实际上是一件好事),但它需要使用专门为此目的的成员变量(需要如果虚方法必须是 const 则可变)。

class Container
{
public:
  void PrepareForInsertion(ObjectToInsert* pObject)
  {
    m_callChainCorrect = false;
    PrepareForInsertionImpl(pObject);
    assert(m_callChainCorrect);
  }

protected:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject)
  {
    m_callChainCorrect = true;
  }

private:
  bool m_callChainCorrect;
};

class SpecializedContainer : public Container
{
protected:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject)
  {
    // Do something and pass on
    Container::PrepareForInsertionImpl(pObject);
  }
};

方法二

另一种方法是用不透明的“cookie”参数替换成员变量并做同样的事情:

class Container
{
public:
  void PrepareForInsertion(ObjectToInsert* pObject)
  {
    bool callChainCorrect = false;
    PrepareForInsertionImpl(pObject, &callChainCorrect);
    assert(callChainCorrect);
  }

protected:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject, void* pCookie)
  {
    *reinrepret_cast<bool*>(pCookie) = true;
  }
};

class SpecializedContainer : public Container
{
protected:
  virtual void PrepareForInsertionImpl(ObjectToInsert* pObject, void* pCookie)
  {
    // Do something and pass on
    Container::PrepareForInsertionImpl(pObject, pCookie);
  }
};

在我看来,这种方法不如第一种,但确实避免了使用专用成员变量。

还有哪些可能?

最佳答案

您已经想出了一些巧妙的方法来做到这一点,但(正如您所承认的)膨胀类和添加的代码不是解决对象的职责而是解决程序员的缺陷。

真正的答案是不要在运行时这样做。这是程序员错误,而不是运行时错误。

在编译时执行:如果语言支持,则使用语言构造,或者使用强制它的模式(例如,,模板方法),或者使您的编译依赖于通过的测试,并设置测试来执行它。

或者,如果传播失败导致派生类失败,则让它失败,并带有异常消息,通知派生类的作者他未能正确使用基类。

关于c++ - 如何确保虚方法调用一直传播到基类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1273385/

相关文章:

c++ - 类成员何时使用智能指针或对象

c++ - 通用 std::function 成员

C++ 模板类型检查 std::is_same 不起作用?

c++ - 类继承 : use parent's function on child's member

C++虚拟模板方法

c# - 在 C# 的属性设置中使用 virtual 和 protected 关键字?

c++ - 错误 : Invalid use of incomplete type

c# - 对于一个对象,是否可以通过反射或者其他方式得到它的所有子类?

c++ - 如何不继承 C++ 类中的变量

c++ - 为什么必须定义未使用的虚函数?