有人可以解释这个“受保护的虚拟”与“公共虚拟”的比较吗?

时间:2012-08-06 08:46:06

标签: c++

问题来自c ++ faq。

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

通过公共重载非虚拟化来改进这一点调用受保护的非重载虚拟用语:

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'虚拟f()更改为非虚拟,并将帮助函数f_int(),f_dbl()放入'protected virtual',就像idiom所说的那样。它没有任何好处,但相反消除了从基类指针/引用动态绑定的可能性。这个成语的真正好处是什么?

任何人都可以提供帮助吗?感谢。

嗨,Kerrek

你说这个吗?我不完全理解你答案的第2段。你能举个例子吗?

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

3 个答案:

答案 0 :(得分:2)

如果派生类声明void f(int),则覆盖虚函数,并隐含virtual说明符。如果派生类声明int f(int),则隐藏基本函数。我知道你对此很熟悉。

当您希望其他人根据您的基类开发代码时,问题就出现了。使用朴素的方法,每个派生类必须小心添加正确的覆盖,以免意外隐藏函数并获得一个有效但错误的程序(即用户说f()但是得到了错误的东西)。使用“公共非虚拟”习语,用户始终保密地调用f(),并且库开发人员可以通过覆盖唯一命名的受保护函数来覆盖她感兴趣的那些部分,而无需触及名称这可能会影响其他用户。

答案 1 :(得分:0)

我只能看到它会降低你链接到SomeDerived::f()而不是使用虚拟Base::f()的可能性,虽然我不记得我抓到了一个编译器犯了罪到目前为止。寻找别人提出更好的答案......

答案 2 :(得分:0)

至少在历史上,使用非公共虚拟的主要动机 功能一直是通过合同来支持编程。该 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的情况下,这种保护可能有点过分; 由于派生类错误实现,我还没有看到任何问题 (或者如果有多个级别,则无法实现它 推导)。然而,对于大多数其他功能来说,它是一个强大的功能 开发健壮软件的有效手段。

如果您在界面中有重载功能,就有 不同的实现,我没有看到任何真正的理由不超载 虚函数。