2010-08-24 73 views
73

为什么友谊至少不能在C++中继承?我明白传递性和反思性被禁止,原因很明显(我只是说这只是为了阻止简单的常见问题引用答案),但是沿着virtual friend class Foo;的路线缺乏一些东西使我困惑。有人知道这个决定背后的历史背景吗?友谊真的只是一个有限的黑客,因为它已经找到了一些晦涩的可敬的用途?为什么C++不允许继承友谊?

编辑澄清:我说的是以下情形,其中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基类进行保护的代理功能,所以有更多或更少没有严格的需要授予友谊的类或虚拟方法heirarchy。我不喜欢使用样板代理(好友基地有效地成为代理),但是我认为这比在大多数情况下更可能被误用的语言机制更可取。我认为这可能是我购买和阅读Stroupstrup的The Design and Evolution of C++,我已经看到足够的人在这里推荐,以获得更好的洞察这些类型的问题的时间...

回答

69

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

但是,我相信编写从Bar派生的类的人吗?
不是。所以他们不应该继承友谊。

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

因此,如果的Foo内部表示被修改然后Bar也必须改变(因为友谊紧密结合BarFoo)。如果继承友谊,那么从Bar派生的所有类别也将与Foo严格绑定,因此如果Foo的内部表示形式发生更改,则需要修改。但我不知道派生类型(我也不应该,他们甚至可能由不同的公司开发)。因此,我将无法更改Foo,因为这样做会在代码库中引入突变(因为我无法修改从Bar派生的所有类)。

因此,如果友谊被遗传了,你无意中引入了对修改类的能力的限制。这是不可取的,因为你基本上渲染了公共API的概念。

注意:Bar的孩子可以通过使用Bar来访问Foo,只是使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

Bingo。这是关于通过改变一个班级内部造成的伤害限制。 – 2010-08-25 02:12:31

+0

@Jeff:http://www.drdobbs.com/184402053 – 2010-08-25 03:05:26

0

猜测:如果一个类声明某个其他类/函数作为朋友,这是因为第二个实体需要特权访问第一个实体。在授予第二个实体有什么用处,从第一个实体获得任意数量的类的特权?

+2

如果A级通缉授予友谊B和它的后代,它可以避免更新其接口为每子类增加或迫使B写传递样板,这是我认为的第一位友谊的一半。 – Jeff 2010-08-24 23:05:29

+0

@杰夫:啊,那我误解了你的意思。我认为你的意思是'B'可以访问从'A'继承的所有类... – 2010-08-24 23:08:13

6

C++标准,第11.4节/ 8

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

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

+1

说Q“朋友”A,而B是从A派生出来的。如果B继承了A的友谊,那么因为B是A的一种类型,在技术上它是一个可以访问Q的私人的A。所以这不会以任何实际的理由来回答这个问题。 – mdenton8 2015-06-25 20:02:46

0

派生类只能继承基类的“成员”。朋友的声明是而不是是一个友谊班的成员。

$ 11.4/1” ......一个朋友的名字是 不能在类的范围和 朋友不与成员 接入运营商(5.2.5)呼吁,除非它是 是另一班的成员。“

$ 11.4 - “此外,由于友元类的基本条款 不是它 成员声明的一部分,友元类的基本条款 不能访问私有的 名称和保护 成员友谊等级授予 。“

和进一步

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

因为‘朋友’不是在首位的基类的一个成员,怎么能由派生类继承的?

+0

尽管通过类似成员的声明给予友谊,但其实并不是真正的成员,其他类别的通知实质上可以忽略对“真实”成员的可见性分类。虽然您引用的规范部分解释了语言如何运用关于这些细微差别和框架行为的自洽术语,但事情本来可能会有所不同,并且不幸的是,上述任何内容都不会成为理论基础的核心。 – Jeff 2010-08-25 03:07:50

7

甲friended类可以通过访问函数暴露其朋友,然后通过这些授予访问权限。

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或可以执行的运行时间限制访问的协议。

+4

我误以为阴茎便士... – Hector 2016-11-04 09:42:00

+0

@Hector:投票重构! ';)' – 2016-12-15 09:03:26

2

因为这只是没有必要的。

关键字friend的用法本身就是可疑的。就耦合而言,这是最糟糕的关系(继承和组合之前的方式)。

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

我自由地承认,对于家庭作业/宠物项目而言,依赖往往是一个很远的考虑因素。在小型项目上并不重要。但是,只要有几个人在同一个项目上工作,这种情况就会增长到几十条线路中,您需要限制更改的影响。

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

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

当然,你可能会影响它的朋友,但有2箱子这里:

  • 朋友免费功能:可能更多的成员函数的反正(我想在这里std::ostream& operator<<(...),这是不是一个成员纯粹的语言规则事故
  • 朋友课?你不需要真正的课堂上的朋友课程。

我会建议使用简单的方法:

class Example; 

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

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

这个简单的Key模式允许你声明的朋友(的方式),而实际上是给它访问你的内部,从而孤立它从变化。此外,如果需要,它可以让这位朋友把钥匙借给受托人(如儿童)。

29

为什么友谊至少不能在C++中继承?

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

+16

公平地说,这个问题引起了你父亲的不安。 。 。 – iheanyi 2015-08-07 16:14:03

+1

@iheanyi:这是关键。 \ o/ – wilx 2015-08-07 16:15:14

+1

这个答案是什么意思?充其量只是一个可疑的,尽管可能轻松的评论 – DeveloperChris 2016-04-12 02:15:13

0

在一个类中的朋友函数将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++的问题是缺乏很好的粒度的 控制从任何地方访问所有的任何东西:

朋友东西可以成为朋友的事*授予事情

多,朋友[评为区]事情的所有子访问。*授予准确访问权限 位于Container类中,通过该朋友的特定命名区域。

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

在另一个命令中,你也可以发现有趣的知道所有的类都与自己友好。换句话说,一个类的实例可以调用另一个同名实例的所有
成员不受限制:

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