【问题标题】:Is C++ friendship among peers healthy?同行之间的 C++ 友谊是否健康?
【发布时间】:2009-02-23 12:02:28
【问题描述】:

虽然阶级友谊是 C++ 的最后手段之一,但这种模式有意义吗?

class Peer
{
public:
    friend class Peer;
    void GetSecret(const Peer& other)
    {
        const std::string& secret = other.GiveSecret();
        std::cout << secret << std::endl;
    }

private:
    const std::string& GiveSecret() const
    {
        return "secrety stuff";
    }
};

int main(int argc, char* argv[])
{
    Peer peerA;
    Peer peerB;
    peerA.GetSecret(peerB);
    return 0;
}

好吧,这种模式的原因是因为 Peers 都是同一级别的,他们需要相互共享知识,但是这个知识是秘密的,因为除了 peer 之外没有人应该使用它,或者程序是不再有效。

一个非常真实的例子是,当一个对等点是从另一个对等点复制构建时,它需要从其源对等点访问秘密信息,但同样没有理由让其他任何人知道这些内部信息,只有对等点.

【问题讨论】:

  • 我和任何同龄人都不是朋友。
  • 也许您想在设计中采用不同的方法。例如,您的同龄人可能有一个负责您打算的行为的主人..很大程度上取决于您要做什么。

标签: c++ class-design friend


【解决方案1】:

friend 在这种情况下是不必要的。一个类的对象可以访问任何其他同类型对象的私有成员。如果没有朋友声明,它应该可以正常工作。

【讨论】:

  • 难以置信!你是对的,友谊声明是没有必要的。我可以发誓即使使用同一类的对象,我也无法访问他人的私有方法。每天都在学习一些东西,即使在 C++ 多年之后!
  • 一个类永远是它自己的朋友。
【解决方案2】:

标准 c++ 说私有子句具有类范围。这意味着每个 Peer 都可以访问任何其他 Peer 的私有部分。这不是特定于实现的

