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

问题描述 投票:85回答:10

为什么友谊至少可以在C ++中继承?由于显而易见的原因,我认为传递性和反身性是被禁止的(我说这只是为了避开简单的常见问题解答引用答案),但缺乏像virtual friend class 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 states,可以通过在friended基类中创建受保护的代理函数来或多或少地模拟效果,因此不需要向类或虚方法层次结构授予友谊。我不喜欢需要样板代理(有条件的基础有效地成为),但我认为这被认为比大多数时候更容易被滥用的语言机制更可取。我想这可能是我买了并阅读Stroupstrup的The Design and Evolution of C++的时间,我已经看到这里有足够多的人推荐,以便更好地了解这些类型的问题......

c++ inheritance language-design friend
10个回答
81
投票

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

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

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

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

因此,如果继承了友谊,则无意中会对修改类的能力施加限制。这是不可取的,因为您基本上无法使用公共API的概念。

注意:Bar的孩子可以使用Foo访问Bar,只需使Bar中的方法受到保护。然后,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);
        }
};

0
投票

简单的逻辑:'我有一个朋友简。仅仅因为我们昨天成为朋友并没有让她的所有朋友成为我的朋友。

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


43
投票

为什么友谊至少可以在C ++中继承?

我认为你的第一个问题的答案就是这个问题:“你父亲的朋友可以接触你的私人吗?”


9
投票

一个朋友的类可以通过访问者功能暴露其朋友,然后通过这些功能授予访问权限。

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可能是protected或可能强制执行运行时限制访问的协议。


7
投票

C ++标准,第11.4 / 8节

友谊既不是遗传也不是传递。

如果友谊会被继承,那么一个不打算成为朋友的类会突然有权访问你的类内部并且违反了封装。


2
投票

因为这是不必要的。

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

对课程内部的任何改变都有可能影响这个班级的朋友......你真的想要一个未知数量的朋友吗?如果从他们那里继承的人也可能成为朋友,你甚至无法列出他们,并且每次都会冒着破坏客户代码的风险,当然这是不可取的。

我坦率地承认,对于家庭作业/宠物项目的依赖性往往是一个遥远的考虑因素。在小型项目上,这无关紧要。但是,只要有几个人在同一个项目上工作,并且这个项目就会成长为数十万行,您需要限制变更的影响。

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

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

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

  • 朋友自由功能:无论如何可能更多的成员功能(我认为std::ostream& operator<<(...)在这里,这不是一个纯粹意外的语言规则的成员
  • 朋友班?你不需要真正的课堂上的朋友课程。

我建议使用简单的方法:

class Example;

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

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

这个简单的Key模式允许您声明一个朋友(在某种程度上),而不实际让它访问您的内部,从而将其与更改隔离。此外,如果需要,它允许这位朋友将其钥匙借给受托人(如儿童)。


0
投票

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


0
投票

派生类只能继承某些东西,它是基础的“成员”。朋友声明不是友谊阶级的成员。

$ 11.4 / 1-“......朋友的名字不在班级的范围内,并且不会使用成员访问操作员(5.2.5)调用朋友,除非它是另一个班级的成员。”

$ 11.4 - “另外,因为朋友类的base-clause不是其成员声明的一部分,所以friend类的base-clause不能从授予友谊的类中访问私有成员和受保护成员的名称。”

并进一步

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

由于'friend'首先不是基类的成员,它如何被派生类继承?


0
投票

类中的Friend函数将extern属性赋给函数。即extern意味着该函数已在类外的某个地方声明和定义。

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


0
投票

朋友在继承方面很好,比如容器的样式接口但对我来说,首先说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 ++的问题是缺乏非常好的粒度来控制来自任何地方的所有访问:

朋友Thing可以成为朋友Thing。*授予访问所有孩子的东西

而且,朋友[命名区域] Thing。*通过特殊命名区域为朋友授予Container类的精确访问权限。

好吧,停止梦想。但现在,你知道朋友的有趣用法。

在另一个顺序,你也可以发现有趣的知道所有类都是友好的自我。换句话说,类实例可以调用all 另一个同名实例的成员没有限制:

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};
© www.soinside.com 2019 - 2024. All rights reserved.