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

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


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


当前回答

好友对于回调也很有用。可以将回调函数作为静态方法来实现

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

回调在内部调用localCallback, clientData中有你的实例。在我看来,

还是……

class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}

这允许友元在cpp中被定义为c风格的函数,而不会使类变得混乱。

类似地,我经常看到的一种模式是将一个类的所有真正的私有成员放到另一个类中,该类在头文件中声明,在cpp中定义,并加为好友。这允许编码器向头文件的用户隐藏类的很多复杂性和内部工作。

在头文件中:

class MyFooPrivate;
class MyFoo
{
    friend class MyFooPrivate;
public:
    MyFoo();
    // Public stuff
private:
    MyFooPrivate _private;
    // Other private members as needed
};

在cpp中,

class MyFooPrivate
{
public:
   MyFoo *owner;
   // Your complexity here
};

MyFoo::MyFoo()
{
    this->_private->owner = this;
}

这样就更容易隐藏下游不需要看到的东西。

其他回答

@roo:这里没有破坏封装,因为类本身规定了谁可以访问它的私有成员。只有当这可能是由类外部引起时,封装才会被破坏,例如,如果你的操作符<<声明“我是类foo的朋友”。

朋友代替了公开的用法,而不是私人的用法!

实际上,c++ FAQ已经回答了这个问题。

我发现了一个使用好友访问的方便地方:私有函数的Unittest。

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

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

你必须非常小心何时何地使用friend关键字,和你一样,我很少使用它。下面是一些关于使用friend和替代用法的注意事项。

假设你想比较两个对象,看它们是否相等。你可以:

使用访问器方法进行比较(检查每个ivar并确定是否相等)。 或者,您可以通过将所有成员设为public直接访问它们。

第一个选项的问题是,这可能会有很多访问器,这比直接变量访问(稍微)慢,更难读取,而且很麻烦。第二种方法的问题是完全破坏了封装。

如果我们可以定义一个外部函数,它仍然可以访问类的私有成员,那就更好了。我们可以用friend关键字做到这一点:

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

equal(Beer, Beer)方法现在可以直接访问a和b的私有成员(可能是char *brand, float percentAlcohol等)。这是一个相当做作的例子,您可能会更早地将friend应用于重载的==操作符,但我们会讲到这一点。

有几件事需要注意:

A friend is NOT a member function of the class It is an ordinary function with special access to the private members of the class Don't replace all accessors and mutators with friends (you may as well make everything public!) Friendship isn't reciprocal Friendship isn't transitive Friendship isn't inherited Or, as the C++ FAQ explains: "Just because I grant you friendship access to me doesn't automatically grant your kids access to me, doesn't automatically grant your friends access to me, and doesn't automatically grant me access to you."

我只有在用另一种方法更难的时候才会用朋友。另一个例子是,由于Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4等的互操作性,许多向量数学函数经常被创建为朋友。而且做朋友比到处使用访问器要容易得多。正如所指出的,friend在应用于<<(非常方便调试)、>>和==运算符时通常很有用,但也可以用于这样的事情:

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

就像我说的,我不经常使用friend这个词,但偶尔它正是你所需要的。希望这能有所帮助!

在工作中,我们广泛地请朋友来测试代码。这意味着我们可以为主应用程序代码提供适当的封装和信息隐藏。但我们也可以有单独的测试代码,使用朋友来检查内部状态和数据进行测试。

可以说,我不会将friend关键字作为设计的重要组成部分。