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

时间:2010-08-24 22:55:01

标签: c++ inheritance language-design friend

为什么友谊至少可以在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,通过在受限制的基类中设置受保护的代理函数,可以或多或少地模拟效果,因此没有严格的需要用于向类或虚拟方法heirarchy授予友谊。我不喜欢需要样板代理(有条件的基础有效地成为),但我认为这被认为比大多数时候更容易被滥用的语言机制更可取。我想这可能是我买了并阅读Stroupstrup The Design and Evolution of C++的时候了,我在这里看到了足够多的人推荐,以便更好地了解这些类型的问题......

10 个答案:

答案 0 :(得分:80)

因为我可以写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);
        }
};

答案 1 :(得分:42)

  

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

我认为你的第一个问题的答案就在于这个问题:"你父亲的朋友可以访问你的私人吗?"

答案 2 :(得分: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,也可以强制执行运行时限制访问协议。

答案 3 :(得分:7)

C ++标准,第11.4 / 8节

  

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

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

答案 4 :(得分:2)

因为这是不必要的。

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

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

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

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

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

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

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

我建议使用简单的方法:

class Example;

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

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

  

$ 11.4 / 1-“......朋友的名字是   不在班级范围内,而且   朋友没有跟会员打电话   访问运营商(5.2.5),除非它   另一个班级的成员。“

     

$ 11.4 - “另外,因为基本条款   朋友班不是其中的一部分   成员声明,​​base-clause   朋友类无法访问   私人和受保护的名称   来自班级的成员授予   友谊“。

并进一步

  

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

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

答案 7 :(得分:0)

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

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

答案 8 :(得分: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类中。

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

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

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

答案 9 :(得分:0)

简单的逻辑:“我有一个朋友简。仅仅因为我们昨天成为朋友,并不能使她的所有朋友都成为我的朋友。”

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