【讨论】:

    【解决方案3】:

    虽然你发现在这种情况下不需要友谊,但我只想说,与上述观点相反,合作班级之间的友谊原则上没有错。事实上,友谊可能实际上促进了它,而不是打破封装。

    考虑为私有数据创建访问器方法的替代方法。如果你这样做了,那么你就可以有效地访问所有客户端的私有数据,而不仅仅是声明为朋友的有限的类/函数集。如果您的朋友类中只有一个方法要访问您的内部,则封装减少的数量与您提供公共访问器方法的数量完全相同。但是,提供公共访问器方法的结果将是更多的客户端将使用方法。

    对于我们编写的每个应用程序类,都有一个影子类,它是类单元测试。单元测试类是应用程序类的朋友,因为它经常需要调用类上的方法,然后检查类内部,或者调用私有方法。通过成为朋友类的封装得到维护。

    无论如何,要进行良好的讨论,请参见此处: http://www.ddj.com/cpp/184401197

    【讨论】:

      【解决方案4】:

      现在我已经说了关于继承的文章,这里有一些可能有助于解决你真正的问题,即如何解决友谊的潜在问题。

      我这样做的方式是创建一个纯界面来访问我想与“朋友”分享的数据。然后我私下实现了这个接口,所以没有人可以直接访问它。最后,我有一些机制允许我将接口的引用仅传递给那些我想要允许的选择类。

      例如:

      // This class defines an interface that allows selected classes to
      // manipulate otherwise private data.
      class SharedData
      {
      public:
          // Set some shared data.
          virtual void SettorA(int value) = 0;
      
          // Get some shared data.
          virtual bool GettorB(void) const;
      };
      
      
      // This class does something with the otherwise private data.
      class Worker
      {
      public:
          void DoSomething(SharedData & data)
          {
              if (data.GettorB() == true)
              {
                  data.SettorA(m_id);
              }
          }
      
      private:
          int m_id;
      };
      
      // This class provides access to its otherwise private data to
      // specifically selected classes.  In this example the classes
      // are selected through a call to the Dispatch method, but there
      // are other ways this selection can be made without using the
      // friend keyword.
      class Dispatcher
          : private SharedData
      {
      public:
          // Get the worker to do something with the otherwise private data.
          void Dispatch(Worker & worker)
          {
              worker.DoSomething(*this);
          }
      
      private:
          // Set some shared data.
          virtual void SettorA(int value)
          {
              m_A = value;
          }
      
          // Get some shared data.
          virtual bool GettorB(void) const
          {
              return (m_B);
          }
      
          int    m_A;
          bool   m_B;
      };
      

      在本例中,SharedData 是一个接口,用于确定可以对数据执行的操作,即可以设置什么以及仅获取什么。 Worker 是一个允许访问这个特殊接口的类。 Dispatcher 以私有方式实现接口,因此访问 Dispatcher 实例并不能让您访问特殊的共享数据,但 Dispatcher 有一个方法可以让 Workers 获得访问权限。

      【讨论】:

        【解决方案5】:

        不健康,它破坏了封装最佳实践

        【讨论】:

          【解决方案6】:

          这种模式有意义吗

          不多。能详细点吗?

          • 您通常会在不同的职业之间建立友谊。这在语法上是有效的。为什么 Peer 会成为 Peer 的朋友?
          • 私有函数只能由成员函数使用。从客户端代码调用它们是错误的。

          关于友情是否打破封装的问题,请看FAQ

          抱歉,罗伯特,看错了名字。

          【讨论】:

          • 它会编译。 GetSecret 是公开的。 GiveSecret 是私人的。当然,我在询问之前编译了这个
          【解决方案7】:

          不健康,它破坏了封装最佳实践

          嗯,是的,也不是。

          如果你不使用方法来访问你需要的数据,它只会破坏封装。

          并不是说封装只是一种保护外来类的方法,以防您更改实现细节时必须重写。如果您碰巧更改了实现,那么无论如何您都必须重新编写您的课程。

          【讨论】:

            【解决方案8】:

            首先我想说的是,通过继承,一个好的类层次结构不需要为了让诸如复制构造函数或运算符重载之类的东西工作而公开私有数据。例如:

            class Sub
                : public Base
            {
            public:
                Sub(const std::string & name)
                    : Base(),
                      m_name(name)
                {
                }
            
                Sub(const Sub & src)
                    : Base(src),
                      m_id(src.m_name)
                {
                }
            
                Sub & operator=(const Sub & rhs)
                {
                    if (&rhs != this)
                    {
                        Base::operator=(rhs);
            
                        m_name = rhs.m_name;
                    }
            
                    return (this);
                }
            
            protected:
                virtual Debug(void)
                {
                    std::cout << "Sub [m_name = " << m_name << "]" << std::endl
                              << "+- ";
            
                    Base::Debug();
                }
            
            private:
                std::string m_name;
            };
            
            class Base
            {
            public:
                Base(int id)
                    : m_id(id)
                {
                }
            
                Base(const Base & src)
                    : m_id(src.m_id)
                {
                }
            
                Base & operator=(const Base & rhs)
                {
                    if (&rhs != this)
                    {
                        m_id = rhs.m_id;
                    }
            
                    return (this);
                }
            
            protected:
                virtual Debug(void)
                {
                    std::cout << "Base [m_id = " << m_id << "]" << std::endl;
                }
            
            private:
                int m_id;
            };
            

            在此示例中,子类能够在复制构造函数和赋值运算符中实例化其基成员。即使是过度设计的 Debug 方法也可以在无法访问基类的私有成员的情况下工作。

            【讨论】:

              猜你喜欢
              • 2011-10-21
              • 2016-11-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-12-10
              • 2014-10-02
              • 2010-10-10
              相关资源
              最近更新 更多