144

What is the advantage of making a private method virtual in C++?

I have noticed this in an open source C++ project:

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};
4

5 回答 5

138

Herb Sutter has very nicely explained it here.

Guideline #2: Prefer to make virtual functions private.

This lets the derived classes override the function to customize the behavior as needed, without further exposing the virtual functions directly by making them callable by derived classes (as would be possible if the functions were just protected). The point is that virtual functions exist to allow customization; unless they also need to be invoked directly from within derived classes' code, there's no need to ever make them anything but private

于 2010-01-31T05:42:55.380 回答
74

如果方法是虚拟的,它可以被派生类覆盖,即使它是私有的。调用虚拟方法时,将调用覆盖的版本。

(与 Prasoon Saurav 在回答中引用的 Herb Sutter 相反,C++ FAQ Lite建议不要使用 private virtuals,主要是因为它经常使人们感到困惑。)

于 2010-01-31T05:46:43.580 回答
12

尽管所有的调用都将虚拟成员声明为私有,但这个论点根本站不住脚。通常,派生类对虚函数的覆盖将不得不调用基类版本。如果声明它不能private

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

必须声明基类方法protected

然后,您必须采取丑陋的权宜之计,通过注释指示该方法应该被覆盖但不被调用。

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

因此 Herb Sutter 的指导方针 #3……但无论如何,这匹马已经离开了谷仓。

当您声明某些内容时,protected您隐含地信任任何派生类的编写者能够理解并正确使用受保护的内部结构,就像friend声明意味着对private成员更深的信任一样。

违反这种信任而导致不良行为的用户(例如,由于不费心阅读您的文档而被标记为“无知”)只能责怪他们自己。

更新:我收到了一些反馈,声称您可以使用私有虚拟函数以这种方式“链接”虚拟函数实现。如果是这样,我肯定想看看。

我使用的 C++ 编译器绝对不会让派生类实现调用私有基类实现。

如果 C++ 委员会放宽“私有”以允许这种特定访问,我将全力支持私有虚拟功能。就目前而言,我们仍然被建议在马被盗后锁上谷仓的门。

于 2016-02-25T16:00:00.127 回答
10

我在阅读 Scott Meyers 的“Effective C++”第 35 条:考虑虚拟函数的替代方案时第一次遇到了这个概念。我想为其他可能感兴趣的人参考 Scott Mayers。

它是通过非虚拟接口成语的模板方法模式的一部分:面向公众的方法不是虚拟的;相反,它们包装了私有的虚拟方法调用。然后基类可以在私有虚函数调用之前和之后运行逻辑:

public:
  void NonVirtualCalc(...)
  {
    // Setup
    PrivateVirtualCalcCall(...);
    // Clean up
  }

我认为这是一个非常有趣的设计模式,我相信您可以看到添加的控件是如何有用的。

  • 为什么要做虚函数private?最好的原因是我们已经提供了一个public面对方法。
  • 为什么不简单地制作它protected,以便我可以将该方法用于其他有趣的事情?我想这将始终取决于您的设计以及您认为基类如何适应。我认为派生类制造商应该专注于实现所需的逻辑;其他一切都已经处理好了。此外,还有封装的问题。

从 C++ 的角度来看,覆盖私有虚拟方法是完全合法的,即使您无法从类中调用它。这支持上述设计。

于 2014-06-11T19:58:27.113 回答
4

我使用它们来允许派生类为基类“填补空白”,而不会将这样的漏洞暴露给最终用户。例如,我有从一个公共基础派生的高度有状态的对象,它只能实现整个状态机的 2/3(派生类根据模板参数提供剩余的 1/3,而基础不能是其他原因)。

我需要有公共基类才能使许多公共 API 正常工作(我正在使用可变参数模板),但我不能让该对象外泄。更糟糕的是,如果我将陨石坑留在状态机中——以纯虚函数的形式——除了“私有”之外的任何地方,我都会允许从其子类之一派生的聪明或无知的用户覆盖用户不应该接触的方法。因此,我将状态机“大脑”放在了 PRIVATE 虚拟功能中。然后基类的直接子类填充其非虚拟覆盖的空白,用户可以安全地使用生成的对象或创建自己的进一步派生类,而不必担心弄乱状态机。

至于你不应该拥有公共虚拟方法的论点,我说 BS。用户可以像公共虚拟一样轻松地不正确地覆盖私有虚拟 - 毕竟他们正在定义新类。如果公众不应该修改给定的 API,请不要在可公开访问的对象中将其设为虚拟。

于 2014-02-05T21:10:15.403 回答