友善和派生阶级

时间:2010-05-04 17:30:31

标签: c++ inheritance compiler-errors friend

假设我有以下类层次结构:

class Base
{
  protected:

    virtual void foo() = 0;

    friend class Other;
};

class Derived : public Base
{
  protected:

    void foo() { /* Some implementation */ };
};

class Other
{
  public:

    void bar()
    {
      Derived* a = new Derived();

      a->foo(); // Compiler error: foo() is protected within this context
    };
};

我想我也可以更改a->Base::foo(),但由于foo()类中Base是纯虚拟的,因此调用将导致调用Derived::foo()

然而,编译器似乎拒绝a->foo()。我想这是合乎逻辑的,但我不能理解为什么。我错过了什么吗?不能(不应该)处理这种特殊情况吗?

谢谢。

5 个答案:

答案 0 :(得分:6)

使用类名限定方法名称时,如Base::foo()动态分派(运行时绑定)不适用。无论Base是否为虚拟,它都始终调用foo()的{​​{1}}实现。因为在这种情况下它是纯虚拟的,所以没有实现,编译器会抱怨。

你的第二个问题是在C ++中,友谊不是继承的。如果您希望foo()具有对Other的特殊访问权限,则需要成为Derived的朋友。

另一方面,这有效:

Derived

因为在这里,您在Base* a = new Derived(); a->foo(); 上呼叫foo(),其中Base*是公开的,并且因为您没有使用类名来限定foo(),所以它使用动态调度并最终调用foo()的{​​{1}}版本。

答案 1 :(得分:1)

我想你可以做到这一点

void bar()
{
  Base* a = new Derived();

  a->foo(); 
};

答案 2 :(得分:0)

尝试把这个“朋友类别人”;在派生类中。

更新:现在想想,我同意Tyler你应该改变一个Base指针。

Base* a = new Derived();

答案 3 :(得分:0)

然而,编译器似乎拒绝这样做。

拒绝什么?听起来好像你说编译器拒绝允许Other通过Base指针调用foo()函数。当然不应该这样。

回答你的基本问题,友谊不是继承的......期间。在与名称解析相同的阶段检查权限范围,并且由于foo()在您使用的名称中受到保护,因此您无法调用它。

另一方面,多态性通过指针重定向解决,与名称解析或访问权限无关。

答案 4 :(得分:0)

这很不幸,但在我看来,C ++中的友好性本来就被打破了:

  • 未继承
  • 无限制地访问所有内部,不可能限制它

我已经放弃了“按原样”使用它,现在我主要使用Key模式(缺少更好的名称)。

///
/// Key definition
///
class Friend;

class FriendKey: boost::noncopyable { friend class Friend; FriendKey() {} };

///
/// Base/Derived definition
///
class Base
{
public:

  void mySpecialMethod(const FriendKey&) { this->mySpecialMethodImpl(); }

private:
  virtual void mySpecialMethodImpl() = 0;
}; // class Base

class Derived: public Base
{
public:

private:
  virtual void mySpecialMethodImpl() {}
}; // class Derived

///
/// Friend definition
///
class Friend
{
public:
  void mySpecialCall()
  {
    Derived d;
    d.mySpecialMethod(FriendKey());
  }
}; // class Friend

概念很简单:每个类都声明一个键(甚至可以在前向头中),而那些希望授予它们特殊访问权限的键只能使这个键成为可能。

它并不完美,因为你当然可以滥用它(通过密钥的传递性)。但是在C ++中你可以滥用一切,所以它更像是一个受到墨菲保护的问题而不是马基雅维利。