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

发布于 2024-09-15 16:49:05 字数 975 浏览 10 评论 0原文

为什么友谊在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

浪推晚风 2024-09-22 16:49:05

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

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

类的内部表示的任何更改都需要对依赖于该表示的任何内容进行修改。因此,班级的所有成员以及班级的所有朋友都需要修改。

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

因此,如果继承了友谊,您就会无意中引入对修改类的能力的限制。这是不可取的,因为您基本上使公共 API 的概念变得毫无用处。

注意:Bar 的子级可以通过 Bar 访问 Foo,只需将 Bar 中的方法设置为 protected 即可。然后,Bar 的子级可以通过调用其父类来访问 Foo

这是你想要的吗?

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

Because I may write Foo and its friend Bar (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 then Bar must also be modified (because friendship tightly binds Bar to Foo). If friendship was inherited then all class derived from Bar would also be tightly bound to Foo and 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 Foo as doing so would introduce breaking changes into the code base (as I could not modify all class derived from 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.

Note: A child of Bar can access Foo by using Bar, just make the method in Bar protected. Then the child of Bar can access a Foo by calling through its parent class.

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);
        }
};
猫性小仙女 2024-09-22 16:49:05

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

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

这允许比可选传递性更好的控制。例如,get_cash 可能受到保护,或者可能强制执行运行时限制访问的协议。

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_cash may be protected or may enforce a protocol of runtime-limited access.

情未る 2024-09-22 16:49:05

C++ 标准,第 11.4/8 节

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

如果友谊将被继承,那么一个本来不应该成为朋友的类会突然访问您的类内部,这违反了封装。

C++ Standard, section 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.

兲鉂ぱ嘚淚 2024-09-22 16:49:05

因为这只是不必要的。

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

对班级内部结构的任何更改都有可能影响该班级的朋友...您真的想要未知数量的朋友吗?如果继承它们的人也可以是朋友,您甚至无法列出它们,并且您每次都会冒着破坏客户代码的风险,这当然是不可取的。

我坦率地承认,对于家庭作业/宠物项目来说,依赖性通常是一个遥远的考虑因素。对于小型项目来说,这并不重要。但是,一旦几个人在同一个项目上工作,并且该项目增长到数十万行,您就需要限制更改的影响。

这带来了一个非常简单的规则:

更改类的内部应该只影响类本身

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

  • 朋友自由功能:可能更多无论如何,一个成员函数(我认为这里 std::ostream&operator<<(...) ,这不是纯粹出于语言规则友
  • 元类的意外而成为成员?你不'在真实的类上不需要友元类,

我建议使用简单的方法:

class Example;

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

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

这个简单的 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:

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

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

旧竹 2024-09-22 16:49:05

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

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?

拔了角的鹿 2024-09-22 16:49:05

派生类只能继承基类的“成员”。好友声明不是好友类的成员。

$11.4/1- “...朋友的名字是
不在该类的范围内,并且
好友未与该会员通话
访问运算符(5.2.5),除非它是
另一个班级的成员。”

$11.4 - “另外,因为基本子句
友元类的成员不属于它的一部分
成员声明、基子句
好友类的成员无法访问
私有和受保护的名称
班级成员授予
友谊。”

以及进一步

$10.3/7- "[注:虚拟说明符
意味着会员资格,因此虚拟
函数不能是非成员 (7.1.2)
功能。虚函数也不可以
是静态成员,因为虚拟
函数调用依赖于特定的
用于确定哪个函数的对象
来调用。声明了一个虚函数
在一个班级中可以被宣布为朋友
在另一个班级。 ]"

既然‘朋友’本来就不是基类的成员,那它怎么能被派生类继承呢?

A derived class can inherit only something, which is 'member' of the base. A friend declaration is not a 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."

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. ]"

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

寒尘 2024-09-22 16:49:05

类中的友元函数将 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.

无人问我粥可暖 2024-09-22 16:49:05

朋友擅长继承,如容器的样式接口
但对我来说,正如第一个所说,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
    }
};

对我来说,C++ 的问题是缺乏很好的粒度
控制从任何地方对任何事物的所有访问:

朋友事物可以成为朋友事物。* 授予事物的所有子代的访问权限

,并且更多,朋友[命名区域]事物。* 授予精确的访问权限
通过朋友的特殊命名区域位于 Container 类中。

好吧,别做梦了。但现在,你知道了朋友的一个有趣的用法。

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

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

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

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 :

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:

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};
恋竹姑娘 2024-09-22 16:49:05

简单的逻辑:“我有一个朋友简。”仅仅因为我们昨天成为朋友并不意味着她的所有朋友都是我的。

我仍然需要认可这些个人友谊,信任程度也会相应提高。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文