什么时候应该在C ++中使用“朋友”?

我一直在阅读C ++常见问题解答 ,并对friend声明感到好奇。 我个人从来没有使用它,但我有兴趣探索的语言。

什么是使用friend的好例子?


再阅读一下FAQ,我喜欢<< >>操作符重载的概念,并将它们添加为这些类的一个朋友。 不过,我不知道这是如何不破坏封装。 什么时候这些exception能够保持在OOP的严格范围之内?

首先(IMO)不要听那些说friend没用的人。 这是有用的。 在许多情况下,您将拥有不具有公开可用数据或function的对象。 对于许多作者而言,这些代码库可能只是表面上熟悉不同的领域,尤其如此。

有朋友说明符的替代品,但通常是麻烦的(cpp-level具体类/ masked typedef)或不是万无一失的(注释或函数名称约定)。

在答案上;

friend说明符允许指定的类访问受保护的数据或类中的function,使朋友声明。 例如,在下面的代码中,任何人都可以要求孩子提供他们的名字,但是只有母亲和孩子可以改变姓名。

你可以进一步考虑一个更复杂的类,如一个窗口,这个简单的例子。 很有可能一个窗口将有许多function/数据元素,不应公开访问,但相关的类,如WindowManager需要。

 class Child { //Mother class members can access the private parts of class Child. friend class Mother; public: string name( void ); protected: void setName( string newName ); }; 

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

只要说我不会使用friend关键字作为你devise的重要组成部分。

friend关键字有很多好用处。 以下是我可以立即看到的两个用途:

朋友定义

Friend定义允许在类范围内定义一个函数,但函数不会被定义为成员函数,而是作为封闭名称空间的一个自由函数,除了依赖于参数的查找之外,通常不会正常显示。 这对于操作符重载非常有用:

 namespace utils { class f { private: typedef int int_type; int_type value; public: // let's assume it doesn't only need .value, but some // internal stuff. friend f operator+(f const& a, f const& b) { // name resolution finds names in class-scope. // int_type is visible here. return f(a.value + b.value); } int getValue() const { return value; } }; } int main() { utils::fa, b; std::cout << (a + b).getValue(); // valid } 

私人CRTP基础类

有时候,你发现需要一个策略需要访问派生类:

 // possible policy used for flexible-class. template<typename Derived> struct Policy { void doSomething() { // casting this to Derived* requires us to see that we are a // base-class of Derived. some_type const& t = static_cast<Derived*>(this)->getSomething(); } }; // note, derived privately template<template<typename> class SomePolicy> struct FlexibleClass : private SomePolicy<FlexibleClass> { // we derive privately, so the base-class wouldn't notice that, // (even though it's the base itself!), so we need a friend declaration // to make the base a friend of us. friend class SomePolicy<FlexibleClass>; void doStuff() { // calls doSomething of the policy this->doSomething(); } // will return useful information some_type getSomething(); }; 

你会在这个答案中find一个非人为的例子。 在这个答案中使用的另一个代码。 CRTP基础强制转换此指针,以便能够使用数据成员指针访问派生类的数据字段。

@roo :封装不会在这里被破坏,因为类本身决定谁可以访问它的私有成员。 如果这种情况可能是由课堂外引起的,例如,如果你的operator <<将宣布”我是fooclass的朋友“,封装将被打破。

friend取代public使用,而不是使用private

其实,C ++常见问题解答已经回答了这个问题。

规范的例子是重载运算符<<。 另一个常见用途是允许帮助者或pipe理员类访问你的内部。

以下是我听说的有关C ++朋友的一些指导原则。 最后一个特别难忘。

  • 你的朋友不是你的孩子的朋友。
  • 你孩子的朋友不是你的朋友。
  • 只有朋友可以触摸你的私人部分。

编辑:阅读常见问题多一点我喜欢的<< >>操作符的概念重载,并添加为这些类的朋友,但是我不知道如何不破坏封装

它将如何破坏封装?

当您允许无限制地访问数据成员时,可以打破封装。 考虑以下类:

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

c1 显然不是封装的。 任何人都可以读取和修改它中的x 。 我们无法执行任何types的访问控制。

c2显然是封装的。 没有公共访问x 。 你所能做的就是调用foo函数,这个函数在类上执行一些有意义的操作

c3 ? 这是less了封装? 它允许无限制地访问x ? 它允许未知的function访问?

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

那么这个封装怎么样呢? 同样数量的代码可以访问该类的私有成员。 每个有权访问的人都在类定义中列出。

friend不打破封装。 这让一些Java程序员感到不舒服,因为当他们说“OOP”时,他们实际上是指 “Java”。 当他们说“封装”时,他们并不意味着“私人成员必须被保护免受任意访问”,而是“一个能够访问私有成员的唯一函数是类成员的Java类”,尽pipe这完全是无稽之谈几个原因

首先,正如已经表明的那样,这太局限了。 没有理由不允许朋友的方法做同样的事情。

其次,限制性不够 。 考虑第四课:

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

根据上述的Java思路,这是完美的封装。 然而,它绝对允许任何人阅读和修改x 。 这怎么有意义呢? (提示:没有)

底线:封装是关于能够控制哪些function可以访问私人会员。 这不是关于这些function的定义究竟在哪里。

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

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

不用担心两条线是否总是按照一致的顺序一起完成,那么可以使这些方法是私有的,并且有一个朋友函数来保证一致性:

 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); } 

换句话说,您可以保持较小的公共接口,并强制切入朋友函数中的类和对象的不variables。

您可以使用“私人/受保护/公共”权限控制成员和function的访问权限? 所以假设这三个层面的每一个都是清楚的,那么我们应该很清楚,我们错过了一些东西。

例如,成员/函数的声明是非常通用的。 你说的是这个function对每个人来说都是遥不可及的(当然,除了遗传的孩子之外)。 但是例外呢? 每个安全系统都可以让你拥有某种types的“白名单”?

所以, 朋友可以让你有弹性的固体物体隔离,但是可以为你觉得合理的事物创造一个“漏洞”。

我想人们说这不是必须的,因为总是有一个devise没有它。 我认为这与全局variables的讨论是类似的:你永远不应该使用它们,没有它们总是有办法做的……但实际上,你会发现最后几乎是最优雅的方式。我认为这与朋友的情况是一样的。

除了让你访问一个成员variables而不使用设置function,它并没有什么好处

这不完全是看待它的方式。 这个想法是控制世界卫生组织可以访问什么,有没有设置function与它没有多大关系。

我发现使用朋友访问方便的地方:unit testing的私人function。

当你构build一个容器,并且你想为这个类实现一个迭代器的时候,朋友会很方便。

简短的答案是:当它实际上改善封装时使用朋友 。 提高可读性和可用性(运营商“和”是规范的例子)也是一个很好的理由。

关于改进封装的例子,专门devise用于与其他类的内部工作的类(想到testing类)是很好的候选。

C ++的创build者说,没有打破任何封装原则,我会引用他的话:

“朋友”是否违反封装? 不,不是的。 “朋友”是授予访问权限的明确机制,就像成员资格一样。 你不能(在一个标准的合格程序中)授予你一个类的访问权限,而不用修改它的源代码。

比清楚…

我曾经在一个我们曾经使用过朋友的公司里遇到过一个有趣的问题。 我在我们的框架部门工作,我们在自定义操作系统上创build了一个基本的引擎级系统 内部我们有一个阶级结构:

  Game / \ TwoPlayer SinglePlayer 

所有这些课程都是框架的一部分,由我们的团队维护。 公司生产的游戏是build立在这个框架的基础之上的。 问题在于Game具有SinglePlayer和TwoPlayer需要访问的各种接口,但是我们不希望暴露在框架类之外。 解决scheme是使这些接口保密,并允许TwoPlayer和SinglePlayer通过友谊访问它们。

实际上,整个问题本来可以通过更好地实施我们的制度来解决,但是我们被locking在我们所拥有的东西上。

另一个用途: 朋友 (+虚拟inheritance)可以用来避免从一个类派生(又名:“让一个类不可派生”)=> 1,2

从2 :

  class Fred; class FredBase { private: friend class Fred; FredBase() { } }; class Fred : private virtual FredBase { public: ... }; 

要多次使用TDD,我在C ++中使用了“friend”关键字。

朋友可以知道我的一切吗?


更新:我发现这个关于来自Bjarne Stroustrup网站的 “朋友”关键字的有价值的答案。

“朋友”是授予访问权限的明确机制,就像成员资格一样。

我只使用friend-keyword来unit testing保护函数。 有人会说,你不应该testing受保护的function。 然而,当我添加新的function时,我发现这个非常有用的工具。

但是,我不直接在类声明中使用关键字,而是使用一个漂亮的模板来达到这个目的:

 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。

您必须非常小心使用friend关键字的时间/地点,而且像您一样,我很less使用它。 以下是关于使用friend和替代品的一些注意事项。

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

  • 使用访问器方法进行比较(检查每个伊娃,并确定相等)。
  • 或者,您可以直接访问所有成员,并将其公开。

第一个select的问题是,这可能是很多访问者,它比直接variables访问(稍微)慢,难以阅读和麻烦。 第二种方法的问题是你完全破坏封装。

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

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

equal(Beer, Beer)现在可以直接访问ab的私有成员(可能是char *brandfloat percentAlcohol等等)。这是一个颇为人为的例子,你可以很快的将friend应用于一个重载的== operator ,但是我们会做到这一点。

有几件事要注意:

  • friend不是class级的成员
  • 这是一个普通的function,可以专门访问class级的私人成员
  • 不要把所有的访问者和变种者都replace成朋友(你也可以把所有东西都public !)
  • 友谊不是对等的
  • 友谊不是传递的
  • 友谊不是遗传的
  • 或者,正如C ++常见问题解答所解释的那样 :“仅仅因为我授予你友谊访问我不会自动授予你的孩子访问我的权限,并不会自动授予你的朋友访问权限,也不会自动授予我访问权限“。

我只有真正使用friends时候,要做到这一点很难。 又如,由于Mat2x2Mat3x3Mat4x4Vec2Vec3Vec4等的互操作性,许多向量math函数经常被创build为friends 。而且要成为朋友更容易,而不是必须随处使用访问器。 正如所指出的,当应用于<< (真正方便debugging), >>或者==运算符时, 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 ,但时不时的是你所需要的。 希望这可以帮助!

树的例子是一个很好的例子:在一个不同的类中实现一个对象而没有inheritance关系。

也许你也可能需要它有一个构造保护,并强迫人们使用你的“朋友”工厂。

好的,坦率地说,你可以没有它。

我使用friend一个具体实例是创buildSingleton类。 friend关键字可以让我创build一个访问器函数,它比在类上总是有一个“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; } 

