C++中的私有虚方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2170688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:22:55  来源:igfitidea点击:

Private virtual method in C++

c++polymorphismaccess-specifier

提问by silverburgh

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

在 C++ 中将私有方法设为虚拟有什么好处?

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

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

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

回答by Prasoon Saurav

Herb Sutter has very nicely explained it here.

Herb Sutter在这里很好地解释了它。

Guideline #2: Prefer to make virtual functions private.

准则#2:优先将虚函数设为私有。

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

这允许派生类覆盖函数以根据需要自定义行为,而无需通过使它们可被派生类调用来进一步暴露虚函数(如果函数只是受保护,则可能如此)。关键是存在允许定制的虚函数;除非它们还需要从派生类的代码中直接调用,否则除了私有之外,没有必要将它们设为任何东西

回答by sth

If the method is virtual it can be overridden by derived classes, even if it's private. When the virtual method is called, the overridden version will be invoked.

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

(Opposed to Herb Sutter quoted by Prasoon Saurav in his answer, the C++ FAQ Lite recommends against private virtuals, mostly because it often confuses people.)

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

回答by Spencer

Despite all of the calls to declare a virtual member private, the argument simply doesn't hold water. Frequently, a derived class's override of a virtual function will have to call the base class version. It can't if it's declared private:

尽管所有的调用都声明一个虚拟成员为私有,但这个论点根本站不住脚。通常,派生类对虚函数的覆盖必须调用基类版本。如果它被声明,则不能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) {}
};

You haveto declare the base class method protected.

必须声明基类方法protected

Then, you have to take the ugly expedient of indicating via a comment that the method should be overridden but not called.

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

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

Thus Herb Sutter's guideline #3...But the horse is out of the barn anyway.

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

When you declare something protectedyou're implicitly trusting the writer of any derived class to understand and properly use the protected internals, just the way a frienddeclaration implies a deeper trust for privatemembers.

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

Users who get bad behavior from violating that trust (e.g. labeled 'clueless' by not bothering to read your documentation) have only themselves to blame.

因违反信任而导致不良行为的用户(例如,因懒得阅读您的文档而被贴上“无知”的标签)只能怪他们自己。

Update: I've had some feedback that claims you can "chain" virtual function implementations this way using private virtual functions. If so, I'd sure like to see it.

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

The C++ compilers I use definitely won't let a derived class implementation call a private base class implementation.

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

If the C++ committee relaxed "private" to allow this specific access, I'd be all for private virtual functions. As it stands, we're still being advised to lock the barn door after the horse is stolen.

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

回答by Pooven

I first came across this concept while reading Scott Meyers' 'Effective C++', Item 35: Consider alternatives to virtual functions.I wanted to reference Scott Mayers for others that may be interested.

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

It's part of the Template Method Pattern via the Non-Virtual Interface idiom: the public facing methods aren't virtual; rather, they wrap the virtual method calls which are private. The base class can then run logic before and after the private virtual function call:

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

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

I think that this is a very interesting design pattern and I'm sure you can see how the added control is useful.

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

  • Why make the virtual function private? The best reason is that we've already provided a publicfacing method.
  • Why not simply make it protectedso that I can use the method for other interesting things? I suppose it will always depend on your design and how you believe the base class fits in. I would argue that the derived class maker should focus on implementing the required logic; everything else is already taken care of. Also, there's the matter of encapsulation.
  • 为什么要制作虚函数private?最好的原因是我们已经提供了一个public面向方法。
  • 为什么不简单地制作它,protected以便我可以将这种方法用于其他有趣的事情?我想这将始终取决于您的设计以及您如何相信基类适合。我认为派生类的制造者应该专注于实现所需的逻辑;其他一切都已经处理好了。此外,还有封装的问题。

From a C++ perspective, it's completely legitimate to override a private virtual method even though you won't be able to call it from your class. This supports the design described above.

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

回答by Zack Yezek

I use them to allow derived classes to "fill in the blanks" for a base class without exposing such a hole to end users. For example, I have highly stateful objects deriving from a common base, which can only implement 2/3 of the overall state machine (the derived classes provide the remaining 1/3 depending on a template argument, and the base cannot be a template for other reasons).

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

I NEED to have the common base class in order to make many of the public APIs work correctly (I'm using variadic templates), but I cannot let that object out into the wild. Worse, if I leave the craters in the state machine- in the form of pure virtual functions- anywhere but in "Private", I allow a clever or clueless user deriving from one of its child classes to override methods that users should never touch. So, I put the state machine 'brains' in PRIVATE virtual functions. Then the immediate children of the base class fill in the blanks on their NON-virtual overrides, and users can safely use the resulting objects or create their own further derived classes without worrying about messing up the state machine.

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

As for the argument that you shouldn't HAVE public virtual methods, I say BS. Users can improperly override private virtuals just as easily as public ones- they're defining new classes after all. If the public shouldn't modify a given API, don't make it virtual AT ALL in publicly accessible objects.

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