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

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


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


当前回答

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

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

其他回答

可能我从上面的答案中漏掉了一些东西,但是封装中另一个重要的概念是隐藏实现。减少对私有数据成员(类的实现细节)的访问,可以更容易地修改代码。如果朋友直接访问私有数据,对实现数据字段(私有数据)的任何更改都会破坏访问该数据的代码。使用访问方法可以很大程度上消除这种情况。我认为相当重要。

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

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

在构建容器并希望为该类实现迭代器时,Friend可以派上用场。

友函数和类提供对类的私有和受保护成员的直接访问,以避免在一般情况下破坏封装。大多数使用是与ostream:我们希望能够键入:

Point p;
cout << p;

但是,这可能需要访问Point的私有数据,因此我们定义了重载操作符

friend ostream& operator<<(ostream& output, const Point& p);

然而,这里有明显的封装含义。首先,现在友类或函数可以完全访问类的所有成员,甚至不属于它的需要。其次,类和友元的实现现在交织在一起,以至于类中的内部更改可以破坏友元。

如果您将好友视为类的扩展,那么从逻辑上讲,这就不是问题。但是,在这种情况下,为什么有必要首先把朋友挖出来呢?

在不破坏封装的情况下,要实现“friends”所宣称的相同目标,可以这样做:

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

封装并没有被破坏,类B不能访问A中的内部实现,但结果与我们将B声明为A的朋友是一样的。 编译器将优化掉函数调用,因此这将导致与直接访问相同的指令。

我认为使用“朋友”是一个简单的捷径,有争议的好处,但一定的成本。

在做TDD的时候,我经常使用c++中的'friend'关键字。朋友能知道我的一切吗?

不,这只是一种单向的友谊。