假设有两个A和B类:
class A {};
class B {};
以下两个例子在哪些方面有所不同?
示例1:
class C : public A, public B {};
示例2:
class C
{
//private
friend class A;
friend class B;
}
答案 0 :(得分:22)
friend
可以触及任何朋友的private
部分(只是有点故意!;)),但A
和B
都不属于C
- 只是意味着“A
和B
可以触及C
的私人位”。 private
和A
当然也可以使用比B
“少”的任何内容,因此如果C
有protected
或public
成员,那么也将有空。
当您继承时,A
和B
将成为C
的一部分。 private
和A
的{{1}}个部分无法B
。在“is-a”与“has-a”命名法中,C
现在是-C
并且是-a A
- 换句话说,它是继承自{{1}因此,从界面的角度来看,它“就像B
一样。”
答案 1 :(得分:3)
有几个很大的不同。继承和友谊是截然不同的。
对于友谊,C类是 NOT A类或B类的实例。因此,如果你有这样的函数:
void processMyClass(A* a);
你不能传递它的C实例,而如果C子类A(公开),它 IS 是A的实例。
通过友谊,A类和B类可以触及C的所有私有成员数据和功能。通过继承,C类可以触及A和B的公共成员和受保护成员。
友谊不是继承的。这意味着,例如:
class D : public C
{
private:
void foo() {
// A and B cannot call this function
}
}
答案 2 :(得分:1)
在您正在使用的上下文中,为了尽我所能回答您的问题,朋友们只是允许您的类共享受保护/私有数据,而继承也会这样做,除非会有更深层次的关系,类是同一个类(例如使用强制转换)。
答案 3 :(得分:0)
虽然你得到的答案相当准确,但我认为他们并没有真正完成。特别是,虽然他们解释友谊和继承之间的区别是什么,但他们并没有真正解释你应该在何时使用,或者差异如何影响你的代码。
继承的主要用途(在C ++中)是在基类中定义接口,并在许多派生类的每一个中实现该接口。派生类必须实现的接口部分通常由基类中的纯虚函数表示。
C ++中friend
的主要用途是定义构成接口一部分的东西,但出于语法原因,它不能成为成员函数。一个非常常见的例子是流插入或提取操作符。要将它们作为成员函数实现,它们必须是流类的成员。由于我们不想经常修改流类,因此他们反而将使用引用流的函数作为其左参数释放,并引用它们插入的类型的(可能是const)对象/提取作为他们的右操作数。
这些 必须必须是班级的friend
- 他们可以被编写为只使用班级'公共界面。但是,如果这样做,通常意味着类在其公共接口中暴露的次数多于其他必要的。界面不再是最小的,这往往表明设计有问题。
但需要注意的一点是:你可以在里面定义一个朋友函数一个类定义:
class Foo {
// ...
friend std::ostream &operator<<(std::ostream &os, Foo const &f) {
// ...
}
};
起初这可能看起来很奇怪(从语法上讲,它有点)。即使它在类定义中定义,friend
也意味着不是成员函数。至少在我看来,这相当准确地反映了它的情况:从概念上讲,它是课堂的一部分。它可以访问私有成员,就像该类的任何其他成员一样。它是一个自由函数而不是一个成员函数这一事实纯粹是一个实现工件,与代码的设计基本上无关。
这也指出了friend
船舶和继承之间的另一个区别:通过继承,您通常主要处理成员函数。每个成员函数仍然接收this
指针,因此每个成员函数都直接与该类的特定实例相关联。是的,如果需要,您可以将其定义为也接收(指向或引用)该类的另一个实例,但无论如何它总是接收this
。朋友(函数或类)没有得到这个 - friend
声明只是意味着{{1>}对于其他类隐私的名称是{{1} }。要访问该类的实际实例,通常需要将其作为参数或该订单上的某些内容传递。
最后,我要注意前面的那种忽略了私有或受保护继承的可能性。私有继承通常意味着派生类是根据基类实现的。如果(例如)派生类类似于基类,但在设计中没有相关性,那么这可能很方便 - 即,您没有断言可以使用派生类的实例在任何地方都需要基类的实例。它对基类的使用是一个实现细节,世界其他地方并不需要知道或关心。
受保护的继承几乎是一个错误。这是允许的,因为它与friend
,public
和private
成员(确实有意义)一致,但对于继承,受保护似乎不会出现完成任何有用的事情。