我一直在阅读C++常见问题,并对该friend
声明感到好奇.我个人从未使用它,但我有兴趣探索这种语言.
什么是使用的好例子friend
?
阅读常见问题更长一点我喜欢<<
>>
运算符重载的想法,并添加为这些类的朋友.但是我不确定这是如何不破坏封装的.这些例外何时可以保持在OOP的严格范围内?
首先(IMO)不听那些说friend
没用的人.它是有益的.在许多情况下,您将拥有数据或功能不可公开的对象.对于具有许多作者的大型代码库尤其如此,这些作者可能只是表面上熟悉不同的领域.
朋友说明符有其他选择,但通常它们很麻烦(cpp级别的具体类/掩码类型的定义)或者不是万无一失的(注释或函数名称约定).
在答案上;
该friend
说明符可以让朋友语句类中指定的类访问受保护的数据或功能.例如,在下面的代码中,任何人都可以询问孩子的姓名,但只有母亲和孩子可以更改姓名.
您可以通过考虑更复杂的类(如Window)来进一步采用这个简单的示例.很可能一个Window将有许多不应公开访问的函数/数据元素,但是相关类(例如WindowManager)需要ARE.
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 ); };
在工作中,我们广泛使用朋友来测试代码.这意味着我们可以为主应用程序代码提供适当的封装和信息隐藏.但是我们也可以使用单独的测试代码来使用朋友来检查内部状态和数据以进行测试.
我只想说我不会将friend关键字用作您设计的重要组成部分.
该friend
关键字有许多好用途.以下是我立即可见的两种用法:
朋友定义允许在类范围内定义一个函数,但该函数不会被定义为成员函数,而是作为封闭命名空间的自由函数,并且除了参数依赖查找之外不会正常显示.这使得它对运算符重载特别有用:
namespace utils { class f { private: typedef int int_type; int_type value; public: // let's assume it doesn't only need .value, but some // internal stuff. friend f operator+(f const& a, f const& b) { // name resolution finds names in class-scope. // int_type is visible here. return f(a.value + b.value); } int getValue() const { return value; } }; } int main() { utils::f a, b; std::cout << (a + b).getValue(); // valid }
有时,您发现策略需要访问派生类:
// possible policy used for flexible-class. templatestruct Policy { void doSomething() { // casting this to Derived* requires us to see that we are a // base-class of Derived. some_type const& t = static_cast (this)->getSomething(); } }; // note, derived privately template class SomePolicy> struct FlexibleClass : private SomePolicy { // we derive privately, so the base-class wouldn't notice that, // (even though it's the base itself!), so we need a friend declaration // to make the base a friend of us. friend class SomePolicy ; void doStuff() { // calls doSomething of the policy this->doSomething(); } // will return useful information some_type getSomething(); };
你会在这个答案中找到一个非人为的例子.使用它的另一个代码就在这个答案中.CRTP基类会转换它的this指针,以便能够使用data-member-pointers访问派生类的数据字段.
@roo:封装不会在这里被破坏,因为类本身决定谁可以访问其私有成员.如果这可能是在课外引起的,那么封装只会被打破,例如,如果你operator <<
宣称"我是班上的朋友" foo
.
friend
替换使用public
,而不是使用private
!
实际上,C++ FAQ 已经解决了这个问题.
规范的例子是重载operator <<.另一个常见用途是允许帮助者或管理员类访问您的内部.
以下是我听说过C++朋友的一些指导原则.最后一个特别值得纪念.
你的朋友不是你孩子的朋友.
你孩子的朋友不是你的朋友.
只有朋友可以触摸您的私人部分.
编辑:读取faq更长一点我喜欢<< >>运算符重载的想法并添加为这些类的朋友,但是我不知道这怎么不破坏封装
它将如何打破封装?
当您允许不受限制地访问数据成员时,您会破坏封装.考虑以下类:
class c1 { public: int x; }; class c2 { public: int foo(); private: int x; }; class c3 { friend int foo(); private: int x; };
c1
是明显不封装.任何人都可以阅读和修改x
它.我们无法强制执行任何类型的访问控制.
c2
显然是封装的.没有公共访问权限x
.您所能做的就是调用foo
函数,该函数对类执行一些有意义的操作.
c3
?是不是封装了?它是否允许不受限制的访问x
?它是否允许未知功能访问?
不.它只允许一个函数访问类的私有成员.就像c2
那样.就像c2
,具有访问权限的一个函数不是"一些随机的,未知的函数",而是"类定义中列出的函数".就像c2
,我们可以通过查看类定义,查看谁有权访问的完整列表.
那么这个封装的确切程度如何呢?相同数量的代码可以访问该类的私有成员.而且大家谁有权访问被列在类的定义.
friend
不破坏封装.这让一些Java程序员感到不舒服,因为当他们说"OOP"时,他们实际上意味着 "Java".当他们说"封装"时,他们并不是说"私人成员必须受到保护以免受任意访问",而是"只有能够访问私人成员的Java类才是类成员",尽管这完全是胡说八道.几个原因.
首先,如图所示,它太局限了.没有理由不允许朋友方法做同样的事情.
其次,它是不是限制性不够.考虑第四类:
class c4 { public: int getx(); void setx(int x); private: int x; };
根据上述Java心态,这是完全封装的. 然而,它绝对允许任何人阅读和修改x.这怎么有意义呢?(提示:它没有)
底线:封装是关于能够控制哪些功能可以访问私有成员.它不是关于这些函数的定义的确切位置.
另一个常见的安德鲁例子,可怕的代码对联
parent.addChild(child); child.setParent(parent);
如果两条线总是以一致的顺序一起完成而不是担心,您可以将这些方法设为私有,并使用友元函数来强制执行一致性:
class Parent; class Object { private: void setParent(Parent&); friend void addChild(Parent& parent, Object& child); }; class Parent : public Object { private: void addChild(Object& child); friend void addChild(Parent& parent, Object& child); }; void addChild(Parent& parent, Object& child) { if( &parent == &child ){ wetPants(); } parent.addChild(child); child.setParent(parent); }
换句话说,您可以保持公共接口更小,并强制执行跨越友元函数中的类和对象的不变量.
您使用私有/受保护/公共权限控制成员和功能的访问权限?所以假设这3个级别中的每个级别的想法都很明确,那么应该很清楚我们遗漏了一些东西......
例如,成员/函数声明受保护是非常通用的.你说这个功能对每个人来说都是遥不可及的(当然除了继承的孩子).但是例外呢?每个安全系统都可以让你拥有某种类型的"白名单"吗?
所以朋友可以让你灵活地拥有坚如磐石的物体隔离,但允许为你认为合理的东西创造一个"漏洞".
我想人们说不需要它,因为总有一种设计可以没有它.我认为它类似于对全局变量的讨论:你永远不应该使用它们,总有一种方法可以不用它们......但实际上,你会看到最终成为(几乎)最优雅方式的情况. ..我认为这与朋友的情况相同.
除了让您在不使用设置功能的情况下访问成员变量之外,它实际上没有任何好处
好吧,这不是看待它的方式.这个想法是控制世界卫生组织可以访问什么,设置功能与否有关.
我找到了方便使用朋友访问的地方:私人功能的单元测试.
当您构建容器并且想要为该类实现迭代器时,Friend会派上用场.