﻿<?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++博客-蓝莓日记Cass#-随笔分类-C/C++</title><link>http://www.cppblog.com/Cass/category/17825.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 01 Dec 2011 06:07:42 GMT</lastBuildDate><pubDate>Thu, 01 Dec 2011 06:07:42 GMT</pubDate><ttl>60</ttl><item><title>进度条控件</title><link>http://www.cppblog.com/Cass/archive/2011/11/30/161250.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Wed, 30 Nov 2011 12:42:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/11/30/161250.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/161250.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/11/30/161250.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/161250.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/161250.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Style  Description <br>PBS_SMOOTH  //平滑<br>PBS_VERTICAL  //垂直<br> 响应消息<br><br>PBM_DELTAPOS  //一个进度条由一个指定的增量当前位置和重绘栏，以反映新的位置。<br>wParam=（的WPARAM）nIncrement <br>lParam = 0; <br>返回原来的位置。<br><br>&nbsp;&nbsp;<a href='http://www.cppblog.com/Cass/archive/2011/11/30/161250.html'>阅读全文</a><img src ="http://www.cppblog.com/Cass/aggbug/161250.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-11-30 20:42 <a href="http://www.cppblog.com/Cass/archive/2011/11/30/161250.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++读写流</title><link>http://www.cppblog.com/Cass/archive/2011/11/30/161249.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Wed, 30 Nov 2011 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/11/30/161249.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/161249.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/11/30/161249.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/161249.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/161249.html</trackback:ping><description><![CDATA[<p>&nbsp; 和C语言不同，C++对文件的操作有自己的方法。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++对文件的操作主要是通过两个类（ofstream---向文件中写入数据。ifstream----从文件中读取数据），通过指定类中的变量取值来达到对文件的操作。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ofstream类的构造函数（有好几个，这个用的最多）ofstream::ofstream <br />ofstream( const char* szName, int nMode = ios::out, int nProt = filebuf::openprot )</p>
<p>szName:指定将要打开的文件名</p>
<p>nMode:指定打开的方式，有以下几种取值 </p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------</p>
<p>ios::app&nbsp;&nbsp; 数据始终添加在文件的末尾，文件的指针不移动。比如输入的是123，在文件的末尾出现的是321（先将1插入文件尾，接下来插入2，2在1的前面....）</p>
<p><br />ios::ate&nbsp;&nbsp; 数据添加在文件的末尾，文件指针会移动，比如输入123，在文件的末尾就出现123.</p>
<p><br />ios::in&nbsp;&nbsp; 如果指定了此模式，则文件的内容不会被截断</p>
<p><br />ios::out&nbsp;&nbsp; 打开文件，用于输出，可以用于所有的ofstream对象</p>
<p><br />ios::trunc&nbsp;&nbsp; 如果文件已经存在，那么文件的内容将被清空</p>
<p><br />ios::nocreate&nbsp;&nbsp; 打开文件的时候不创建文件，意思是如果文件不存在，则函数失败</p>
<p><br />ios::noreplace&nbsp;&nbsp; 不覆盖文件，意思是如果文件存在，则函数调用失败。</p>
<p><br />ios::binary&nbsp;&nbsp; 以二进制方式打开文件，默认是以文本方式打开</p>
<p><br />--------------------------------------------------------------------------------<br />nProt:指定文件保护规格说明，有以下几种取值<br />filebuf::sh_compat&nbsp;&nbsp; 兼容共享模式filebuf::openprot和此种方式一样<br />filebuf::sh_none&nbsp;&nbsp; 独占模式，不共享<br />filebuf::sh_read&nbsp;&nbsp;&nbsp; 共享，只读方式<br />filebuf::sh_write&nbsp;&nbsp; 共享，可以对文件执行写入操作</p>
<p>从文件中读取数据是通过ifstream的对象进行的，其构造函数如下<br />ifstream::ifstream <br />ifstream( const char* szName, int nMode = ios::in, int nProt = filebuf::openprot );各参数的意义同上</p>
<p>对于C++的文件操作，需要先构建ofstream和ifstream类的对象，然后通过该对象的成员函数进行文件的读写操作（例如write和read函数）</p>
<p>例子：</p>
<p>#include <br />#include <br />#include <br />using namespace std;</p>
<p>int main()<br />{<br />&nbsp;&nbsp;&nbsp; //打开文件，如果文件不存在则创建文件，然后向文件内写入数据<br />&nbsp;&nbsp;&nbsp; ofstream outFile("2.txt",ios::app);<br />&nbsp;&nbsp;&nbsp; //将数据写入文件<br />&nbsp;&nbsp;&nbsp; outFile.write("c++对文件的操作方法",strlen("c++对文件的操作方法"));<br />&nbsp;&nbsp;&nbsp; outFile.close();</p>
<p>&nbsp;&nbsp;&nbsp; //在文件的末尾写入数据，先将文件的指针移到末尾<br />&nbsp;&nbsp;&nbsp; outFile.open("2.txt",ios::app);<br />&nbsp;&nbsp;&nbsp; outFile.seekp(0,ios::end);<br />&nbsp;&nbsp;&nbsp; outFile.write(",重复写一次：c++对文件的操作方法",strlen(",重复写一次：c++对文件的操作方法"));<br />&nbsp;&nbsp;&nbsp; outFile.close();</p>
<p>&nbsp;&nbsp;&nbsp; //读取文件的内容，并将其显示在屏幕上<br />&nbsp;&nbsp;&nbsp; ifstream inFile;<br />&nbsp;&nbsp;&nbsp; inFile.open("2.txt",ios::in);<br />&nbsp;&nbsp;&nbsp; char buffer[100];<br />&nbsp;&nbsp;&nbsp; inFile.read(buffer,99);<br />&nbsp;&nbsp;&nbsp; buffer[99]='';<br />&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;100;i++)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout&lt;&lt;buffer[i];<br />&nbsp;&nbsp;&nbsp; inFile.close();</p>
<p>&nbsp;&nbsp;&nbsp; return 0;<br />}<br /></p><img src ="http://www.cppblog.com/Cass/aggbug/161249.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-11-30 20:40 <a href="http://www.cppblog.com/Cass/archive/2011/11/30/161249.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于类的作用域 （全局域 、类域、作用域）</title><link>http://www.cppblog.com/Cass/archive/2011/11/30/161247.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Wed, 30 Nov 2011 12:33:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/11/30/161247.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/161247.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/11/30/161247.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/161247.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/161247.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: （1）、成员函数<br>成员函数有一个非成员函数不具有的属性——它的类itsclass 指向成员函数的指针必须与向其赋值的函数类型匹配不是两个而是三个方面都要匹配：<br>1 参数的类型和个数2 返回类型3 它所属的类类型<br><br>例如类screen：short Screen::*ps_Screen = &Screen::_height;<br><br>数据成员指针在被用来访问数据成员之前必须先被绑定到一个对象或指针上<br><br>// 所有指向类成员的指针都可以用0 赋值<br>int (Screen::*pmf1)() = 0;<br>int (Screen::*pmf2)() = &Screen::height;//或者可以这样写：int Screen::*pmf2 = &Screen::height;<br>注意：静态类成员指针是该类的全局对象和函数，引用的是普通指针<br><br><br>(2)作用域<br><br><br>1.全局域、类域、局部域的区别<br><br>&nbsp;&nbsp;<a href='http://www.cppblog.com/Cass/archive/2011/11/30/161247.html'>阅读全文</a><img src ="http://www.cppblog.com/Cass/aggbug/161247.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-11-30 20:33 <a href="http://www.cppblog.com/Cass/archive/2011/11/30/161247.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的一些基础</title><link>http://www.cppblog.com/Cass/archive/2011/11/30/161246.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Wed, 30 Nov 2011 12:32:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/11/30/161246.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/161246.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/11/30/161246.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/161246.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/161246.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一、类型转换<br><br>1、强制类型转换：：类型不同，而且不属于基本数据类型(int double...)时，经常需要强制类型转换<br>①、显示强制类型转换<br>TYPE b = (TYPE) a；<br><br>C++中强制类型转换函数有4个：<br>const_cast(用于去除const属性），<br>static_cast(用于基本类型的强制转换），<br>dynamic_cast(用于多态类型之间的类型转换），<br><br>&nbsp;&nbsp;<a href='http://www.cppblog.com/Cass/archive/2011/11/30/161246.html'>阅读全文</a><img src ="http://www.cppblog.com/Cass/aggbug/161246.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-11-30 20:32 <a href="http://www.cppblog.com/Cass/archive/2011/11/30/161246.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++内存中的数据对齐问题</title><link>http://www.cppblog.com/Cass/archive/2011/10/01/157281.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Sat, 01 Oct 2011 02:13:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/10/01/157281.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/157281.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/10/01/157281.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/157281.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/157281.html</trackback:ping><description><![CDATA[<p>数据对齐，是指数据所在的内存地址必须是该数据长度的整数倍。比如DWORD数据的内存其实地址能被4除尽，WORD数据的内存地址能被2除尽。x86 CPU能直接访问对齐的数据，当它试图访问一个未对齐的数据时，会在内部进行一系列的调整，这些调整对于程序来说是透明的，但是会降低运行速度，所以编译器在编译程序时会尽量保持数据对齐。</p>
<p>C/C++编译器在内存分配时也保持了数据对齐，请看下例：</p>
<p>struct{</p>
<p>short a1;</p>
<p>short a2;</p>
<p>short a3;</p>
<p>}A;</p>
<p>struct{</p>
<p>long&nbsp;<wbr> a1;</p>
<p>short a2;</p>
<p>}B;</p>
<p>cout&lt;&lt;sizeof(A)&lt;&lt;","&lt;&lt;sizeof(B)&lt;&lt;endl;//其它代码略去</p>
<p>结构体A和B的大小分别是多少呢？</p>
<p>默认情况下，为了方便对结构体元素的访问和管理，当结构体内的元素都小于处理器长度的时候，便以结构体里面最长的数据为对齐单位，也就是说，<strong>结构体的长度一定是最长数据长度的整数倍。</strong></p>
<p>如果结构体内部存在长度大于处理器位数时就以处理器位数为对齐单位。</p>
<p>结构体内类型相同的连续元素将存在连续的空间内，和数组一样。</p>
<p>上例中:</p>
<p>A有3个short类型变量，各自占2字节，总和为6，6是2的倍数，所以sizeof(A)=6;</p>
<p>B有一个long类型变量，占4字节，一个short类型的变量，占2字节，总和6不是最大长度4的倍数，所以要补空字节以增至8实现对齐，所以sizeof(8)=8。</p>
<p>&nbsp;<wbr></p>
<p>在C++类的设计中遵循同样的道理，但需注意，空类需要占1个字节，静态变量(static)由于在栈中分配，不在sizeof计算范围内。</p><img src ="http://www.cppblog.com/Cass/aggbug/157281.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-10-01 10:13 <a href="http://www.cppblog.com/Cass/archive/2011/10/01/157281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚函数和多态 (二)</title><link>http://www.cppblog.com/Cass/archive/2011/09/30/157256.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Fri, 30 Sep 2011 15:17:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/09/30/157256.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/157256.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/09/30/157256.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/157256.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/157256.html</trackback:ping><description><![CDATA[<strong>
<h3 class="headline-2 bk-sidecatalog-title"><span style="color: red" class="headline-content"><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium Simsun; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 23px; font-family: simsun; font-size: 14px" class="Apple-style-span">多态性，这个面向对象编程领域的核心概念，本身的内容博大精深，要以一文说清楚实在是不太可能。加之作者本人也还在不断学习中，水平有限。因此本文只能描一下多态的轮廓，使读者能够了解个大概。如果有描的不准的地方，欢迎指出，或与作者探讨（作者Email：nicrosoft@sunistudio.com）<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 首先，什么是多态（Polymorphisn）？按字面的意思就是&#8220;多种形状&#8221;。我手头的书上没有找到一个多态的理论性的概念的描述。暂且引用一下Charlie &nbsp; Calverts的对多态的描述吧&#8212;&#8212;多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术，赋值之后，父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作（摘自&#8220;Delphi4 &nbsp; 编程技术内幕&#8221;）。简单的说，就是一句话：允许将子类类型的指针赋值给父类类型的指针。多态性在Object &nbsp; Pascal和C++中都是通过虚函数（Virtual &nbsp; Function）实现的。<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 好，接着是&#8220;虚函数&#8221;（或者是&#8220;虚方法&#8221;）。虚函数就是允许被其子类重新定义的成员函数。而子类重新定义父类虚函数的做法，称为&#8220;覆盖&#8221;（override），或者称为&#8220;重写&#8221;。<span class="Apple-converted-space">&nbsp;</span><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; 这里有一个初学者经常混淆的概念。覆盖（override）和重载（overload）。上面说了，覆盖是指子类重新定义父类的虚函数的做法。而重载，是指允许存在多个同名函数，而这些函数的参数表不同（或许参数个数不同，或许参数类型不同，或许两者都不同）。其实，重载的概念并不属于&#8220;面向对象编程&#8221;，重载的实现是：编译器根据函数不同的参数表，对同名函数的名称做修饰，然后这些同名函数就成了不同的函数（至少对于编译器来说是这样的）。如，有两个同名函数：function &nbsp; func(p:integer):integer;和function &nbsp; func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的：int_func、str_func。对于这两个函数的调用，在编译器间就已经确定了，是静态的（记住：是静态）。也就是说，它们的地址在编译期就绑定了（早绑定），因此，重载和多态无关！真正和多态相关的是&#8220;覆盖&#8221;。当子类重新定义了父类的虚函数后，父类指针根据赋给它的不同的子类指针，动态（记住：是动态！）的调用属于子类的该函数，这样的函数调用在编译期间是无法确定的（调用的子类的虚函数的地址无法给出）。因此，这样的函数地址是在运行期绑定的（晚邦定）。结论就是：重载只是一种语言特性，与多态无关，与面向对象也无关！<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 引用一句Bruce &nbsp; Eckel的话：&#8220;不要犯傻，如果它不是晚邦定，它就不是多态。&#8221;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 那么，多态的作用是什么呢？我们知道，封装可以隐藏实现细节，使得代码模块化；继承可以扩展已存在的代码模块（类）；它们的目的都是为了&#8212;&#8212;代码重用。而多态则是为了实现另一个目的&#8212;&#8212;接口重用！而且现实往往是，要有效重用代码很难，而真正最具有价值的重用是接口重用，因为&#8220;接口是公司最有价值的资源。设计接口比用一堆类来实现这个接口更费时间。而且接口需要耗费更昂贵的人力的时间。&#8221;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 其实，继承的为重用代码而存在的理由已经越来越薄弱，因为&#8220;组合&#8221;可以很好的取代继承的扩展现有代码的功能，而且&#8220;组合&#8221;的表现更好（至少可以防止&#8220;类爆炸&#8221;）。因此笔者个人认为，继承的存在很大程度上是作为&#8220;多态&#8221;的基础而非扩展现有代码的方式了。<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 什么是接口重用？我们举一个简单的例子，假设我们有一个描述飞机的基类（Object &nbsp; Pascal语言描述，下同）：<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; type<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; plane &nbsp; = &nbsp; class<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; fly(); &nbsp; virtual; &nbsp; abstract; &nbsp; //起飞纯虚函数<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; land(); &nbsp; virtual; &nbsp; abstract; &nbsp; //着陆纯虚函数<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; function &nbsp; modal() &nbsp; : &nbsp; string; &nbsp; virtual; &nbsp; abstract; &nbsp; //查寻型号纯虚函数<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 然后，我们从plane派生出两个子类，直升机（copter）和喷气式飞机（jet）：<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; copter &nbsp; = &nbsp; class(plane)<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fModal &nbsp; : &nbsp; String;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; constructor &nbsp; Create();<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; destructor &nbsp; Destroy(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; fly(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; land(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; function &nbsp; modal() &nbsp; : &nbsp; string; &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; jet &nbsp; = &nbsp; class(plane)<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fModal &nbsp; : &nbsp; String;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; constructor &nbsp; Create();<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; destructor &nbsp; Destroy(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; fly(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; land(); &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; function &nbsp; modal() &nbsp; : &nbsp; string; &nbsp; override;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 现在，我们要完成一个飞机控制系统，有一个全局的函数 &nbsp; plane_fly，它负责让传递给它的飞机起飞，那么，只需要这样：<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; procedure &nbsp; plane_fly(const &nbsp; pplane &nbsp; : &nbsp; plane);<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; begin<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pplane.fly();<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; end;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 就可以让所有传给它的飞机（plane的子类对象）正常起飞！不管是直升机还是喷气机，甚至是现在还不存在的，以后会增加的飞碟。因为，每个子类都已经定义了自己的起飞方式。<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 可以看到 &nbsp; plane_fly函数接受参数的是 &nbsp; plane类对象引用，而实际传递给它的都是 &nbsp; plane的子类对象，现在回想一下开头所描述的&#8220;多态&#8221;：多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术，赋值之后，父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 很显然，parent &nbsp; = &nbsp; child; &nbsp; 就是多态的实质！因为直升机&#8220;是一种&#8221;飞机，喷气机也&#8220;是一种&#8221;飞机，因此，所有对飞机的操作，都可以对它们操作，此时，飞机类就作为一种接口。<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><br />&nbsp; &nbsp; &nbsp; &nbsp; 多态的本质就是将子类类型的指针赋值给父类类型的指针（在OP中是引用），只要这样的赋值发生了，多态也就产生了，因为实行了&#8220;向上映射&#8221;。</span></span><br /><br /><br /><br /><br /><br /><br />多态性</span></h3>　　是允许<strong>将父对象设置成为和</strong>一个或多个<strong>它的子对象相等</strong>的技术，比如Parent:=Child； 多态性使得能够<strong>利用同一类</strong>(基类)类型<strong>的指针</strong>来<strong>引用不同类的对象</strong>,以及<strong>根据所引用对象的不同</strong>,<strong>以不同的方式执行相同的操作.</strong> 
<h3 class="headline-2 bk-sidecatalog-title"><span class="headline-content">c++中多态更容易理解的概念为</span></h3>　　<strong>允许父类指针或名称</strong>来<strong>引用子类对象</strong>，或对象方法，而实际调用的方法为对象的类类型方法。<br /><span style="color: red" class="headline-content">作用</span>　　<br />把不同的子类对象都当作父类来看，可以屏蔽不同子类对象之间的差异，写出通用的代码，做出通用的编程，以适应需求的不断变化。 
<div class="spctrl"></div>　　赋值之后，父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说，父亲的行为像儿子，而不是儿子的行为像父亲。 
<div class="spctrl"></div>　　举个例子：从一个基类中派生，响应一个虚命令，产生不同的结果。 
<div class="spctrl"></div>　　比如从某个基类继承出多个对象，其基类有一个虚方法Tdoit，然后其子类也有这个方法，但行为不同，然后这些子对象中的任何一个可以赋给其基类的对象，这样其基类的对象就可以执行不同的操作了。实际上你是在通过其基类来访问其子对象的，你要做的就是一个赋值操作。 
<div class="spctrl"></div>　　使用继承性的结果就是可以创建一个类的家族，在认识这个类的家族时，就是把导出类的对象当作基类的对象，这种认识又叫作upcasting。这样认识的重要性在于：我们可以只针对基类写出一段程序，但它可以适应于这个类的家族，因为<a href="http://baike.baidu.com/view/487018.htm" target="_blank"><font color="#136ec2">编译器</font></a>会自动就找出合适的对象来执行操作。这种现象又称为多态性。而实现多态性的手段又叫称动态绑定(dynamic binding)。 
<div class="spctrl"></div>　　简单的说，建立一个父类的<a href="http://baike.baidu.com/view/2387.htm" target="_blank"><font color="#136ec2">对象</font></a>，它的内容可以是这个父类的，也可以是它的子类的,当子类拥有和父类同样的<a href="http://baike.baidu.com/view/15061.htm" target="_blank"><font color="#136ec2">函数</font></a>，当使用这个对象调用这个函数的时候，定义这个对象的类（也就是父类）里的同名函数将被调用，当在父类里的这个函数前加virtual关键字，那么子类的同名函数将被调用。</strong> <img src ="http://www.cppblog.com/Cass/aggbug/157256.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-09-30 23:17 <a href="http://www.cppblog.com/Cass/archive/2011/09/30/157256.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚函数和多态 (一)</title><link>http://www.cppblog.com/Cass/archive/2011/09/30/157249.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Fri, 30 Sep 2011 13:58:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/09/30/157249.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/157249.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/09/30/157249.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/157249.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/157249.html</trackback:ping><description><![CDATA[<div>1、什么是虚函数？<br />&#9312;、虚函数必须是基类的非<font color="#136ec2">静态成员</font>函数<br />&#9313;、其访问权限可以是protected或public。不能是private ，因为子类继承时，子类不能访问。<br />&#9314;、在编译时是动态联编的：：编译程序在编译阶段并不能确切知道将要调用的函数，只有在<strong>程序执行时</strong>才能确定将要调用的函数，为此要确切知道该调用的函数，要求联编工作要在程序运行时进行，这种在程序运行时进行联编工作被称为动态联编。 <span style="color: red">动态联编规定，只能通过指向基类的指针或基类对象的引用来调用虚函数</span><br /><br />2、定义形式。<br />virtual 函数返回值类型 虚函数名（形参表） 
<div class="spctrl"></div>　　{ 函数体 }<br /><br /><span style="color: red">纯虚函数</span>：virtual 函数名=0&nbsp;&nbsp;&nbsp; <br /><br />3、虚函数内部机制。<br />&#9312;、每个实例对象里有自己的指针。<br />&#9313;、虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。<br />&#9314;、我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。<br /><span style="color: red">例子：</span><br />&nbsp; 
<p>假设我们有这样的一个类： </p>
<p>class Base { </p>
<p>public: </p>
<p>virtual void f() { cout &lt;&lt; "Base::f" &lt;&lt; endl; } </p>
<p>virtual void g() { cout &lt;&lt; "Base::g" &lt;&lt; endl; } </p>
<p>virtual void h() { cout &lt;&lt; "Base::h" &lt;&lt; endl; } </p>
<p>}; </p>
<p>按照上面的说法，我们可以通过Base的实例来得到虚函数表。 下面是实际例程： </p>
<p>typedef void(*Fun)(void); </p>
<p>Base b; </p>
<p>Fun pFun = NULL; </p>
<p>cout &lt;&lt; "虚函数表地址：" &lt;&lt; (int*)(&amp;b) &lt;&lt; endl; </p>
<p>cout &lt;&lt; "虚函数表 &#8212; 第一个函数地址：" &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl; </p>
<p>/*这里的一点争议的个人看法*/</p>
<p>原文认为<span style="color: red">(int*)(&amp;b)</span>是虚表的地址，而很多网友都说，（包括我也认为）：<span style="color: red">(int *)*(int*)(&amp;b)</span>才是虚表地址</p>
<p>而<span style="color: red">(int*)*((int*)*(int*)(&amp;b))</span>; 才是虚表第一个虚函数的地址。</p>
<p>其实看后面的调用pFun = (Fun)*((int*)*(int*)(&amp;b)); 就可以看出，*((int*)*(int*)(&amp;b));转成函数指针给pFun，然后正确的调用到了虚函数virtual void f()。</p>
<p>// Invoke the first virtual function </p>
<p>pFun = (Fun)*((int*)*(int*)(&amp;b)); </p>
<p>pFun(); </p>
<p>实际运行经果如下：(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3) </p>
<p>虚函数表地址：0012FED4 </p>
<p>虚函数表 &#8212; 第一个函数地址：0044F148 </p>
<p>Base::f </p>
<p>通过这个示例，我们可以看到，我们可以通过强行把&amp;b转成int *，取得虚函数表的地址，然后，再次取址就可以得到第一个虚函数的地址了，也就是Base::f()，这在上面的程序中得到了验证（把int* 强制转成了函数指针）。通过这个示例，我们就可以知道如果要调用Base::g()和Base::h()，其代码如下： </p>
<p>(Fun)*((int*)*(int*)(&amp;b)+0); // Base::f() </p>
<p>(Fun)*((int*)*(int*)(&amp;b)+1); // Base::g() </p>
<p>(Fun)*((int*)*(int*)(&amp;b)+2); // Base::h() </p>
<p>这个时候你应该懂了吧。什么？还是有点晕。也是，这样的代码看着太乱了。没问题，让我画个图解释一下。如下所示： </p>
<p>&nbsp;</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_vtable1.jpg" width="331" height="129" /><br />注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符&#8220;\0&#8221;一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。 </p>
<p>下面，我将分别说明&#8220;无覆盖&#8221;和&#8220;有覆盖&#8221;时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。 <br /></p>
<p><strong style="color: red; font-size: 18pt">一般继承（无虚函数覆盖）</strong><br />下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系： </p>
<p>&nbsp;</p>
<p><img style="width: 78px; height: 194px" border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_Drawing3.jpg" width="78" height="194" /><br />请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示： </p>
<p>对于实例：Derive d; 的虚函数表如下： </p>
<p>&nbsp;</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_vtable2.jpg" width="551" height="124" /><br />我们可以看到下面几点： </p>
<p>1）虚函数按照其声明顺序放于表中。 </p>
<p>2）父类的虚函数在子类的虚函数前面。 </p>
<p>我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。 </p>
<p><strong style="color: red; font-size: 18pt">一般继承（有虚函数覆盖）</strong><br />覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。 </p>
<p>&nbsp;</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_Drawing4.jpg" width="78" height="194" /><br />为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：&nbsp;<br /></p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_vtable3.jpg" width="500" height="124" /><br />我们从表中可以看到下面几点， </p>
<p>1）覆盖的f()函数被放到了虚表中原来父类虚函数的位置。 </p>
<p>2）没有被覆盖的函数依旧。 </p>
<p>这样，我们就可以看到对于下面这样的程序， </p>
<p>Base *b = new Derive(); </p>
<p>b-&gt;f(); </p>
<p>由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代，于是在实际调用发生时，是Derive::f()被调用了。这就实现了<span style="color: red">多态</span>。 </p>
<p><strong style="color: red; font-size: 18pt">多重继承（无虚函数覆盖）</strong><strong><br /></strong>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。 </p>
<p>&nbsp;</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_Drawing1.jpg" width="282" height="192" /><br />对于子类实例中的虚函数表，是下面这个样子： </p>
<p>&nbsp;</p>
<p><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_vtable4.jpg" width="493" height="173" />我们可以看到： </p>
<p>1） 每个父类都有自己的虚表。 </p>
<p>2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的） </p>
<p>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。 </p>
<p><strong style="color: red; font-size: 18pt">多重继承（有虚函数覆盖）</strong><strong><br /></strong>下面我们再来看看，如果发生虚函数覆盖的情况。 </p>
<p>下图中，我们在子类中覆盖了父类的f()函数：&nbsp;</p>
<p><br /><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_Drawing2.jpg" width="282" height="192" /><br />下面是对于子类实例中的虚函数表的图：&nbsp;<br /><img border="0" alt="" src="http://images.cnblogs.com/cnblogs_com/myzone2009/o_vtable5.jpg" width="420" height="173" /><br />我们可以看见，三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样，我们就可以任一静态类型的父类来指向子类，并调用子类的f()了。如： </p>
<p>Derive d; </p>
<p>Base1 *b1 = &amp;d; </p>
<p>Base2 *b2 = &amp;d; </p>
<p>Base3 *b3 = &amp;d; </p>
<p>b1-&gt;f(); //Derive::f() </p>
<p>b2-&gt;f(); //Derive::f() </p>
<p>b3-&gt;f(); //Derive::f() </p>
<p>b1-&gt;g(); //Base1::g() </p>
<p>b2-&gt;g(); //Base2::g() </p>
<p>b3-&gt;g(); //Base3::g() </p>
<p><strong>安全性<br /></strong>每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。 </p>
<p>一、通过父类型的指针访问子类自己的虚函数 </p>
<p>我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数： </p>
<p>Base1 *b1 = new Derive(); </p>
<p>b1-&gt;f1(); //编译出错 </p>
<p>任何妄图<span style="color: red">使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法</span>，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点） </p>
<p>二、访问non-public的虚函数 </p>
<p>另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。 </p>
<p>如： </p>
<p>class Base { </p>
<p>private: </p>
<p>virtual void f() { cout &lt;&lt; "Base::f" &lt;&lt; endl; } </p>
<p>}; </p>
<p>class Derive : public Base{ </p>
<p>}; </p>
<p>typedef void(*Fun)(void); </p>
<p>void main() { </p>
<p>Derive d; </p>
<p>Fun pFun = (Fun)*((int*)*(int*)(&amp;d)+0); </p>
<p>pFun(); </p>
<p>} </p>
<p>结束语<br />C++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。</p>
<p><br />本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/hairetz/archive/2009/04/29/4137000.aspx</p></div> <img src ="http://www.cppblog.com/Cass/aggbug/157249.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-09-30 21:58 <a href="http://www.cppblog.com/Cass/archive/2011/09/30/157249.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>类继承和子类型 多继承和虚拟继承</title><link>http://www.cppblog.com/Cass/archive/2011/09/30/157232.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Fri, 30 Sep 2011 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/09/30/157232.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/157232.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/09/30/157232.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/157232.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/157232.html</trackback:ping><description><![CDATA[<span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium Simsun; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class="Apple-style-span"><span style="text-align: left; line-height: 26px; font-family: Arial; color: rgb(51,51,51); font-size: 14px" class="Apple-style-span">
<div style="text-indent: -18pt; margin-left: 18pt"><span>//转自网友博客、<br />1、&nbsp;</span>派生类对象与普通类对象的相同之处在于，可以直接访问该类的所有对象（包括this指针指向的对象和其他对象）的protected和private成员（包括其基类成员）。不同之处在于派生类对象只能访问其对应基类对象的protected成员（有隐式this指针传递），而不能访问其基类的其他对象的protect成员，而普通类对象则也可以直接访问该类所有对象的成员。</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>2、&nbsp;</span>在C++中，基类指针只能访问在该基类中被声明（或继承）的数据成员和成员函数（包括虚拟成员函数），而与它可能指向的实际对象无关，所以如果需要用基类指针来访问一个没有在该基类中声明但是又在其派生类中定义了的成员，则需要执行dynamic_cast来完成从基类指针到派生类指针的安全向下转换。把一个成员声明为虚拟的，只推延了&#8220;在程序执行期间根据指针指向的实际类类型，对于要调用实例的解析过程&#8221;</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>3、&nbsp;</span>关于基类，派生类的相关补充：</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>1、&nbsp;</span>派生表中指定的类必须先被定义好，方可被指定为基类。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>2、&nbsp;</span>派生类的前向声明不能包括其派生表，而只需要类名即可。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>3、&nbsp;</span>缺省的继承是private。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>4、&nbsp;</span>继承而来的派生类的虚拟函数一般加上virtual较好，也可以省略。但基类中一定要声明为virtual。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>5、&nbsp;</span>对于基类的静态成员，所有派生类对象都引用基类创建的这个相同，单一，共享的静态成员，而不是创建该派生类的另一个独立的静态成员。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>6、&nbsp;</span>友员关系不会被继承，派生类没有成为&#8220;向它的基类授权友谊的类&#8221;的友员。</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>4、&nbsp;</span>继承机制下，派生类对象的构造函数（析构函数）调用顺序为：</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>1、&nbsp;</span>基类（子对象的）构造函数，若有多个基类，则以类派生表中出现的顺序为序。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>2、&nbsp;</span>成员类对象的构造函数，若有多个成员类对象，则以它们在类定义中被声明的顺序为序。</div>
<div style="margin-left: 21pt">3、派生类自己的构造函数。</div>
<div style="text-indent: -15.75pt; margin-left: 36.75pt">4、派生类对象的析构函数的调用顺序与它的构造函数相反。继承机制下，析构函数的行为如下：派生类的析构函数先被调用，再静态调用基类的析构函数（从直接基类开始）。注意一般基类的析构函数不应该是protected，因为虚拟函数承接了&#8220;调用者所属类类型的访问级别&#8221;。作为一般规则，我们建议将类层次结构的根基类（声明了一个或多个虚拟函数）的析构函数声明为虚拟的。</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>5、&nbsp;</span>关于继承机制下基类构造函数（析构函数）相关的几点说明：</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>1、&nbsp;</span>作为一般规则，派生类构造函数应不能直接向一个基类的数据成员赋值，而是要把值传递给适当的基类构造函数来达到初始化赋值的目的。（一般是通过成员初始化表的方式）</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>2、&nbsp;</span>若基类不用于创建对象，则最好将其构造函数放在protect区，只允许其派生类对象调用；若基类只允许创建某一个特定的派生类类型的对象，则应该将基类的构造函数放在private区，并将此特定的派生类声明为该基类的友元来达到目的。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>3、&nbsp;</span>派生类并不继承基类的构造函数，每个派生类都必须提供自己的构造函数集，派生类的构造函数只能合法的调用其直接基类的构造函数。（注意这里虚拟继承提供了一个特例：虚拟基类的初始化变成了最终派生类的责任）。</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>6、&nbsp;</span>关于虚拟函数的相关</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>1、&nbsp;</span>必须使用指针或者引用来支持虚拟函数机制（面向对象程序设计），基类对象由于其静态编译，故不会保留派生类的类型身份。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>2、&nbsp;</span>第一次引入虚拟函数的基类时，必须在类体中将虚拟函数声明为virtual，但若在该基类外部定义该虚拟函数时不能指定virtual。该基类的派生类中该虚拟函数virtual可加可不加，但从多重继承考虑，最好加上。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>3、&nbsp;</span>派生类改写的基类虚拟函数，其原型必须与基类虚拟函数完全匹配（包括const和返回值），但返回值有个特例：派生类实例的返回值可以是基类实例返回类型的公有派生类类型。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>4、&nbsp;</span>纯虚拟函数（声明后紧跟=0，函数定义可写可不写）只是提供了一个可被其派生类改写的接口，其本身不能通过虚拟机制被调用，但可以静态调用（写了函数定义的虚基类的纯虚拟函数）。一般来说，虚拟函数的静态调用的目的是为了效率（避免动态绑定）。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>5、&nbsp;</span>包含（或继承）了一个或多个纯虚拟函数的类被编译器识别为抽象基类，抽象基类不能用来创建独立的类对象，只能作为子对象出现在后续的派生类中。</div>
<div style="text-indent: -15.75pt; margin-left: 36.75pt">6、通过基类指针来调用的虚拟函数的真正实例是在运行时刻确定的。但传给虚拟函数的缺省实参是在编译时刻根据被调用函数的对象的类型决定的（也即是若通过基类指针或引用调用派生类实例的虚拟函数，则传递给它的缺省实参是由基类指定的）。</div>
<div>&nbsp;</div>
<div style="text-indent: -18pt; margin-left: 18pt"><span>7、&nbsp;</span>虚拟继承和多继承相关：</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>1、&nbsp;</span>虚拟继承主要实为了解决继承了多个基类实例，但是只需要一份单独的共享实例的情况。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>2、&nbsp;</span>非虚拟派生中，派生类只能显式的初始化其直接基类（即派生类只能调用其直接基类的构造函数），而在虚拟派生中，虚拟基类的初始化变成了最终派生类的责任，这个最终派生类是由每个特定的类对象声明来决定的，其非虚拟基类的初始化同非虚拟派生一样，只能由其直接派生类完成。（即中间派生类的对于虚拟基类构造函数的调用被抑制）。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>3、&nbsp;</span>虚拟继承下构造函数的调用顺序按直接基类的声明顺序，对每个继承子树作深度优先遍历。第一步按此顺序调用所有虚拟基类的构造函数；第二步按此顺序调用非虚拟基类的构造函数。析构函数的调用顺序与构造函数相反。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>4、&nbsp;</span>虚拟基类成员的可视性，对于虚拟基类成员的继承比该成员后来重新定义的实例权值（优先级）小，故特化的派生类实例名覆盖了共享的虚拟基类的实例名。而在非虚拟派生下的解析引用过程，每个继承得到的实例都有相同的权值（优先级）。</div>
<div style="text-indent: -18pt; margin-left: 39pt"><span>5、&nbsp;</span>继承下派生类的类域被嵌套在基类类域中，若一个名字在派生类域中没有被解析出来，则编译器在外围基类域中查找该名字定义。在多继承下，名字解析查找过程为先是在本类类域中查找，再对继承子树中的所有基类同时查找，每个继承得到的实例都有相同的权值（优先级）。若在两个或多个基类子树中都找到了该名字，则对其的使用是二义的。</div></span></span><img src ="http://www.cppblog.com/Cass/aggbug/157232.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-09-30 16:18 <a href="http://www.cppblog.com/Cass/archive/2011/09/30/157232.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深拷贝、浅拷贝 与拷贝构造函数的关系</title><link>http://www.cppblog.com/Cass/archive/2011/09/27/156903.html</link><dc:creator>Yu_</dc:creator><author>Yu_</author><pubDate>Mon, 26 Sep 2011 17:24:00 GMT</pubDate><guid>http://www.cppblog.com/Cass/archive/2011/09/27/156903.html</guid><wfw:comment>http://www.cppblog.com/Cass/comments/156903.html</wfw:comment><comments>http://www.cppblog.com/Cass/archive/2011/09/27/156903.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cass/comments/commentRss/156903.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cass/services/trackbacks/156903.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 拷贝即是通常所说的复制(Copy)或克隆(Clone)，对象的拷贝也就是从现有对象复制一个&#8220;一模一样&#8221;的新对象出来。虽然都是复制对象，但是不同的复制方法，复制出来的新对象却并非完全一模一样，对象内部存在着一些差异。通常的拷贝方法有两种，即深拷贝和浅拷贝，那二者之间有何区别呢？<br />我的理解是：<br /><br />1、深拷贝和浅拷贝之间的区别在于是否复制了子对象。<br />2、如果一个类拥有资源(堆，或者是其它系统资源)，，当这个类的对象发生复制过程的时候，资源重新分配，这个过程就是深拷贝，反之对象存在资源，但复制过程并未复制资源的情况视为浅拷贝。<br /><br />
<p><font face="Verdana">当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候，拷贝构造函数就会被自动调用。也就是说，当类的对象需要拷贝时，拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数：<br /></font><font face="Verdana">&#9312;、一个对象以值传递的方式传入函数体：这个好理解，因为传递给函数体的参数不是&nbsp; str 而是 _str&nbsp; ,是str的复制品。所以必然会调用拷贝构造函数。&nbsp;<br /></font><font face="Verdana">&#9313;、一个对象以值传递的方式从函数返回 ：相当于构造一个新的对象。<br /></font><font face="Verdana">&#9314;、一个对象需要通过另外一个对象进行初始化。：同上、<br /></font><br />正如您理解那样 &#8220;浅拷贝：只拷贝对象的基本属性，其他的引用不拷贝，还是保留引用&#8221;如果在类中没有显式地声明一个拷贝构造函数，那么，编译器将会自动生成一个默认的拷贝构造函数，该构造函数完成对象之间的位拷贝。当对象没有指针时，按照上面的规则，则一切正常，浅拷贝把数据复制过新对象。但当对象有指针时，因为浅拷贝引用不拷贝，所以新对象与旧对象他们指向的是同一个内存区，这时当释放内存时就出现释放两次，出错了。<br /><br />这时需要深拷贝..................<br />所以通常我们需要自己写拷贝构造函数，以免出现错误。<br />//////////////参考资料所得，正确与否欢迎讨论、<br /><br /></p>
<p><font face="Verdana"></font>&nbsp;</p><img src ="http://www.cppblog.com/Cass/aggbug/156903.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cass/" target="_blank">Yu_</a> 2011-09-27 01:24 <a href="http://www.cppblog.com/Cass/archive/2011/09/27/156903.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>