关于运营商<<和运营商>>,没有很好的理由让这些运营商成为朋友。 诚然,他们不应该是成员职能,但他们也不需要成为朋友。

最好的办法是创build公共打印(ostream&)和读取(istream&)function。 然后,根据这些函数编写运算符<<和运算符>>。 这提供了额外的好处,让您可以使这些function虚拟,从而提供虚拟的序列化。

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

 Point p; cout << p; 

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

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

然而,有明显的封装影响。 首先,现在这个朋友类或函数可以完全访问这个类的所有成员,甚至是那些不需要的类。 其次,class级和朋友的实施现在已经陷入到class级的内部变化可以打破朋友的地步。

如果你把朋友看作是class级的延伸,那么从逻辑上讲,这不是一个问题。 但是,在这种情况下,为什么首先要说出朋友呢?

为了达到“朋友”声称要达到的目标,但是没有破坏封装,可以这样做:

 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的朋友一样。编译器会优化掉函数调用,所以这将导致相同的结果作为直接访问的指示。

我认为使用“朋友”只是一个有争论的捷径,但是确定的代价。

在C ++中,“friend”关键字在运算符重载和Making Bridge中很有用。

1.)运算符重载中的Friend关键字:
运算符重载的例子是:假设我们有一个类“Point”,它有两个浮点variables
“x”(对于x坐标)和“y”(对于y坐标)。 现在我们必须重载"<<" (提取操作符),使得如果我们调用"cout << pointobj"那么它将打印x和y坐标(其中pointobj是类Point的对象)。 要做到这一点,我们有两个select:

    1.在“ostream”类中加载“operator <<()”函数。
    2.在“Point”类中加载“operator <<()”function。 

