首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解“受保护的虚拟人”与“公共虚拟人”的比较

理解“受保护的虚拟人”与“公共虚拟人”的比较
EN

Stack Overflow用户
提问于 2012-08-06 08:46:06
回答 3查看 269关注 0票数 2

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

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

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

代码语言:javascript
复制
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
};

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

代码语言:javascript
复制
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()放在’受保护的虚拟‘中,就像习语的名称一样。它没有任何好处,但相反,它消除了从基类指针/引用中进行动态绑定的可能性。这个成语的真正好处是什么?

更新

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

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-08-06 08:53:47

如果派生类声明void f(int),则重写虚拟函数,并隐含virtual说明符。如果派生类声明int f(int),则隐藏基函数。我想你对此很熟悉。

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

票数 2
EN

Stack Overflow用户

发布于 2012-08-06 08:53:02

我所能看到的是,这会降低链接到SomeDerived::f()的可能性,而不是使用虚拟Base::f(),尽管我还不记得到目前为止我还没有发现一个编译器犯了这个错误。寻找其他人提出一个更好的答案。

票数 0
EN

Stack Overflow用户

发布于 2012-08-06 09:20:15

至少在历史上,使用非公共虚拟函数的主要动机是通过合同支持编程。Base中的(非虚拟)公共函数定义了一个契约,其中包含了前后条件和不变量的assert (或类似的东西)。它们为实际工作转发到私有或受保护的虚拟函数。因此,例如,可以定义经典的clone函数:

代码语言:javascript
复制
class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*this) == typeid( *results ) );
        return results;
    }
};

clone的例子中,这种保护可能是过度的;由于派生类简化了它(如果存在多个派生级别,则无法实现它),我还没有看到任何问题。然而,对于大多数其他功能来说,它是开发健壮软件的一种强大而有效的方法。

如果您在接口中有重载的函数,并且它们有不同的实现,我看不出有什么真正的理由不重载这些虚拟函数。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11825039

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档