同伴之间的C ++友谊是否健康?

时间:2009-02-23 12:02:28

标签: c++ class-design friend

虽然阶级友谊是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;
}

好的,这种模式的原因是因为对等体都是相同的等级,并且他们需要彼此分享知识,但是这种知识是秘密的,因为除了对等体之外没有人应该使用它,或者程序是不再有效。

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

8 个答案:

答案 0 :(得分:18)

在这种情况下,不需要朋友。类的对象可以访问任何其他相同类型的对象的私有成员。没有朋友声明它应该工作得很好。

答案 1 :(得分:3)

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

答案 2 :(得分:2)

虽然你已经发现在这种情况下不需要友谊,但我只是说,与上述观点相反,合作阶级之间的友谊原则上没有任何错误。事实上,友谊可能实际上不会破坏封装。

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

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

无论如何要进行讨论,请看这里: http://www.ddj.com/cpp/184401197

答案 3 :(得分:1)

既然我已经说过我的遗产,那么这可能有助于你的真正问题,即如何解决友谊的潜在问题。

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

例如:

// 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是一个接口,用于确定可以对数据执行的操作,即可以设置什么以及什么是get-only。 Worker是一个允许访问此特殊接口的类。 Dispatcher私有地实现接口,因此访问Dispatcher实例不会授予您访问特殊共享数据的权限,但Dispatcher有一个方法可以让Workers访问。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

  

这种模式是否有意义

不多。你能详细说明吗?

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

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

对不起罗伯特,误读了这些名字。

答案 6 :(得分:0)

  

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

嗯,是的,没有。

如果您不使用方法来访问所需的数据,它只能破坏封装。

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

答案 7 :(得分:0)

首先,我想说,对于继承,一个好的类层次结构不需要公开私有数据,以便复制构造函数或运算符重载等工作。例如:

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方法也无法访问基类的私有成员。