现在,第一个选项是不好的,因为如果我们需要重载这个运算符为一些不同的类,那么我们必须再次在“ostream”类中进行更改。
这就是为什么第二是最好的select。 现在编译器可以调用"operator <<()"函数:

  1.使用ostream对象cout.As:cout.operator <<(Pointobj)(formsostream类)。 
2.呼叫没有对象。如:operator <<(cout,Pointobj)(来自Point类)。

因为我们在Point类中实现了重载。 所以要调用这个没有对象的函数,我们必须添加"friend"关键字,因为我们可以调用没有对象的朋友函数。 现在函数声明将如下所示:
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.) Friend keyword in making bridge :
Suppose we have to make a function in which we have to access private member of two or more classes ( generally termed as "bridge" ) . How to do this:
To access private member of a class it should be member of that class. Now to access private member of other class every class should declare that function as a friend function. For example : Suppose there are two class A and B. A function "funcBridge()" want to access private member of both classes. Then both class should declare "funcBridge()" as:
friend return_type funcBridge(A &a_obj, B & b_obj);

I think this would help to understand friend keyword.

When implementing tree algorithms for class, the framework code the prof gave us had the tree class as a friend of the node class.

It doesn't really do any good, other than let you access a member variable without using a setting function.

To do TDD many times I've used 'friend' keyword in C++.
Can a friend know everything about me?

