2

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

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(). 我想这是合乎逻辑的,但我真的不明白为什么。我错过了什么吗?它不能(不应该)处理这种特殊情况吗?

谢谢你。

4

5 回答 5

6

当您使用类名限定方法名时,Base::foo()动态调度(运行时绑定)不适用。无论是否是虚拟的,它都会调用Base实现。由于在这种情况下它是纯虚拟的,因此没有实现并且编译器会抱怨。foo()foo()

您的第二个问题是,在 C++ 中,友谊不会被继承。如果你想Other有特殊的访问权限Derived,它需要成为Derived特殊的朋友。

另一方面,这有效:

Base* a = new Derived();

a->foo(); 

因为在这里,您调用foo()的是一个Base*wherefoo()是公共的,并且由于您没有foo()使用类名限定,它使用动态调度并最终Derived调用Foo.

于 2010-05-04T17:38:08.130 回答
1

我想你可以这样做

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

  a->foo(); 
};
于 2010-05-04T17:40:45.100 回答
0

试着把这个“朋友类其他;” 在派生类中。

更新:现在想想,我同意 Tyler 的观点,你应该将 a 更改为 Base 指针。

Base* a = new Derived();
于 2010-05-04T17:36:50.647 回答
0

但是,编译器似乎拒绝这样做。

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

要回答您的基本问题,友谊不是遗传的……时期。在名称解析的同一阶段检查权限范围,并且由于 foo() 在您使用的名称中受到保护,因此您不能调用它。

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

于 2010-05-04T17:38:52.963 回答
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++ 中你可以滥用一切,所以它更多的是保护墨菲而不是马基雅维利。

于 2010-05-05T07:31:11.973 回答