<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>凋零的羽 &#187; C/C++</title>
	<atom:link href="http://www.halfroom.com/categories/cpp/feed" rel="self" type="application/rss+xml" />
	<link>http://www.halfroom.com</link>
	<description>乘着风游荡在蓝天边</description>
	<lastBuildDate>Tue, 07 Feb 2012 07:54:44 +0000</lastBuildDate>
	<language>zh-cn</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>ISO C++委员会批准C++0x最终草案</title>
		<link>http://www.halfroom.com/iso-cpp-committee-approve-0x-draft.html</link>
		<comments>http://www.halfroom.com/iso-cpp-committee-approve-0x-draft.html#comments</comments>
		<pubDate>Thu, 31 Mar 2011 01:47:18 +0000</pubDate>
		<dc:creator>灰灰</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[ISO]]></category>

		<guid isPermaLink="false">http://www.halfroom.com/?p=1408</guid>
		<description><![CDATA[<p>Posted in <a href="http://www.halfroom.com/tumblog/links">Links</a></p><p><a href="http://" rel="bookmark" title="ISO C++委员会批准C++0x最终草案" target="_blank">http://</a></p>IS0 C++委员会正式批准了C++编程语言国际标准最终草案(FDIS)。 标准本身已经完成，接下来将是根据委员会会议修改意见更新工作草案，预计将用三周时间完成FDIS草案，然后交给日内瓦的ITTF，最新的C++标准将在夏天发布，先前被临时命名为C++0x的新标准将被称为C++ 2011。从2003年发布的C++03到2011年的C++ 2011，新标准的制定历经了8年时间。GCC和Visual C++编译器都已加入了C++2011/C++0x的支持。 C++0x FAQ(由C++作者Bjarne Stroustrup维护) via Solidot]]></description>
			<content:encoded><![CDATA[<p>IS0 C++委员会<a href="http://developers.slashdot.org/story/11/03/26/1949225/ISO-C-Committee-Approves-C0x-Final-Draft">正式批准</a>了C++编程语言国际标准最终草案(FDIS)。</p>
<p><em>标准本身已经完成，接下来将是根据委员会会议修改意见更新工作草案，预计将用三周时间完成FDIS草案，然后交给日内瓦的ITTF，最新的C++标准将在夏天发布，先前被临时命名为<a href="http://zh.wikipedia.org/wiki/C++0x">C++0x</a>的新标准将被称为C++ 2011。从2003年发布的C++03到2011年的C++ 2011，新标准的制定历经了8年时间。<a href="http://gcc.gnu.org/projects/cxx0x.html">GCC</a>和<a href="http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx">Visual C++</a>编译器都已加入了C++2011/C++0x的支持。</em> <a href="http://www2.research.att.com/%7Ebs/C++0xFAQ.html">C++0x FAQ</a>(由C++作者Bjarne Stroustrup维护)</p>
<p>via <a href="http://developers.solidot.org/article.pl?sid=11/03/27/1056211" target="_blank">Solidot</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.halfroom.com/iso-cpp-committee-approve-0x-draft.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>C语言实现trim函数</title>
		<link>http://www.halfroom.com/c-tri.html</link>
		<comments>http://www.halfroom.com/c-tri.html#comments</comments>
		<pubDate>Thu, 24 Mar 2011 11:47:34 +0000</pubDate>
		<dc:creator>灰灰</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[trim]]></category>

		<guid isPermaLink="false">http://www.halfroom.com/c%e8%af%ad%e8%a8%80%e5%ae%9e%e7%8e%b0trim%e5%87%bd%e6%95%b0.html</guid>
		<description><![CDATA[<p>Posted in <a href="http://www.halfroom.com/tumblog/quotes">Quotes</a></p><p><cite>Unknown ~ <a href="http://" title="C语言实现trim函数">Unknown</a></cite></p>]]></description>
			<content:encoded><![CDATA[<pre class="brush: cpp; title: ; notranslate">
char *trim(char *str)
{
    char *start = str;
    char *end;
    if(start)
    {
        end = start + strlen(str) - 1;
        while(*start &amp;&amp; isspace(*start)) start++;
        while(end &gt; start &amp;&amp; isspace(*end)) *end-- = '&#92;&#48;';
}
    return start;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.halfroom.com/c-tri.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++虚函数表解析(转)update</title>
		<link>http://www.halfroom.com/cpp-virtual-fun.html</link>
		<comments>http://www.halfroom.com/cpp-virtual-fun.html#comments</comments>
		<pubDate>Thu, 29 Jul 2010 12:00:58 +0000</pubDate>
		<dc:creator>灰灰</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Digest]]></category>

		<guid isPermaLink="false">http://www.halfroom.com/?p=1120</guid>
		<description><![CDATA[<p>Posted in <a href="http://www.halfroom.com/categories/cpp" title="C/C++">C/C++</a><a href="http://www.halfroom.com/categories/digest" title="Digest">Digest</a></p>update: 今天花了一下午仔细看了这篇文章，却怎么也琢磨不透，因为当中的说法前后存在一些矛盾，后来脑子快崩溃的时候，做了一个大胆的假设： 也许里面有些地方说错了。 果然，当我回头看这篇文章下面的评论的时候，才知道，作者确实某些地方说错了。。。但是，回过头来评价这篇文章，真的是一绝的好文，堪称一绝是无可厚非的。 现在我将其中的错误作出相应的修改，以及矛盾的说明，并以红色字说明。 original: C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。 关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。 当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。 言归正传，让我们一起进入虚函数的世界。 虚函数表 对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。 在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，在有虚函数的类的实例中这个表被分配在了 这个实例的内存中（应该更正为 一个类的虚函数表是静态的，也就是说对这个类的每个实例，他的虚函数表的是固定的，不会为每个实例生成一个相应的虚函数表。），所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。 这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到，编译器必需要保证虚函数表的指针(这里明明说对象实例的最前面的位置存的是虚函数表的指针，注意，不是虚函数表，而是指向虚函数表的指针，然后再看下面的例子上的代码)存在于对象实例中最前面的位置（这是为了保证正确取到虚函数的偏移量）。 这意味着我们通过对象实例的地址得到这张虚函数表(这话没错，不过，通过对象实例的地址一次是取不到的，需要两次)，然后就可以遍历其中函数指针，并调用相应的函数。 听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。 假设我们有这样的一个类： class Base { public: virtual void f() { cout &#60;&#60; &#8220;Base::f&#8221; &#60;&#60; endl; } virtual void &#8230; <a href="http://www.halfroom.com/cpp-virtual-fun.html">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><span style="color: #ff0000;">update: 今天花了一下午仔细看了这篇文章，却怎么也琢磨不透，因为当中的说法前后存在一些矛盾，后来脑子快崩溃的时候，做了一个大胆的假设： 也许里面有些地方说错了。 </span></p>
<p><span style="color: #ff0000;">果然，当我回头看这篇文章下面的评论的时候，才知道，作者确实某些地方说错了。。。但是，回过头来评价这篇文章，真的是一绝的好文，堪称一绝是无可厚非的。</span></p>
<p><span style="color: #ff0000;">现在我将其中的错误作出相应的修改，以及矛盾的说明，并以红色字说明。</span></p>
<p>original:</p>
<p>C++中的虚函数的作用主要是实现了多态的机制。关于多态，简而言之就是用父类型别的指针指向其子类的实例，然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”，这是一种泛型技术。所谓泛型技术，说白了就是试图使用不变的代码来实现可变的算法。比如：模板技术，RTTI技术，虚函数技术，要么是试图做到在编译时决议，要么试图做到运行时决议。</p>
<p>关于虚函数的使用方法，我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中，我只想从虚函数的实现机制上面为大家 一个清晰的剖析。</p>
<p>当然，相同的文章在网上也出现过一些了，但我总感觉这些文章不是很容易阅读，大段大段的代码，没有图片，没有详细的说明，没有比较，没有举一反三。不利于学习和阅读，所以这是我想写下这篇文章的原因。也希望大家多给我提意见。</p>
<p>言归正传，让我们一起进入虚函数的世界。</p>
<h3>虚函数表</h3>
<p>对C++ 了解的人都应该知道虚函数（Virtual Function）是通过一张虚函数表（Virtual Table）来实现的。简称为V-Table。 在这个表中，主是要一个类的虚函数的地址表，这张表解决了继承、覆盖的问题，保证其容真实反应实际的函数。这样，<span style="text-decoration: line-through;">在有虚函数的类的实例中这个表被分配在了 这个实例的内存中</span><span style="color: #ff0000;">（应该更正为 一个类的虚函数表是静态的，也就是说对这个类的每个实例，他的虚函数表的是固定的，不会为每个实例生成一个相应的虚函数表。）</span>，所以，当我们用父类的指针来操作一个子类的时候，这张虚函数表就显得由为重要了，它就像一个地图一样，指明了实际所应该调用的函数。</p>
<p>这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到，编译器必需要保证<span style="color: #000000;">虚函数表的指针</span><span style="color: #ff0000;">(这里明明说对象实例的最前面的位置存的是虚函数表的指针，注意，不是虚函数表，而是指向虚函数表的指针，然后再看下面的例子上的代码)</span>存在于对象实例中最前面的位置（这是为了保证正确取到虚函数的偏移量）。 这意味着我们通过对象实例的地址得到这张虚函数表<span style="color: #ff0000;">(这话没错，不过，通过对象实例的地址一次是取不到的，需要两次)</span>，然后就可以遍历其中函数指针，并调用相应的函数。</p>
<p>听我扯了那么多，我可以感觉出来你现在可能比以前更加晕头转向了。 没关系，下面就是实际的例子，相信聪明的你一看就明白了。</p>
<p>假设我们有这样的一个类：</p>
<p>class Base {</p>
<p>public:</p>
<p>virtual void f() { cout &lt;&lt; &#8220;Base::f&#8221; &lt;&lt; endl; }</p>
<p>virtual void g() { cout &lt;&lt; &#8220;Base::g&#8221; &lt;&lt; endl; }</p>
<p>virtual void h() { cout &lt;&lt; &#8220;Base::h&#8221; &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; &#8220;虚函数表地址：&#8221; &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;  <span style="color: #ff0000;">//这只是对象实例的地址，而非虚函数表的地址</span>，*(int*)(&amp;b)才是指向虚函<span style="color: #ff0000;">数表的指针，也就是虚函数表的地址，这与最前面的括号里面的注明相呼应。</span></p>
<p>cout &lt;&lt; &#8220;虚函数表 — 第一个函数地址：&#8221; &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl;  <span style="color: #ff0000;">//而这个才是虚函数表的地址，虚函数表的第一个函数地址（函数指针）应该是*(int*)*(int*)(&amp;b)，这样下面的函数指针赋值才说得通：pFun = (Fun)*((int*)*(int*)(&amp;b)); ，不然下面的“</span></p>
<p><span style="color: #ff0000;">(Fun)*((int*)*(int*)(&amp;b)+0); // Base::f()</span></p>
<p><span style="color: #ff0000;">(Fun)*((int*)*(int*)(&amp;b)+1); // Base::g()</span></p>
<p><span style="color: #ff0000;">(Fun)*((int*)*(int*)(&amp;b)+2); // Base::h()</span></p>
<p><span style="color: #ff0000;">”</span></p>
<p><span style="color: #ff0000;">的+0， +1， +2这样的函数指针偏移方式也说不通了。<br />
</span></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>虚函数表 — 第一个函数地址：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><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable1.jpg" border="0" alt="" /></p>
<p>注意：在上面这个图中，我在虚函数表的最后多加了一个结点，这是虚函数表的结束结点，就像字符串的结束符“\0”一样，其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下，这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下，这个值是如果1，表示还有下一个虚函数表，如果值是0，表示是最后一个虚函数表。</p>
<p>下面，我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况，主要目的是为了给一个对比。在比较之下，我们可以更加清楚地知道其内部的具体实现。</p>
<h3>一般继承（无虚函数覆盖）</h3>
<p>下面，再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing3.jpg" border="0" alt="" /></p>
<p><span id="more-1120"></span></p>
<p>请注意，在这个继承关系中，子类没有重载任何父类的函数。那么，在派生类的实例中，其虚函数表如下所示：</p>
<p>对于实例：Derive d; 的虚函数表如下：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable2.JPG" border="0" alt="" /></p>
<p>我们可以看到下面几点：</p>
<p>1）虚函数按照其声明顺序放于表中。</p>
<p>2）父类的虚函数在子类的虚函数前面。</p>
<p>我相信聪明的你一定可以参考前面的那个程序，来编写一段程序来验证。</p>
<h3>一般继承（有虚函数覆盖）</h3>
<p>覆盖父类的虚函数是很显然的事情，不然，虚函数就变得毫无意义。下面，我们来看一下，如果子类中有虚函数重载了父类的虚函数，会是一个什么样子？假设，我们有下面这样的一个继承关系。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing4.jpg" border="0" alt="" /></p>
<p>为了让大家看到被继承过后的效果，在这个类的设计中，我只覆盖了父类的一个函数：f()。那么，对于派生类的实例，其虚函数表会是下面的一个样子：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable3.JPG" border="0" alt="" /></p>
<p>我们从表中可以看到下面几点，</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()被调用了。这就实现了多态。</p>
<h3>多重继承（无虚函数覆盖）</h3>
<p>下面，再让我们来看看多重继承中的情况，假设有下面这样一个类的继承关系。注意：子类并没有覆盖父类的函数。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing1.jpg" border="0" alt="" /></p>
<p>对于子类实例中的虚函数表，是下面这个样子：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable4.JPG" border="0" alt="" /></p>
<p>我们可以看到：</p>
<p>1） 每个父类都有自己的虚表。</p>
<p>2） 子类的成员函数被放到了第一个父类的表中。（所谓的第一个父类是按照声明顺序来判断的）</p>
<p>这样做就是为了解决不同的父类类型的指针指向同一个子类实例，而能够调用到实际的函数。</p>
<h3>多重继承（有虚函数覆盖）</h3>
<p>下面我们再来看看，如果发生虚函数覆盖的情况。</p>
<p>下图中，我们在子类中覆盖了父类的f()函数。</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing2.jpg" border="0" alt="" /></p>
<p>下面是对于子类实例中的虚函数表的图：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable5.jpg" border="0" alt="" /></p>
<p>我们可以看见，三个父类虚函数表中的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>
<h3>安全性</h3>
<p>每次写C++的文章，总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述，相信我们对虚函数表有一个比较细致的了解了。水可载舟，亦可覆舟。下面，让我们来看看我们可以用虚函数表来干点什么坏事吧。</p>
<p><strong>一、通过父类型的指针访问子类自己的虚函数</strong></p>
<p>我们知道，子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数，但我们根本不可能使用下面的语句来调用子类的自有虚函数：</p>
<p>Base1 *b1 = new Derive();</p>
<p>b1-&gt;f1(); //编译出错</p>
<p>任何妄图使用父类指针想调用子类中的<strong>未覆盖父类的成员函数</strong>的行为都会被编译器视为非法，所以，这样的程序根本无法编译通过。但在运行时，我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。（关于这方面的尝试，通过阅读后面附录的代码，相信你可以做到这一点）</p>
<p><strong>二、访问non-public</strong><strong>的虚函数</strong></p>
<p>另外，如果父类的虚函数是private或是protected的，但这些非public的虚函数同样会存在于虚函数表中，所以，我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数，这是很容易做到的。</p>
<p>如：</p>
<p>class Base {</p>
<p>private:</p>
<p>virtual void f() { cout &lt;&lt; &#8220;Base::f&#8221; &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>
<h3>结束语</h3>
<p>C++这门语言是一门Magic的语言，对于程序员来说，我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言，我们就必需要了解C++里面的那些东西，需要去了解C++中那些危险的东西。不然，这是一种搬起石头砸自己脚的编程语言。</p>
<p><span style="color: #ff0000;">为了说明文章里提到的问题，我在下面附上我的代码，环境是g++ 编译的，编译时要注意多一个参数，</span></p>
<pre class="brush: bash; title: ; notranslate">
g++ virtual_func.cpp -Wno-pmf-conversions -o virtual_func
</pre>
<p><span style="color: #ff0000;">代码：</span></p>
<p><span style="color: #ff0000;"><br />
</span></p>
<pre class="brush: cpp; title: ; notranslate">
#include&lt;iostream&gt;
#include&lt;stdio.h&gt;
using namespace std;
typedef void(*Fun)(void);
typedef void (*func_type)(void * obj);
class Base {

public:
    virtual void f() { cout &lt;&lt; &quot;Base::f&quot; &lt;&lt; endl; }

    virtual void g() { cout &lt;&lt; &quot;Base::g&quot; &lt;&lt; endl; }

    virtual void h() { cout &lt;&lt; &quot;Base::h&quot; &lt;&lt; endl; }
}; 

int main(int argc, char* argv[])
{
    Base a,b;
    func_type pa,pb;
    pa = &amp;Base::f;
    pb = &amp;Base::f;
    cout &lt;&lt; &quot;Addr of b: &quot; &lt;&lt; (int*)(&amp;b) &lt;&lt; endl;
    cout &lt;&lt; &quot;Addr of v-table in b: &quot; &lt;&lt; (int*)*(int*)(&amp;b) &lt;&lt; endl; //虚函数表的地址。
    cout &lt;&lt; &quot;Addr of first virtual func in v-table of b: &quot; &lt;&lt; (int*)*(int*)*(int*)(&amp;b) &lt;&lt; endl; //第一个虚函数的地址
    cout &lt;&lt; &quot;Addr of a: &quot; &lt;&lt; (int*)(&amp;a) &lt;&lt; endl;
    cout &lt;&lt; &quot;Addr of v-table in a: &quot; &lt;&lt; (int*)*(int*)(&amp;a) &lt;&lt; endl;
    cout &lt;&lt; &quot;Addr of first virtual func in v-table of a: &quot; &lt;&lt; (int*)*(int*)*(int*)(&amp;a) &lt;&lt; endl; //从这里我们可以看到，无论是实例a，还是实例b，他们的虚函数地址是一样的，也就是上面红色字体说的，虚函数表是静态的，不会为每个实例生成一个相应的虚函数表。
   Fun pFun = NULL;
   Fun pFuni = NULL;
   // Invoke the first virtual function
   pFun = (Fun)*((int*)*(int*)(&amp;b));
   cout &lt;&lt; &quot;pFun callback: &quot; ;
   pFun();
   cout &lt;&lt; endl;
   cout &lt;&lt; &quot;Callback of b: &quot; ;
   pa(&amp;b);
   cout &lt;&lt; endl;
   cout &lt;&lt; &quot;Callback of a: &quot; ;
   pb(&amp;a);
   cout &lt;&lt; endl;
   //printf(&quot;(int*)*(int*)(&amp;b): 0x%x\n&quot;,*(int*)*(int*)(&amp;b));
   printf(&quot;pFun: 0x%x\n&quot;, pFun); //这行以及下一行其实没多大意义，只是为了证明所有实例的虚函数表地址和虚函数 地址是一样的。
   printf(&quot;pb: 0x%x\n&quot;, pb);
   printf(&quot;pa: 0x%x\n&quot;, pa);
   return 0;
 }
</pre>
<p>运行结果如下：</p>
<pre class="brush: bash; title: ; notranslate">
Addr of b: 0xbfffe088
Addr of v-table in b: 0x8048c98
Addr of first virtual func in v-table of b: 0x8048a8a
Addr of a: 0xbfffe08c
Addr of v-table in a: 0x8048c98
Addr of first virtual func in v-table of a: 0x8048a8a
pFun callback: Base::f

Callback of b: Base::f

Callback of a: Base::f

pFun: 0x8048a8a
pb: 0x8048a8a
pa: 0x8048a8a
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.halfroom.com/cpp-virtual-fun.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>C++的字符串分割函数</title>
		<link>http://www.halfroom.com/split-str-in-c.html</link>
		<comments>http://www.halfroom.com/split-str-in-c.html#comments</comments>
		<pubDate>Fri, 16 Jul 2010 08:26:04 +0000</pubDate>
		<dc:creator>灰灰</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.halfroom.com/?p=1177</guid>
		<description><![CDATA[<p>Posted in <a href="http://www.halfroom.com/categories/cpp" title="C/C++">C/C++</a></p>定义一个数据结构，其中成员有int len, char* internal_buf, char** strings。其中len是被分割后的字符串的个数，internal_buf用于保存原字符串。char** strings是字符串指针(或者你可以看做是指向字符数组的字符串指针，指针指向的类型是字符串，而不是单一的字符，char*strings[])，每个字符串指针用于保存被分割后的每个字符串的首地址。]]></description>
			<content:encoded><![CDATA[<p>定义一个数据结构，其中成员有int len, char* internal_buf, char** strings。其中len是被分割后的字符串的个数，internal_buf用于保存原字符串。char** strings是字符串指针(或者你可以看做是指向字符数组的字符串指针，指针指向的类型是字符串，而不是单一的字符，char*strings[])，每个字符串指针用于保存被分割后的每个字符串的首地址。</p>
<pre class="brush: cpp; title: ; notranslate">
#define STRING_TERMINATER '&#92;&#48;'

/*必须用完后调用free_sc_exlode_t释放内存，否则内存泄露*/
typedef struct sc_exlode_struct
{
    char** strings;
    int len;
    char* internal_buf;
} sc_exlode_t;

void free_sc_exlode_t(sc_exlode_t *string_array)
{
    if(NULL == string_array)
    {
        return;
    }
    free(string_array-&amp;gt;internal_buf);
    free(string_array-&amp;gt;strings);
    free(string_array);
}

void split_string(char delimiter, char *string ,sc_exlode_t *string_array)
{
    int count = 1;
    char *pchar, **ptr;

    if ( NULL != string_array)
    {
        memset(string_array, 0, sizeof(sc_exlode_t));
    }

    if(NULL == string || NULL == string_array)
    {
        return;
    }

    string_array-&amp;gt;internal_buf = malloc(strlen(string) + 1);

    strcpy(string_array-&amp;gt;internal_buf,string);
    if(NULL == string_array-&amp;gt;internal_buf)
    {
        return;
    }

    pchar = string;
    while(STRING_TERMINATER != *pchar) //计算原字符串可以分割成的字符串个数
    {
        if (delimiter == *pchar)
        {
            count++;
        }
            pchar++;
    }
    string_array-&amp;gt;strings = (char**)malloc(count*sizeof(char*));
    if(NULL == string_array-&amp;gt;strings)
    {
        return;
    }
    string_array-&amp;gt;len = count;

    ptr = string_array-&amp;gt;strings;
    *ptr = string_array-&amp;gt;internal_buf;
    pchar = string_array-&amp;gt;internal_buf;
    while(STRING_TERMINATER != *pchar)
    {
        if (delimiter == *pchar)  //遇到指定字符时走此分支
        {
            ptr++;
            *ptr = pchar+1;
            *pchar = STRING_TERMINATER;
            pchar++;
                                               //每个ptr所指向的字符串末尾应该为'&#92;&#48;'，
                                               //这样才能结束，所以将';'置为 '&#92;&#48;'，
                                               //并让下一个字符串的首个字符地址，
                                               //即*ptr，指向‘；’后面的新字符串
                                               //的首地址。
    }
}
int main(void)
{
    sc_exlode_t *string_array = malloc(sizeof(sc_exlode_t));
    char *ip_range = &quot;10.11.111.0;192.168.0.1;192.168.0.101&quot;;
    split_string(';', ip_range, string_array);
    printf(&quot;The spilited result: \n&quot;);

    for(int i=0;ilen;i++)
    {
        printf(&quot;%s\n&quot;,string_array-&amp;gt;strings[i]);
    }
    free_sc_exlode_t(string_array);
    return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.halfroom.com/split-str-in-c.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>C++/C中指针与数组的差别</title>
		<link>http://www.halfroom.com/compare-ptr-and-array-in-cpp.html</link>
		<comments>http://www.halfroom.com/compare-ptr-and-array-in-cpp.html#comments</comments>
		<pubDate>Thu, 15 Jul 2010 12:49:24 +0000</pubDate>
		<dc:creator>灰灰</dc:creator>
				<category><![CDATA[C/C++]]></category>

		<guid isPermaLink="false">http://www.halfroom.com/?p=1172</guid>
		<description><![CDATA[<p>Posted in <a href="http://www.halfroom.com/categories/cpp" title="C/C++">C/C++</a></p>C++/C中指针与数组的差别简述： C++/C 程序中，指针和数组在不少地方可以相互替换着用，让人产生一种错觉，以 为两者是等价的。 数组要么在静态存储区被创建（如全局数组），要么在栈上被创建。数组名对应着 （而不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改 变。 指针可以随时指向任意类型的内存块，它的特征是“可变”，所以我们常用指针来 操作动态内存。指针远比数组灵活，但也更危险。]]></description>
			<content:encoded><![CDATA[<p>C++/C中指针与数组的差别简述：<br />
C++/C 程序中，指针和数组在不少地方可以相互替换着用，让人产生一种错觉，以<br />
为两者是等价的。<br />
<span style="color: #ff0000;"> 数组要么在静态存储区被创建（如全局数组），要么在栈上被创建。数组名对应着<br />
（而不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改<br />
变。</span><br />
指针可以随时指向任意类型的内存块，它的特征是“可变”，所以我们常用指针来<br />
操作动态内存。指针远比数组灵活，但也更危险。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.halfroom.com/compare-ptr-and-array-in-cpp.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

