为什么 C++ 不允许继承友谊?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3561648/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:08:32  来源:igfitidea点击:

Why does C++ not allow inherited friendship?

c++inheritancelanguage-designfriend

提问by Jeff

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?

为什么在 C++ 中,友谊至少不能选择性地继承?我理解出于显而易见的原因禁止传递性和自反性(我说这只是为了阻止简单的常见问题解答引用答案),但缺乏类似的东西virtual friend class Foo;让我感到困惑。有谁知道这个决定背后的历史背景?友谊真的只是一个有限的黑客,后来进入了一些不起眼的可敬用途吗?

Edit for clarification:I'm talking about the following scenario, notwhere 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.

编辑澄清:我在谈论以下场景,而不是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. */ };

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 needfor 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 ...

接受的答案:正如Loki 所说的,可以通过在友元基类中创建受保护的代理函数来或多或少地模拟效果,因此没有严格需要为类或虚拟方法层次结构授予友元。我不喜欢样板代理的需要(友好的基础实际上变成了),但我认为这被认为比大多数时间更有可能被滥用的语言机制更可取。我想我可能是时候购买并阅读 Stroupstrup 的The Design and Evolution of C++ 了,我在这里看到了足够多的人推荐,以便更好地了解这些类型的问题......

采纳答案by Martin York

Because I may write Fooand its friend Bar(thus there is a trust relationship).

因为我可能会写Foo和它的朋友Bar(因此有一种信任关系)。

But do I trust the people who write classes that are derived from Bar?
Not really. So they should not inherit friendship.

但是我相信那些编写派生自 的类的人Bar吗?
并不真地。所以他们不应该继承友谊。

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 Foois modified then Barmust also be modified (because friendship tightly binds Barto Foo). If friendship was inherited then all class derived from Barwould also be tightly bound to Fooand thus require modification if Foo'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 change Fooas doing so would introduce breaking changes into the code base (as I could not modify all class derived from Bar).

因此,如果的内部表示Foo被修改然后Bar还必须被修改(因为友谊紧密结合BarFoo)。如果友爱被继承,那么所有派生自的类Bar也将被紧密绑定Foo,因此如果Foo的内部表示发生变化,则需要修改。但我不了解派生类型(我也不应该。它们甚至可能由不同的公司等开发)。因此,我将无法更改,Foo因为这样做会在代码库中引入重大更改(因为我无法修改从 派生的所有类Bar)。

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.

因此,如果友谊是继承而来的,那么您会在不经意间引入对修改类的能力的限制。这是不可取的,因为您基本上使公共 API 的概念变得无用。

Note: A child of Barcan access Fooby using Bar, just make the method in Barprotected. Then the child of Barcan access a Fooby calling through its parent class.

注意: 的孩子Bar可以Foo通过 using访问Bar,只需将方法置于Barprotected 中。然后子类Bar可以Foo通过其父类调用来访问 a 。

Is this what you want?

这是你想要的吗?

class A
{
    int x;
    friend class B;
};

class B
{
    protected:
       // Now children of B can access foo
       void foo(A& a, int n) { a.x = n; }
};

class D : public B
{
    public:
        foo(A& a, int n)
        {
            B::foo(a, n + 5);
        }
};

回答by wilx

Why is friendship not at least optionally inheritable in C++?

为什么在 C++ 中,友谊至少不能选择性地继承?

I think that the answer to your first question is in this question: "Do your father's friends have access to your privates?"

我想你的第一个问题的答案就在这个问题上:“你父亲的朋友可以接触你的隐私吗?”

回答by Potatoswatter

A friended class may expose its friend through accessor functions, and then grant access through those.

友元类可以通过访问器函数公开它的友元,然后通过这些函数授予访问权限。

class stingy {
    int pennies;
    friend class hot_girl;
};

class hot_girl {
public:
    stingy *bf;

    int &get_cash( stingy &x = *bf ) { return x.pennies; }
};

class moocher {
public: // moocher can access stingy's pennies despite not being a friend
    int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

This allows finer control than optional transitivity. For example, get_cashmay be protectedor may enforce a protocol of runtime-limited access.

这允许比可选传递性更精细的控制。例如,get_cash可以是protected或可以强制执行运行时限制访问的协议。

回答by David

C++ Standard, section 11.4/8

C++ 标准,第 11.4/8 节

Friendship is neither inherited nor transitive.

友谊既不是继承的,也不是传递的。

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.

如果友爱会被继承,那么原本不是朋友的类将突然可以访问您的类内部,这违反了封装。

回答by Matthieu M.

Because it's just unnecessary.

因为这只是不必要的。

The usage of the friendkeyword is itself suspicious. In term of coupling it's the worst relationship (way ahead of inheritance and composition).

friend关键字的使用本身就是可疑的。就耦合而言,它是最糟糕的关系(远远领先于继承和组合)。

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:

当然,你可能会影响它的朋友,但这里有两种情况:

