C ++中的私有虚方法

时间:2010-01-31 05:39:57

标签: c++ polymorphism access-specifier

在C ++中创建私有方法虚拟的优点是什么?

我在开源C ++项目中注意到了这一点:

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

5 个答案:

答案 0 :(得分:109)

Herb Sutter非常好地解释了它here

指南#2:更喜欢将虚拟功能设为私有。

这使得派生类可以根据需要覆盖函数来自定义行为,而无需通过派生类调用它们来直接公开虚函数(如果函数只是受到保护,就可以实现)。关键是存在虚拟功能以允许定制;除非他们还需要直接从派生类的代码中调用,所以除了私有之外没有必要做任何事情

答案 1 :(得分:61)

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

(反对Prasoon Saurav在他的回答中引用的Herb Sutter,C ++ FAQ Lite recommends against private virtuals,主要是因为它经常让人感到困惑。)

答案 2 :(得分:9)

尽管所有要求将虚拟成员声明为私有的调用,但该参数根本不成立。通常,派生类重写虚函数必须调用基类版本。如果它被声明为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声明意味着对{{1成员。

因违反该信任而遭受不良行为的用户(例如,通过不打扰阅读您的文档而标记为“无能为力”)只能归咎于他们。

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

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

如果C ++委员会放宽了“私有”以允许这种特定访问,那么我将全部用于私有虚拟功能。目前,我们仍被建议在马被盗后锁上谷仓门。

答案 3 :(得分:8)

我在阅读Scott Meyers的“有效C ++”时第一次遇到这个概念,第35项:考虑虚拟功能的替代方案。我想引用Scott Mayers给其他可能感兴趣的人。

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

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

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

  • 为什么要制作虚拟函数private?最好的理由是我们已经提供了public面向方法。
  • 为什么不简单地使它protected,以便我可以将该方法用于其他有趣的事情?我想它总是取决于你的设计以及你如何相信基类适合。我认为派生类制造者应该专注于实现所需的逻辑;其他一切都已经得到了解决。此外,还有封装问题。

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

答案 4 :(得分:3)

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

我需要拥有公共基类才能使许多公共API正常工作(我使用的是可变参数模板),但我不能让这个对象暴露无遗。更糟糕的是,如果我将陨石坑留在状态机中 - 以纯虚函数的形式 - 在“私人”的任何地方,我允许从其子类之一派生的聪明或无能的用户覆盖用户永远不应该触摸的方法。所以,我把状态机'大脑'放在PRIVATE虚函数中。然后,基类的直接子节点填充其非虚拟覆盖的空白,用户可以安全地使用生成的对象或创建自己的其他派生类,而不必担心弄乱状态机。

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

相关问题