C++ 中的私有虚拟方法

发布于 2024-08-19 13:54:21 字数 310 浏览 11 评论 0原文

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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

聊慰 2024-08-26 13:54:21

Herb Sutter 在此处对此进行了很好的解释。

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

这使得派生类可以重写该函数来自定义
根据需要进行行为,无需进一步公开虚函数
直接通过使它们可由派生类调用(就像
如果功能刚刚受到保护,则可能)。重点是
虚拟功能的存在是为了允许定制;除非他们也需要
要直接从派生类的代码中调用,没有
需要将它们设置为非私有的

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

聆听风音 2024-08-26 13:54:21

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

(与 Prasoon Saurav 在回答中引用的 Herb Sutter 相反,C++ FAQ Lite 建议反对私有虚拟,主要是因为它经常让人们感到困惑。)

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.)

灼疼热情 2024-08-26 13:54:21

尽管所有的人都呼吁将虚拟成员声明为私有,但这个论点根本站不住脚。通常,派生类对虚函数的重写必须调用基类版本。如果它被声明为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 */ }
 ...

因此,赫伯·萨特的准则#3...但无论如何,马已经走出了谷仓。

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

由于违反这种信任而产生不良行为的用户(例如,由于不费心阅读您的文档而被贴上“无知”的标签)只能归咎于他们自己。

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

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

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

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:

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 have to declare the base class method 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.

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

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.

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.

℉服软 2024-08-26 13:54:21

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

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

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

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

  • 为什么要将虚函数设为私有?最好的原因是我们已经提供了一个面向公共的方法。
  • 为什么不简单地对其进行保护,以便我可以将该方法用于其他有趣的事情?我想这将始终取决于您的设计以及您认为基类如何适合。我认为派生类创建者应该专注于实现所需的逻辑;其他一切都已经处理好了。另外,还有封装的问题。

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

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.

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 public facing method.
  • Why not simply make it protected so 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.

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.

暖伴 2024-08-26 13:54:21

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

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

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

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).

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.

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.

心如荒岛 2024-08-26 13:54:21

另一个原因可能是所有继承类的公共逻辑:

class Base
{
public:
    void interfaceMethod() {
        /** common logic **/
        factoryMethod();
    }
private:
    virtual void factoryMethod() = 0;
};

class concrete1 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};
    
class concrete2 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};

Than、for

 Base* obj = new concrete1();

 Base* obj = new concrete2();

obj.interfaceMethod() 将为每个具体对象执行公共逻辑和特定逻辑。

another reason can be a common logic for all inherit classes:

class Base
{
public:
    void interfaceMethod() {
        /** common logic **/
        factoryMethod();
    }
private:
    virtual void factoryMethod() = 0;
};

class concrete1 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};
    
class concrete2 : Base 
{
private:
    void factoryMethod() override {
        /** specific logic **/
    }
};

Than, for

 Base* obj = new concrete1();

or

 Base* obj = new concrete2();

The obj.interfaceMethod() will execute common logic and specific logic for each concrete object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文