  • friend free function: probably more of a member function anyway (I am think std::ostream& operator<<(...)here, which is not a member purely by accident of the language rules
  • friend class ? you don't need friend classes on real classes.
  • 朋友自由函数:无论如何可能更多的是成员函数(我在std::ostream& operator<<(...)这里认为,这不是纯粹由于语言规则而导致的成员)
  • 朋友班?你不需要在真正的班级上交友班。

I would recommend the use of the simple method:

我建议使用简单的方法:

class Example;

class ExampleKey { friend class Example; ExampleKey(); };

class Restricted
{
public:
  void forExampleOnly(int,int,ExampleKey const&);
};

This simple Keypattern 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.

这个简单的Key模式允许你声明一个友元(以某种方式),而实际上并不允许它访问你的内部,从而将它与更改隔离开来。此外,如果需要,它允许该朋友将其密钥借给受托人(如儿童)。

回答by Chubsdad

A derived class can inherit only something, which is 'member' of the base. A friend declaration is nota member of the befriending class.

派生类只能继承一些东西,它是基类的“成员”。朋友声明不是交友类的成员。

$11.4/1- "...The name of a friend is not in the scope of the class, and the friend is not called with the member access operators (5.2.5) unless it is a member of another class."

$11.4 - "Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship."

$11.4/1- “...朋友的名字不在类的范围内,除非它是另一个类的成员,否则不会使用成员访问运算符(5.2.5)调用朋友。”

$11.4 - “另外,因为朋友类的基子句不是其成员声明的一部分,朋友类的基子句不能访问授予友谊的类的私有成员和受保护成员的名称。”

and further

并进一步

$10.3/7- "[Note: the virtual specifier implies membership, so a virtual function cannot be a nonmember (7.1.2) function. Nor can a virtual function be a static member, since a virtual function call relies on a specific object for determining which function to invoke. A virtual function declared in one class can be declared a friend in another class. ]"

$10.3/7- "[注意:虚拟说明符暗示成员资格,因此虚拟函数不能是非成员 (7.1.2) 函数。虚拟函数也不能是静态成员,因为虚拟函数调用依赖于特定对象确定要调用哪个函数。在一个类中声明的虚函数可以在另一个类中声明为友元。]”

Since the 'friend' is not a member of the base class in the first place, how can it be inherited by the derived class?

既然“朋友”一开始就不是基类的成员,那么它如何被派生类继承呢?

回答by Oliver Charlesworth

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?

猜测:如果一个类将某个其他类/函数声明为友元,那是因为第二个实体需要对第一个实体的特权访问。授予第二个实体对从第一个派生的任意数量的类的特权访问有什么用?

回答by abdul rizwan

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.

类中的 Friend 函数将 extern 属性分配给该函数。即 extern 表示该函数已在类之外的某处声明和定义。

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.

因此这意味着友元函数不是类的成员。所以继承只允许你继承一个类的属性,而不是外部的东西。并且如果友元函数允许继承,则第三方类继承。

回答by abdul rizwan

Friend is good in inheritance like style interface for container But for me, as the first say, C++ lack the propagatable inheritance

Friend 擅长继承,就像容器的样式接口一样 但是对我来说,正如第一个所说的,C++ 缺乏可传播的继承

class Thing;

//an interface for Thing container's
struct IThing {
   friend Thing;
   protected:
       int IThing_getData() = 0;
};

//container for thing's
struct MyContainer : public IThing {
    protected: //here is reserved access to Thing
         int IThing_getData() override {...}
};

struct Thing {
    void setYourContainer(IThing* aContainerOfThings) {
        //access to unique function in protected area 
        aContainerOfThings->IThing_getData(); //authorized access
    }
};

struct ChildThing : public Thing {
    void doTest() {
        //here the lack of granularity, you cannot access to the container.
        //to use the container, you must implement all 
        //function in the Thing class
        aContainerOfThings->IThing_getData(); //forbidden access
    }
};

For me the problem of C++ is the lack of very good granularity to control all access from anywhere for anything :

对我来说,C++ 的问题是缺乏非常好的粒度来控制从任何地方对任何东西的所有访问:

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.

此外,朋友 [命名区域] Thing.* 授予精确访问权限,通过朋友的特殊命名区域在 Container 类中。

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:

在另一个顺序中,您还可以发现有趣的是,已知所有类都对自己友好。换句话说,一个类实例可以
不受限制地调用另一个同名实例的所有成员:

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};

回答by Robert Hamm

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.

我仍然需要批准那些个人友谊,信任程度也会相应。