﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-kenwell-文章分类-c++学习心得</title><link>http://www.cppblog.com/kenwell/category/1181.html</link><description>自己学习所用</description><language>zh-cn</language><lastBuildDate>Tue, 22 Jul 2008 04:05:20 GMT</lastBuildDate><pubDate>Tue, 22 Jul 2008 04:05:20 GMT</pubDate><ttl>60</ttl><item><title>纯虚析构函数、虚函数与抽象类[By Skywind]</title><link>http://www.cppblog.com/kenwell/articles/56824.html</link><dc:creator>c++ 学习</dc:creator><author>c++ 学习</author><pubDate>Tue, 22 Jul 2008 02:06:00 GMT</pubDate><guid>http://www.cppblog.com/kenwell/articles/56824.html</guid><wfw:comment>http://www.cppblog.com/kenwell/comments/56824.html</wfw:comment><comments>http://www.cppblog.com/kenwell/articles/56824.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kenwell/comments/commentRss/56824.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kenwell/services/trackbacks/56824.html</trackback:ping><description><![CDATA[我们知道，为了能够正确的调用对象的析构函数，一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候，必须要通过虚函数找到真正的析构函数。<br><a name="entrymore"></a><br>如：<br><br>
<div class="code"><br><layer id="google-toolbar-hilite-0" style="background-color: Yellow; color: black;">C</layer>++:<br><br>class CObject{<br>public:<br> &nbsp; &nbsp;CObject()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;}<br> &nbsp; &nbsp;virtual ~CObject()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;};<br>};<br>class CX: public CObject<br>{<br>public:<br> &nbsp; &nbsp;CX(){...};<br> &nbsp; &nbsp;~CX(){...};<br>}<br>void f()<br>{<br> &nbsp; &nbsp;CObject *px;<br> &nbsp; &nbsp;px = new CX;<br> &nbsp; &nbsp;delete px;<br>} <br></div>
<br>否则delete px只会执行CObject的析构函数，而不是真正的CX析构函数。<br>现在的问题是，我们想把CObject做出抽象类，不能直接构造对象，需要在其中定义一个<layer id="google-toolbar-hilite-9" style="background-color: Fuchsia; color: black;">纯虚函数</layer>。如果其中没有其他合适的函数，可以把析构函数定义为纯虚的，即将前面的CObject定义改成：<br><br>
<div class="code"><br><layer id="google-toolbar-hilite-1" style="background-color: Yellow; color: black;">C</layer>++:<br><br>class CObject{<br>public:<br> &nbsp; &nbsp;CObject()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;}<br> &nbsp; &nbsp;virtual ~CObject() = 0;<br>}; <br><br></div>
<br>可
是，这段代码不能通过编译，通常是链接错误，不能找到~CObject()的引用(gcc的错误报告)。这是因为，析构函数、构造函数和其他内部函数不一
样，在调用时，编译器需要产生一个调用链。也就是，CX的析构函数里面隐含调用了CObecjt的析构函数。而刚才的代码中，缺少～CObecjt的函数
体，当然会出现错误。<br><br>这里面有一个误区，有人认为，virtual f()=0这种<layer id="google-toolbar-hilite-10" style="background-color: Fuchsia; color: black;">纯虚函数</layer>语法就是没有定义体的语义。其实，这是不对的。这种语法只是表明这个函数是一个<layer id="google-toolbar-hilite-11" style="background-color: Fuchsia; color: black;">纯虚函数</layer>，因此这个类变成了抽象类，不能产生对象。我们完全可以为<layer id="google-toolbar-hilite-12" style="background-color: Fuchsia; color: black;">纯虚函数</layer>指定函数体。通常的<layer id="google-toolbar-hilite-13" style="background-color: Fuchsia; color: black;">纯虚函数</layer>不需要函数体，是因为我们一般不会调用抽象类的这个函数，只会调用派生类的对应函数。这样，我们就有了一个纯虚析构函数的函数体，上面的代码需要改成：<br><br>
<div class="code"><br><layer id="google-toolbar-hilite-2" style="background-color: Yellow; color: black;">C</layer>++:<br><br>class CObject{<br>public:<br> &nbsp; &nbsp;CObject()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;}<br> &nbsp; &nbsp;virtual ~CObject() = 0;<br>};<br> <br>CObject::~CObject()<br>{<br>} <br><br></div>
<br>从语法角度来说，不可以将上面的析构函数直接写入定义中（内联函数的写法）。这或许是一个不正交化的地方。<br><br>这个问题看起来有些学术化，因为一般我们完全可以在CObject中找到一个更加适合的函数，通过将其定义为没有实现体的<layer id="google-toolbar-hilite-14" style="background-color: Fuchsia; color: black;">纯虚函数</layer>，而将整个类定义为抽象类。但这种技术也有一些应用，如这个例子：<br><br>
<div class="code"><br><layer id="google-toolbar-hilite-3" style="background-color: Yellow; color: black;">C</layer>++:<br><br>class CB{<br>public:<br> &nbsp; &nbsp;virtual ~CB(){};<br> &nbsp; &nbsp;virtual void Hiberarchy() const = 0;<br>};<br> <br>void CB::Hiberarchy() const<br>{<br> &nbsp; &nbsp;std::cout &lt;&lt;"CB::";<br>}<br> <br>class CD : public CB<br>{<br>public:<br> &nbsp; &nbsp;CD()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;};<br> &nbsp; &nbsp;virtual void Hiberarchy() const<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp; &nbsp; &nbsp;CB::Hiberarchy();<br> &nbsp; &nbsp; &nbsp; &nbsp;std::cout &lt;&lt;"CD::";<br> &nbsp; &nbsp;};<br> &nbsp; &nbsp;virtual void f()<br> &nbsp; &nbsp;{<br> &nbsp; &nbsp;}<br>};<br> <br> <br>int main(){<br> &nbsp; &nbsp;CB* x=new CD();<br> &nbsp; &nbsp;x-&gt;Hiberarchy();<br> &nbsp; &nbsp;x-&gt;CB::Hiberarchy();<br> &nbsp; &nbsp;return 0;<br>} <br><br></div>
<br>在
这个例子中，我们试图打印出类的继承关系。在根基类中定义了虚函数Hiberarchy，然后在每个派生类中都重载此函数。我们再一次看到，由于想把CB
做成个抽象类，而这个类中没有其他合适的方法成员可以定义为纯虚的，我们还是只好将Hiberarchy定义为纯虚的。（当然，完全可以定义～CB函数，
这就和上面的讨论一样了。^_^）<br><br>另外，可以看到，在main中有两种调用方法，第一种是普通的方式，进行动态链接，执行虚函数，得到结果"CB::CD::"；第二种是指定类的方式，就不再执行虚函数的动态链接过程了，结果是"CB::"。<br><br>通过上面的分析可以看出，定义<layer id="google-toolbar-hilite-15" style="background-color: Fuchsia; color: black;">纯虚函数</layer>的真正目的是为了定义抽象类，而并不是函数本身。与之对比，在java中，定义抽象类的语法是 abstract class，也就是在类的一级作指定（当然虚函数还是也要加上abstract关键字）。是不是这种方式更好一些呢？在Stroustrup的《<layer id="google-toolbar-hilite-4" style="background-color: Yellow; color: black;">C</layer>++语言的设计与演化》中我找到这样一段话：<br><br>&#8220;我选择的是将个别的函数描述为纯虚的方式，没有采用将完整的类声明定义为抽象的形式，这是因为<layer id="google-toolbar-hilite-16" style="background-color: Fuchsia; color: black;">纯虚函数</layer>的概念更加灵活一些。我很看重能够分阶段定义类的能力；也就是说，我发现预先定义一些<layer id="google-toolbar-hilite-17" style="background-color: Fuchsia; color: black;">纯虚函数</layer>，并把另外一些留给进一步的派生类去定义也是很有用的&#8221;。<br><br>我
还没有完全理解后一句话，我想从另外一个角度来阐述这个概念。那就是，在一个多层复杂类结构中，中间层次的类应该具体化一些抽象函数，但很可能并不是所有
的。中间类没必要知道是否具体化了所有的虚函数，以及其祖先已经具体化了哪些函数，只要关注自己的职责就可以了。也就是说，中间类没必要知道自己是否是一
个真正的抽象类，设计者也就不用考虑是否需要在这个中间类的类级别上加上类似abstract的说明了。<br><br>当然，一个语言的设计有多种因素，好坏都是各个方面的。这只是一个解释而已。<br><br>最后，总结一下关于虚函数的一些常见问题：<br><br>虚函数是动态绑定的，也就是说，使用虚函数的指针和引用能够正确找到实际类的对应函数，而不是执行定义类的函数。这是虚函数的基本功能，就不再解释了。 <br>构造函数不能是虚函数。而且，在构造函数中调用虚函数，实际执行的是父类的对应函数，因为自己还没有构造好。 <br>析构函数可以是虚函数，而且，在一个复杂类结构中，这往往是必须的。 <br>将一个函数定义为<layer id="google-toolbar-hilite-18" style="background-color: Fuchsia; color: black;">纯虚函数</layer>，实际上是将这个类定义为抽象类，不能实例化对象。 <br><layer id="google-toolbar-hilite-19" style="background-color: Fuchsia; color: black;">纯虚函数</layer>通常没有定义体，但也完全可以拥有。纯虚析构函数必须有定义体，因为析构函数的调用是在子类中隐含的。 <br>非纯的虚函数必须有定义体，不然是一个错误。 <br>派
生类的重载虚函数定义必须和父类完全一致。除了一个特例，如果父类中返回值是一个指针或引用，子类重载时可以返回这个指针（或引用）的派生。例如，在上面
的例子中，在CB中定义了 virtual CB* clone(); 在CD中可以定义为 virtual CD*
clone()。可以看到，这种放松对于Clone模式是非常有用的。 <br>其他，有待补充。 <img src ="http://www.cppblog.com/kenwell/aggbug/56824.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kenwell/" target="_blank">c++ 学习</a> 2008-07-22 10:06 <a href="http://www.cppblog.com/kenwell/articles/56824.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个google论坛上的问题</title><link>http://www.cppblog.com/kenwell/articles/4351.html</link><dc:creator>c++ 学习</dc:creator><author>c++ 学习</author><pubDate>Mon, 20 Mar 2006 01:17:00 GMT</pubDate><guid>http://www.cppblog.com/kenwell/articles/4351.html</guid><wfw:comment>http://www.cppblog.com/kenwell/comments/4351.html</wfw:comment><comments>http://www.cppblog.com/kenwell/articles/4351.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kenwell/comments/commentRss/4351.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kenwell/services/trackbacks/4351.html</trackback:ping><description><![CDATA[
		<p>
				<strong>
						<font color="#ff0000">The question is:</font>
				</strong>
				<br />  Yesterday my friend had taken interview in Microsft.If u know this<br />Plz answer this<br /><br />main()   // line 1<br />{<br />   printf("TWO");<br /><br /><br /><br />}   //line4<br /><br /><br />U don't change from line1 to line4.  u will add anything before the<br />main function only.<br />write the solution with 10 ways.<br />We want the ouput is-&gt;<br /><br />One<br />Two            //注意，不是TWO！<br />Three<br /><br /><font color="#ff0000">There are several ways to solve the question, the following is:<br /></font><font color="#ff0000">First：to ignore the main funcion</font><br />#include &lt;stdio.h&gt;<br />int main(int argc, char *argv[])<br />{<br />        printf("One\nTwo\nThree\n");<br />        return 0;<br />}<br /><br />#define main XXX<br /><br />main()   // line 1<br />{<br />   printf("TWO");<br /><br /><br /><br />}   //line4<br /><br /><font color="#ff0000">The second way:To ignore the printf function<br /><font color="#000000">int xxx(const char* x)<br />{<br />        return printf("One\nTwo\nThree\n");<br />}<br />#define printf xxx<br /><br />main()   // line 1<br />{<br />   printf("TWO");<br /><br /><br /><br />}   //line4</font><br /></font><br /><font color="#ff0000">The last way: using struct <br /><font color="#000000">#include &lt;stdio.h&gt;<br /><br />struct Foo<br />{<br />        Foo(){printf("One\nTwo\nThree\n");exit(0);}<br />}foo;<br /><br />main()   // line 1<br />{<br />   printf("TWO");<br /><br /><br /><br />}   //line4</font><br /><br /></font><br /></p>
<img src ="http://www.cppblog.com/kenwell/aggbug/4351.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kenwell/" target="_blank">c++ 学习</a> 2006-03-20 09:17 <a href="http://www.cppblog.com/kenwell/articles/4351.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>