【问题标题】:Public and private access for the same member functions相同成员函数的公共和私有访问
【发布时间】:2010-10-04 23:48:39
【问题描述】:

我有一个类(A 类),它旨在被其他人编写的其他类继承。 我还有另一个类(B 类),它也继承自 A。

B 必须访问 A 的一些成员函数,而这些成员函数不应被其他继承类访问。

因此,这些 A 的成员函数对于 B 应该是公共的,但对于其他人来说应该是私有的。

如何在不使用“朋友”指令的情况下解决它?

谢谢。

编辑:为什么我需要它。

class A
{
public:
  void PublicFunc()
  {
    PrivateFunc();
    // and other code
  }
private:
  virtual void PrivateFunc();
};

class B : public class A
{
private:
  virtual void PrivateFunc()
  {
    //do something and call A's PrivateFunc
    A::PrivateFunc(); // Can't, it's private!
  }
};

【问题讨论】:

  • 你能写出你为什么需要它吗?
  • 好吧,代码示例说明了为什么在语法上需要它,而不是为什么在设计上需要它。对于这样一个微不足道的案子朋友做了该做的事。有多少派生类需要好友访问? >1?

标签: c++ inheritance private


【解决方案1】:

你不能。这就是朋友的目的。

另一种方法是更改​​程序的设计/架构。但是对于这方面的提示,我需要更多的上下文。