No, its only a one way friendship :`(

You may use friendship when different classes (not inheriting one from the other) are using private or protected members of the other class.

Typical use cases of friend functions are operations that are conducted between two different classes accessing private or protected members of both.

from http://www.cplusplus.com/doc/tutorial/inheritance/ .

You can see this example where non-member method accesses the private members of a class. This method has to be declared in this very class as a friend of the class.

 // friend functions #include <iostream> using namespace std; class Rectangle { int width, height; public: Rectangle() {} Rectangle (int x, int y) : width(x), height(y) {} int area() {return width * height;} friend Rectangle duplicate (const Rectangle&); }; Rectangle duplicate (const Rectangle& param) { Rectangle res; res.width = param.width*2; res.height = param.height*2; return res; } int main () { Rectangle foo; Rectangle bar (2,3); foo = duplicate (bar); cout << foo.area() << '\n'; return 0; } 

Probably I missed something from the answers above but another important concept in encapsulation is hiding of implementation. Reducing access to private data members (the implementation details of a class) allows much easier modification of the code later. If a friend directly accesses the private data, any changes to the implementation data fields (private data), break the code accessing that data. Using access methods mostly eliminates this. Fairly important I would think.

This may not be an actual use case situation but may help to illustrate the use of friend between classes.

The ClubHouse

 class ClubHouse { public: friend class VIPMember; // VIP Members Have Full Access To Class private: unsigned nonMembers_; unsigned paidMembers_; unsigned vipMembers; std::vector<Member> members_; public: ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {} addMember( const Member& member ) { // ...code } void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code } Amenity getAmenity( unsigned memberID ) { // ...code } protected: void joinVIPEvent( unsigned memberID ) { // ...code } }; // ClubHouse 

The Members Class's

 class Member { public: enum MemberShipType { NON_MEMBER_PAID_EVENT, // Single Event Paid (At Door) PAID_MEMBERSHIP, // Monthly - Yearly Subscription VIP_MEMBERSHIP, // Highest Possible Membership }; // MemberShipType protected: MemberShipType type_; unsigned id_; Amenity amenity_; public: Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {} virtual ~Member(){} unsigned getId() const { return id_; } MemberShipType getType() const { return type_; } virtual void getAmenityFromClubHouse() = 0 }; class NonMember : public Member { public: explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {} void getAmenityFromClubHouse() override { Amenity = ClubHouse::getAmenity( this->id_ ); } }; class PaidMember : public Member { public: explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {} void getAmenityFromClubHouse() override { Amenity = ClubHouse::getAmenity( this->id_ ); } }; class VIPMember : public Member { public: friend class ClubHouse; public: explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {} void getAmenityFromClubHouse() override { Amenity = ClubHouse::getAmenity( this->id_ ); } void attendVIPEvent() { ClubHouse::joinVIPEvent( this->id ); } }; 

设施

 class Amenity{}; 

If you look at the relationship of these classes here; the ClubHouse holds a variety of different types of memberships and membership access. The Members are all derived from a super or base class since they all share an ID and an enumerated type that are common and outside classes can access their IDs and Types through access functions that are found in the base class.

However through this kind of hierarchy of the Members and its Derived classes and their relationship with the ClubHouse class the only one of the derived class's that has "special privileges" is the VIPMember class. The base class and the other 2 derived classes can not access the ClubHouse's joinVIPEvent() method, yet the VIP Member class has that privilege as if it has complete access to that event.

So with the VIPMember and the ClubHouse it is a two way street of access where the other Member Classes are limited.

As the reference for friend declaration says:

The friend declaration appears in a class body and grants a function or another class access to private and protected members of the class where the friend declaration appears.

So just as a reminder, there are technical errors in some of the answers which say that friend can only visit protected members.

You could adhere to the strictest and purest OOP principles and ensure that no data members for any class even have accessors so that all objects must be the only ones that can know about their data with the only way to act on them is through indirect messages , ie, methods.

But even C# has an internal visibility keyword and Java has its default package level accessibility for some things. C++ comes actually closer to the OOP ideal by minimizinbg the compromise of visibility into a class by specifying exactly which other class and only other classes could see into it.

I don't really use C++ but if C# had friend s I would that instead of the assembly-global internal modifier, which I actually use a lot. It doesn't really break incapsulation, because the unit of deployment in .NET is an assembly.

But then there's the InternalsVisibleTo Attribute(otherAssembly) which acts like a cross-assembly friend mechanism. Microsoft uses this for visual designer assemblies.

Friends are also useful for callbacks. You could implement callbacks as static methods

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

where callback calls localCallback internally, and the clientData has your instance in it. 我的想法是,

要么…

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

What this allows is for the friend to be a defined purely in the cpp as a c-style function, and not clutter up the class.

Similarly, a pattern I've seen very often is to put all the really private members of a class into another class, which is declared in the header, defined in the cpp, and friended. This allows the coder to hide a lot of the complexity and internal working of the class from the user of the header.

In the header:

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

In the cpp,

 class MyFooPrivate { public: MyFoo *owner; // Your complexity here }; MyFoo::MyFoo() { this->_private->owner = this; } 

It becomes easier to hide things that the downstream needn't see this way.