📄 [20] inheritance virtual functions, c++ faq lite.htm
字号:
<P>相比而言,虚成员函数是动态确定的(在运行时)。也就是说,成员函数(在运行时)被动态地选择,该选择基于对象的类型,而不是指向该对象的指针/引用的类型。这被称作“动态绑定”。大多数的编译器使用以下的一些的技术:如果对象有一个或多个虚函数,编译器将一个<BR>隐藏的指针放入对象,该指针称为“virtual-pointor”或“v-pointer”。这个v-pointer指向一个全局表,该表称为“虚函数表(virtural-table)”或“v-table”。<BR><BR>编译器为每个含有至少一个虚函数的类创建一个v-table。例如,如果Cirle类有虚函数d<TT>draw()</TT>、<TT>move()</TT>
和
<TT>resize()</TT>,那么将有且只有一个和Cricle类相关的v-table,即使有一大堆Circle对象。并且每个 Circle对象的 v-poiner将指向 Circle的这个 v-table。该 v-table自己有指向类的各个虚函数的指针。例如,Circle 的v-table 会有三个指针:一个指向<TT>Circle::draw()</TT>,一个指向 <TT>Circle::move()</TT>,还有一个指向<TT>Circle::resize()</TT>。<BR><BR>在分发一个虚函数时,运行时系统跟随对象的 v-pointer找到类的 v-table,然后跟随v-table中适当的项找到方法的代码。<BR><BR>以上技术的空间开销是存在的:每个对象一个额外的指针(仅仅对于需要动态绑定的对象),加上每个方法一个额外的指针(仅仅对于虚方法)。时间开销也是有的:和普通函数调用比较,虚函数调用需要两个额外的步骤(得到v-pointer的值,得到方法的地址)。由于编译器在编译时就通过指针类型解决了非虚函数的调用,所以这些开销不会发生在非虚函数上。<BR><BR>注意:由于没有涉及诸如多继承,虚继承,RTTI等内容,也没有涉及诸如page
fault,通过指向函数的指针调用函数等空间/时间论的内容,所以以上讨论是相当简单的。如果你想知道其他的内容,请询问 <EM><A
href="news:comp.lang.c++"><TT>comp.lang.c++</TT></A></EM>;而不要给我发E-MAIL!
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/basics-of-inheritance.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[20.4]></A>
<DIV class=FaqTitle>
<H3>[20.4] 析构函数何时该时虚拟的?</H3></DIV>
<P>当你可能通过基类指针删除派生类对象时。
<P>虚函数绑定到对象的类的代码,而不是指针/引用的类。如果基类有虚析构函数,<TT>delete basePtr</TT>时(译注:即基类指针),*basePtr 的对象类型的析构函数被调用,而不是该指针的类型的析构函数。这通常是一件好事情。
<P><EM>TECHNO-GEEK WARNING; PUT YOUR PROPELLER HAT ON.</EM>
<BR>从技术上来说,如果你打算允许其他人通过基类指针调用对象的析构函数(通过<TT>delete</TT>这样做是正常的),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数成为虚拟的。如果一个类有显式的析构函数,或者有成员对象,该成员对象或基类有重要的析构函数,那么这个类就有重要的析构函数。(注意这是一个递归的定义(例如,某个具有重要析构函数的类,它有一个成员对象(它有基类(该基类有成员对象(它有基类(该基类有显式的析构函数))))))<BR><EM>END
TECHNO-GEEK WARNING; REMOVE YOUR PROPELLER HAT</EM>
<P>如果你对以上的规则理解有困难,试试这个简单的:类应该有虚析构函数,除非这个类没有虚函数。原理:如果有虚函数,说明你想通过基类指针来使用派生对象,并且你所可能做的事情之中,可能包含了调用析构函数(通常通过<TT>delete</TT>隐含完成)。一旦你在类中加上了一个虚函数,你就已经需要为每一个对象支付空间代价(每个对象一个指针;注意这是理论上的编译器特性;实际上每个编译器都是这样做的),所以这时使析构函数成为虚拟的通常不会额外付出什么。
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/basics-of-inheritance.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html">Next section</A>
]</SMALL>
<HR>
<P><A name=[20.5]></A>
<DIV class=FaqTitle>
<H3>[20.5] 什么是“虚构造函数(<TT>virtual</TT> constructor)”?</H3></DIV>
<P>一种允许你做一些 C++ 不直接支持的事情的用法。
<P>你可能通过虚函数 <TT>virtual</TT> <TT>clone()</TT>(对于拷贝构造函数)或虚函数
<TT>virtual</TT> <TT>create()</TT>(对于<A
href="http://www.sunistudio.com/cppfaq/ctors.html#[10.4]">默认构造函数</A>),得到虚构造函数产生的效果。
<P>
<DIV
class=CodeBlock><TT> class Shape {<BR> public:<BR> virtual ~Shape() { } </TT><EM>// <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#[20.4]">虚析构函数</A></EM><TT><BR> virtual void draw() = 0; </TT><EM>// <A
href="http://www.sunistudio.com/cppfaq/abcs.html#[22.4]">纯虚函数</A></EM><TT><BR> virtual void move() = 0;<BR> </TT><EM>// ...</EM><TT><BR> virtual Shape* clone() const = 0; </TT><EM>// 使用拷贝构造函数</EM><TT><BR> virtual Shape* create() const = 0; </TT><EM>// 使用<A
href="http://www.sunistudio.com/cppfaq/ctors.html#[10.4]">默认构造函数</A></EM><TT><BR> };<BR> <BR> class Circle : public Shape {<BR> public:<BR> Circle* clone() const { return new Circle(*this); }<BR> Circle* create() const { return new Circle(); }<BR> </TT><EM>// ...</EM><TT><BR> };
</TT></DIV>
<P>在 <TT>clone()</TT> 成员函数中,代码 <TT>new Circle(*this)
</TT>调用 <TT>Circle
</TT>的拷贝构造函数来复制<TT>this</TT>的状态到新创建的<TT>Circle</TT>对象。在 <TT>create()</TT>成员函数中,代码 <TT>new Circle()</TT> 调用<TT>Circle</TT>的<A
href="http://www.sunistudio.com/cppfaq/ctors.html#[10.4]">默认构造函数</A>。<BR><BR>用户将它们看作“虚构造函数”来使用它们:<BR>
<DIV
class=CodeBlock><TT> void userCode(Shape& s)<BR> {<BR> Shape* s2 = s.clone();<BR> Shape* s3 = s.create();<BR> </TT><EM>// ...</EM><TT><BR> delete s2; </TT><EM>// 在此处,你可能需要<A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#[20.4]">虚析构函数</A></EM><TT><BR> delete s3;<BR> }
</TT></DIV>
<P>这个函数将正确工作,而不管 <TT>Shape</TT> 是一个<TT>Circle</TT>,<TT>Square</TT>,或是其他种类的 <TT>Shape</TT>,甚至它们还并不存在。
<P>注意:成员函数<TT>Circle</TT>'s <TT>clone()</TT>的返回值类型故意与成员函数<TT>Shape</TT>'s
<TT>clone()</TT>的不同。这种特征被称为“协变的返回类型”,该特征最初并不是语言的一部分。如果你的编译器不允许在<TT>Circle</TT>类中这样声明<TT>Circle* clone() const</TT>(如,提示“The
return type is different”或“The member function's type differs from the base
class virtual function by return type
alone”),说明你的编译器陈旧了,那么你必须改变返回类型为<TT>Shape*。</TT>
<P><SMALL>[ <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#top">Top</A>
| <A
href="http://www.sunistudio.com/cppfaq/virtual-functions.html#bottom">Bottom</A>
| <A
href="http://www.sunistudio.com/cppfaq/basics-of-inheritance.html">Previous section</A>
| <A
href="http://www.sunistudio.com/cppfaq/proper-inheritance.html">Next section</A>
]</SMALL>
<HR>
<P><A name=bottom></A><A href="mailto:cline@parashift.com"><IMG height=26
alt=E-Mail src="[20] Inheritance virtual functions, 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 + -