为什么 C++不允许继承友谊?
为什么友谊在 C++ 中至少不能选择性地继承?我理解传递性和自反性由于明显的原因而被禁止(我这么说只是为了阻止简单的常见问题解答引用答案),但缺乏类似于虚拟朋友类 Foo; 的东西让我困惑。有谁知道这个决定背后的历史背景吗?友谊真的只是一种有限的黑客手段,后来却被用于一些不起眼的、受人尊敬的用途吗?
编辑澄清:我谈论的是以下场景,不是 A 的子级暴露于 B 或 B 及其子级。我还可以想象有选择地授予对友元函数覆盖的访问权限等。
class A {
int x;
friend class B;
};
class B {
// OK as per friend declaration above.
void foo(A& a, int n) { a.x = n; }
};
class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };
接受的答案:为Loki 指出,通过在友元基类中创建受保护的代理函数或多或少可以模拟效果,因此没有严格的需要用于授予类或虚拟方法层次结构友谊。我不喜欢对样板代理的需求(好友库实际上变成了样板代理),但我认为这比大多数时候更可能被滥用的语言机制更可取。我想我可能是时候购买并阅读 Stroupstrup 的 C++ 的设计和演化< /strong>,我已经看到足够多的人在这里推荐,以更好地洞察这些类型的问题......
Why is friendship not at least optionally inheritable in C++? I understand transitivity and reflexivity being forbidden for obvious reasons (I say this only to head off simple FAQ quote answers), but the lack of something along the lines of virtual friend class Foo;
puzzles me. Does anyone know the historical background behind this decision? Was friendship really just a limited hack that has since found its way into a few obscure respectable uses?
Edit for clarification: I'm talking about the following scenario, not where children of A are exposed to either B or to both B and its children. I can also imagine optionally granting access to overrides of friend functions, etc.
class A {
int x;
friend class B;
};
class B {
// OK as per friend declaration above.
void foo(A& a, int n) { a.x = n; }
};
class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };
Accepted answer: as Loki states, the effect can be simulated more or less by making protected proxy functions in friended base classes, so there is no strict need for granting friendship to a class or virtual method heirarchy. I dislike the need for boilerplate proxies (which the friended base effectively becomes), but I suppose that this was deemed preferable over a language mechanism that would more likely be misused most of the time. I think it's probably time I bought and read Stroupstrup's The Design and Evolution of C++, which I've seen enough people here recommend, to get better insight to these types of questions ...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
因为我可能会写
Foo
和它的朋友Bar
(因此存在信任关系)。但是我信任那些编写从
Bar
派生的类的人吗?并不真地。所以他们不应该继承友谊。
类的内部表示的任何更改都需要对依赖于该表示的任何内容进行修改。因此,班级的所有成员以及班级的所有朋友都需要修改。
因此,如果
Foo
的内部表示被修改,那么Bar
也必须被修改(因为友谊将Bar
与Foo
紧密绑定) >)。如果继承了 Friendship,那么从Bar
派生的所有类也将紧密绑定到Foo
,因此如果Foo
的内部表示发生更改,则需要进行修改。但我不了解派生类型(我也不应该。它们甚至可能是由不同的公司等开发的)。因此,我将无法更改Foo
,因为这样做会向代码库引入重大更改(因为我无法修改从Bar
派生的所有类)。因此,如果继承了友谊,您就会无意中引入对修改类的能力的限制。这是不可取的,因为您基本上使公共 API 的概念变得毫无用处。
注意:
Bar
的子级可以通过Bar
访问Foo
,只需将Bar
中的方法设置为 protected 即可。然后,Bar
的子级可以通过调用其父类来访问Foo
。这是你想要的吗?
Because I may write
Foo
and its friendBar
(thus there is a trust relationship).But do I trust the people who write classes that are derived from
Bar
?Not really. So they should not inherit friendship.
Any change in the internal representation of a class will require a modification to anything that is dependent on that representation. Thus all members of a class and also all friends of the class will require modification.
Therefore if the internal representation of
Foo
is modified thenBar
must also be modified (because friendship tightly bindsBar
toFoo
). If friendship was inherited then all class derived fromBar
would also be tightly bound toFoo
and thus require modification ifFoo
's internal representation is changed. But I have no knowledge of derived types (nor should I. They may even be developed by different companies etc). Thus I would be unable to changeFoo
as doing so would introduce breaking changes into the code base (as I could not modify all class derived fromBar
).Thus if friendship was inherited you are inadvertently introducing a restriction on the ability to modify a class. This is undesirable as you basically render useless the concept of a public API.
Note: A child of
Bar
can accessFoo
by usingBar
, just make the method inBar
protected. Then the child ofBar
can access aFoo
by calling through its parent class.Is this what you want?
友元类可以通过访问器函数公开其友元,然后通过这些函数授予访问权限。
这允许比可选传递性更好的控制。例如,
get_cash
可能受到保护
,或者可能强制执行运行时限制访问的协议。A friended class may expose its friend through accessor functions, and then grant access through those.
This allows finer control than optional transitivity. For example,
get_cash
may beprotected
or may enforce a protocol of runtime-limited access.C++ 标准,第 11.4/8 节
如果友谊将被继承,那么一个本来不应该成为朋友的类会突然访问您的类内部,这违反了封装。
C++ Standard, section 11.4/8
If friendship would be inherited, then a class that wasn't meant to be a friend would suddenly have access to your class internals and that violates encapsulation.
因为这只是不必要的。
friend
关键字的使用本身就很可疑。就耦合而言,它是最糟糕的关系(远远领先于继承和组合)。对班级内部结构的任何更改都有可能影响该班级的朋友...您真的想要未知数量的朋友吗?如果继承它们的人也可以是朋友,您甚至无法列出它们,并且您每次都会冒着破坏客户代码的风险,这当然是不可取的。
我坦率地承认,对于家庭作业/宠物项目来说,依赖性通常是一个遥远的考虑因素。对于小型项目来说,这并不重要。但是,一旦几个人在同一个项目上工作,并且该项目增长到数十万行,您就需要限制更改的影响。
这带来了一个非常简单的规则:
更改类的内部应该只影响类本身
当然,你可能会影响它的朋友,但这里有两种情况:
std::ostream&operator<<(...)
,这不是纯粹出于语言规则友我建议使用简单的方法:
这个简单的 Key 模式允许您(以某种方式)声明一个友元,而无需实际授予它访问您的内部的权限,因此此外,如果需要,它允许该朋友将其密钥借给受托人(例如孩子)。
Because it's just unnecessary.
The usage of the
friend
keyword is itself suspicious. In term of coupling it's the worst relationship (way ahead of inheritance and composition).Any change to the internals of a class have a risk to impact the friends of this class... do you really want an unknown number of friends ? You would not even be able to list them if those who inherit from them could be friends also, and you would run in the risk of breaking your clients code each time, surely this is not desirable.
I freely admit that for homework/pet projects dependency is often a far away consideration. On small size projects it doesn't matter. But as soon as several persons work on the same project and this grows into the dozens of thousands of lines you need to limit the impact of changes.
This bring a very simple rule:
Changing the internals of a class should only affect the class itself
Of course, you'll probably affect its friends, but there are two cases here:
std::ostream& operator<<(...)
here, which is not a member purely by accident of the language rulesI would recommend the use of the simple method:
This simple
Key
pattern allows you to declare a friend (in a way) without actually giving it access to your internals, thus isolating it from changes. Furthermore it allows this friend to lend its key to trustees (like children) if required.猜测:如果一个类将其他某个类/函数声明为友元,那是因为第二个实体需要对第一个实体进行特权访问。授予第二个实体对从第一个实体派生的任意数量的类的特权访问有什么用?
A guess: If a class declares some other class/function as a friend, it's because that second entity needs privileged access to the first. What use is there in granting the second entity privileged access to an arbitrary number of classes derived from the first?
派生类只能继承基类的“成员”。好友声明不是好友类的成员。
以及进一步
既然‘朋友’本来就不是基类的成员,那它怎么能被派生类继承呢?
A derived class can inherit only something, which is 'member' of the base. A friend declaration is not a member of the befriending class.
and further
Since the 'friend' is not a member of the base class in the first place, how can it be inherited by the derived class?
类中的友元函数将 extern 属性分配给该函数。即 extern 意味着该函数已在类外的某个位置声明和定义。
因此,这意味着友元函数不是类的成员。所以继承只允许你继承类的属性而不是外部的东西。而且,如果友元函数允许继承,则第三方类可以继承。
Friend function in a class assigns the extern property to the function. i.e. extern means that the function has been declared and defined somewhere out of the class.
Hence it means friend function is not a member of a class. So the inheritance only allows you to inherit the properties of a class not external things. And also if inheritance is allowed for friend functions, then a third party class inheriting.
朋友擅长继承,如容器的样式接口
但对我来说,正如第一个所说,C++ 缺乏可传播的继承
对我来说,C++ 的问题是缺乏很好的粒度
控制从任何地方对任何事物的所有访问:
朋友事物可以成为朋友事物。* 授予事物的所有子代的访问权限
,并且更多,朋友[命名区域]事物。* 授予精确的访问权限
通过朋友的特殊命名区域位于 Container 类中。
好吧,别做梦了。但现在,你知道了朋友的一个有趣的用法。
在另一个顺序中,你还可以发现有趣的是知道所有班级都对自己友好。换句话说,一个类实例可以调用所有
另一个同名实例的成员,不受限制:
Friend is good in inheritance like style interface for container
But for me, as the first say, C++ lack the propagatable inheritance
For me the problem of C++ is the lack of very good granularity to
control all access from anywhere for anything :
friend Thing can become friend Thing.* to grant access to all child of Thing
And more, friend [named area] Thing.* to grant access for a precise
are in the Container class via special named area for the friend.
Ok stop the dream. But now, you know an interesting usage of friend.
In another order, you can also found interesting to known all class are friendly with self. In other word, a class instance can call all
members of another instance of same name without restriction:
简单的逻辑:“我有一个朋友简。”仅仅因为我们昨天成为朋友并不意味着她的所有朋友都是我的。
我仍然需要认可这些个人友谊,信任程度也会相应提高。
Simple logic : 'I have a friend Jane. Just because we became friends yesterday does not make all of her friends mine.'
I still need to approve those individual friendships, and the level of trust would be accordingly.