我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
我一直在阅读c++常见问题,对朋友声明很好奇。我个人从未使用过它,但我对探索这门语言很感兴趣。
使用friend的一个好例子是什么?
读了一些常见问题,我喜欢<< >>操作符重载和添加作为这些类的朋友的想法。然而,我不确定这如何不会破坏封装。什么时候这些异常可以保持在OOP的严格范围内?
当前回答
您使用私有/受保护/公共权限控制成员和函数的访问权限? 所以假设这3个层面中的每一个都很清楚,那么很明显我们遗漏了一些东西……
例如,将成员/函数声明为protected是非常通用的。你的意思是,这个功能对每个人来说都是遥不可及的(当然,继承的孩子除外)。但是异常呢?每个安全系统都让你有某种类型的“白名单”,对吗?
所以,“朋友”让你拥有坚如磐石的对象隔离的灵活性,但也允许为你认为合理的事情创造一个“漏洞”。
我想人们之所以说不需要它,是因为总有一种设计可以不需要它。我认为这类似于讨论全局变量:你永远不应该使用它们,总有办法不用它们……但在现实中,你会看到在某些情况下,这是(几乎)最优雅的方式……我认为朋友之间也是如此。
它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量
但这并不是正确的看法。 其理念是控制WHO可以访问什么,是否有设置功能与此关系不大。
其他回答
您可以坚持最严格和最纯粹的OOP原则,并确保任何类的数据成员都没有访问器,这样所有对象都必须是唯一可以知道它们的数据的对象,并且对它们进行操作的唯一方法是通过间接消息(即方法)。
但即使是c#也有一个内部可见性关键字,Java也有默认的包级可访问性。c++实际上更接近于OOP的理想,它通过精确地指定哪些其他类或只有其他类可以看到一个类,从而最大限度地减少了类的可见性。
我不太使用c++,但如果c#有朋友,我会用它来代替我经常使用的汇编全局内部修饰符。它并没有真正打破封装,因为。net中的部署单元是一个程序集。
但是还有InternalsVisibleToAttribute(otherAssembly),它的作用类似于跨组装的友元机制。微软将此用于可视化设计器程序集。
在为类实现树算法时,教授给我们的框架代码将树类作为节点类的朋友。
它实际上没有任何好处,除了让你在不使用设置函数的情况下访问成员变量。
好友对于回调也很有用。可以将回调函数作为静态方法来实现
class MyFoo
{
private:
static void callback(void * data, void * clientData);
void localCallback();
...
};
回调在内部调用localCallback, clientData中有你的实例。在我看来,
还是……
class MyFoo
{
friend void callback(void * data, void * callData);
void localCallback();
}
这允许友元在cpp中被定义为c风格的函数,而不会使类变得混乱。
类似地,我经常看到的一种模式是将一个类的所有真正的私有成员放到另一个类中,该类在头文件中声明,在cpp中定义,并加为好友。这允许编码器向头文件的用户隐藏类的很多复杂性和内部工作。
在头文件中:
class MyFooPrivate;
class MyFoo
{
friend class MyFooPrivate;
public:
MyFoo();
// Public stuff
private:
MyFooPrivate _private;
// Other private members as needed
};
在cpp中,
class MyFooPrivate
{
public:
MyFoo *owner;
// Your complexity here
};
MyFoo::MyFoo()
{
this->_private->owner = this;
}
这样就更容易隐藏下游不需要看到的东西。
简短的回答是:在friend确实可以改善封装时使用它。提高可读性和可用性(操作符<<和>>是典型的例子)也是一个很好的理由。
至于改进封装的例子,专门设计用于处理其他类的内部结构的类(想到了测试类)是很好的候选。
首先(依我看)不要听那些说朋友没用的人。它是有用的。在许多情况下,您将拥有具有不打算公开可用的数据或功能的对象。对于许多作者可能只是表面上熟悉不同领域的大型代码库尤其如此。
友元说明符也有替代方案,但通常都很麻烦(cppp级别的具体类/掩码类型定义),或者不是万无一错(注释或函数名约定)。
在答案上;
友元说明符允许指定类访问发出友元语句的类内受保护的数据或功能。例如,在下面的代码中,任何人都可以询问孩子的名字,但只有母亲和孩子可以更改名字。
您可以通过考虑一个更复杂的类(如Window)来进一步考虑这个简单的示例。一个窗口很可能会有许多不应该被公开访问的函数/数据元素,但是被相关的类(如WindowManager)所需要。
class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;
public:
string name( void );
protected:
void setName( string newName );
};