C++中的私有虚方法

3phpmpom  于 2023-03-20  发布在  其他
关注(0)|答案(6)|浏览(142)

在C中使私有方法成为虚方法的好处是什么?
我在一个开源C
项目中注意到了这一点:

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

ifmq2ha21#

Herb Sutter已经很好地解释了它here

准则2:希望将虚函数设为私有。

这允许派生类重写函数以根据需要定制行为,而不需要通过使虚函数可由派生类调用来进一步直接公开虚函数(如果函数只是受保护的,则可能如此)。除非它们也需要直接从派生类的代码中调用,否则不需要将它们设置为私有的

7uhlpewt

7uhlpewt2#

如果方法是虚的,它可以被派生类覆盖,即使它是私有的。当虚方法被调用时,被覆盖的版本也会被调用。
(与Prasoon Saurav在回答中引用的Herb Sutter相反,C++ FAQ Lite建议不要使用私有虚拟,主要是因为它经常混淆人们。)

arknldoa

arknldoa3#

尽管所有的调用都声明了一个虚成员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) {}
};

您必须声明基类方法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 ...但马是出了谷仓无论如何。
当你声明protected时,你隐含地信任任何派生类的编写者理解并正确地使用受保护的内部结构,就像friend声明隐含着对private成员更深的信任一样。
如果用户违反了这种信任而做出了不好的行为(例如,由于懒得阅读你的文档而被贴上“无能”的标签),那只能怪他们自己。

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

我使用的C编译器绝对不会让派生类实现调用私有基类实现。
如果C
委员会放宽“private”的限制,允许这种特定的访问,我会全力支持私有虚函数。照目前的情况看,我们仍然被建议在马被偷后锁上谷仓的门。

jtoj6r0c

jtoj6r0c4#

我第一次接触到这个概念是在阅读Scott Meyers的“有效的C++”,* 第35项:考虑虚函数的替代方法。* 我想为其他可能感兴趣的人参考Scott Mayers。
它是 * 通过非虚拟接口习惯用法的模板方法模式 * 的一部分:面向公众的方法不是虚拟的相反,它们 Package 了私有的虚函数调用。2基类可以在私有虚函数调用之前和之后运行逻辑:

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

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

  • 为什么要让虚函数成为private呢?最好的理由是我们已经提供了一个面向public的方法。
  • 为什么不简单地把它做成protected,这样我就可以把这个方法用在其他有趣的事情上呢?我想这总是取决于你的设计和你认为基类如何适合你。我认为派生类的生成器应该把重点放在实现所需的逻辑上;其他的一切都已经处理好了。另外,还有封装的问题。

从C++的Angular 来看,重写一个私有虚方法是完全合法的,即使你不能从类中调用它。

s6fujrry

s6fujrry5#

我使用它们来允许派生类为基类“填补空白”,而不会向最终用户暴露这样的漏洞。例如,我有从公共基派生的高状态对象,它只能实现整个状态机的2/3(派生类提供剩余的1/3,这取决于模板参数,而基由于其他原因不能是模板)。
我需要有公共基类,以便使许多公共API正常工作(我使用的是可变参数模板),但我不能让这个对象随意出现。更糟糕的是,如果我把这些坑留在状态机中--以纯虚函数的形式--除了“Private”之外的任何地方,我允许一个聪明的或者无知的用户从它的子类派生来重写那些用户永远不应该接触的方法。我把状态机的“大脑”放在PRIVATE虚函数中,然后基类的直接子类填充它们的非虚覆盖上的空白,用户可以安全地使用结果对象或创建自己的进一步派生类,而不必担心会弄乱状态机。
至于你不应该拥有公共虚方法的论点,我说BS.用户可以不适当地覆盖私有虚方法,就像覆盖公共虚方法一样容易--毕竟他们是在定义新类.如果公共方法不应该修改给定的API,那么在公共可访问对象中就不要让它成为虚方法.

c0vxltue

c0vxltue6#

另一个原因可能是所有继承类通用逻辑:

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 **/
    }
};

比,为

Base* obj = new concrete1();

Base* obj = new concrete2();

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

相关问题