【问题标题】:Understanding a comparison between 'protected virtuals' and 'public virtuals'了解“受保护虚拟”和“公共虚拟”之间的比较
【发布时间】:2012-08-06 08:46:06
【问题描述】:

问题来自 C++ 常见问题。

http://www.parashift.com/c++-faq-lite/protected-virtuals.html

使用公共重载虚拟的代码:

class Base {
public:
  virtual void f(int x);    ← may or may not be pure virtual
  virtual void f(double x); ← may or may not be pure virtual
};

通过 Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals 习语改进这一点:

class Base {
public:
  void f(int x)    { f_int(x); }  ← non-virtual
  void f(double x) { f_dbl(x); }  ← non-virtual
protected:
  virtual void f_int(int);
  virtual void f_dbl(double);
};

作者说:

Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals 习惯用法的想法是将公共重载方法更改为 non-virtuals,并使那些调用 protected non-overloaded virtuals。

但我不明白作者关于这个成语如何提高风险的说法:

该成语将正确管理隐藏规则的复杂性打包到基类(单数)中。这意味着派生类(复数)或多或少会自动处理隐藏规则,因此生成这些派生类的各种开发人员几乎可以完全专注于派生类本身的细节——他们不需要关心 (微妙且经常被误解)隐藏规则。这大大减少了派生类的编写者搞砸隐藏规则的机会。

为什么这解决了隐藏问题?据我了解,名称隐藏与成员函数是否为“虚拟”无关。如果'base'的派生类重写了函数f(),它仍然会隐藏f(int)和f(double),对吧?

从这个成语中我只能看到作者将'base'虚拟f()更改为非虚拟,并将辅助函数f_int(),f_dbl()放入'protected virtual',就像成语的名字所说的那样。它还没有什么好处,但相反消除了从基类指针/引用动态绑定的可能性。这个成语的真正好处是什么?

更新

Kerrek,你是在说这个吗?我不完全理解你回答的第二段。可以举个例子吗?

class base {
public: 
    virtual void f(int x);
    virtual void f(double x);
}

class derived : public base {
public:
    virtual int f(int x); // oops, will hide base::f(int x) AND base::f(double x)
}

base *bp = new base();
base *dp = new derived();
bp->f(int i);    // ok
dp->f(int i);    // surprise!
dp->f(double d); // compile error!


class Base {
public:
    void f(int x)    { f_int(x); }  
    void f(double x) { f_dbl(x); } 
protected:
    virtual void f_int(int);
    virtual void f_dbl(double);
};

class derived : public base {
public:
    // nothing to override here 'cause f() is non virtual
protected:
    // because f_int() and f_dbl are unique names, override or hide f_int() will not affect f_dbl()?
    virtual int f_int(int);  // oops, will hide base::f(int x), but developer may want this on purpose
                             // no effect on f_dbl(), which is good
}

base bobj;
derived dobj;
bobj.f(int i);    // ok
dobj.f(double d); // ok

【问题讨论】:

    标签: c++


    【解决方案1】:

    如果派生类声明了void f(int),那么它覆盖虚函数,并且隐含了virtual 说明符。如果派生类声明int f(int),它隐藏基函数。我猜你对此很熟悉。

    当您希望其他人基于您的基类开发代码时,问题就来了。使用幼稚的方法,每个派生类都必须小心添加正确的覆盖,以免意外隐藏函数并获得工作但错误的程序(即用户说f()但得到错误的东西)。使用“public non-virtual”习惯用法,用户总是可以放心地调用f(),库开发人员可以通过覆盖唯一命名的受保护函数来仅覆盖她感兴趣的那些部分,而无需触及可能影响其他用户。

    【讨论】:

    • 非虚拟公共功能还有另一个原因,即它们可以集中检查前后条件、日志记录和其他一般内容。但是,我认为这些理由并不令人信服。有一个权衡,即拥有这些不同功能的复杂性,任何重载解决问题都可能并且应该通过测试浮出水面——可能是程序员自己对功能的快速检查。
    • 稍微不同的是,FAQ 建议使用虚拟 protected 而不是 private 对于 C++03 来说很有意义,因为在某种程度上你可以断言函数是覆盖,它要求在派生类中可以访问基类的声明。然而,有了 C++11,我们终于有了一个标准的override 规范。它也适用于private virtuals。
    • @Alf 这也取决于你在哪里学习合约编程。这里的参考是 Bertrand Meyer 和 Eiffel 编程语言。在这些情况下,派生类中的函数可以调用基类中的函数,而无需进行前置和后置条件检查。 (恕我直言,如果基类函数提供了部分实现,它的名称应该与虚函数不同,而且我一直将我的虚函数设为私有。)
    • Kerrek,你是什么意思'使用“公共非虚拟”成语,用户总是在信任中调用 f(),'? 'in confidence' 秘密的意思?我仍然看不到它。一个例子?
    • 谢谢克雷克。所以现在我明白这个成语是为了阻止开发人员直接在派生类中接触 f(),但总是在派生类中更改 f_int() 和 f_dbl() 以实现目标。因为如果开发人员不够小心,派生类中的其他覆盖视觉总是有隐藏 Base::f 的风险。我说的对吗?
    【解决方案2】:

    我所能看到的是,它会减少您链接到SomeDerived::f() 而不是使用虚拟Base::f() 的可能性,尽管我不记得到目前为止我已经发现编译器对此有罪。正在寻找其他人提出更好的答案...

    【讨论】:

      【解决方案3】:

      至少从历史上看,使用非公共虚拟的主要动机 功能一直是支持按合同编程。这 Base 中的(非虚拟)公共函数定义了一个合约,其中 assert(或类似的)前置条件和后置条件和 不变量。他们转发到私有或受保护的虚拟功能 实际工作。因此,例如,经典的clone 函数 可能被定义:

      class Base
      {
          virtual Base* doClone() const = 0;
      public:
          Base* clone() const
          {
              Base* results = doClone();
              assert( typeid(*this) == typeid( *results ) );
              return results;
          }
      };
      

      clone 的情况下,这种保护可能是矫枉过正; 由于派生类错误实现它,我还没有看到任何问题 (或未能实施,如果有多个级别 推导)。然而,对于大多数其他功能,它是一个强大且 开发健壮软件的有效方法。

      如果你在接口中有重载函数,并且他们有 不同的实现,我没有看到任何不重载的真正理由 虚函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-09-26
        • 2011-06-01
        • 2014-04-08
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        • 2011-11-15
        相关资源
        最近更新 更多