您何时应该使用'朋友'在c++?
我一直在阅读 c ++ faq ,对 朋友
声明。我个人从未使用过它,但是我有兴趣探索该语言。
使用 friend
的好例子是什么?
阅读FAQ更长的时间,我喜欢<<
>>>
操作员重载和添加这些类的朋友的想法。但是,我不确定这不会打破封装。这些例外何时可以保持在OOP的严格范围内?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
首先(IMO)不要听那些说
friend
的人没有用。这很有用。在许多情况下,您将拥有具有数据或功能的对象,这些对象不打算公开可用。大型代码库尤其如此,许多作者可能只能表面上熟悉不同领域。还有朋友指定符的替代方案,但通常它们很麻烦(CPP级混凝土类/蒙版Typedefs)或没有万无一失的(注释或功能名称公约)。
回答;
friend
指定符允许指定的类访问“ Friend”语句的类中的受保护数据或功能的访问。例如,在以下代码中,任何人都可以要求孩子姓名,但只有母亲和孩子才能更改名称。您可以通过考虑更复杂的类(例如窗口)来进一步以这个简单的示例。很可能有一个窗口具有许多功能/数据元素,不应公开访问,而是相关类(例如WindowManager)所需的。
Firstly (IMO) don't listen to people who say
friend
is not useful. It IS useful. In many situations you will have objects with data or functionality that are not intended to be publicly available. This is particularly true of large codebases with many authors who may only be superficially familiar with different areas.There ARE alternatives to the friend specifier, but often they are cumbersome (cpp-level concrete classes/masked typedefs) or not foolproof (comments or function name conventions).
Onto the answer;
The
friend
specifier allows the designated class access to protected data or functionality within the class making the friend statement. For example in the below code anyone may ask a child for their name, but only the mother and the child may change the name.You can take this simple example further by considering a more complex class such as a Window. Quite likely a Window will have many function/data elements that should not be publicly accessible, but ARE needed by a related class such as a WindowManager.
在工作中,我们使用朋友大量测试代码。这意味着我们可以为主要应用程序代码提供适当的封装和信息隐藏。但是,我们也可以使用单独的测试代码,该代码使用朋友检查内部状态和数据进行测试。
可以说我不会将朋友关键字作为您设计的重要组成部分。
At work we use friends for testing code, extensively. It means we can provide proper encapsulation and information hiding for the main application code. But also we can have separate test code that uses friends to inspect internal state and data for testing.
Suffice to say I wouldn't use the friend keyword as an essential component of your design.
friend
关键字具有许多很好的用途。这是我立即可见的两种用途:朋友定义
朋友定义允许在类cope中定义一个函数,但是该函数不会定义为成员函数,而是封闭名称空间的免费函数,并且不会正常可见,除了依赖参数的查找。这使其对于运营商的重载特别有用:
私人CRTP基类
有时会发现策略需要访问派生类的需要:
您会在 this 答案。 CRTP基础施放了该指针,以便能够使用Data-Member-Pointers访问派生类的数据场。
The
friend
keyword has a number of good uses. Here are the two uses immediately visible to me:Friend Definition
Friend definition allows to define a function in class-scope, but the function will not be defined as a member function, but as a free function of the enclosing namespace, and won't be visible normally except for argument dependent lookup. That makes it especially useful for operator overloading:
Private CRTP Base Class
Sometimes, you find the need that a policy needs access to the derived class:
You will find a non-contrived example for that in this answer. Another code using that is in this answer. The CRTP base casts its this pointer, to be able to access data-fields of the derived class using data-member-pointers.
@rooo :封装在这里没有打破,因为课程本身本身决定谁可以访问其私人成员。只有当您的
操作员<<
会宣布“我是classfoo
>的朋友”时,只有在班级外部引起的封装才会被打破。朋友
代替public
的使用,而不是使用private
!实际上,c ++常见问题已经回答了这一点。
@roo: Encapsulation is not broken here because the class itself dictates who can access its private members. Encapsulation would only be broken if this could be caused from outside the class, e.g. if your
operator <<
would proclaim “I'm a friend of classfoo
.”friend
replaces use ofpublic
, not use ofprivate
!Actually, the C++ FAQ answers this already.
规范的例子是超载运算符&lt;&lt;。另一个常见的用途是允许辅助或管理员类访问您的内部。
这是我听说过C ++朋友的几个准则。最后一个特别令人难忘。
The canonical example is to overload operator<<. Another common use is to allow a helper or admin class access to your internals.
Here are a couple of guidelines I heard about C++ friends. The last one is particularly memorable.
如何破坏封装?
当您允许无限制访问数据成员时,您会断开封装。考虑以下类:
c1
是明显未封装的。任何人都可以在其中读取和修改x
。我们无法执行任何类型的访问控制。C2
显然已封装。无法公开访问X
。您所能做的就是调用foo
函数,该功能在类上执行一些有意义的操作。C3
?那封装不太封装吗?它是否允许对x
的无限制访问?它允许未知功能访问吗?否。它允许精确一个功能访问类的私人成员。就像
C2
一样。就像C2
一样,具有访问权限的一个函数不是“某些随机,未知函数”,而是“类定义中列出的函数”。就像c2
一样,我们可以看到,只需查看类定义,a 完整谁访问了。那么,这少封装到底如何呢?相同数量的代码可以访问班级的私人成员。 拥有访问权限的每个人都在类定义中列出。
朋友
不会断开封装。这使一些Java人程序员感到不舒服,因为当他们说“ OOP”时,他们实际上是 “ Java”。当他们说“封装”时,他们并不是说“私人成员必须受到任意访问的保护”,而是“ Java类,其中唯一能够访问私人成员的功能是班级成员”,即使这是完全胡说八道< em>出于多种原因。首先,如前所述,它太限制了。没有理由不应该允许朋友方法这样做。
其次,它不是限制性足够的。考虑第四节:
根据上述Java的心态,这是完美封装的。
但是,它绝对允许任何人阅读和修改x 。这甚至有意义? (提示:不是)
底线:
封装是关于能够控制哪些功能可以访问私人成员。它不是 准确地说是这些函数的定义所在的位置。
How would it break encapsulation?
You break encapsulation when you allow unrestricted access to a data member. Consider the following classes:
c1
is obviously not encapsulated. Anyone can read and modifyx
in it. We have no way to enforce any kind of access control.c2
is obviously encapsulated. There is no public access tox
. All you can do is call thefoo
function, which performs some meaningful operation on the class.c3
? Is that less encapsulated? Does it allow unrestricted access tox
? Does it allow unknown functions access?No. It allows precisely one function to access the private members of the class. Just like
c2
did. And just likec2
, the one function which has access is not "some random, unknown function", but "the function listed in the class definition". Just likec2
, we can see, just by looking at the class definitions, a complete list of who has access.So how exactly is this less encapsulated? The same amount of code has access to the private members of the class. And everyone who has access is listed in the class definition.
friend
does not break encapsulation. It makes some Java people programmers feel uncomfortable, because when they say "OOP", they actually mean "Java". When they say "Encapsulation", they don't mean "private members must be protected from arbitrary accesses", but "a Java class where the only functions able to access private members, are class members", even though this is complete nonsense for several reasons.First, as already shown, it is too restricting. There's no reason why friend methods shouldn't be allowed to do the same.
Second, it is not restrictive enough. Consider a fourth class:
This, according to aforesaid Java mentality, is perfectly encapsulated.
And yet, it allows absolutely anyone to read and modify x. How does that even make sense? (hint: It doesn't)
Bottom line:
Encapsulation is about being able to control which functions can access private members. It is not about precisely where the definitions of these functions are located.
安德鲁(Andrew)示例的另一个常见版本是可怕的代码耦合,
而不必担心是否始终在一起完成两行,并且以一致的顺序完成,您可以将方法私有化并具有朋友函数来执行一致性:
换句话说,您可以保持公共接口保持较小的,强制执行不变的,这些不变性在朋友函数中跨越类和对象。
Another common version of Andrew's example, the dreaded code-couplet
Instead of worrying if both lines are always done together and in consistent order you could make the methods private and have a friend function to enforce consistency:
In other words you can keep the public interfaces smaller and enforce invariants that cut across classes and objects in friend functions.
您可以使用私人/保护/公共权利控制会员的访问权限和功能吗?
因此,假设这三个级别中的每个级别的每一个都很清楚,那么我们应该很明显我们缺少某些东西……
例如,将成员/函数声明为保护是相当通用的。您说的是,此功能是每个人(当然是继承的孩子)。但是例外呢?每个安全系统都可以让您拥有某种类型的“白色列表”,对吗
?感觉是有道理的,
我认为这是不需要的,因为我认为这与全球变量的讨论相似。他们...但是实际上,您会看到最终成为(几乎)最优雅的方式的情况...我认为这与朋友是同一情况。
,这并不是查看它的方法。
这个想法是控制谁可以访问什么,具有或不设置函数与之无关。
You control the access rights for members and functions using Private/Protected/Public right?
so assuming the idea of each and every one of those 3 levels is clear, then it should be clear that we are missing something...
The declaration of a member/function as protected for example is pretty generic. You are saying that this function is out of reach for everyone (except for an inherited child of course). But what about exceptions? every security system lets you have some type of 'white list" right?
So friend lets you have the flexibility of having rock solid object isolation, but allows for a "loophole" to be created for things that you feel are justified.
I guess people say it is not needed because there is always a design that will do without it. I think it is similar to the discussion of global variables: You should never use them, There is always a way to do without them... but in reality, you see cases where that ends up being the (almost) most elegant way... I think this is the same case with friends.
well that is not exactly the way to look at it.
The idea is to control WHO can access what, having or not a setting function has little to do with it.
我找到了使用朋友访问的方便的地方:私人功能的UNITSEST。
I found handy place to use friend access: Unittest of private functions.
C ++的创建者说,这并不是任何封装原则,我会引用他:
很清楚...
The creator of C++ says that isn't broking any encapsulation principle, and I will quote him:
Is more than clear...
当您构建一个容器时,朋友会派上方便,并且想为该课程实现迭代器。
Friend comes handy when you are building a container and you want to implement an iterator for that class.
我们曾在我以前在我们使用朋友来体面影响的公司工作的公司出现一个有趣的问题。我在框架部门工作,我们在自定义操作系统上创建了一个基本的发动机级系统。在内部,我们有一个课堂结构:
所有这些课程都是框架的一部分,并由我们的团队维护。该公司制作的游戏是建立在此框架之上的,该框架源于其中一个儿童游戏。问题在于,游戏与单个玩家和Twoplayer需要访问的各种事物具有界面,但我们不希望在框架类别之外展示。解决方案是将这些接口私有化,并允许Twoplayer和单人游戏通过友谊访问它们。
坦白说,这整个问题本可以通过更好地实施我们的系统来解决,但我们被锁定在我们所拥有的。
We had an interesting issue come up at a company I previously worked at where we used friend to decent affect. I worked in our framework department we created a basic engine level system over our custom OS. Internally we had a class structure:
All of these classes were part of the framework and maintained by our team. The games produced by the company were built on top of this framework deriving from one of Games children. The issue was that Game had interfaces to various things that SinglePlayer and TwoPlayer needed access to but that we did not want expose outside of the framework classes. The solution was to make those interfaces private and allow TwoPlayer and SinglePlayer access to them via friendship.
Truthfully this whole issue could have been resolved by a better implementation of our system but we were locked into what we had.
简短的答案是:使用 friend 实际上改进封装。提高可读性和可用性(运营商&lt;&lt;&gt;&gt;是规范的例子)也是一个很好的理由。
至于改进封装的示例,专门设计用于与其他类(想到的测试课程)合作的课程是良好的候选人。
The short answer would be: use friend when it actually improves encapsulation. Improving readability and usability (operators << and >> are the canonical example) is also a good reason.
As for examples of improving encapsulation, classes specifically designed to work with the internals of other classes (test classes come to mind) are good candidates.
在C ++“朋友”中,关键字可用于操作员的过载和制作桥梁。
1.)运算符重载中的朋友关键字:
操作员重载的示例是:假设我们有一个具有两个float变量的类“点”
“ x”(用于x坐标)和“ y”(用于y坐标)。现在,我们必须超载
“&lt;&lt;“
(提取操作员),这样,如果我们调用“ cout&lt&lt;&lt; pointobj”
,则它将打印x和y坐标(其中pointOBJ是类点的对象)。为此,我们有两个选择:Now First option is not good because if we need to overload again this operator for some different class then we have to again make change in "ostream" class.
That's why second is best option. Now compiler can call
"operator <<()"
function:BeaCause我们已经在积分类中实施了重载。因此,要在没有对象的情况下调用此函数,我们必须添加
“朋友”
关键字,因为我们可以在没有对象的情况下调用朋友函数。现在函数声明将为:
“朋友ostream&amp; ocerator&lt;&lt;(ostream&amp; cout,point&amp; pointobj);“
2.)Friend制作桥梁的关键字:
假设我们必须发挥一个函数,在该功能中,我们必须访问两个或多个类的私人成员(通常称为“桥”)。
如何做到这一点:
要访问班级的私人成员,应该是该课程的成员。现在,要访问其他班级的私人成员,每个班级应将该功能声明为朋友函数。例如 :
假设有两个A和B类B。函数
“ funcbridge()”
想要访问两个类的私人成员。然后两个类都应声明“ funcbridge()”
as:朋友return_type funcbridge(a&amp; a_obj,b&amp; b_obj);
我认为这将有助于了解朋友关键字。
In C++ "friend" keyword is useful in Operator overloading and Making Bridge.
1.) Friend keyword in operator overloading :
Example for operator overloading is: Let say we have a class "Point" that has two float variable
"x"(for x-coordinate) and "y"(for y-coordinate). Now we have to overload
"<<"
(extraction operator) such that if we call"cout << pointobj"
then it will print x and y coordinate (where pointobj is an object of class Point). To do this we have two option:Now First option is not good because if we need to overload again this operator for some different class then we have to again make change in "ostream" class.
That's why second is best option. Now compiler can call
"operator <<()"
function:Beacause we have implemented overloading in Point class. So to call this function without an object we have to add
"friend"
keyword because we can call a friend function without an object.Now function declaration will be As:
"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.
作为
因此,正如提醒的那样,某些答案中存在技术错误,这些答案说
friend
只能访问受保护成员。As the reference for friend declaration says:
So just as a reminder, there are technical errors in some of the answers which say that
friend
can only visit protected members.另一个用途: friend (+虚拟继承)可用于避免从类派生(aka:“使class class ofer class noverable vivable”)=&gt; 1 , 2
来自 2 :
Another use: friend (+ virtual inheritance) can be used to avoid deriving from a class (aka: "make a class underivable") => 1, 2
From 2:
为了多次进行TDD,我在C ++中使用了“朋友”关键字。
朋友可以知道我的一切吗?
更新:我从 bjarne stroustroup site 。
To do TDD many times I've used 'friend' keyword in C++.
Can a friend know everything about me?
Updated: I found this valuable answer about "friend" keyword from Bjarne Stroustrup site.
您必须非常谨慎地使用
friend
关键字的何时/地点,并且像您一样,我很少使用它。以下是有关使用friend
和替代方案的一些注释。假设您要比较两个对象,以查看它们是否相等。您可以:
第一个选项的问题在于,这可能是很多访问者,这比直接变量访问速度(稍微)慢,难以读取和麻烦。第二种方法的问题是您完全打破了封装。
很好的是,如果我们可以定义一个外部功能,该功能仍然可以访问班级的私人成员。我们可以使用
friend
关键字来完成此操作:方法
均等(啤酒,啤酒)
现在可以直接访问a
和b
/code>的私人成员(可以是
char *brand
,float percental ochol
等。这是一个相当理想的例子,您会尽快应用friend /代码>到Overloaded
==操作员
,我们要注意的是:
friend
不是类的成员功能public
!)”
朋友
以其他方式更难做到这一点。作为另一个示例,由于mat2x2
,mat3x3
,mat4x4 friends
>,vec2
,vec3
,vec4
等。变得更容易成为朋友,而不必在各地使用访问者。正如指出的那样,当应用于&lt;&lt;
时,friend
通常是有用的==
运算符,但也可以用于这样的事情:正如我所说,我根本不使用
friend
,但时不时地是正是您需要的。希望这有帮助!You have to be very careful about when/where you use the
friend
keyword, and, like you, I have used it very rarely. Below are some notes on usingfriend
and the alternatives.Let's say you want to compare two objects to see if they're equal. You could either:
The problem with the first option, is that that could be a LOT of accessors, which is (slightly) slower than direct variable access, harder to read, and cumbersome. The problem with the second approach is that you completely break encapsulation.
What would be nice, is if we could define an external function which could still get access to the private members of a class. We can do this with the
friend
keyword:The method
equal(Beer, Beer)
now has direct access toa
andb
's private members (which may bechar *brand
,float percentAlcohol
, etc. This is a rather contrived example, you would sooner applyfriend
to an overloaded== operator
, but we'll get to that.A few things to note:
friend
is NOT a member function of the classpublic
!)I only really use
friends
when it's much harder to do it the other way. As another example, many vector maths functions are often created asfriends
due to the interoperability ofMat2x2
,Mat3x3
,Mat4x4
,Vec2
,Vec3
,Vec4
, etc. And it's just so much easier to be friends, rather than have to use accessors everywhere. As pointed out,friend
is often useful when applied to the<<
(really handy for debugging),>>
and maybe the==
operator, but can also be used for something like this:As I say, I don't use
friend
very often at all, but every now and then it's just what you need. Hope this helps!似乎我迟到了14年。但是来了。
TLDR TLDR
朋友类在那里,以便您可以将封装扩展到包含您数据结构的一组类。
TLDR
通常由多个类组成。与传统类(由您的编程语言支持)类似,您的数据结构是一个广义类,它在该数据上还具有数据和不变性,这些数据跨越了多个类的对象。封装可以保护那些不变的人免受外部数据的意外修改,以便数据结构的操作(“成员函数”)正常工作。朋友类将封装从类扩展到您的广义类。
太长的
A 类是与不变的一起指定数据类型值的子集,称为有效状态。对象是类的有效状态。类的A 成员函数将给定对象从有效状态移至另一个。
至关重要的是,对象数据不会从类成员函数外部进行修改,因为这可能会破坏类不变(即将对象移至无效状态)。 封装禁止从班级外部访问对象数据。这是编程语言的重要安全功能,因为它很难使班级不变。
一类通常是实现数据结构的自然选择,因为数据结构的属性(例如性能)取决于其数据的不变性(例如红色%e2%80%93Black_tree“ rel =“ nofollow noreferrer”>红黑树不变式)。但是,有时单个类不足以描述数据结构。
A 数据结构是将数据从有效状态转移到另一个数据的任何数据,不变性和功能。这是一个班级的概括。微妙的区别在于,数据可以散布在数据类型上,而不是集中在单个数据类型上。
数据结构示例
数据结构的原型示例是 vertex ),边缘(class
edge
)和图形(类Graph
)。这些课程没有独立的意义。图类创建vertex
s andedge
由其成员函数(例如graph.addvertex()
and andgraph.addedge(avertex) ,bvertex)
),并返回与它们的指针(或类似)。vertex
s andedge
S同样被其拥有Graph
(例如 graph.removevertex(vertex)和和和<代码> graph.removeedge(edge))。顶点
对象的收集,edge
对象和图形
对象一起编码数学图。在此示例中,目的是vertex
/edge
对象在Graph> Graph
对象之间未共享(其他设计选择也是可能的)。Graph
对象可以存储其所有顶点和边缘的列表,而每个vertex
可以存储一个指向其拥有的指针graph
。因此,Graph
对象代表整个数学图,每当需要数学图时,您都会将其传递。不变的示例
图形数据结构不变的是
vertex
在其所有者Graph
的列表中列出。这个不变的跨越vertex
对象和Graph
对象。多种类型的多种对象可以参与给定的不变。封装示例
类似于类,数据结构受益于封装,它可以防止意外修改其数据。这是因为数据结构需要保留不变性,才能像一类一样以应许的方式运行。
在图形数据结构示例中,您会声明
vertex
是Graph
的朋友,也使构造函数和数据成员的vertex
>私有,以便只能通过Graph
创建顶点
。特别是,vertex
将具有一个私有构造函数,它可以接受指向其拥有图的指针。该构造函数在graph.addvertex()
中被称为,这是可能的,因为vertex
是graph
的朋友。 (但是请注意,Graph
不是vertex
的朋友:无需vertex
能够访问Graph 的顶点列表,例如。)
术语
数据结构的定义本身就像类一样。我建议我们开始使用术语“广义类”来用于将数据从有效状态转移到另一个数据的任何数据,不变性和功能。然后,C ++类是一种特定类型的广义类。然后,不言而喻的朋友类是将封装从C ++类扩展到广义类的精确机制。
(实际上,我希望“班级”一词被替换为“广义班级”的概念,并将“本地班级”用于编程语言支持的类的特殊情况。然后,当教课程时,您将了解这两个本地阶级和这些广义班级。
Seems I'm about 14 years late to the party. But here goes.
TLDR TLDR
Friend classes are there so that you can extend encapsulation to the group of classes which comprise your data structure.
TLDR
Your data structure in general consists of multiple classes. Similarly to a traditional class (supported by your programming language), your data structure is a generalized class which also has data and invariants on that data which spans across objects of multiple classes. Encapsulation protects those invariants against accidental modification of the data from the outside, so that the data-structure's operations ("member functions") work correctly. Friend classes extend encapsulation from classes to your generalized class.
The too long
A class is a datatype together with invariants which specify a subset of the values of the datatype, called the valid states. An object is a valid state of a class. A member function of a class moves a given object from a valid state to another.
It is essential that object data is not modified from outside of the class member functions, because this could break the class invariants (i.e. move the object to an invalid state). Encapsulation prohibits access to object data from outside of the class. This is an important safety feature of programming languages, because it makes it hard to inadvertedly break class invariants.
A class is often a natural choice for implementing a data structure, because the properties (e.g. performance) of a data structure is dependent on invariants on its data (e.g. red-black tree invariants). However, sometimes a single class is not enough to describe a data structure.
A data structure is any set of data, invariants, and functions which move that data from a valid state to another. This is a generalization of a class. The subtle difference is that the data may be scattered over datatypes rather than be concentrated on a single datatype.
Data structure example
A prototypical example of a data structure is a graph which is stored using separate objects for vertices (class
Vertex
), edges (classEdge
), and the graph (classGraph
). These classes do not make sense independently. The Graph class createsVertex
s andEdge
s by its member functions (e.g.graph.addVertex()
andgraph.addEdge(aVertex, bVertex)
) and returns pointers (or similar) to them.Vertex
s andEdge
s are similarly destroyed by their owningGraph
(e.g.graph.removeVertex(vertex)
andgraph.removeEdge(edge)
). The collection ofVertex
objects,Edge
objects and theGraph
object together encode a mathematical graph. In this example the intention is thatVertex
/Edge
objects are not shared betweenGraph
objects (other design choices are also possible).A
Graph
object could store a list of all its vertices and edges, while eachVertex
could store a pointer to its owningGraph
. Hence, theGraph
object represents the whole mathematical graph, and you would pass that around whenever the mathematical graph is needed.Invariant example
An invariant for the graph data structure then would be that a
Vertex
is listed in its ownerGraph
's list. This invariant spans both theVertex
object and theGraph
object. Multiple objects of multiple types can take part in a given invariant.Encapsulation example
Similarly to a class, a data structure benefits from encapsulation which protects against accidental modification of its data. This is because the data structure needs to preserve invariants to be able to function in promised manner, exactly like a class.
In the graph data structure example, you would state that
Vertex
is a friend ofGraph
, and also make the constructors and data-members ofVertex
private so that aVertex
can only be created and modified byGraph
. In particular,Vertex
would have a private constructor which accepts a pointer to its owning graph. This constructor is called ingraph.addVertex()
, which is possible becauseVertex
is a friend ofGraph
. (But note thatGraph
is not a friend ofVertex
: there is no need forVertex
to be able to accessGraph
's vertex-list, say.)Terminology
The definition of a data structure acts itself like a class. I propose that we start using the term 'generalized class' for any set of data, invariants, and functions which move that data from a valid state to another. A C++ class is then a specific kind of a generalized class. It is then self-evident that friend classes are the precise mechanism for extending encapsulation from C++ classes to generalized classes.
(In fact, I'd like the term 'class' to be replaced with the concept of 'generalized class', and use 'native class' for the special case of a class supported by the programming language. Then when teaching classes you would learn of both native classes and these generalized classes. But perhaps that would be confusing.)
关于运营商&lt;&lt;和操作员&gt;&gt;没有充分的理由结交这些运营商的朋友。的确,他们不应该是会员功能,但也不需要成为朋友。
最好的办法是创建公共打印(Ostream&amp;)并读取(istream&amp;)功能。然后,写操作员&lt;&lt;和操作员&gt;&gt;就这些功能而言。这给您带来了允许您虚拟函数的额外好处,从而提供虚拟序列化。
With regards to operator<< and operator>> there is no good reason to make these operators friends. It is true that they should not be member functions, but they don't need to be friends, either.
The best thing to do is create public print(ostream&) and read(istream&) functions. Then, write the operator<< and operator>> in terms of those functions. This gives the added benefit of allowing you to make those functions virtual, which provides virtual serialization.
我只使用Friend-keyword来获得Unitest Wearp的功能。有人会说您不应该测试受保护的功能。但是,在添加新功能时,我会发现此非常有用的工具。
但是,我不直接在类声明中使用关键字,而是使用漂亮的模板黑客来实现这一点:
这使我能够执行以下操作:
在GCC和MSVC上工作。
I'm only using the friend-keyword to unittest protected functions. Some will say that you shouldn't test protected functionality. I, however, find this very useful tool when adding new functionality.
However, I don't use the keyword in directly in the class declarations, instead I use a nifty template-hack to achive this:
This enables me to do the following:
Works on GCC and MSVC atleast.
树示例是一个很好的例子:
在没有几个不同的类中实现对象
具有继承关系。
也许您也可能需要它以保护构造函数并强制
人们使用您的“朋友”工厂。
...好吧,坦率地说,您可以没有它。
The tree example is a pretty good example :
Having an object implemented in a few different class without
having an inheritance relationship.
Maybe you could also need it to have a constructor protected and force
people to use your "friend" factory.
... Ok, Well frankly you can live without it.
不,这只是一种一种一种方式友谊:`(
No, its only a one way friendship :`(
我使用
friend
的一个特定实例是创建 singletleton classe。friend
关键字使我可以创建一个访问器函数,该功能比在类上使用“ getInstance()”方法更简洁。One specific instance where I use
friend
is when creating Singleton classes. Thefriend
keyword lets me create an accessor function, which is more concise than always having a "GetInstance()" method on the class.朋友功能和课程可直接访问私人和受保护的班级成员,以避免在一般情况下破坏封装。大多数用法是关于Ostream:我们希望能够键入:
但是,这可能需要访问点的私人数据,因此我们定义了超载运算
符,但是,显而易见的封装含义。首先,现在,朋友的班级或功能可以完全访问班级的所有成员,即使是与其需求无关的成员。其次,班级和朋友的实现现在被淘汰到班级内部变化可以破坏朋友的地步。
如果您将朋友视为班级的扩展,那么从逻辑上讲,这不是问题。但是,在这种情况下,为什么有必要首先将朋友带出来。
为了实现“朋友”声称要实现的同一目标,但是如果不违反封装,就可以做到这一点:
封装没有破坏,B类无法访问A中的内部实现,但结果与我们一样b宣布为A的朋友。
编译器将优化函数调用,因此这将导致与直接访问相同的说明。
我认为使用“朋友”只是一个具有可争议的好处的捷径,但成本确定。
Friend functions and classes provide direct access to private and protected members of class to avoid breaking encapsulation in the general case. Most usage is with ostream: we would like to be able to type:
However, this may require access to the private data of Point, so we define the overloaded operator
There are obvious encapsulation implications, however. First, now the friend class or function has full access to ALL members of the class, even ones that do not pertain to its needs. Second, the implementations of the class and the friend are now enmeshed to the point where an internal change in the class can break the friend.
If you view the friend as an extension of the class, then this is not an issue, logically speaking. But, in that case, why was it necessary to spearate out the friend in the first place.
To achieve the same thing that 'friends' purport to achieve, but without breaking encapsulation, one can do this:
Encapsulation is not broken, class B has no access to the internal implementation in A, yet the result is the same as if we had declared B a friend of A.
The compiler will optimize away the function calls, so this will result in the same instructions as direct access.
I think using 'friend' is simply a shortcut with arguable benefit, but definite cost.
在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。
除了让您无需使用设置功能而访问成员变量之外,它并没有真正的好处。
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.
您 可以遵守最严格,最纯粹的OOP原则,并确保任何班级的数据成员甚至都具有 concectors ,以便所有对象必须是只有能够以唯一的方式对数据了解他们的数据的人是通过间接消息,即方法。
但是,即使C#也具有内部可见性关键字,Java的默认值 package 对某些事物的级别可访问性。 C ++实际上是通过最小化可见性妥协到类中的OOP理想来实现的。进入它。
我实际上并不使用C ++,但是如果C#有 friend s,我会代替汇编全球内部修饰符,我实际上使用了很多。它并没有真正破坏裁员,因为.NET 中的部署单位是一个组件。
但是,还有 internalsvisibleto 属性(其他设备),它的作用像跨组件 friend 机制。 Microsoft将其用于Visual 设计器组件。
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, i.e., 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 friends 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 InternalsVisibleToAttribute(otherAssembly) which acts like a cross-assembly friend mechanism. Microsoft uses this for visual designer assemblies.
当不同的班级(不是另一个阶层)使用另一个班级的私人或受保护的成员时,您可能会使用友谊。
来自 http://www.cplusplus.com/doc/doc/tutorial/tutorial/inheritance/ 。
您可以在此示例中看到非成员方法访问类的私人成员。该方法必须在该班级中为班级的朋友声明。
You may use friendship when different classes (not inheriting one from the other) are using private or protected members of the other class.
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.
可能我错过了上面答案中的一些东西,但是封装中的另一个重要概念是隐藏实施。减少对私人数据成员的访问(类的实现详细信息),可以更轻松地修改代码。如果朋友直接访问私人数据,则任何更改实现数据字段(私有数据)的任何更改,请打破访问该数据的代码。使用访问方法大多消除了这一点。我认为很重要。
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.
这可能不是实际的用例情况,但可能有助于说明课程之间的朋友使用。
俱乐部会所
成员班级的
便利设施
如果您在这里查看这些课程的关系,则 ;俱乐部会所拥有各种不同类型的会员资格和会员资格。成员都源自超级或基类,因为它们都共享一个常见的ID和枚举类型,外部类可以通过在基类中找到的访问功能访问其ID和类型。
但是,通过这种成员及其派生阶级的层次结构及其与Clubhouse阶级的关系,唯一具有“特殊特权”的阶级是Vipmember班级。基类和其他两个派生类无法访问Clubhouse的JoinVipeVent()方法,但是VIP成员类具有该特权,就好像它可以完全访问该活动一样。
因此,借助Vipmember和会所,这是一条两条通道的两条通道,在其他会员类也有限。
This may not be an actual use case situation but may help to illustrate the use of friend between classes.
The ClubHouse
The Members Class's
Amenities
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.