📄 [12] assignment operators, 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=(0058)http://www.sunistudio.com/cppfaq/assignment-operators.html -->
<HTML><HEAD><TITLE>[12] Assignment operators, C++ FAQ Lite</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META http-equiv=Content-Language content=zh-cn>
<META content=assignment-operators.html name=FILENAME>
<META content="[12] Assignment operators, 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="[12] Assignment operators, C++ FAQ Lite.files/cpp-faq.css" type=text/css
rel=stylesheet></HEAD>
<BODY>
<H1><A name=top></A>[12] 赋值算符<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 [12]:</H3>
<UL>
<LI><A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.1]">[12.1]
什么是“自赋值”?</A>
<LI><A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.2]">[12.2]
为什么应该当心“自赋值”?</A>
<LI><A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.3]">[12.3]
好,好;我会处理自赋值的。但如何做呢?</A> <IMG alt=UPDATED!
src="[12] Assignment operators, C++ FAQ Lite.files/updated.gif"> </LI></UL>
<P>
<HR>
<P><A name=[12.1]></A>
<DIV class=FaqTitle>
<H3>[12.1] 什么是“自赋值”?</H3></DIV>
<P>自赋值就是将对象赋值给本身。例如,
<P>
<DIV
class=CodeBlock><TT> #include "Fred.hpp" </TT><EM>// 声明
<TT>Fred</TT>
类 </EM><TT><BR> <BR> void userCode(Fred& x)<BR> {<BR> x = x; </TT><EM>// 自赋值</EM><TT><BR> }
</TT></DIV>
<P>很明显,以上代码进行了显式的自赋值。但既然多个指针或引用可以指向相同对象(别名),那么进行了自赋值而不知道的情况也是可能的:
<P>
<DIV
class=CodeBlock><TT> #include "Fred.hpp" </TT><EM>// </EM><EM>声明
</EM><EM><TT>Fred</TT>
类</EM><TT><BR> <BR> void userCode(Fred& x, Fred& y)<BR> {<BR> x = y; </TT><EM>// 如果<TT>&x == &y</TT>就可能是自赋值</EM><TT><BR> }<BR> <BR> int main()<BR> {<BR> Fred z;<BR> userCode(z, z);<BR> }
</TT></DIV>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/dtors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[12.2]></A>
<DIV class=FaqTitle>
<H3>[12.2] 为什么应该当心“自赋值”?</H3></DIV>
<P>如果不注意<A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.1]">自赋值</A>,将会使你的用户遭受非常微妙的并且一般来说非常严重的bug。例如,如下的类在自赋值的情况下将导致灾难:
<P>
<DIV
class=CodeBlock><TT> class Wilma { };<BR> <BR> class Fred {<BR> public:<BR> Fred() : p_(new Wilma()) { }<BR> Fred(const Fred& f) : p_(new Wilma(*f.p_)) { }<BR> ~Fred() { delete p_; }<BR> Fred& operator= (const Fred& f)<BR> {<BR> </TT><EM>// 差劲的代码:没有处理自赋值!</EM><TT><BR> delete p_; </TT><EM>// Line #1</EM><TT><BR> p_ = new Wilma(*f.p_); </TT><EM>// Line #2</EM><TT><BR> return *this;<BR> }<BR> private:<BR> Wilma* p_;<BR> };
</TT></DIV>
<P>如果有人将 <TT>Fred </TT>对象赋给其本身,由于<TT>*this</TT>和 <TT>f</TT> 是同一个对象,line
#1同时删除了<TT>this->p_</TT>和<TT>f.p_</TT>。而 line
#2使用了已经不存在的对象<TT>*f.p_</TT>,这样很可能导致严重的灾难。
<P>作为 <TT>Fred</TT><TT> </TT>类的作者,你最起码有责任<A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.3]">确信在<TT>Fred</TT>对象上自赋值是无害的</A>。不要假设用户不会在对象上这样做。如果对象由于自赋值而崩溃,那是<I>你的</I>过失。
<BLOCKQUOTE>
<P><SMALL>另外:上述的</SMALL><SMALL><TT>Fred::operator= (const Fred&)</TT></SMALL><SMALL>还有第二个问题:如果在执行</SMALL><SMALL><TT>new Wilma(*f.p_)</TT></SMALL><SMALL>时,<A
href="http://www.sunistudio.com/cppfaq/exceptions.html">抛出了异常</A>(例如,<A
href="http://www.sunistudio.com/cppfaq/freestore-mgmt.html#[16.5]">内存不够的异常</A>或者</SMALL><A
href="http://www.sunistudio.com/cppfaq/exceptions.html#[17.2]"><TT><SMALL>Wilma</SMALL></TT><SMALL>的拷贝构造函数中的异常</SMALL><SMALL><!--rawtext:[17.2]:rawtext--></SMALL></A><SMALL>),</SMALL><SMALL>
<TT>this->p_</TT></SMALL><SMALL>将成为悬空指针——它所指向的内存不再是可用的。这可以通过在删除就对象前创建对象来解决。</SMALL></P></BLOCKQUOTE>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/dtors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[12.3]></A>
<DIV class=FaqTitle>
<H3>[12.3] 好,好;我会处理自赋值的。但如何做呢? <IMG alt=UPDATED!
src="[12] Assignment operators, C++ FAQ Lite.files/updated.gif"></H3></DIV><SMALL><EM>[Recently
reworded the last paragraph (on 7/00). <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html#[13.3]">Click
here to go to the next FAQ in the "chain" of recent changes<!--rawtext:[13.3]:rawtext--></A>.]</EM></SMALL>
<P><A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.2]">在你创建类的每时每刻,都应该当心自赋值</A>。这并不意味着需要为你所有的类都增加额外的代码:只要对象优美地处理自赋值,而不管是否必须增加额外的代码。
<P>如果不需要为赋值算符增加额外代码,这里有一个简单而有效的技巧:
<P>
<DIV
class=CodeBlock><TT> Fred& Fred::operator= (const Fred& f)<BR> {<BR> if (this == &f) return *this; </TT><EM>// 优美地处理<A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.1]">自赋值</A></EM><TT><BR> <BR> </TT><EM>// 此处写正常赋值的代码...</EM><TT><BR> <BR> return *this;<BR> }
</TT></DIV>
<P>显式的测试并不总是必要的。例如,如果修正<A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.2]">前一个 FAQ<!--rawtext:[12.2]:rawtext-->中的赋值算符</A>使之<A
href="http://www.sunistudio.com/cppfaq/freestore-mgmt.html#[16.5]">处理<TT>new</TT><!--rawtext:[16.5]:rawtext-->抛出的异常</A>和/或<TT>Wilma</TT>类的<A
href="http://www.sunistudio.com/cppfaq/exceptions.html#[17.2]">拷贝构造函数抛出的异常</A>,可能会写出如下的代码。注意这段代码有(令人高兴的)自动处理自赋值的附带效果:
<P>
<DIV
class=CodeBlock><TT> Fred& Fred::operator= (const Fred& f)<BR> {<BR> </TT><EM>// 这段代码优美地(但隐含的)处理<A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.1]">自赋值</A></EM><TT><BR> Wilma* tmp = new Wilma(*f.p_); </TT><EM>// 如果<A
href="http://www.sunistudio.com/cppfaq/exceptions.html">异常</A>在此处被抛出也没有问题</EM><TT><BR> delete p_;<BR> p_ = tmp;<BR> return *this;<BR> }
</TT></DIV>
<P>在象这个例子的情况下(自赋值是无害的但是低效),一些程序员想通过增加另外的不必要的测试,如“<TT>if (this == &f) return *this;</TT>”来改善自赋值时的效率。通常来说,使自赋值情况更高效而使得非自赋值情况更低效的折衷是错误的。例如,为<TT>Fred</TT>类的赋值算符增加如上的<TT>if</TT>测试会使得非自赋值情况更低效(一个额外的(而且不必要的)条件分支)。如果自赋值实际上一千次才发生一次,那么
<TT>if</TT><TT> </TT>将浪费99.9%的时间周期。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/dtors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/operator-overloading.html">Next section</A>
]</SMALL>
<HR>
<P><A name=bottom></A><A href="mailto:cline@parashift.com"><IMG height=26
alt=E-Mail src="[12] Assignment operators, 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 + -