在什么样的场景中,我们会将成员函数声明为“朋友函数”?.. 违背 OOP 的“封装”中心概念之一的“朋友函数”的确切目的是什么?
6 回答
当您尝试构建必须物理跨越多个 C++ 类或函数的抽象或接口时,友元函数和类不会违反封装!这就是朋友被发明的原因。
这些类型的情况并不经常出现,但有时您不得不使用不同的类和函数来实现抽象或接口。经典示例是实现某种类型的复数类。非成员运算符函数与主复数类有友谊。
我还记得在 C++ 中使用 CORBA 编程时这样做。CORBA 迫使我有单独的类来实现 CORBA 仆人。但是对于我们软件的特定部分,我需要将它们结合在一起作为一个界面。Friendship 允许这两个类一起工作,为我们软件的一部分提供无缝服务。
能够将另一个类上的特定成员函数标记为你的类的朋友可能看起来更奇怪,但这只是一种严格控制友谊的方式。您只允许其成员函数之一访问,而不是允许整个其他类作为您的朋友“加入”。同样,这并不常见,但在您需要时非常有用。
有时朋友在语法上更好(例如,在 Fred 类中,朋友函数允许 Fred 参数位于第二位,而成员则要求它位于第一位)。友元函数的另一个很好的用途是二进制中缀算术运算符。例如,如果您还想允许 aFloat + aComplex ,则 aComplex + aComplex 应该定义为朋友而不是成员(成员函数不允许提升左侧参数,因为这会改变对象的类成员函数调用的接收者)。
有时公共/私人/受保护的保护级别对于现实世界的情况来说是不够的。因此,我们给出了一个小的退出子句,该子句无需公开可访问的方法即可提供帮助。
我个人使用它的方式与 Java 使用“包”保护级别的方式相同。
如果我在同一个包中有一个需要访问的类,我会考虑使用朋友。如果它是另一个包中的一个类,那么我会想知道为什么这个其他类需要访问并查看我的设计。
我发现相关的一点:成员类可以访问包含类的私有部分。有时这可能是“朋友”的更好选择。
class A
{
private:
int b;
public:
class MemberNotFriend {
public:
static void test() {
A a;
a.b = 0;
}
};
};
void test()
{
A::MemberNotFriend::test();
}
这是我如何使用朋友功能的一个简单而具体的示例:
我有一个游戏,其中每个精灵对象都将其信息(例如 X、Y 位置)存储为私有成员。但是,我希望将游戏对象与渲染分开:游戏对象不需要其渲染方式的确切细节。一个游戏对象只存储游戏状态,并且这个游戏状态可以以多种不同的方式呈现。
因此游戏对象类有一个友元函数:render()。render() 函数在游戏对象类之外实现,但它可以根据需要访问 X、Y 位置位置成员以渲染游戏对象。