3

虽然阶级友谊是 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 之外没有人应该使用它,或者程序不再有效.

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

4

8 回答 8

17

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

于 2009-02-23T12:11:10.757 回答
3

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

于 2009-02-23T12:51:02.050 回答
2

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

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

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

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

于 2009-02-23T14:27:37.527 回答
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 是一个接口,它决定了可以对数据做什么,即可以设置什么,什么是只能获取的。Worker 是一个允许访问这个特殊接口的类。Dispatcher 以私有方式实现接口,因此访问 Dispatcher 实例不会让您访问特殊的共享数据,但 Dispatcher 有一个方法可以让 Workers 获得访问权限。

于 2009-02-23T16:31:07.747 回答
0

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

于 2009-02-23T12:05:16.293 回答
0

这种模式有意义吗

不多。你能详细说明吗?

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

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

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

于 2009-02-23T12:08:50.997 回答
0

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

嗯,是的,也不是。

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

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

于 2009-02-23T12:34:48.203 回答
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 方法也可以在无法访问基类的私有成员的情况下工作。

于 2009-02-23T15:58:12.787 回答