我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。

使用friend的一个好例子是什么?


读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?


当前回答

正如朋友声明的参考资料所说:

友元声明出现在类主体中,并授予函数或另一个类对出现友元声明的类的私有和受保护成员的访问权。

所以提醒一下,有些回答中有技术错误,说朋友只能访问受保护的成员。

其他回答

安德鲁例子的另一个常见版本,可怕的密码对联

parent.addChild(child);
child.setParent(parent);

与其担心这两行是否总是一起执行,并且顺序一致,你可以将方法设为私有,并有一个friend函数来强制一致性:

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

换句话说,您可以保持公共接口更小,并强制在友元函数中跨越类和对象的不变量。

在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。

它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量。

我只使用friend关键字来单元测试受保护的函数。有些人会说不应该测试受保护的功能。然而,在添加新功能时,我发现这个工具非常有用。

然而,我没有直接在类声明中使用关键字in,而是使用一个漂亮的模板-hack来实现这一点:

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

这使我能够做到以下几点:

friendMe(this, someClassInstance).someProtectedFunction();

至少适用于GCC和MSVC。

对于操作符<<和操作符>>,没有很好的理由让这些操作符成为朋友。它们确实不应该是成员函数,但它们也不需要是朋友函数。

最好的方法是创建公共打印(ostream&)和读取(istream&)函数。然后,根据这些函数写出操作符<<和操作符>>。这提供了额外的好处,允许您将这些函数设置为虚拟函数,从而提供虚拟序列化。

在做TDD的时候,我经常使用c++中的'friend'关键字。

朋友能知道我的一切吗?


更新:我从Bjarne Stroustrup网站上找到了这个关于“朋友”关键字的有价值的答案。

“好友”是一种授予访问权限的显式机制,就像会员资格一样。