📄 [11] destructors, c++ faq lite.htm
字号:
class=CodeBlock><TT> void someCode()<BR> {<BR> {<BR> File f;<BR> </TT><EM>// ... [这些代码在
<TT>f
</TT>打开的时候执行] ...</EM><TT><BR> }<BR> </TT><EM>// ^— <TT>f</TT> 的析构函数在此处会被自动调用!</EM><TT><BR> <BR> </TT><EM>// ... [</EM><EM>这些代码在
<TT>f</TT> 关闭后执行</EM><EM>] ...</EM><TT><BR> } </TT></DIV>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.8]></A>
<DIV class=FaqTitle>
<H3>[11.8] 如果我无法将局部对象包裹于人为的块中,怎么办?</H3></DIV>
<P>大多数时候艘,你可以通过<A
href="http://www.sunistudio.com/cppfaq/dtors.html#[11.7]">将局部对象包裹于人为的<TT>{</TT>...<TT>}</TT>块中</A>,限制其生命期。但如果由于一些原因无法这样做,则增加一个模拟析构函数作用的成员函数。但<I>不要调用析构函数本身</I>!
<P>例如,<TT>File</TT>类的情况下,可以添加一个<TT>close()</TT>方法。典型的析构函数只是调用<TT>close()</TT>方法。注意<TT>close()</TT>方法需要标记
<TT>File </TT>对象,以便后续的调用不会再次关闭一个已经关闭的文件。举例来说,可以将一个<TT>fileHandle_</TT>数据成员设置为
-1,并且在开头检查<TT>fileHandle_</TT>是否已经等于-1:
<P>
<DIV
class=CodeBlock><TT> class File {<BR> public:<BR> void close();<BR> ~File();<BR> </TT><EM>// ...</EM><TT><BR> private:<BR> int fileHandle_; </TT><EM>// <TT>当且仅当文件打开时
fileHandle_ >= 0</TT></EM><TT><BR> };<BR> <BR> File::~File()<BR> {<BR> close();<BR> }<BR> <BR> void File::close()<BR> {<BR> if (fileHandle_ >= 0) {<BR> </TT><EM>// ... [执行一些操作-系统调用来关闭文件] ...</EM><TT><BR> fileHandle_ = -1;<BR> }<BR> }
</TT></DIV>
<P>注意其他的 <TT>File</TT>方法可能也需要检查<TT>fileHandle_</TT>是否为 -1(也就是说,检查文件是否被关闭了)。
<P>还要注意任何没有实际打开文件的构造函数,都应该将<TT>fileHandle_</TT>设置为 -1。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.9]></A>
<DIV class=FaqTitle>
<H3>[11.9] 如果我是用<TT>new</TT>分配对象的,可以显式调用析构函数吗?</H3></DIV>
<P>可能不行。
<P>除非你使用<A href="http://www.sunistudio.com/cppfaq/dtors.html#[11.10]">定位放置
<TT>new</TT></A>,否则应该 <TT>delete </TT>对象而不是显式调用析构函数。例如,假设通过一个典型的 <TT>new
</TT>表达式分配一个对象:
<P>
<DIV class=CodeBlock><TT> Fred* p = new Fred();
</TT></DIV>
<P>那么,当你<TT>delete</TT>它时,析构函数 <TT>Fred::~Fred() </TT>会被调用:
<P>
<DIV
class=CodeBlock><TT> delete p; </TT><EM>// 自动调用 <TT>p->~Fred()</TT></EM><TT>
</TT></DIV>
<P>由于显式调用析构函数不会释放 <TT>Fred </TT>对象本身分配的内存,因此不要这样做。记住:<A
href="http://www.sunistudio.com/cppfaq/freestore-mgmt.html#[16.8]"><TT>delete p</TT>
做了两件事情</A>:调用析构函数,回收内存。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.10]></A>
<DIV class=FaqTitle>
<H3>[11.10] 什么是“定位放置<TT>new</TT>(placement <TT>new</TT>)”,为什么要用它 ?</H3></DIV>
<P>定位放置<TT>new</TT>(placement <TT>new</TT>)有很多作用。最简单的用处就是将对象放置在内存中的特殊位置。这是依靠
<TT>new</TT>表达式部分的指针参数的位置来完成的:
<P>
<DIV
class=CodeBlock><TT> #include <new> </TT><EM>// 必须 <TT>#include</TT> 这个,才能使用 "placement <TT>new</TT>"</EM><TT><BR> #include "Fred.h" </TT><EM>// <TT>class</TT> <TT>Fred
的声明</TT></EM><TT><BR> <BR> void someCode()<BR> {<BR> char memory[sizeof(Fred)]; </TT><EM>// Line #1</EM><TT><BR> void* place = memory; </TT><EM>// Line #2</EM><TT><BR> <BR> Fred* f = new(place) Fred(); </TT><EM>// Line #3 (详见以下的“危险”)</EM><TT><BR> </TT><EM>// The pointers <TT>f</TT> and <TT>place</TT> will be equal</EM><TT><BR> <BR> </TT><EM>// ...</EM><TT><BR> }
</TT></DIV>
<P>Line #1 在内存中创建了一个<TT>sizeof(Fred)</TT>字节大小的数组,足够放下 <TT>Fred </TT>对象。Line #2
创建了一个指向这块内存的首字节的<TT>place</TT>指针(有经验的 C 程序员会注意到这一步是多余的,这儿只是为了使代码更明显)。Line #3
本质上只是调用了构造函数
<TT>Fred::Fred()</TT>。<TT>Fred</TT>构造函数中的<TT>this</TT>指针将等于<TT>place</TT>。因此返回的
<TT>f </TT>将等于<TT>place</TT>。
<P>建议:万不得已时才使用“placement
<TT>new</TT>”语法。只有当你真的在意对象在内存中的特定位置时才使用它。例如,你的硬件有一个内存映象的
I/O计时器设备,并且你想放置一个<TT>Clock</TT>对象在那个内存位置。
<P>危险:你要独自承担这样的责任,传递给“placement
<TT>new</TT>”操作符的指针所指向的内存区域必须足够大,并且可能需要为所创建的对象进行边界调整。编译器和运行时系统都不会进行任何的尝试来检查你做的是否正确。如果
<TT>Fred</TT><TT>
</TT>类需要将边界调整为4字节,而你提供的位置没有进行边界调整的话,你就会亲手制造一个严重的灾难(如果你不明白“边界调整”的意思,那么就不要使用placement
<TT>new</TT>语法)。
<P>你还有析构放置的对象的责任。这通过显式调用析构函数来完成:
<P>
<DIV
class=CodeBlock><TT> void someCode()<BR> {<BR> char memory[sizeof(Fred)];<BR> void* p = memory;<BR> Fred* f = new(p) Fred();<BR> </TT><EM>// ...</EM><TT><BR> f->~Fred(); </TT><EM>// 显式调用定位放置的对象的析构函数</EM><TT><BR> }
</TT></DIV>
<P>这是显式调用析构函数的唯一时机。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.11]></A>
<DIV class=FaqTitle>
<H3>[11.11] 编写析构函数时,需要显式调用成员对象的析构函数吗?</H3></DIV>
<P>不!永远不需要显式调用析构函数(除了<A
href="http://www.sunistudio.com/cppfaq/dtors.html#[11.10]">定位放置
<TT>new</TT></A>的情况)。
<P>类的析构函数(不论你是否显式地定义了)自动调用成员对象的析构函数。它们以出现在类声明中的顺序的反序被析构。
<P>
<DIV
class=CodeBlock><TT> class Member {<BR> public:<BR> ~Member();<BR> </TT><EM>// ...</EM><TT><BR> };<BR> <BR> class Fred {<BR> public:<BR> ~Fred();<BR> </TT><EM>// ...</EM><TT><BR> private:<BR> Member x_;<BR> Member y_;<BR> Member z_;<BR> };<BR> <BR> Fred::~Fred()<BR> {<BR> </TT><EM>// 编译器自动调用 <TT>z_.~Member()</TT></EM><TT><BR> </TT><EM>// 编译器自动调用 <TT>y_.~Member()</TT></EM><TT><BR> </TT><EM>// 编译器自动调用 <TT>x_.~Member()</TT></EM><TT><BR> }
</TT></DIV>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.12]></A>
<DIV class=FaqTitle>
<H3>[11.12] 当我写派生类的析构函数时,需要显式调用基类的析构函数吗?</H3></DIV>
<P>不!永远不需要显式调用析构函数(除了<A
href="http://www.sunistudio.com/cppfaq/dtors.html#[11.10]">定位放置
<TT>new</TT></A>的情况)。
<P>派生类的析构函数(不论你是否显式地定义了)自动调用基类子对象的析构函数。基类在成员对象之后被析构。在多重继承的情况下,直接基类以出现在继承列表中的顺序的反序被析构。
<P>
<DIV
class=CodeBlock><TT> class Member {<BR> public:<BR> ~Member();<BR> </TT><EM>// ...</EM><TT><BR> };<BR> <BR> class Base {<BR> public:<BR> virtual ~Base(); </TT><EM>// <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#[20.4]">虚析构函数</A></EM><TT><BR> </TT><EM>// ...</EM><TT><BR> };<BR> <BR> class Derived : public Base {<BR> public:<BR> ~Derived();<BR> </TT><EM>// ...</EM><TT><BR> private:<BR> Member x_;<BR> };<BR> <BR> Derived::~Derived()<BR> {<BR> </TT><EM>// 编译器自动调用 <TT>x_.~Member()</TT></EM><TT><BR> </TT><EM>// 编译器自动调用 <TT>Base::~Base()</TT></EM><TT><BR> }
</TT></DIV>
<P>注意:虚拟继承的顺序相关性是多变的。如果你在一个虚拟继承层次中依赖于其顺序相关性,那么你需要比这个FAQ更多的信息。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[11.13]></A>
<DIV class=FaqTitle>
<H3>[11.13] 当析构函数检测到错误时,可以抛出异常吗?<IMG alt=NEW!
src="[11] Destructors, C++ FAQ Lite.files/new.gif"></H3></DIV><SMALL><EM>[Recently
created (on 7/00). <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html#[12.3]">Click
here to go to the next FAQ in the "chain" of recent changes<!--rawtext:[12.3]:rawtext--></A>.]</EM></SMALL>
<P>谨防!!! 详见 <A href="http://www.sunistudio.com/cppfaq/exceptions.html#[17.3]">该
FAQ<!--rawtext:[17.3]:rawtext--></A>。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/dtors.html#top">Top</A> | <A
href="http://www.sunistudio.com/cppfaq/dtors.html#bottom">Bottom</A> | <A
href="http://www.sunistudio.com/cppfaq/ctors.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/assignment-operators.html">Next section</A>
]</SMALL>
<HR>
<P><A name=bottom></A><A href="mailto:cline@parashift.com"><IMG height=26
alt=E-Mail src="[11] Destructors, 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 + -