【讨论】:

    【解决方案2】:

    你说的是:A 有两组子类。一组应该有访问权限,另一组不应该。只有一个品牌的子类(即 B)“看到”A 的成员感觉不对。

    如果您的意思是:只有 我们 可以使用 部分功能,而我们的客户不能,那么还有其他方法。

    (通过继承实现的功能重用经常让您陷入此类问题。如果您通过聚合实现重用,您可能会绕过它。)

    一个建议:

    // separate the 'invisible' from the 'visible'.
    class A_private_part {
    protected: 
      int inherited_content();
    public:
      int public_interface();          
    };
    
    class B_internal : public A_private_part {
    };
    
    class A_export : private A_private_part {
    public:
        int public_interface() { A_private_part::public_interface(); }
    };
    
    // client code
    class ClientClass : public A_export {
    };
    

    但更好的做法是采用聚合方式,将当前的“A”拆分为可见部分和不可见部分:

    class InvisibleFunctionality {
    };
    
    class VisibleFunctionality {
    };
    
    class B {
        InvisibleFunctionality m_Invisible;
        VisibleFunctionality m_Visible;
    };
    
    // client code uses VisibleFunctionality only
    class ClientClass {
        VisibleFunctionality m_Visible;
    };
    

    【讨论】:

    • 看起来私有继承真的可以为我解决这个问题。谢谢!
    • 这两种解决方案(以及我的第一次尝试)的问题是“B”和其他从“A_export”派生的类型没有共同的基础——因此不能通过相同的 API。要解决这个问题,我认为您需要多重继承和虚拟基础(根据我的上一个示例)。
    • 完全正确。伊戈尔,你的问题需要这个吗? B 和客户端的类需要通过同一个 API 传递吗?如果是这样,您将需要另一种解决方案!
    • 是的,这也是真的...如果是这样,看起来(因为我没有足够的时间重新设计整个包),我就让A的所有功能都公开..
    【解决方案3】:

    嗯 - 如果您想要完全符合您的描述,那么 friend 是最好的解决方案。每个编码标准都建议不要使用 friend,但如果替代设计更复杂 - 那么也许值得例外。

    要解决没有朋友的问题需要不同的架构

    一种解决方案可能是使用pImpl idiom 的一种形式,其中“B”派生自内部实现对象,而其他客户端派生自外部类。

    另一个可能是在“A”和“其他客户端”之间放置一个额外的继承层。比如:

    class A {
    public:
      void foo ();
      void bar ();
    };
    
    class B : public A {  // OK access to both 'foo' and 'bar'
    };
    
    class ARestricted : private A {
    public:
      inline void foo () { A::foo (); };    // Forwards 'foo' only
    };
    

    但是,这个解决方案仍然存在问题。 'ARestricted' 无法转换为 'A',因此这需要通过 'A' 的其他“getter”来解决。但是,您可以以不会被意外调用的方式命名此函数:

      inline A & get_base_type_A_for_interface_usage_only () { return *this; }
    

    在尝试考虑其他解决方案后,并假设您的层次结构需要如您所描述的那样,我建议您只使用 friend

    编辑:所以xtofl 建议将类型“A”重命名为“AInternal”,将“ARestricted”类型重命名为“A”。

    这行得通,只是我注意到“B”不再是“A”。但是,AInternal 可以虚拟继承 - 然后“B”可以从“AInternal”和“A”派生!

    class AInternal {
    public:
      void foo ();
      void bar ();
    };
    
    class A : private virtual AInternal {
    public:
      inline void foo () { A::foo (); };    // Forwards 'foo' only
    };
    
    // OK access to both 'foo' and 'bar' via AInternal
    class B : public virtual AInternal, public A {
    public:
      void useMembers ()
      {
        AInternal::foo ();
        AInternal::bar ();
      }
    };
    
    void func (A const &);
    
    int main ()
    {
      A a;
      func (a);
    
      B b;
      func (b);
    }
    

    当然,现在您有了虚拟基础和多重继承!嗯....现在,这比一个 friend 声明好还是坏?

    【讨论】:

    • 如果您随后将 A 重命名为 A_Internal,并将 ARestricted 重命名为 A,那么您就在您想要的位置。
    【解决方案4】:

    我认为你这里有一个更大的问题。您的设计似乎不合理。

    1) 我认为“朋友”结构一开始就有问题

    2) 如果“朋友”不是您想要的,您需要重新检查您的设计。

    我认为您要么需要使用“朋友”来完成工作,要么开发更强大的架构。看看somedesign patterns,我相信你会发现一些有用的东西。

    编辑:

    看到您的示例代码后,您肯定需要重新构建。 A 类可能不在您的控制之下,所以这有点棘手,但您可能希望将 B 类重新做为“有”类而不是“是”类。

    public Class B
    {
        B() 
        {
    
        }
    
        void someFunc()
        {
           A a; //the private functions is now called and a will be deleted when it goes out of scope
        }
    
    };
    

    【讨论】:

      【解决方案5】:

      我觉得这是一个有趣的挑战。以下是我解决问题的方法:

      class AProtectedInterface
      {
      public:
          int m_pi1;
      };
      
      class B;
      class A : private AProtectedInterface
      {
      public:
          void GetAProtectedInterface(B& b_class);
      
          int m_p1;
      };
      
      class B : public A
      {
      public:
          B();
          void SetAProtectedInterface(::AProtectedInterface& interface);
      
      private:
          ::AProtectedInterface* m_AProtectedInterface;
      };
      
      class C : public A
      {
      public:
          C();
      };
      
      C::C()
      {
          m_p1 = 0;
      //    m_pi1 = 0; // not accessible error
      }
      
      B::B()
      {
          GetAProtectedInterface(*this);
      
          // use m_AProtectedInterface to get to restricted areas of A
          m_p1 = 0;
          m_AProtectedInterface->m_pi1 = 0;
      }
      
      void A::GetAProtectedInterface(B& b_class)
      {
          b_class.SetAProtectedInterface(*this);
      }
      
      void B::SetAProtectedInterface(::AProtectedInterface& interface)
      {
          m_AProtectedInterface = &interface;
      }
      

      如果你一直使用这种模式,你可以通过使用模板来减少代码。

      template<class T, class I>
      class ProtectedInterfaceAccess : public I
      {
      public:
          void SetProtectedInterface(T& protected_interface)
          {
              m_ProtectedInterface = &protected_interface;
          }
      
      protected:
          T& GetProtectedInterface()
          {
              return *m_ProtectedInterface;
          }
      
      private:
          T* m_ProtectedInterface;
      };
      
      template<class T, class I>
      class ProtectedInterface : private T
      {
      public:
          void SetupProtectedInterface(I& access_class)
          {
              access_class.SetProtectedInterface(*this);
          }
      };
      
      class Bt;
      class At : public ProtectedInterface <::AProtectedInterface, Bt>
      {
      public:
          int m_p1;
      };
      
      class Bt : public ProtectedInterfaceAccess<::AProtectedInterface, At>
      {
      public:
          Bt();
      };
      
      class Ct : public At
      {
      public:
          Ct();
      };
      
      Ct::Ct()
      {
          m_p1 = 0;
          // m_pi1 = 0; // not accessible error
      }
      
      Bt::Bt()
      {
          SetupProtectedInterface(*this);
      
          m_p1 = 0;
          GetProtectedInterface().m_pi1 = 0;
      }
      

      【讨论】:

        【解决方案6】:

        如果我明白:

        • A 将被其他开发者继承。
        • B 将被其他开发者继承并继承自 A。
        • A 有一些您不希望外部开发人员通过 B 访问的方法。

        我认为不使用朋友就无法做到这一点。我没有办法让超类的成员只对直接继承者可用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-25
          • 1970-01-01
          • 1970-01-01
          • 2014-10-27
          • 1970-01-01
          • 2014-04-22
          • 1970-01-01
          • 2023-04-08
          相关资源
          最近更新 更多