【问题标题】:Friendness and derived class友情和派生类
【发布时间】:2011-02-15 14:06:25
【问题描述】:

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

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

谢谢。

【问题讨论】:

    标签: c++ inheritance compiler-errors friend


    【解决方案1】:

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

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

    Base* a = new Derived();
    

    【讨论】:

    • 谢谢。我想避免它(如果可以的话)。
    【解决方案2】:

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

    您的第二个问题是,在 C++ 中,友谊不会被继承。如果您想让OtherDerived 有特殊访问权限,则需要专门成为Derived 的好友。

    另一方面,这有效:

    Base* a = new Derived();
    
    a->foo(); 
    

    因为在这里,您在Base* 上调用foo(),其中foo() 是公共的,并且由于您没有使用类名限定foo(),因此它使用动态调度并最终调用Derived Foo 的版本。

    【讨论】:

    • 实际上,当我指定a->Base::foo() 时,编译器似乎可以接受。你确定吗?
    • 您是否一直到链接步骤?链接器应该抱怨对 Base::foo 方法的未定义引用。
    • 我没有进入链接步骤,你是对的。感谢您提供替代/更正确的方法。
    • 如果你反过来将 Derived 转换为 Base 会怎样?
    【解决方案3】:

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

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

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

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

    【讨论】:

    • 我的错...我编辑了我的问题并删除了关于“那个”的歧义,它旨在引用a->foo()
    【解决方案4】:

    我猜你可以这样做

    void bar()
    {
      Base* a = new Derived();
    
      a->foo(); 
    };
    

    【讨论】:

      【解决方案5】:

      这很不幸,但在我看来,友好性在 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++ 中,你可以滥用一切,所以这更像是保护墨菲而不是马基雅维利的问题。

      【讨论】:

        猜你喜欢
        • 2015-12-30
        • 1970-01-01
        • 1970-01-01
        • 2011-05-04
        • 2012-12-04
        • 2018-01-05
        • 1970-01-01
        • 2014-09-05
        • 2020-11-24
        相关资源
        最近更新 更多