什么时候应该在 C++ 中使用“朋友”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should you use 'friend' in C++?
提问by roo
I have been reading through the C++ FAQand was curious about the friend
declaration. I personally have never used it, however I am interested in exploring the language.
我一直在阅读C++ FAQ并对friend
声明感到好奇。我个人从未使用过它,但我对探索该语言很感兴趣。
What is a good example of using friend
?
什么是使用的好例子friend
?
Reading the FAQ a bit longer I like the idea of the <<
>>
operator overloading and adding as a friend of those classes. However I am not sure how this doesn't break encapsulation. When can these exceptions stay within the strictness that is OOP?
多读一点常见问题解答,我喜欢<<
>>
运算符重载并添加为这些类的朋友的想法。但是我不确定这不会破坏封装。这些异常何时可以保持在 OOP 的严格范围内?
采纳答案by Andrew Grant
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.
首先(IMO)不要听那些说friend
没有用的人。它是有益的。在许多情况下,您将拥有不打算公开提供的数据或功能的对象。对于拥有许多作者的大型代码库尤其如此,他们可能只是表面上熟悉不同领域。
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).
友元说明符有多种替代方法,但它们通常很麻烦(cpp 级别的具体类/掩码类型定义)或不是万无一失的(注释或函数名称约定)。
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.
该friend
说明符可以让朋友语句类中指定的类访问受保护的数据或功能。例如在下面的代码中,任何人都可以向孩子询问他们的名字,但只有母亲和孩子可以更改名字。
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.
您可以通过考虑更复杂的类(例如 Window)来进一步了解这个简单的示例。一个 Window 很可能具有许多不应公开访问的函数/数据元素,但相关类(例如 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 );
};
回答by Daemin
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.
我只想说我不会使用朋友关键字作为您设计的重要组成部分。
回答by Johannes Schaub - litb
The friend
keyword has a number of good uses. Here are the two uses immediately visible to me:
该friend
关键字有许多很好的用途。以下是我可以立即看到的两种用途:
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:
友元定义允许在类范围内定义函数,但该函数不会被定义为成员函数,而是作为封闭命名空间的自由函数,并且除了参数依赖查找之外通常不可见。这使得它对于运算符重载特别有用:
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::f a, b;
std::cout << (a + b).getValue(); // valid
}
Private CRTP Base Class
私有 CRTP 基类
Sometimes, you find the need that a policy needs access to the derived class:
有时,您会发现需要访问派生类的策略:
// 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();
};
You will find a non-contrived example for that in thisanswer. Another code using that is in thisanswer. The CRTP base casts its this pointer, to be able to access data-fields of the derived class using data-member-pointers.
你会在这个答案中找到一个非人为的例子。另一个使用它的代码在这个答案中。CRTP 基转换它的 this 指针,以便能够使用数据成员指针访问派生类的数据字段。
回答by Konrad Rudolph
@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 class foo
.”
@roo:这里没有破坏封装,因为类本身决定了谁可以访问其私有成员。只有当这可能是由班级之外引起的,例如你operator <<
会宣布“我是班级的朋友”时,封装才会被破坏foo
。
friend
replaces use of public
, not use of private
!
friend
代替使用public
,而不是使用private
!
Actually, the C++ FAQ answers this already.
实际上,C++ FAQ已经回答了这个问题。
回答by Mark Harrison
The canonical example is to overload operator<<. Another common use is to allow a helper or admin class access to your internals.
典型的例子是重载 operator<<。另一个常见用途是允许助手或管理类访问您的内部。
Here are a couple of guidelines I heard about C++ friends. The last one is particularly memorable.
以下是我听到的关于 C++ 朋友的一些指导方针。最后一个特别难忘。
- Your friends are not your child's friends.
- Your child's friends are not your friends.
- Only friends can touch your private parts.
- 你的朋友不是你孩子的朋友。
- 你孩子的朋友不是你的朋友。
- 只有朋友才能触摸您的私密部位。
回答by jalf
edit: Reading the faq a bit longer I like the idea of the << >> operator overloading and adding as a friend of those classes, however I am not sure how this doesn't break encapsulation
编辑:阅读 faq 多一点我喜欢 << >> 运算符重载的想法并将其添加为这些类的朋友,但是我不确定这不会破坏封装
How would it break encapsulation?
它如何打破封装?
You break encapsulation when you allow unrestrictedaccess to a data member. Consider the following classes:
当您允许不受限制地访问数据成员时,您就破坏了封装。考虑以下类:
class c1 {
public:
int x;
};
class c2 {
public:
int foo();
private:
int x;
};
class c3 {
friend int foo();
private:
int x;
};
c1
is obviouslynot encapsulated. Anyone can read and modify x
in it. We have no way to enforce any kind of access control.
c1
是明显不封装。任何人都可以x
在其中阅读和修改。我们无法强制执行任何类型的访问控制。
c2
is obviously encapsulated. There is no public access to x
. All you can do is call the foo
function, which performs some meaningful operation on the class.
c2
显然是封装的。没有公共访问权限x
。您所能做的就是调用该foo
函数,该函数对类执行一些有意义的操作。
c3
? Is that less encapsulated? Does it allow unrestricted access to x
? Does it allow unknown functions access?
c3
? 是不是封装的少?它允许不受限制地访问x
吗?是否允许未知功能访问?
No. It allows precisely onefunction to access the private members of the class. Just like c2
did. And just like c2
, the one function which has access is not "some random, unknown function", but "the function listed in the class definition". Just like c2
, we can see, just by looking at the class definitions, a completelist of who has access.
不。它只允许一个函数访问类的私有成员。就像c2
那样。就像c2
,可以访问的一个函数不是“一些随机的、未知的函数”,而是“类定义中列出的函数”。就像 一样c2
,我们可以通过查看类定义来看到谁拥有访问权限的完整列表。
So how exactly is this less encapsulated? The same amount of code has access to the private members of the class. And everyonewho 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.
friend
不会破坏封装。这让一些 Java 人程序员感到不舒服,因为当他们说“OOP”时,他们实际上是指“Java”。当他们说“封装”时,他们并不是说“必须保护私有成员免受任意访问”,而是“一个 Java 类,其中唯一能够访问私有成员的函数是类成员”,尽管这对于几个原因。
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:
二是限制性不够。考虑第四类:
class c4 {
public:
int getx();
void setx(int x);
private:
int x;
};
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)
这,按照上述Java的思路,是完美封装的。 然而,它绝对允许任何人阅读和修改 x。这还有什么意义?(提示:没有)
Bottom line: Encapsulation is about being able to control which functions can access private members. It is notabout precisely where the definitions of these functions are located.
底线:封装是关于能够控制哪些函数可以访问私有成员。这与这些函数的定义的确切位置无关。
回答by maccullt
Another common version of Andrew's example, the dreaded code-couplet
安德鲁例子的另一个常见版本,可怕的代码对联
parent.addChild(child);
child.setParent(parent);
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:
不必担心这两行是否总是一起完成并以一致的顺序完成,您可以将方法设为私有并使用友元函数来强制执行一致性:
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);
}
In other words you can keep the public interfaces smaller and enforce invariants that cut across classes and objects in friend functions.
换句话说,您可以保持公共接口更小,并强制执行跨越友元函数中的类和对象的不变量。
回答by csmba
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...
您使用私有/受保护/公共权限控制成员和功能的访问权限?因此,假设这 3 个级别中的每一个级别的概念都很清楚,那么很明显我们遗漏了一些东西......
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?
例如,将成员/函数声明为 protected 是非常通用的。你是说这个功能对每个人来说都是遥不可及的(当然除了继承的孩子)。但是异常呢?每个安全系统都允许您拥有某种类型的“白名单”,对吗?
So friendlets 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.
因此,friend可以让您灵活地进行坚如磐石的对象隔离,但允许为您认为合理的事情创造“漏洞”。
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.
我想人们会说不需要它,因为总有一种设计可以不用它。我认为这类似于对全局变量的讨论:你永远不应该使用它们,没有它们总有办法……但实际上,你会看到最终成为(几乎)最优雅方式的情况。 ..我想这和朋友的情况是一样的。
It doesn't really do any good, other than let you access a member variable without using a setting function
除了让您在不使用设置函数的情况下访问成员变量之外,它实际上没有任何好处
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 functionhas little to do with it.
好吧,这不完全是看待它的方式。这个想法是控制 WHO 可以访问什么,有没有设置功能与它无关。
回答by VladimirS
I found handy place to use friend access: Unittest of private functions.
我找到了使用朋友访问的好地方:私有函数的单元测试。
回答by rptony
Friend comes handy when you are building a container and you want to implement an iterator for that class.
当您构建容器并且想要为该类实现迭代器时,Friend 会派上用场。