有人能解释一下吗;受保护的虚拟对象';与';公共虚拟世界';比较? 问题来自C++ FAQ。< /P>

有人能解释一下吗;受保护的虚拟对象';与';公共虚拟世界';比较? 问题来自C++ FAQ。< /P>,c++,C++,使用公共重载虚拟对象的代码: 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 }; 通过公共重载非虚拟调用受保护的非重载虚拟习惯用法来改进这一点: class Base { public: void f(int x) { f_int(x); } ←

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

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
};
通过公共重载非虚拟调用受保护的非重载虚拟习惯用法来改进这一点:

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);
};
作者说:

公共重载的非虚拟对象调用保护的非重载虚拟对象习惯用法的思想是将公共重载方法更改为非虚拟对象,并使这些调用保护的非重载虚拟对象

但我不明白作者是怎么说这个成语如何提高风险的:

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

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

从这个习惯用法中,我所能看到的是作者将'base'virtual f()改为non-virtual,并将helper函数f_int(),f_dbl()放在'protected virtual'中,就像这个习惯用法的名称所说的那样。它并没有什么好处,但恰恰相反,它消除了从基类指针/引用进行动态绑定的可能性。这个成语的真正好处是什么

更新 凯瑞克,你是这么说的吗?我不完全理解你回答的第二段。你能举个例子吗

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

我所能看到的是,它将减少您链接到
SomeDerived::f()
而不是使用虚拟
Base::f()
的可能性,尽管我不记得我已经抓住了一个编译器犯了这个错误。正在寻找其他人提出更好的答案…

如果派生类声明了
void f(int)
,那么它将重写虚函数,并隐含
virtual
说明符。如果派生类声明
intf(int)
,它将隐藏基函数。我想你对这件事很熟悉


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

主要动机,至少在历史上,用于使用非公共虚拟机 功能一直是支持合同编程。这个
Base
中的(非虚拟)公共函数定义一个合同,其中 断言前后条件和 不变量。它们转发到专用或受保护的虚拟函数以用于 实际工作。因此,例如,经典的
clone
函数 可定义为:

class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*this) == typeid( *results ) );
        return results;
    }
};
对于
克隆
,这种保护可能是过度的; 由于派生类错误地实现了它,我还没有看到任何问题 (或如果存在多个级别的 推导)。然而,对于大多数其他功能来说,它是一个功能强大且 开发健壮软件的有效手段

如果在接口中有重载函数,并且 不同的实现,我看不到任何不过载的真正理由
虚拟功能。

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