📄 [14] friends, c++ faq lite.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<!-- saved from url=(0045)http://www.sunistudio.com/cppfaq/friends.html -->
<HTML><HEAD><TITLE>[14] Friends, C++ FAQ Lite</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META http-equiv=Content-Language content=zh-cn>
<META content=friends.html name=FILENAME>
<META content="[14] Friends, C++ FAQ Lite" name=ABSTRACT>
<META content=cline@parashift.com name=OWNER>
<META content="Marshall Cline, cline@parashift.com" name=AUTHOR>
<META content="MSHTML 6.00.2462.0" name=GENERATOR>
<META content=FrontPage.Editor.Document name=ProgId><LINK rev=made
href="mailto:cline@parashift.com"><LINK
href="[14] Friends, C++ FAQ Lite.files/cpp-faq.css" type=text/css
rel=stylesheet></HEAD>
<BODY>
<H1><A name=top></A>[14] 友元<BR><SMALL><SMALL>(Part of <A
href="http://www.sunistudio.com/cppfaq/index.html"><EM>C++ FAQ Lite</EM></A>, <A
href="http://www.sunistudio.com/cppfaq/copy-permissions.html#[1.2]">Copyright ©
1991-2001</A>, <A href="http://www.parashift.com/" target=OutsideTheFAQ>Marshall
Cline</A>, <A
href="mailto:cline@parashift.com">cline@parashift.com</A>)</SMALL></SMALL></H1>
<P>简体中文版翻译:<A href="http://www.sunistudio.com/nicrosoft">申旻</A>,<A
href="mailto:nicrosoft@sunistudio.com">nicrosoft@sunistudio.com</A>(<A
href="http://www.sunistudio.com/">东日制作室</A>,<A
href="http://www.sunistudio.com/asp/sunidoc.asp">东日文档</A>)</P>
<HR>
<H3>FAQs in section [14]:</H3>
<UL>
<LI><A href="http://www.sunistudio.com/cppfaq/friends.html#[14.1]">[14.1]
什么是友元(<TT>friend</TT></A><A
href="http://www.sunistudio.com/cppfaq/friends.html#[14.1]">)?</A>
<LI><A href="http://www.sunistudio.com/cppfaq/friends.html#[14.2]">[14.2]
友元破坏了封装吗?</A><IMG alt=UPDATED!
src="[14] Friends, C++ FAQ Lite.files/updated.gif">
<LI><A href="http://www.sunistudio.com/cppfaq/friends.html#[14.3]">[14.3]
使用友元函数的优缺点是什么?</A>
<LI><A href="http://www.sunistudio.com/cppfaq/friends.html#[14.4]">[14.4]
“友元关系既不继承,也不传递”是什么意思?</A> <IMG alt=UPDATED!
src="[14] Friends, C++ FAQ Lite.files/updated.gif">
<LI><A href="http://www.sunistudio.com/cppfaq/friends.html#[14.5]">[14.5]
我的类应该使用成员函数还是友元函数?</A> </LI></UL>
<P>
<HR>
<P><A name=[14.1]></A>
<DIV class=FaqTitle>
<H3>[14.1] 什么是友元(<TT>friend</TT>)?</H3></DIV>
<P>允许另一个类或函数访问你的类的东西。<BR><BR>友元可以是函数或者是其他的类。类授给它的友元特别的访问权。通常同一个开发者会在技术和非技术上控制类的友元和成员函数(否则当你想更新你的类时,还要征得其它部分的拥有者的同意)。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/friends.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/friends.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/input-output.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[14.2]></A>
<DIV class=FaqTitle>
<H3>[14.2] 友元破坏了封装吗?<IMG alt=UPDATED!
src="[14] Friends, C++ FAQ Lite.files/updated.gif"></H3></DIV><SMALL><EM>[Recently
made a bit more emphatic (on 4/01). <A
href="http://www.sunistudio.com/cppfaq/friends.html#[14.4]">Click here to go to
the next FAQ in the "chain" of recent
changes<!--rawtext:[14.4]:rawtext--></A>.]</EM></SMALL>
<P>如果被适当的使用,实际上可以增强封装。<BR><BR>当一个类的两部分会有不同数量的实例或者不同的生命周期时,你经常需要将一个类分割成两部分。在这些情况下,两部分通常需要直接存取彼此的数据(这两部分原来在同一个类中,所以你不必增加直接存取一个数据结构的代码;你只要将代码改为两个类就行了)。实现这种情况的最安全途径就是使这两部分成为彼此的友元。<BR><BR>如果你象刚才所描述的那样使用友元,就可以使私有的(<TT>private</TT>)保持私有。不理解这些的人在以上这种情形下还天真的想避免使用友元,他们要么使用公有的(<TT>public</TT>)数据(罕见!),要么通过公有的 <TT>get()</TT>和<TT>set()</TT>成员函数使两部分可以访问数据。而他们实际上破坏了封装。只有当在类外(从用户的角度)看待私有数据仍“有意义”时,为私有数据设置公有的<TT>get()</TT>和<TT>set()</TT>成员函数才是合理的。在许多情况下,这些 <TT>get()</TT>/<TT>set()</TT>成员函数和公有数据一样差劲:它们仅仅隐藏了私有数据的名称,而没有隐藏私有数据本身。<BR><BR>同样,如果你将友元函数当做一种类的<TT>public:</TT>存取函数的语法不同的变种来使用的话,友元函数就和破坏封装的成员函数一样会破坏封装。换一种说法,类的友元不会破坏封装的壁垒:和类的成员函数一样,它们就是封装的壁垒。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/friends.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/friends.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/input-output.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[14.3]></A>
<DIV class=FaqTitle>
<H3>[14.3] 使用友元函数的优缺点是什么?</H3></DIV>
<P>友元函数在接口设计选择上提供了一定程度的自由。</P>
<P>成员函数和友元函数具有同等的特权(100% 的)。主要的不同在于友元函数象<TT>f(x)</TT>这样调用,而成员函数象 <TT>x.f()</TT>这样调用。因此,可以在成员函数(<TT>x.f()</TT>)和友元函数(<TT>f(x)</TT>)之间选择的能力允许设计者选择他所认为更具可读性的语法来降低维护成本。<BR><BR>友元函数主要缺点是需要额外的代码来支持动态绑定时。要得到虚友元(<TT>virtual</TT>
<TT>friend</TT>)的效果,友元函数应该调用一个隐藏的(通常是 <TT>protected:</TT>)<A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">虚</A>成员函数。这称为<A
href="http://www.sunistudio.com/cppfaq/input-output.html#[15.9]">虚友元函数用法(Virtual
Friend Function Idiom)</A>。例如:<BR></P>
<DIV
class=CodeBlock><TT> class Base {<BR> public:<BR> friend void f(Base& b);<BR> </TT><EM>// ...</EM><TT><BR> protected:<BR> virtual void do_f();<BR> </TT><EM>// ...</EM><TT><BR> };<BR> <BR> inline void f(Base& b)<BR> {<BR> b.do_f();<BR> }<BR> <BR> class Derived : public Base {<BR> public:<BR> </TT><EM>// ...</EM><TT><BR> protected:<BR> virtual void do_f(); </TT><EM>// "覆盖" <TT>f(Base& b)</TT>的行为</EM><TT><BR> </TT><EM>// ...</EM><TT><BR> };<BR> <BR> void userCode(Base& b)<BR> {<BR> f(b);<BR> }
</TT></DIV>
<P>在<TT>userCode(Base&)</TT>中的<TT>f(b)</TT>语句将调用<A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html">虚拟</A>的
<TT>b.do_f()</TT>。这意味着如果<TT>b</TT>实际是一个派生类的对象,那么<TT>Derived::do_f()</TT>将获得控制权。注意派生类覆盖的是保护的虚(<TT>protected:</TT>
<A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html"><TT>virtual</TT></A>)成员函数 <TT>do_f()</TT>; 而不是它友元函数<TT>f(Base&)</TT>。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/friends.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/friends.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/input-output.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[14.4]></A>
<DIV class=FaqTitle>
<H3>[14.4] “友元关系既不继承,也不传递”是什么意思?<IMG alt=UPDATED!
src="[14] Friends, C++ FAQ Lite.files/updated.gif"></H3></DIV><SMALL><EM>[Recently
added the "not reciprocal" item thanks to <A
href="mailto:Karel.Roose@barco.com">Karel Roose</A> (on 4/01). <A
href="http://www.sunistudio.com/cppfaq/input-output.html#[15.1]">Click here to
go to the next FAQ in the "chain" of recent
changes<!--rawtext:[15.1]:rawtext--></A>.]</EM></SMALL>
<P>仅仅因为我承认对你的友情,允许你访问我,并不自动地允许你的孩子访问我,并不自动地允许你的朋友访问我,并不自动地允许我访问你。
<UL>
<LI>我不必信任我朋友的孩子。友元的特权不被继承。友元的派生类不一定是友元。如果 <TT>Fred</TT>
类声明<TT>Base</TT>类是友元,那么<TT>Base</TT>类的派生类不会自动地被赋予对于<TT>Fred</TT>的对象的访问特权。
<LI>我不必信任我朋友的朋友。友元的特权不被传递。友元的友元不一定是友元。如果<TT>Fred</TT>类声明<TT>Wilma</TT>类是友元,并且<TT>Wilma</TT>类声明<TT>Betty</TT>类是友元,那么<TT>Betty</TT>类不会自动地被赋予对于<TT>Fred</TT>的对象的访问特权。
<LI>你不必仅仅因为我声称你是我的朋友就信任我。友元的特权不是自反的。如果<TT>Fred</TT>类声明<TT>Wilma</TT>类是友元,则<TT>Wilma</TT>对象拥有访问<TT>Fred</TT>对象的特权,但<TT>Fred</TT>对象不会自动地拥有对<TT>Wilma</TT>对象的访问特权。
</LI></UL>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/friends.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/friends.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/input-output.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[14.5]></A>
<DIV class=FaqTitle>
<H3>[14.5] 我的类应该使用成员函数还是友元函数?</H3></DIV>
<P>尽量使用成员函数,不得已时使用友元。<BR><BR>有时在语法上,友元更好(例如,<TT>Fred</TT>类中,友元函数允许<TT>Fred</TT>参数作为第二个参数,而成员函数必须是第一个)。另一个好的用法是二元中缀运算符。例如,如果你想允许<TT>aFloat + aComplex</TT>
的话,<TT>aComplex + aComplex</TT>
应该被定义为友元而不是成员函数。(成员函数不允许提升左边的参数,因为那样会改变成员函数调用对象的类)。<BR><BR>在其他情况下,首选成员函数。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/friends.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/friends.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/input-output.html">Next section</A>
]</SMALL>
<HR>
<P><A name=bottom></A><A href="mailto:cline@parashift.com"><IMG height=26
alt=E-Mail src="[14] Friends, C++ FAQ Lite.files/mbox.gif" width=89> E-mail
the author</A><BR>[ <A
href="http://www.sunistudio.com/cppfaq/index.html"><EM>C++ FAQ Lite</EM></A>
| <A
href="http://www.sunistudio.com/cppfaq/index.html#table-of-contents">Table of contents</A>
| <A
href="http://www.sunistudio.com/cppfaq/subject-index.html">Subject index</A>
| <A
href="http://www.sunistudio.com/cppfaq/copy-permissions.html#[1.1]">About the author</A>
| <A
href="http://www.sunistudio.com/cppfaq/copy-permissions.html#[1.2]">©</A>
| <A
href="http://www.sunistudio.com/cppfaq/on-line-availability.html#[2.2]">Download your own copy</A> ]<BR><SMALL>Revised
Apr 8, 2001</SMALL> </P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -