我一直在阅读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);
}

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

其他回答

典型的例子是重载操作符<<。另一个常见的用法是允许助手或管理类访问您的内部。

下面是我从c++朋友那里听到的一些指导原则。最后一个尤其令人难忘。

你的朋友不是你孩子的朋友。 你孩子的朋友并不是你的朋友。 只有朋友才能碰你的隐私部位。

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

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

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

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

编辑:阅读faq有点长,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法,但我不确定这如何不打破封装

它将如何破坏封装?

当允许对数据成员进行不受限制的访问时,就打破了封装。考虑以下类:

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

C1显然没有封装。任何人都可以读取和修改其中的x。我们没有办法实施任何形式的访问控制。

C2明显被封装了。没有对x的公共访问。你所能做的就是调用foo函数,它对类执行一些有意义的操作。

c3吗?它的封装程度低吗?它是否允许无限制地访问x?它是否允许未知函数访问?

不。它只允许一个函数访问类的私有成员。就像c2一样。就像c2一样,有访问权的函数不是“某个随机的未知函数”,而是“类定义中列出的函数”。就像c2一样,通过查看类定义,我们可以看到拥有访问权限的完整列表。

那么,这到底是如何减少封装的呢?同样数量的代码可以访问类的私有成员。类定义中列出了所有具有访问权限的人。

Friend不会破坏封装。这让一些Java程序员感到不舒服,因为当他们说“面向对象”时,他们实际上指的是“Java”。当他们说“封装”时,他们并不是说“必须保护私有成员不受任意访问”,而是说“在Java类中,唯一能够访问私有成员的函数是类成员”,尽管出于几个原因,这完全是无稽之谈。

首先,如上所述,它限制太大。没有理由不允许朋友方法做同样的事情。

第二,限制不够。考虑第四个类:

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

根据上述Java思想,这是完美封装的。 但是,它允许任何人读取和修改x,这有什么意义呢?(提示:事实并非如此)

底线: 封装是关于能够控制哪些函数可以访问私有成员。这与这些函数的定义具体位于何处无关。

我使用friend的一个特定实例是在创建Singleton类时。friend关键字允许我创建一个访问器函数,这比总是在类上使用“GetInstance()”方法更简洁。

/////////////////////////
// Header file
class MySingleton
{
private:
    // Private c-tor for Singleton pattern
    MySingleton() {}

    friend MySingleton& GetMySingleton();
}

// Accessor function - less verbose than having a "GetInstance()"
//   static function on the class
MySingleton& GetMySingleton();


/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
    static MySingleton theInstance;
    return theInstance;
}