﻿<?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++博客-ivy-jie-文章分类-c++</title><link>http://www.cppblog.com/ivy-jie/category/10520.html</link><description>progress ...</description><language>zh-cn</language><lastBuildDate>Sun, 28 Jun 2009 07:55:02 GMT</lastBuildDate><pubDate>Sun, 28 Jun 2009 07:55:02 GMT</pubDate><ttl>60</ttl><item><title>有意思的矩阵输出题</title><link>http://www.cppblog.com/ivy-jie/articles/88687.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sun, 28 Jun 2009 04:04:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/88687.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/88687.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/88687.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/88687.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/88687.html</trackback:ping><description><![CDATA[<p>题目要求:<br>用C编写程序并注释<br>N*N矩阵输出<br>N=3时输出<br>1 2 3<br>&nbsp; 8 9 4<br>&nbsp; 7 6 5<br>N＝4时输出<br>&nbsp;&nbsp; 1&nbsp; 2&nbsp; 3&nbsp;&nbsp; 4 <br>&nbsp;&nbsp; 12 13 14&nbsp; 5<br>&nbsp;&nbsp; 11 16 15&nbsp; 6<br>&nbsp;&nbsp; 10&nbsp; 9&nbsp; 8&nbsp; 7</p>
<p>编写当N时的输出<br><br>代码:<br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;string.h&gt;</p>
<p>enum {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MASK_UP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MASK_DOWN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 2,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MASK_LEFT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MASK_RIGHT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_NONE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_UP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_DOWN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 2,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_LEFT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 3,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_RIGHT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4,<br>};</p>
<p>int directs[5][2] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, 0},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 0:TURN_NONE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {-1, 0},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 1:TURN_UP<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {1, 0},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 2:TURN_DOWN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, -1},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3:TURN_LEFT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, 1},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 4:TURN_RIGHT<br>};</p>
<p>int rules[16] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_NONE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 0:none<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_UP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 1:up<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_DOWN,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 2:down<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_NONE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 3:invalid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_LEFT,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 4:left<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_LEFT,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 5:left and up<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_DOWN,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 6:left and down<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_NONE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 7:invalid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_RIGHT,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 8:right<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_UP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 9:right and up<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_RIGHT,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 10:right and down<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TURN_NONE,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 11-15:invalid<br>};<br>int next_pos(int *matrix, int n, int i, int j, int *nexti, int *nextj)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i == -1 || j == -1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *nexti = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *nextj = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mask = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i - 1 &gt;= 0 &amp;&amp; matrix[(i - 1) * n + j] == 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // up ok<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask |= MASK_UP;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i + 1 &lt;= n - 1 &amp;&amp; matrix[(i + 1) * n + j] == 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // down ok<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask |= MASK_DOWN;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (j - 1 &gt;= 0 &amp;&amp; matrix[i * n + (j - 1)] == 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // left ok<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask |= MASK_LEFT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (j + 1 &lt;= n - 1 &amp;&amp; matrix[i * n + (j + 1)] == 0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // right ok<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mask |= MASK_RIGHT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int rule = rules[mask];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int *direct = directs[rule];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (direct[0] == 0 &amp;&amp; direct[1] == 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *nexti = i + direct[0];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *nextj = j + direct[1];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 1;<br>}</p>
<p>int main(int argc, char *argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (argc != 2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int n = atoi(argv[1]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (n &lt;= 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int *matrix = (int*)malloc(n * n * sizeof(int));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(matrix, 0, n * n * sizeof(int));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int j = -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int value = 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (next_pos(matrix, n, i, j, &amp;i, &amp;j) != 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; matrix[i * n + j] = value++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; n; i++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (j = 0; j &lt; n; j++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%d", matrix[i * n + j]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (j != n - 1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("\t");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("\n");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; free(matrix);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/88687.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-28 12:04 <a href="http://www.cppblog.com/ivy-jie/articles/88687.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚继承与虚基类的本质</title><link>http://www.cppblog.com/ivy-jie/articles/87104.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 08 Jun 2009 14:34:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87104.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87104.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87104.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87104.html</trackback:ping><description><![CDATA[<div>虚继承与虚基类的本质<br>&nbsp;&nbsp;&nbsp; 虚继承和虚基类的定义是非常的简单的，同时也是非常容易判断一个继承是否是虚继承<br>的，虽然这两个概念的定义是非常的简单明确的，但是在C++语言中虚继承作为一个比较生<br>僻的但是又是绝对必要的组成部份而存在着，并且其行为和模型均表现出和一般的继承体系<br>之间的巨大的差异（包括访问性能上的差异），现在我们就来彻底的从语言、模型、性能和<br>应用等多个方面对虚继承和虚基类进行研究。<br>&nbsp;&nbsp;&nbsp; 首先还是先给出虚继承和虚基类的定义。<br>&nbsp;&nbsp;&nbsp; 虚继承：在继承定义中包含了virtual关键字的继承关系；<br>&nbsp;&nbsp;&nbsp; 虚基类：在虚继承体系中的通过virtual继承而来的基类，需要注意的是：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CSubClass : public virtual CBase {}; 其中CBase称之为CSubClass<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 的虚基类，而不是说CBase就是个虚基类，因为CBase还可以不不是虚继承体系<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中的基类。<br>&nbsp;&nbsp;&nbsp; 有了上面的定义后，就可以开始虚继承和虚基类的本质研究了，下面按照语法、语义、<br>模型、性能和应用五个方面进行全面的描述。<br><br>&nbsp;&nbsp;&nbsp; 1. 语法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 语法有语言的本身的定义所决定，总体上来说非常的简单，如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CSubClass : public virtual CBaseClass {};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中可以采用public、protected、private三种不同的继承关键字进行修饰，只要<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 确保包含virtual就可以了，这样一来就形成了虚继承体系，同时CBaseClass就成为<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 了CSubClass的虚基类了。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实并没有那么的简单，如果出现虚继承体系的进一步继承会出现什么样的状况呢？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如下所示：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 带有数据成员的基类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: red">&nbsp; struct CBaseClass1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CBaseClass1( size_t i ) : m_val( i ) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t m_val;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * 虚拟继承体系<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CSubClassV1 : public virtual CBaseClass1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CSubClassV1( size_t i ) : CBaseClass1( i ) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CSubClassV2 : public virtual CBaseClass1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CSubClassV2( size_t i ) : CBaseClass1( i ) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CDiamondClass1 : public CSubClassV1, public CSubClassV2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct CDiamondSubClass1 : public CDiamondClass1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意上面代码中的CDiamondClass1和CDiamondSubClass1两个类的构造函数初始化列<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 表中的内容。可以发现其中均包含了虚基类CBaseClass1的初始化工作，如果没有这<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 个初始化语句就会导致编译时错误，为什么会这样呢？一般情况下不是只要在<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CSubClassV1和CSubClassV2中包含初始化就可以了么？要解释该问题必须要明白虚<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 继承的语义特征，所以参看下面语义部分的解释。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 2. 语义<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在C++中虚函数必须要通过一种间接的运行时（而不是编译时）机制才能够激活（<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用）的函数，而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但间接。其中关键就在于存在、间接和共享这三种特征。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于虚函数而言，这三个特征是很好理解的，间接性表明了他必须在运行时根据实际<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 的对象来完成函数寻址，共享性表象在基类会共享被子类重载后的虚函数，其实指向<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 相同的函数入口。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于虚继承而言，这三个特征如何理解呢？存在即表示虚继承体系和虚基类确实存在，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 间接性表明了在访问虚基类的成员时同样也必须通过某种间接机制来完成（下面模型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中会讲到），共享性表象在虚基类会在虚继承体系中被共享，而不会出现多份拷贝。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那现在可以解释语法小节中留下来的那个问题了，&#8220;为什么一旦出现了虚基类，就必<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 须在每一个继承类中都必须包含虚基类的初始化语句&#8221;。由上面的分析可以知道，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虚基类是被共享的，也就是在继承体系中无论被继承多少次，对象内存模型中均只会<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 出现一个虚基类的子对象（这和多继承是完全不同的），这样一来既然是共享的那么<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每一个子类都不会独占，但是总还是必须要有一个类来完成基类的初始化过程（因为<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所有的对象都必须被初始化，哪怕是默认的），同时还不能够重复进行初始化，那到<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 底谁应该负责完成初始化呢？C++标准中（也是很自然的）选择在每一次继承子类中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 都必须书写初始化语句（因为每一次继承子类可能都会用来定义对象），而在最下层<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 继承子类中实际执行初始化过程。所以上面在每一个继承类中都要书写初始化语句，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是在创建对象时，而仅仅会在创建对象用的类构造函数中实际的执行初始化语句，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其他的初始化语句都会被压制不调用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 3. 模型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了实现上面所说的三种语义含义，在考虑对象的实现模型（也就是内存模型）时就<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很自然了。在C++中对象实际上就是一个连续的地址空间的语义代表，我们来分析虚<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 继承下的内存模型。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.1. 存在<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 也就是说在对象内存中必须要包含虚基类的完整子对象，以便能够完成通过地址<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 完成对象的标识。那么至于虚基类的子对象会存放在对象的那个位置（头、中间、<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 尾部）则由各个编译器选择，没有差别。（在VC8中无论虚基类被声明在什么位置，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 虚基类的子对象都会被放置在对象内存的尾部）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.2. 间接<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 间接性表明了在直接虚基承子类中一定包含了某种指针（偏移或表格）来完成通<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 过子类访问虚基类子对象（或成员）的间接手段（因为虚基类子对象是共享的，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没有确定关系），至于采用何种手段由编译器选择。（在VC8中在子类中放置了<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个虚基类指针vbc，该指针指向虚函数表中的一个slot，该slot中存放则虚基<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类子对象的偏移量的负值，实际上就是个以补码表示的int类型的值，在计算虚<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 基类子对象首地址时，需要将该偏移量取绝对值相加，这个主要是为了和虚表<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 中只能存放虚函数地址这一要求相区别，因为地址是原码表示的无符号int类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 的值）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.3. 共享<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 共享表明了在对象的内存空间中仅仅能够包含一份虚基类的子对象，并且通过<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 某种间接的机制来完成共享的引用关系。在介绍完整个内容后会附上测试代码，<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 体现这些内容。<br>&nbsp;&nbsp;&nbsp; 4. 性能<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于有了间接性和共享性两个特征，所以决定了虚继承体系下的对象在访问时必然<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 会在时间和空间上与一般情况有较大不同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.1. 时间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在通过继承类对象访问虚基类对象中的成员（包括数据成员和函数成员）时，都<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 必须通过某种间接引用来完成，这样会增加引用寻址时间（就和虚函数一样），<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实就是调整this指针以指向虚基类对象，只不过这个调整是运行时间接完成的。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; （在VC8中通过打开汇编输出，可以查看*.cod文件中的内容，在访问虚基类对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 成员时会形成三条mov间接寻址语句，而在访问一般继承类对象时仅仅只有一条mov<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 常量直接寻址语句）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.2. 空间<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于共享所以不同在对象内存中保存多份虚基类子对象的拷贝，这样较之多继承<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 节省空间。<br>&nbsp;&nbsp;&nbsp; 5. 应用<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 谈了那么多语言特性和内容，那么在什么情况下需要使用虚继承，而一般应该如何使<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用呢？<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个问题其实很难有答案，一般情况下如果你确性出现多继承没有必要，必须要共享<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 基类子对象的时候可以考虑采用虚继承关系（C++标准ios体系就是这样的）。由于每<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个继承类都必须包含初始化语句而又仅仅只在最底层子类中调用，这样可能就会使<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 得某些上层子类得到的虚基类子对象的状态不是自己所期望的（因为自己的初始化语<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 句被压制了），所以<span style="COLOR: red">一般建议不要在虚基类中包含任何数据成员（不要有状态），只<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以作为接口类来提供。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#000000>假定通过多个派生路径继承名为X的成员，有下面三种可能性：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: red"> 1）如果在每个路径中X表示同一虚基类成员，则没有二义性，因为共享该成员的单个实例；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）如果在某个路径中X是虚基类的成员，而在另一路径中X是后代派生类的成员，也没有二义性——特定派生类实例的优先级高于共享虚基类实例。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;3）如果沿每个继承路径X表示后代派生类的不同成员，则该成员的直接访问是二义性的。<br></span><br><br><br><br>&nbsp;</font>&nbsp;<br>
<div></span><br><br>附录：测试代码<br>#include &lt;ctime&gt;<br>#include &lt;iostream&gt;<br><br>/*<br>&nbsp;* 带有数据成员的基类<br>&nbsp;*/<br>struct CBaseClass1<br>{<br>&nbsp;&nbsp;&nbsp; CBaseClass1( size_t i ) : m_val( i ) {}<br><br>&nbsp;&nbsp;&nbsp; size_t m_val;<br>};<br>/*<br>&nbsp;* 虚拟继承体系<br>&nbsp;*/<br>struct CSubClassV1 : public virtual CBaseClass1<br>{<br>&nbsp;&nbsp;&nbsp; CSubClassV1( size_t i ) : CBaseClass1( i ) {}<br>};<br><br>struct CSubClassV2 : public virtual CBaseClass1<br>{<br>&nbsp;&nbsp;&nbsp; CSubClassV2( size_t i ) : CBaseClass1( i ) {}<br>};<br><br>struct CDiamondClass1 : public CSubClassV1, public CSubClassV2<br>{<br>&nbsp;&nbsp;&nbsp; CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}<br>};<br><br>struct CDiamondSubClass1 : public CDiamondClass1<br>{<br>&nbsp;&nbsp;&nbsp; CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}<br>};<br>/*<br>&nbsp;* 正常继承体系<br>&nbsp;*/<br>struct CSubClassN1 : public CBaseClass1<br>{<br>&nbsp;&nbsp;&nbsp; CSubClassN1( size_t i ) : CBaseClass1( i ) {}<br>};<br>struct CSubClassN2 : public CBaseClass1<br>{<br>&nbsp;&nbsp;&nbsp; CSubClassN2( size_t i ) : CBaseClass1( i ) {}<br>};<br>struct CMultiClass1 : public CSubClassN1, public CSubClassN2<br>{<br>&nbsp;&nbsp;&nbsp; CMultiClass1( size_t i ) : CSubClassN1( i ), CSubClassN2( i ) {}<br>};<br>struct CMultiSubClass1 : public CMultiClass1<br>{<br>&nbsp;&nbsp;&nbsp; CMultiSubClass1( size_t i ) : CMultiClass1( i ) {}<br>};<br>/*<br>&nbsp;* 不带有数据成员的接口基类<br>&nbsp;*/<br>struct CBaseClass2<br>{<br>&nbsp;&nbsp;&nbsp; virtual void func() {};<br>&nbsp;&nbsp;&nbsp; virtual ~CBaseClass2() {}<br>};<br>/*<br>&nbsp;* 虚拟继承体系<br>&nbsp;*/<br>// struct CBaseClassX { CBaseClassX() {i1 = i2 = 0xFFFFFFFF;} size_t i1, i2;};<br>struct CSubClassV3 : public virtual CBaseClass2<br>{<br>};<br>struct CSubClassV4 : public virtual CBaseClass2<br>{<br>};<br>struct CDiamondClass2 : public CSubClassV3, public CSubClassV4<br>{<br>};<br>struct CDiamondSubClass2 : public CDiamondClass2<br>{<br>};<br><br>/*<br>&nbsp;* 正常继承体系<br>&nbsp;*/<br>struct CSubClassN3 : public CBaseClass2<br>{<br>};<br>struct CSubClassN4 : public CBaseClass2<br>{<br>};<br>struct CMultiClass2 : public CSubClassN3, public CSubClassN4<br>{<br>};<br>struct CMultiSubClass2 : public CMultiClass2<br>{<br>};<br><br>/*<br>&nbsp;* 内存布局用类声明.<br>&nbsp;*/<br>struct CLayoutBase1<br>{<br>&nbsp;&nbsp;&nbsp; CLayoutBase1() : m_val1( 0 ), m_val2( 1 ) {}<br><br>&nbsp;&nbsp;&nbsp; size_t m_val1, m_val2;<br>};<br>struct CLayoutBase2<br>{<br>&nbsp;&nbsp;&nbsp; CLayoutBase2() : m_val1( 3 ) {}<br><br>&nbsp;&nbsp;&nbsp; size_t m_val1;<br>};<br>struct CLayoutSubClass1 : public virtual CBaseClass1, public CLayoutBase1, public CLayoutBase2<br>{<br>&nbsp;&nbsp;&nbsp; CLayoutSubClass1() : CBaseClass1( 2 ) {}<br>};<br><br><br>#define MAX_TEST_COUNT 1000 * 1000 * 16<br>#define TIME_ELAPSE() ( std::clock() - start * 1.0 ) / CLOCKS_PER_SEC<br><br>int main( int argc, char *argv[] )<br>{<br>&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp; * 类体系中的尺寸.<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "================================ sizeof ================================" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "&nbsp;&nbsp;&nbsp; ----------------------------------------------------------------" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CBaseClass1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CBaseClass1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassV1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassV1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassV2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassV2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CDiamondClass1 )&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CDiamondClass1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CDiamondSubClass1 ) = " &lt;&lt; sizeof( CDiamondSubClass1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassN1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassN1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassN2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassN2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CMultiClass1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CMultiClass1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CMultiSubClass1 )&nbsp;&nbsp; = " &lt;&lt; sizeof( CMultiSubClass1 ) &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "&nbsp;&nbsp;&nbsp; ----------------------------------------------------------------" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CBaseClass2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CBaseClass2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassV3 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassV3 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassV4 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassV4 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CDiamondClass2 )&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CDiamondClass2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CDiamondSubClass2 ) = " &lt;&lt; sizeof( CDiamondSubClass2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassN3 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassN3 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassN4 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassN4 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CMultiClass2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = " &lt;&lt; sizeof( CMultiClass2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CMultiSubClass2 )&nbsp;&nbsp; = " &lt;&lt; sizeof( CMultiSubClass2 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp; * 对象内存布局<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "================================ layout ================================" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "&nbsp;&nbsp;&nbsp; --------------------------------MI------------------------------" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; CLayoutSubClass1 *lsc = new CLayoutSubClass1;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CLayoutSubClass1 )&nbsp;&nbsp; = " &lt;&lt; sizeof( CLayoutSubClass1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CLayoutBase1 offset of CLayoutSubClass1 is " &lt;&lt; (char*)(CLayoutBase1 *)lsc - (char*)lsc &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CBaseClass1&nbsp; offset of CLayoutSubClass1 is " &lt;&lt; (char*)(CBaseClass1&nbsp; *)lsc - (char*)lsc &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CLayoutBase2 offset of CLayoutSubClass1 is " &lt;&lt; (char*)(CLayoutBase2 *)lsc - (char*)lsc &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; int *ptr = (int*)lsc;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "vbc in CLayoutSubClass1 is " &lt;&lt; *(int*)ptr[3] &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; delete lsc;<br><br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "&nbsp;&nbsp;&nbsp; --------------------------------SI------------------------------" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; CSubClassV1 *scv1 = new CSubClassV1( 1 );<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "sizeof( CSubClassV1 )&nbsp;&nbsp; = " &lt;&lt; sizeof( CSubClassV1 ) &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CBaseClass1 offset of CSubClassV1 is " &lt;&lt; (char*)(CBaseClass1 *)scv1 - (char*)scv1 &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; ptr = (int*)scv1;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "vbc in CSubClassV1 is " &lt;&lt; *(int*)ptr[0] &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; delete scv1;<br><br>&nbsp;&nbsp;&nbsp; /*<br>&nbsp;&nbsp;&nbsp;&nbsp; * 性能测试<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "================================ Performance ================================" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; double times[4];<br>&nbsp;&nbsp;&nbsp; size_t idx = 0;<br><br>&nbsp;&nbsp;&nbsp; CSubClassV1 *ptr1 = new CDiamondClass1( 1 );<br>&nbsp;&nbsp;&nbsp; std::clock_t start = std::clock();<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for ( size_t i = 0; i &lt; MAX_TEST_COUNT; ++i )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ptr1-&gt;m_val = i;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; times[idx++] = TIME_ELAPSE();<br>&nbsp;&nbsp;&nbsp; delete static_cast&lt;CDiamondClass1*&gt;( ptr1 );<br><br>&nbsp;&nbsp;&nbsp; CSubClassN1 *ptr2 = new CMultiClass1( 0 );<br>&nbsp;&nbsp;&nbsp; start = std::clock();<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for ( size_t i = 0; i &lt; MAX_TEST_COUNT; ++i )<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ptr2-&gt;m_val = i;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; times[idx++] = TIME_ELAPSE();<br>&nbsp;&nbsp;&nbsp; delete static_cast&lt;CMultiClass1*&gt;( ptr2 );<br><br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CSubClassV1::ptr1-&gt;m_val " &lt;&lt; times[0] &lt;&lt; " s" &lt;&lt; std::endl;<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "CSubClassN1::ptr2-&gt;m_val " &lt;&lt; times[1] &lt;&lt; " s" &lt;&lt; std::endl;<br><br>&nbsp;&nbsp;&nbsp; return 0; <br>}<br><br>测试环境：<br>&nbsp;&nbsp;&nbsp; 软件环境：Visual Studio2005 Pro + SP1, boost1.34.0<br>&nbsp;&nbsp;&nbsp; 硬件环境：PentiumD 3.0GHz, 4G RAM<br>测试数据：<br>================================ sizeof ================================<br>&nbsp;&nbsp;&nbsp; ----------------------------------------------------------------<br>sizeof( CBaseClass1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br><br>sizeof( CSubClassV1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CSubClassV2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CDiamondClass1 )&nbsp;&nbsp;&nbsp; = 12<br>sizeof( CDiamondSubClass1 ) = 12<br><br>sizeof( CSubClassN1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br>sizeof( CSubClassN2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br>sizeof( CMultiClass1 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CMultiSubClass1 )&nbsp;&nbsp; = 8<br>&nbsp;&nbsp;&nbsp; ----------------------------------------------------------------<br>sizeof( CBaseClass2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br><br>sizeof( CSubClassV3 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CSubClassV4 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CDiamondClass2 )&nbsp;&nbsp;&nbsp; = 12<br>sizeof( CDiamondSubClass2 ) = 12<br><br>sizeof( CSubClassN3 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br>sizeof( CSubClassN4 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 4<br>sizeof( CMultiClass2 )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 8<br>sizeof( CMultiSubClass2 )&nbsp;&nbsp; = 8<br>================================ layout ================================<br>&nbsp;&nbsp;&nbsp; --------------------------------MI------------------------------<br>sizeof( CLayoutSubClass1 )&nbsp;&nbsp; = 20<br>CLayoutBase1 offset of CLayoutSubClass1 is 0<br>CBaseClass1&nbsp; offset of CLayoutSubClass1 is 16<br>CLayoutBase2 offset of CLayoutSubClass1 is 8<br>vbc in CLayoutSubClass1 is -12<br>&nbsp;&nbsp;&nbsp; --------------------------------SI------------------------------<br>sizeof( CSubClassV1 )&nbsp;&nbsp; = 8<br>CBaseClass1 offset of CSubClassV1 is 4<br>vbc in CSubClassV1 is 0<br>================================ Performance ================================<br>CSubClassV1::ptr1-&gt;m_val 0.062 s<br>CSubClassN1::ptr2-&gt;m_val 0.016 s<br><br>结果分析：<br>&nbsp;&nbsp;&nbsp; 1. 由于虚继承引入的间接性指针所以导致了虚继承类的尺寸会增加4个字节；<br>&nbsp;&nbsp;&nbsp; 2. 由Layout输出可以看出，虚基类子对象被放在了对象的尾部（偏移为16），并且vbc<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 指针必须紧紧的接在虚基类子对象的前面，所以vbc指针所指向的内容为&#8220;偏移 - 4&#8221;；<br>&nbsp;&nbsp;&nbsp; 3. 由于VC8将偏移放在了虚函数表中，所以为了区分函数地址和偏移，所以偏移是用补<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 码int表示的负值；<br>&nbsp;&nbsp;&nbsp; 4. 间接性可以通过性能来看出，在虚继承体系同通过指针访问成员时的时间一般是一般<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类访问情况下的4倍左右，符合汇编语言输出文件中的汇编语句的安排。<br></div>
</div>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-08 22:34 <a href="http://www.cppblog.com/ivy-jie/articles/87104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一道多继承和虚继承的面试题</title><link>http://www.cppblog.com/ivy-jie/articles/87102.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 08 Jun 2009 14:30:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/87102.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/87102.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/87102.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/87102.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/87102.html</trackback:ping><description><![CDATA[<p><br>class A{<br>public A { std::cout&lt;&lt;A&lt;&lt;}<br>}<br><br>class B: public A{<br>... // print B<br>}<br><br>class C: public A, pulic B {<br>... // print C<br>}<br><br>int main(){<br>C obj;<br>}<br>输出: AABC。继承关系是三角形：A-&gt;B-&gt;C加A-&gt;C。<br><br>问题是：这三条边如果考虑虚继承，每条边有两种可能，共八种。那么其他七种组合的<br>输出是什么。 <br>编译器: VC6.0<br>#include "stdafx.h"<br>#include "iostream"<br>using namespace std;<br>class AA {<br>public:<br>&nbsp;AA(){ std::cout&lt;&lt;"AA"&lt;&lt;endl;}<br>};<br>class BB: public <span style="COLOR: red">virtual</span> AA<br>{<br>public: <br>&nbsp;BB(){ std::cout&lt;&lt;"BB"&lt;&lt;endl;}<br>};</p>
<p>class CC: public <span style="COLOR: red">virtual</span> AA,&nbsp; public&nbsp;<span style="COLOR: red">(virtual)</span> BB <span style="COLOR: red">//括号的virtual可有可无<br></span>{<br>public: <br>&nbsp;CC(){ std::cout&lt;&lt;"CC"&lt;&lt;endl;}<br>};</p>
<p>int main(int argc, char* argv[])<br>{<br>&nbsp;CC obj;</p>
<p>&nbsp;return 0;<br>}<br>输出: AA<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BB<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CC<br>其它情况编译会出错<br>主要考察多继承的虚基类在内存中只有一份copy,如果不用虚继承的话，容易造成二义性，编译不过:)</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/87102.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-06-08 22:30 <a href="http://www.cppblog.com/ivy-jie/articles/87102.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向过程的静态成员</title><link>http://www.cppblog.com/ivy-jie/articles/85576.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sun, 24 May 2009 02:47:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85576.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85576.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85576.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85576.html</trackback:ping><description><![CDATA[1、静态全局变量 <br><br>在全局变量前，加上关键字static，该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子，如下： <br>//Example 1#include &lt;iostream.h&gt;void fn();static int n; //定义静态全局变量void main(){ n=20; cout&lt;&lt;n&lt;&lt;endl; fn();}void fn(){ n++; cout&lt;&lt;n&lt;&lt;endl;} <br><br>静态全局变量有以下特点： <br>该变量在<span style="COLOR: red"><u><strong>全局数据区</strong></u></span>分配内存； <br>未经初始化的静态全局变量会被程序自动初始化为0（自动变量的值是随机的，除非它被显式初始化）； <br><span style="COLOR: red"><u><strong>静态全局变量在声明它的整个文件都是可见的，而在文件之外是不可见的；</strong></u></span> <br>静态变量都在全局数据区分配内存，包括后面将要提到的静态局部变量。对于一个完整的程序，在内存中的<br><br><span style="COLOR: red"><u><strong>分布情况如下图： <br>代码区 <br>全局数据区 <br>堆区 <br>栈区</strong></u></span> <br><br>一般程序的由new产生的动态数据存放在堆区，函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间，静态数据（即使是函数内部的静 态局部变量）也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现，Example 1中的代码中将 <br><br><strong><span style="COLOR: red"><u>static int n; //定义静态全局变量</u></span></strong> <br>改为 <br><strong style="COLOR: red"><u>int n; //定义全局变量</u></strong> <br>程序照样正常运行。 <br>的确，<strong><u style="COLOR: red">定义全局变量就可以实现变量在文件中的共享，但定义静态全局变量还有以下好处： <br>静态全局变量不能被其它文件所用； <br>其它文件中可以定义相同名字的变量，不会发生冲突；</u></strong> <br>您可以将上述示例代码改为如下： <br><br><span style="COLOR: red"><u><strong>//Example 2//File1#include &lt;iostream.h&gt;void fn();static int n; //定义静态全局变量void main(){ n=20; cout&lt;&lt;n&lt;&lt;endl; fn();}//File2#include &lt;iostream.h&gt;<span style="COLOR: #000000">extern int n;</span>void fn(){ n++; cout&lt;&lt;n&lt;&lt;endl;} <br>编译并运行Example 2，您就会发现上述代码可以分别通过编译，但运行时出现错误。 试着将 <br>static int n; //定义静态全局变量 <br>改为 <br>int n; //定义全局变量 <br>再次编译运行程序，细心体会全局变量和静态全局变量的区别。 <br></strong></u></span>2、静态局部变量 <br><br>在局部变量前，加上关键字static，该变量就被定义成为一个静态局部变量。 <br><br>我们先举一个静态局部变量的例子，如下： <br><br>//Example 3#include &lt;iostream.h&gt;void fn();void main(){ fn(); fn(); fn();}void fn(){ static n=10; cout&lt;&lt;n&lt;&lt;endl; n++;} <br>通常，在函数体内定义了一个变量，每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体，系统就会收回栈内存，局部变量也相应失效。 <br>但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来，变量已经不再属于函数本身了，不再仅受函数的控制，给程序的维护带来不便。 <br>静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区，而不是保存在栈中，每次的值保持到下一次调用，直到下次赋新值。 <br>静态局部变量有以下特点： <br><br>该变量在全局数据区分配内存； <br>静态局部变量在程序执行到该对象的声明处时被首次初始化，即以后的函数调用不再进行初始化； <br>静态局部变量一般在声明处初始化，如果没有显式初始化，会被程序自动初始化为0； <br><span style="COLOR: #ff0000"><u><strong>它始终驻留在全局数据区，直到程序运行结束。但其<span style="COLOR: #000000">作用域为局部作用域</span>，当定义它的函数或语句块结束时，其作用域随之结束； <br></strong></u></span>3、静态函数 <br><br>在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同，它只能在声明它的文件当中可见，不能被其它文件使用。 <br><br>静态函数的例子： <br><br>//Example 4#include &lt;iostream.h&gt;static void fn();//声明静态函数void main(){ fn();}void fn()//定义静态函数{ int n=10; cout&lt;&lt;n&lt;&lt;endl;} <br>定义静态函数的好处： <br><span style="COLOR: #ff0000"><u><strong>静态函数不能被其它文件所用； <br>其它文件中可以定义相同名字的函数，不会发生冲突；</strong></u></span>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-24 10:47 <a href="http://www.cppblog.com/ivy-jie/articles/85576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向对象的静态成员</title><link>http://www.cppblog.com/ivy-jie/articles/85575.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Sun, 24 May 2009 02:42:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85575.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85575.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85575.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85575.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85575.html</trackback:ping><description><![CDATA[&nbsp;静态类成员包括<font color=#ff0000>静态数据成员</font>和<font color=#ff0000>静态函数成员</font>两部分。&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>1<strong> 静态数据成员：</strong>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类体中的数据成员的声明前加上<font color=#3366ff>static</font>关键字，该数据成员就成为了该类的<font color=#ff0000>静态数据成员</font>。和其他数据成员一样，<font color=#ff0000>静态数据成员</font>也遵守<font color=#3366ff>public/protected/private</font>访问规则。同时，<font color=#ff0000>静态数据成员</font>还具有以下特点： &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; 1.静态数据成员的定义。 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="COLOR: red"> 静态数据成员实际上是类域中的全局变量。所以，<font style="COLOR: red" color=#000000>静态成员不能在类定义里边初始化，只能在class body外初始化。</font></span>&nbsp;&nbsp;&nbsp; 举例如下： &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; xxx.h文件 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; const &nbsp; int &nbsp; _i;//声明，标准c++支持有序类型在类体中初始化,但vc6不支持。 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; xxx.cpp文件 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; const &nbsp; int &nbsp; base::_i=10;//<span style="COLOR: red"><u><strong>定义(初始化)时不受private和protected访问限制.</strong></u></span>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<em>&nbsp;&nbsp;&nbsp;<br></em>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.<span style="COLOR: red"><font color=#ff0000>静态数据成员</font>被<font color=#ff0000>类</font>的所有对象所共享，包括该类派生类的对象。</span>即派生类对象与基类对象共享基类的静态数据成员。&nbsp;<br>&nbsp;&nbsp;<br><span style="COLOR: red"><strong><u>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;初始化(或定义)，访问方式<br></u></strong></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;静态数据成员初始化的格式为： <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ＜数据类型＞＜类名＞::＜静态数据成员名＞=＜值＞ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类的静态数据成员有两种访问形式： <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ＜类对象名＞<u><strong style="COLOR: red">.</strong></u>＜静态数据成员名＞ 或 ＜类类型名＞<span style="COLOR: red"><u><strong>::</strong></u></span>＜静态数据成员名＞ <br>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 如果静态数据成员的访问权限允许的话（<span style="COLOR: red"><u><strong>即public的成员</strong></u></span>），可在程序中，按上述格式来引用静态数据成员&nbsp;;<br><br>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.<font style="COLOR: #000000" color=#ff0000>静态数据成员</font>可以成为成员函数的可选参数，而普通数据成员则不可以。举例如下： &nbsp; <br>&nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; : &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; int &nbsp; _staticVar; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int &nbsp; _var; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; void &nbsp; foo1(int &nbsp; i=_staticVar);//<font color=#339966>正确,_staticVar为静态数据成员</font> &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; void &nbsp; foo2(int &nbsp; i=_var);//<font color=#339966>错误,_var为普通数据成员</font> &nbsp; <br>&nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp;&nbsp;&nbsp; 4.★<font style="COLOR: #000000" color=#ff0000>静态数据成员</font>的类型可以是所属类的类型，而普通数据成员则不可以。普通数据成员的只能声明为&nbsp;所属类类型的指针或引用。举例如下： &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public &nbsp; : &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; base &nbsp; _object1;//正确，静态数据成员 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base &nbsp; _object2;//错误 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base &nbsp; *pObject;//正确，指针 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base &nbsp; &amp;mObject;//正确，引用 &nbsp; <br>&nbsp; &nbsp; &nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; 5.★这个特性，我不知道是属于标准c++中的特性，还是vc6自己的特性。 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; 静态数据成员的值在const成员函数中可以被合法的改变。举例如下： &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public: &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; base(){_i=0;_val=0;} &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mutable &nbsp; int &nbsp; _i; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; int &nbsp; _staticVal; &nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int &nbsp; _val; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; void &nbsp; test() &nbsp; const{//const &nbsp; 成员函数 &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _i++;//正确，mutable数据成员 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _staticVal++;//正确，static数据成员 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _val++;//错误 &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; <br>&nbsp; &nbsp; &nbsp; }; &nbsp; <br>&nbsp; &nbsp; &nbsp; int &nbsp; base::_staticVal=0;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>2&nbsp; 静态成员函数</strong>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.静态成员函数的地址可用普通函数指针储存，而普通成员函数地址需要用 &nbsp; 类成员函数指针来储存。举例如下： &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; int &nbsp; func1(); &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int &nbsp; func2(); &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; <br>&nbsp; &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int &nbsp; (*pf1)()=&amp;base::func1;//普通的函数指针 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int &nbsp; (base::*pf2)()=&amp;base::func2;//成员函数指针&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 2.<span style="COLOR: red">静态成员函数不可以调用类的非静态成员。<u><strong>因为静态成员函数不含this指针</strong></u></span><u><strong>。</strong></u>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; 通常情况下，this 是缺省的。如函数fn()实际上是this-&gt;fn()。但是与普通函数相比，静态成员函数由于不是与任何的对象相联系，因此它不具有this指 针。从这个意义上讲，<span style="COLOR: red"><u><strong>它无法访问属于类对象的非静态数据成员</strong></u></span>，<span style="COLOR: red"><u><strong>也无法访问非静态成员函数</strong></u></span>，它<span style="COLOR: red"><u><strong>只能调用其余的静态成员函数</strong></u></span>。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.静态成员函数不可以同时声明为 &nbsp; virtual、const、volatile函数。举例如下： &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; class &nbsp; base{ &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtual &nbsp; static &nbsp; void &nbsp; func1();//错误 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; void &nbsp; func2() &nbsp; const;//错误 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; static &nbsp; void &nbsp; func3() &nbsp; volatile;//错误 &nbsp; <br>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;};&nbsp;&nbsp;&nbsp;<br><br>3&nbsp;&nbsp; 静态构造函数<br>&nbsp;&nbsp;&nbsp;&nbsp; 静态构造函数自动被调用，不能被显式调用。虽然提供了许多约束条件，但是静态构造函数执行的确切时间和顺序是不确定的：<br>一个类的静态构造函数在这个类的任何实例被创建前执行。<br>一个类的静态构造函数在类的任何静态成员被引用前执行。<br>一个类的静态构造函数在它的所有派生类的静态构造函数执行之后执行。<br>一个类的静态构造函数从不会被执行一次以上。<br>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-24 10:42 <a href="http://www.cppblog.com/ivy-jie/articles/85575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>子类父类构造函数调用顺序</title><link>http://www.cppblog.com/ivy-jie/articles/85481.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 22 May 2009 16:27:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85481.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85481.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85481.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85481.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85481.html</trackback:ping><description><![CDATA[<p style="TEXT-INDENT: 2em">子类继承和调用父类的构造方法</p>
<p style="TEXT-INDENT: 2em">
<table>
    <tbody>
        <tr>
            <td>
            <p>&#160;</p>
            <p style="TEXT-INDENT: 2em">1.如果子类没有定义构造方法,则调用父类的无参数的构造方法,.</p>
            <p style="COLOR: red; TEXT-INDENT: 2em">2.如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先<u><strong>执行父类无参数的构造方法</strong></u>,然后执行自己的构造方法。</p>
            <p style="TEXT-INDENT: 2em">3.如果子类调用父类带参数的构造方法,可以通过super(参数)调用所需要的父类的构造方法,切该语句做为<span style="COLOR: red">子类构造方法中的第一条语句</span>。</p>
            <p style="TEXT-INDENT: 2em">4.如果某个构造方法调用类中的其他的构造方法,则可以用this(参数),切该语句放在构造方法的第一条.</p>
            <p style="TEXT-INDENT: 2em">说白了:原则就是,先调用父亲的.(没有就默认调,有了就按有的调,反正只要有一个就可以了.)</p>
            <p style="TEXT-INDENT: 2em">package test;</p>
            <p style="TEXT-INDENT: 2em">class Father{</p>
            <p style="TEXT-INDENT: 2em">String s = "Run constructor method of Father";</p>
            <p style="TEXT-INDENT: 2em">public Father(){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; System.out.println(s);</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">public Father(String str){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; s= str;</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; System.out.println(s);</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">class Son extends Father{</p>
            <p style="TEXT-INDENT: 2em">String s= "Run constructor method of son";</p>
            <p style="TEXT-INDENT: 2em">public Son(){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; //实际上在这里加上super()，和没加是一个样的</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; System.out.println(s);</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">public Son(String str){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; this();//这里调用this()表示调用本类的Son(),因为Son()中有了一个super()了，所以这里不能再加了。</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; s = str;</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; System.out.println(s);</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">public Son(String str1, String str2){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; super(str1+" "+str2);//因为这里已经调用了一个父类的带参数的super("---")了，所以不会再自动调用了无参数的了。</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; s = str1;</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; System.out.println(s);</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">public class MyClass9 {</p>
            <p style="TEXT-INDENT: 2em">public static void main(String[] args){</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; Father obfather1 = new Father();</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; Father obfather2 = new Father("Hello Father");</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; Son obson1 = new Son();</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; Son obson2 = new Son("hello son");</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp; Son obson3 = new Son("hello son","hello father");</p>
            <p style="TEXT-INDENT: 2em">&nbsp;&nbsp;</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">}</p>
            <p style="TEXT-INDENT: 2em">===============</p>
            <p style="TEXT-INDENT: 2em">结果:</p>
            <p style="TEXT-INDENT: 2em">Run constructor method of Father</p>
            <p style="TEXT-INDENT: 2em">Hello Father</p>
            <p style="TEXT-INDENT: 2em">Run constructor method of Father</p>
            <p style="TEXT-INDENT: 2em">Run constructor method of son</p>
            <p style="TEXT-INDENT: 2em">Run constructor method of Father</p>
            <p style="TEXT-INDENT: 2em">Run constructor method of son</p>
            <p style="TEXT-INDENT: 2em">hello son</p>
            <p style="TEXT-INDENT: 2em">hello son hello father</p>
            <p style="TEXT-INDENT: 2em">hello son</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85481.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-23 00:27 <a href="http://www.cppblog.com/ivy-jie/articles/85481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>输入函数scanf()详解</title><link>http://www.cppblog.com/ivy-jie/articles/85332.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Fri, 22 May 2009 01:01:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/85332.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/85332.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/85332.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/85332.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/85332.html</trackback:ping><description><![CDATA[1 由于scanf函数在接收数据时，是通过空格来区分数据项的. &nbsp; <br>&nbsp; 如果你： &nbsp; <br>&nbsp; char &nbsp; c[20]; &nbsp; <br>&nbsp; scanf("%s",c); &nbsp; <br>&nbsp; 输入:this &nbsp; is &nbsp; a &nbsp; c &nbsp; program!&nbsp;&nbsp;&nbsp;<br>&nbsp; <br>&nbsp; 它接收到this后遇到空格，认为对应%s的数据项已结束，后面的（包含空格）不能输入。 &nbsp; <br>&nbsp; 你想输入带空格的字符串，只有使用gets(c); &nbsp; <br>&nbsp; 但遗憾的是用gets()函数一次只能输入一个字符串。<br>&nbsp;2 总结<br>%d,遇到数字,'+','-'以外的字符结束,可跳过空格 <br><br>%f,,%lf:遇到数字, '.', 'e','+','-'之外的字符结束,可跳过空格<br><br>%s,从第一个非空格字符起直到下一个空格结束. <br><br>%c,遇到字符就赋值,不管是空格还是回车之类的. <br><br>&nbsp; 假如程序中出现这样的代码,<span style="COLOR: #000000">scanf("%d,%s,%c,%c").虽然程序是以 ', '作为分隔的,但%s是不认这个 ', '的,它只认空格,所以会出现变量不能正确赋值的情况,如果你将','去掉,但由于%c却遇到空格又不会跳过,所以也不会满足需要,通过上面的我们知道,%c遇到空格是不会跳过的,所以我采取下面这个方式来解决,在%c的前面加一个空格来处理,这样就可以解决上面的所有问题了,所以我们在<span style="COLOR: #ff0000">使用%c时,要在前面加上一个空格</span>,养成这种习惯可以省去不少麻烦,并且在使用%c之前最好还加上一句<span style="COLOR: #ff0000">fflush(stdin);</span>用来清除缓存.<br>3 gets()<br>&nbsp;&nbsp; 功能:从stdin流中读取字符串，直至接受到换行符或EOF时停止，并将读取的结果存放在str指针所指向的字符数组中。换行符不作为读取串的内容，读取的换行符被转换为null值，并由此来结束字符串。<br>&nbsp;&nbsp;&nbsp;注意:本函数可以无限读取，不会判断上限，所以程序员应该确保str的空间足够大，以便在执行读操作时不发生溢出。<br>&nbsp;&nbsp;&nbsp;实例：#include"stdio.h" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void main()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char str[15];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gets(str);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("%s/n",str);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp; <br><br></span><br><br>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/85332.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-22 09:01 <a href="http://www.cppblog.com/ivy-jie/articles/85332.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转:qsort七种应用</title><link>http://www.cppblog.com/ivy-jie/articles/83327.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 18 May 2009 15:52:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/83327.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/83327.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/83327.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/83327.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/83327.html</trackback:ping><description><![CDATA[七种qsort排序方法 <br><br>&lt;本文中排序都是采用的从小到大排序&gt; <br><br>一、对int类型数组排序 <br><br>int num[100]; <br><br>Sample: <br><br>int cmp ( const void *a , const void *b ) <br>{ <br>return *(int *)a - *(int *)b; <br>} <br><br>qsort(num,100,sizeof(num[0]),cmp); <br><br>二、对char类型数组排序（同int类型） <br><br>char word[100]; <br><br>Sample: <br><br>int cmp( const void *a , const void *b ) <br>{ <br>return *(char *)a - *(int *)b; <br>} <br><br>qsort(word,100,sizeof(word[0]),cmp); <br><br>三、对double类型数组排序（特别要注意） <br><br>double in[100]; <br><br>int cmp( const void *a , const void *b ) <br>{ <br>return *(double *)a &gt; *(double *)b ? 1 : -1; <br>} <br><br>qsort(in,100,sizeof(in[0]),cmp)； <br><br>四、对结构体一级排序 <br><br>struct In <br>{ <br>double data; <br>int other; <br>}s[100] <br><br>//按照data的值从小到大将结构体排序,关于结构体内的排序关键数据data的类型可以很多种，参考上面的例子写 <br><br>int cmp( const void *a ,const void *b) <br>{ <br>return (*(In *)a).data &gt; (*(In *)b).data ? 1 : -1; <br>} <br><br>qsort(s,100,sizeof(s[0]),cmp); <br><br>五、对结构体二级排序 <br><br>struct In <br>{ <br>int x; <br>int y; <br>}s[100]; <br><br>//按照x从小到大排序，当x相等时按照y从大到小排序 <br><br>int cmp( const void *a , const void *b ) <br>{ <br>struct In *c = (In *)a; <br>struct In *d = (In *)b; <br>if(c-&gt;x != d-&gt;x) return c-&gt;x - d-&gt;x; <br>else return d-&gt;y - c-&gt;y; <br>} <br><br>qsort(s,100,sizeof(s[0]),cmp); <br><br>六、对字符串进行排序 <br><br>struct In <br>{ <br>int data; <br>char str[100]; <br>}s[100]; <br><br>//按照结构体中字符串str的字典顺序排序 <br><br>int cmp ( const void *a , const void *b ) <br>{ <br>return strcmp( (*(In *)a)-&gt;str , (*(In *)b)-&gt;str ); <br>} <br><br>qsort(s,100,sizeof(s[0]),cmp); <br><br>七、计算几何中求凸包的cmp <br><br>int cmp(const void *a,const void *b) //重点cmp函数，把除了1点外的所有点，旋转角度排序 <br>{ <br>struct point *c=(point *)a; <br>struct point *d=(point *)b; <br>if( calc(*c,*d,p[1]) &lt; 0) return 1; <br>else if( !calc(*c,*d,p[1]) &amp;&amp; dis(c-&gt;x,c-&gt;y,p[1].x,p[1].y) &lt; dis(d-&gt;x,d-&gt;y,p[1].x,p[1].y)) //如果在一条直线上，则把远的放在前面 <br>return 1; <br>else return -1; <br>} <br><br>PS: <br><br>其中的qsort函数包含在&lt;stdlib.h&gt;的头文件里，strcmp包含在&lt;string.h&gt;的头文件里 
<img src ="http://www.cppblog.com/ivy-jie/aggbug/83327.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-18 23:52 <a href="http://www.cppblog.com/ivy-jie/articles/83327.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++箴言1:绝不在构造或析构期调用虚函数</title><link>http://www.cppblog.com/ivy-jie/articles/83251.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 18 May 2009 09:26:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/83251.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/83251.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/83251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/83251.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/83251.html</trackback:ping><description><![CDATA[你不应该在构造或析构期间调用虚函数，因为这样的调用不会如你想象那样工作，而且它们做的事情保证会让你很郁闷。 假设你有一套模拟股票处理的类层次结构，例如，购入流程，出售流程等。对这样的处理来说可以核查是非常重要的，所以随时会创建一个 Transaction 对象，将这个创建记录在核查日志中是一个适当的要求。下面是一个看起来似乎合理的解决问题的方法：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>class Transaction { // base class for all<br>　public: // transactions<br>　　Transaction();<br><br>　　virtual void logTransaction() const = 0; // make type-dependent<br>　　// log entry<br>　　...<br>};<br><br>Transaction::Transaction() // implementation of<br>{ <br>　// base class ctor<br>　...<br>　logTransaction(); // as final action, log this<br>} // transaction<br><br>class BuyTransaction: public Transaction { <br>　// derived class<br>　public:<br>　　virtual void logTransaction() const; // how to log trans-<br>　　// actions of this type<br>　　...<br>};<br><br>class SellTransaction: public Transaction { <br>// derived class<br>public:<br>　virtual void logTransaction() const; // how to log trans-<br>　// actions of this type<br>...<br>};</td>
        </tr>
    </tbody>
</table>
<br>　　考虑执行这行代码时会发生什么：<br><br>
<table borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
    <tbody>
        <tr>
            <td>BuyTransaction b;</td>
        </tr>
    </tbody>
</table>
<br>　　很明显 BuyTransaction 的构造函数会被调用，但是首先，Transaction 的构造函数必须先被调用，派生类对象中的基类部分先于派生类部分被构造。Transaction 的构造函数的最后一行调用虚函数 logTransaction，但是结果会让你大吃一惊，被调用的 logTransaction 版本是在 Transaction 中的那个，而不是 BuyTransaction 中的——即使被创建的对象类型是 BuyTransaction。基类构造期间，虚函数从来不会向下匹配（go down）到派生类。取而代之的是，那个对象的行为就好像它的类型是基类。非正式地讲，<span style="COLOR: #ff0000">基类构造期间，虚函数禁止</span>。 这个表面上看起来匪夷所思的行为存在一个很好的理由。因为基类的构造函数在派生类构造函数之前执行，当基类构造函数运行时，派生类数据成员还没有被初始化。如果基类构造期间调用的虚函数向下匹配（go down）到派生类，派生类的函数理所当然会涉及到本地数据成员，但是那些数据成员还没有被初始化。这就会<span style="COLOR: #ff0000">为未定义行为和悔之晚矣的调试噩梦开了一张通行证</span>。调用涉及到一个对象还没有被初始化的部分自然是危险的，所以 C++ 告诉你此路不通。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在实际上还有比这更多的更深层次的原理。在派生类对象的基类构造期间，对象的类型是那个基类的。<span style="COLOR: #ff0000">不仅虚函数会解析到基类，而且语言中用到运行时类型信息（runtime type information）的配件（例如，dynamic_cast和 typeid），也会将对象视为基类类型。</span>在我们的例子中，当 Transaction 构造函数运行初始化 BuyTransaction 对象的基类部分时，对象的类型是 Transaction。C++ 的每一个配件将以如下眼光来看待它，并对它产生这样的感觉：对象的 BuyTransaction 特有的部分还没有被初始化，所以安全的对待它们的方法就是视若无睹。在派生类构造函数运行之前，一个对象不会成为一个派生类对象。<br><br>　　同样的原因也适用于析构过程。一旦派生类析构函数运行，这个对象的派生类数据成员就被视为未定义的值，所以 C++ 就将它们视为不再存在。在进入基类析构函数时，对象就成为一个基类对象，C++ 的所有配件——虚函数，dynamic_casts 等——都如此看待它。
<img src ="http://www.cppblog.com/ivy-jie/aggbug/83251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-18 17:26 <a href="http://www.cppblog.com/ivy-jie/articles/83251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>虚函数实现机制</title><link>http://www.cppblog.com/ivy-jie/articles/83250.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 18 May 2009 01:26:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/83250.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/83250.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/83250.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/83250.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/83250.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有&#8220;多种形态&#8221;，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。<br>1 虚函数表<br>&nbsp;&nbsp;&nbsp; 虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。 在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了 这个实例的内存中，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。
<p>&nbsp;&nbsp;&nbsp; 这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到，编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置（这是为了保证正确取到虚函数的偏移量）。 这意味着我们通过对象实例的地址得到这张虚函数表，然后就可以遍历其中函数指针，并调用相应的函数。 </p>
2 代码：<br>#include &lt;stdio.h&gt;<br>#include &lt;iostream.h&gt;<br>#include &lt;stdlib.h&gt;<br>typedef void (*Fun) (void);<br>class Base{<br>public:<br>&nbsp;virtual void f() {cout&lt;&lt;"Base::f"&lt;&lt;endl;}<br>&nbsp;virtual void g() {cout&lt;&lt;"Base::g"&lt;&lt;endl;}<br>&nbsp;virtual void h() {cout&lt;&lt;"Base::h"&lt;&lt;endl;}<br>};<br>void main(int argc,char * argv[])<br>{<br>&nbsp; Base b;<br>&nbsp; Fun pFun = NULL;<br>&nbsp; int *var1= (int *)&amp;b;<br>&nbsp; cout&lt;&lt;"虚函数表地址:"&lt;&lt;var1&lt;&lt;endl; //(int*)(&amp;b)<br>&nbsp; int *var2 = (int*)*(int*)(&amp;b);<br>&nbsp; cout&lt;&lt;"虚函数表-第一个函数地址:"&lt;&lt; var2 &lt;&lt; endl;//(int*)*(int*)(&amp;b)<br>&nbsp; //Invoke the first virtual function<br>&nbsp; pFun = <span style="COLOR: red">(Fun)*((int*)*(int*)(&amp;b)+0);</span> //f()<br>&nbsp; pFun();<br>&nbsp; pFun = (Fun)*((int*)*(int*)(&amp;b)+1); //g()<br>&nbsp; pFun();<br>&nbsp; pFun = (Fun)*((int*)*(int*)(&amp;b)+2); // h() <br>&nbsp; pFun();<br>}<br>//结果<br>虚函数表地址:0x0012FF7C<br>虚函数表-第一个函数地址:0x0042804C<br>Base::f<br>Base::g<br>Base::h<br>
<img src ="http://www.cppblog.com/ivy-jie/aggbug/83250.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-18 09:26 <a href="http://www.cppblog.com/ivy-jie/articles/83250.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数指针实现回调</title><link>http://www.cppblog.com/ivy-jie/articles/83247.html</link><dc:creator>ivy-jie</dc:creator><author>ivy-jie</author><pubDate>Mon, 18 May 2009 00:40:00 GMT</pubDate><guid>http://www.cppblog.com/ivy-jie/articles/83247.html</guid><wfw:comment>http://www.cppblog.com/ivy-jie/comments/83247.html</wfw:comment><comments>http://www.cppblog.com/ivy-jie/articles/83247.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/ivy-jie/comments/commentRss/83247.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/ivy-jie/services/trackbacks/83247.html</trackback:ping><description><![CDATA[void (*p) (); //p是指向某函数的指针<br>&nbsp; p是指向某函数的指针，该函数无输入参数，返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值，值的内容是署名匹配的函数名和返回类型。例如：<br><br>void func()&nbsp;<br>{<br>/* do something */<br>}&nbsp;<br>p = func;&nbsp;<br><br>p的赋值可以不同，但一定要是函数的地址，并且署名和返回类型相同。<br><br>传递回调函数的地址给调用者<br><br>现在可以将p传递给另一个函数（调用者）- caller()，它将调用p指向的函数，而此函数名是未知的：<br><br>void caller(void(*ptr)())<br>{<br>ptr(); /* 调用ptr指向的函数 */&nbsp;<br>}<br>void func();<br>int main()<br>{<br>p = func;&nbsp;<br>caller(p); /* 传递函数地址到调用者 */<br>}<br><br>&nbsp;&nbsp;&nbsp; 如果赋了不同的值给p（不同函数地址），那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。<br><br>调用规范<br><br>&nbsp;&nbsp;&nbsp; 到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。<br><br>&nbsp;&nbsp;&nbsp; 将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如：<br><br>// 被调用函数是以int为参数，以int为返回值<br>__stdcall int callee(int);&nbsp;<br><br>// 调用函数以函数指针为参数<br>void caller( __cdecl int(*ptr)(int));&nbsp;<br><br>// 在p中企图存储被调用函数地址的非法操作<br>__cdecl int(*p)(int) = callee; // 出错<br><br><br>&nbsp;&nbsp;&nbsp; 指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列。
<img src ="http://www.cppblog.com/ivy-jie/aggbug/83247.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/ivy-jie/" target="_blank">ivy-jie</a> 2009-05-18 08:40 <a href="http://www.cppblog.com/ivy-jie/articles/83247.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>