<?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>Programming Life with Music &#187; C++</title>
	<atom:link href="http://jackaldire.com/tag/c/feed/" rel="self" type="application/rss+xml" />
	<link>http://jackaldire.com</link>
	<description>JackalDire &#039;s Blog</description>
	<lastBuildDate>Tue, 13 Jul 2010 08:29:29 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>[C++]一个由C-Style类型转换引发的血案</title>
		<link>http://jackaldire.com/201001/c-style-cast-issues/</link>
		<comments>http://jackaldire.com/201001/c-style-cast-issues/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 13:13:21 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=78</guid>
		<description><![CDATA[没事搜了一下自己域名，无意间发现老早提交的open directory申请居然通过了&#8230;想到一个多月没写东西，大惭&#8230; 为了尽快阻止这个连续N天没有日志的记录，先找一篇凑数&#8230; 先上开胃小菜 Appetizer 有人在byr论坛C++版上问了这样一道C++面试题： class A &#123; public: void fun&#40;&#41; &#123; &#125; &#125;; &#160; class B: public A &#123; public: virtual void fun&#40;&#41; &#123; &#125; &#125;; &#160; class C: public B &#123; public: void fun&#40;&#41;&#123;&#125; &#125;; &#160; class D: virtual public A &#123; public: void fun&#40;&#41;&#123;&#125; &#125;; &#160; int main&#40;void&#41; &#123; void *p; [...]]]></description>
			<content:encoded><![CDATA[<p>没事搜了一下自己域名，无意间发现老早提交的<a href="http://www.dmoz.org/World/Chinese_Simplified/%E8%AE%A1%E7%AE%97%E6%9C%BA/%E4%BA%92%E8%81%94%E7%BD%91%E7%BB%9C/%E7%BD%91%E7%BB%9C%E8%B5%84%E6%BA%90/%E5%8D%9A%E5%AE%A2/%E6%8A%80%E6%9C%AF/">open directory</a>申请居然通过了&#8230;想到一个多月没写东西，大惭&#8230;</p>
<p>为了尽快阻止这个连续N天没有日志的记录，先找一篇凑数&#8230;</p>
<h2>先上开胃小菜 Appetizer</h2>
<p>有人在<a href="http://forum.byr.edu.cn/wForum/disparticle.php?boardName=CPP&#038;ID=34447&#038;pos=49&#038;page=1">byr论坛C++版</a>上问了这样一道C++面试题：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> A <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>    <span style="color: #008000;">&#123;</span>    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> B<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> C<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> B <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">public</span><span style="color: #008080;">:</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">class</span> D<span style="color: #008080;">:</span> <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#123;</span><span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span>  <span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">void</span> <span style="color: #000040;">*</span>p<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>C<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// why fault here?</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>D<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #0000ff;">NULL</span><span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>为什么在((C*)NULL)->fun()这句挂了呢？</p>
<p><span id="more-78"></span><br />
原因很简单：B的fun是虚函数, 而C继承B, 所以通过C对象指针调用fun的时候触发基类virtual方法的多态特性, 需要查通过vptr查询虚表, 但是对NULL强制类型转换并不会设置vptr（vptr的设置是在构造函数中完成的），编译器就把地址0（NULL）中的内容当vptr，一解引用就segment fault了。</p>
<p>其实一开始看((A*)NULL)->fun();这句也觉得别扭，没有构造对象就直接调用非static的成员方法，居然还没有运行错误。想了一想，实际上在编译期，编译器都会通过一个<a href="http://en.wikipedia.org/wiki/Name_mangling">name mangling机制</a>将类的成员函数转换成一个具有唯一名字的非成员函数。程序开始运行时，成员函数和非成员函数一样都被载入到内存。所以只要在调用的成员函数不用到需要由类构造函数构造的成分（比如成员变量和vptr），这种通过指针调用成员函数就不会出错。</p>
<p>其实这个问题并不复杂，但是jmpesp老兄在回帖中写了一道更恶心的题目。</p>
<h2>主菜来了 Main Dishes</h2>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> A <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;A&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>  
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>  
&nbsp;
<span style="color: #0000ff;">class</span> B <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;B&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>  
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>  
&nbsp;
<span style="color: #0000ff;">class</span> C<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> B, <span style="color: #0000ff;">public</span> A <span style="color: #008000;">&#123;</span>  
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>  
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">float</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;C float&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">void</span> fun<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span>   <span style="color: #008000;">&#123;</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;C int&quot;</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span> 
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>       
    C<span style="color: #000040;">*</span> p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> C<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#40;</span><span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p<span style="color: #008000;">&#41;</span><span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>fun<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>问该段代码的输出是什么？</p>
<p>自己编译运行了一下输出是：C float&#8230;</p>
<p>首先输出C是确定的，因为无论怎么转型， p都是指向一个C对象，而A、B中的fun都是虚函数，所以调用的肯定是C类方法。输出float看起来比较诡异，原因肯定在这个(B*)(A*)c-style转型上。</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
     <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #008000;">&#40;</span>B<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>A<span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>p <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span></pre></div></div>

<p>上面的代码得到以下输出：<br />
00382E30<br />
00382E34<br />
00382E30</p>
<p>可见(B*)(A*)p和(A*)p是相同的。</p>
<p>分析如下：</p>
<p>C继承了A和B，所以一个C对象里包含了一个A对象和B对象。由上面的输出知道(A*)p使得p偏移到了C对象中的A对象，是个static_cast。 接下来(B*)再对指向A对象的p进行转型，这时候问题就出现了，B对象并不包含A对象，编译器也不知道p指向的A对象实际上是包含在一个C对象里，所以 (B*)这次转换，只是改变了p的静态类型，并没有改变p指向的位置，是个reinterpret_cast也就是说p还是指向一个A对象。所以通过p调用fun(1)的时候，查的是A的虚表，先查到virtual void A::fun(float)，再到void C::fun(float)</p>
<p>这个问题的关键就在(B*)(A*)p这两个C-Style的函数式转型(functional cast)上:<br />
(A*)p是从派生类C到基类A的转换，有可能是个向从派生类到基类的static_cast，也有可能是个简单的reinterpret_cast, 从运行结果上看(A*)是个static_cast，而由(A*)到(B*)的转换不可能是static_cast（static_cast (static_cast(p)))是编译不过的），所以只能是reinterpret_cast</p>
<p>这里又出现了一个问题，为什么(A*)p是个static_cast而不是reinterpret_cast？至少这与我的直觉不服，在ISO C++标准5.4节找到如下定义(见图):</p>
<p>也就是说一个C-style的强制类型转换如果可以解释成多个列表里的C++ style转型，取在列表里位置最前面的一个。static_cast在reinterpret_cast前，所以得到了下面的结果：</p>
<p style="text-align: center;"><strong>(B*)(A*)p 等价于 reinterpret_cast(static_cast(p))</strong></p>
<h2><strong>由这道题目说明（Conclusion）：</strong></h2>
<ol>
<li><strong>千万不要用C-style对对象指针进行转型，请用C++-style的static_cast、reinterpret_cast和dynamic_cast，以免造成意料之外的错误。</strong></li>
<li><strong>C++标准是个好东西，它胜过任何技术手册，一旦对语言特性有困惑，查标准是最好的解决方法。</strong></li>
</ol>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/200906/cost-of-class-inherit/" title="类继承的开销 ——《Inside the C++ Object Model》笔记二 (2009年06月20日)">类继承的开销 ——《Inside the C++ Object Model》笔记二</a> (0)</li>
	<li><a href="http://jackaldire.com/200905/note-constructor/" title="构造函数 ——《Inside the C++ Object Model》笔记一 (2009年05月15日)">构造函数 ——《Inside the C++ Object Model》笔记一</a> (0)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/201001/c-style-cast-issues/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>类继承的开销 ——《Inside the C++ Object Model》笔记二</title>
		<link>http://jackaldire.com/200906/cost-of-class-inherit/</link>
		<comments>http://jackaldire.com/200906/cost-of-class-inherit/#comments</comments>
		<pubDate>Sat, 20 Jun 2009 15:17:24 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=58</guid>
		<description><![CDATA[一、不含多态的继承 即基类里没有虚函数。这时候派生类成员的存取时间并没有增加，但是空间上会有浪费。比如有如下两个类 class X &#123; int a; char b; &#125;; class Y : public X &#123; char c; &#125;; 一个X对象占用4+1+3(padding, 假设4字节对齐) = 8字节。而一个Y对象并不是占用 4+1+1+2(padding)＝8个字节，而是占用4+1+3(padding)+1+3(padding)=12个字节。编译器之所以如此设计，是为了使派生类对象中基类对象的内存结构和原始的基类对象一致，这种一致性保证了通过指针复制对象时不出错。如果按照前一种内存布局，会产生错误，比如： X *px = new X&#40;&#41;, *py = new Y&#40;&#41;; *py = *px; 将一个基类指针的内容赋给一个派生类对象，基类对象8字节大，前4个字节是int a，第5个字节为char b，后面全是padding（无效数据），但是派生类对象的第六个字节是char c，所以这样的复制会将py->c给抹掉，造成数据丢失。 所以，编译器总是把派生类新加入的成员变量直接附到基类后面。由于内存对齐，每多家一层继承最多浪费3个字节，也不算很大的开销。 二、加上多态 多态是OO的一个重要概念，在C++里则表现为函数重载、虚函数和虚继承。 如果基类里加了虚函数，那么存取时间和存储空间都会有额外的开销。以一下两个类为例： class Point2d &#123; public: virtual int z&#40;&#41; &#123; return 0; &#125; [...]]]></description>
			<content:encoded><![CDATA[<h2>一、不含多态的继承</h2>
<p style="text-indent:2em">即基类里没有虚函数。这时候派生类成员的存取时间并没有增加，但是空间上会有浪费。比如有如下两个类</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> X <span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">int</span> a<span style="color: #008080;">;</span>
	<span style="color: #0000ff;">char</span> b<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">class</span> Y <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> X <span style="color: #008000;">&#123;</span>
	<span style="color: #0000ff;">char</span> c<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></pre></div></div>

<p style="text-indent:2em">一个X对象占用4+1+3(padding, 假设4字节对齐) = 8字节。而一个Y对象并不是占用 4+1+1+2(padding)＝8个字节，而是占用4+1+3(padding)+1+3(padding)=12个字节。编译器之所以如此设计，是为了使派生类对象中基类对象的内存结构和原始的基类对象一致，这种一致性保证了通过指针复制对象时不出错。如果按照前一种内存布局，会产生错误，比如：</p>
<p><span id="more-58"></span></p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">X <span style="color: #000040;">*</span>px <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> X<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, <span style="color: #000040;">*</span>py <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> Y<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #000040;">*</span>py <span style="color: #000080;">=</span> <span style="color: #000040;">*</span>px<span style="color: #008080;">;</span></pre></div></div>

<dl id="attachment_60" class="wp-caption aligncenter" style="width: 393px;">
<dt class="wp-caption-dt"><img class="size-full wp-image-60" title="example" src="http://jackaldire.com/wordpress/wp-content/uploads/2009/06/pictures.jpeg" alt="padding-example" width="383" height="169" /></dt>
</dl>
<p style="text-indent:2em">将一个基类指针的内容赋给一个派生类对象，基类对象8字节大，前4个字节是int a，第5个字节为char b，后面全是padding（无效数据），但是派生类对象的第六个字节是char c，所以这样的复制会将py->c给抹掉，造成数据丢失。</p>
<p style="text-indent:2em">所以，编译器总是把派生类新加入的成员变量直接附到基类后面。由于内存对齐，每多家一层继承最多浪费3个字节，也不算很大的开销。</p>
<h2>二、加上多态</h2>
<p style="text-indent:2em">多态是OO的一个重要概念，在C++里则表现为函数重载、虚函数和虚继承。</p>
<p style="text-indent:2em">如果基类里加了虚函数，那么存取时间和存储空间都会有额外的开销。以一下两个类为例：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">class</span> Point2d <span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
	<span style="color: #0000ff;">virtual</span> <span style="color: #0000ff;">int</span> z<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span> <span style="color: #666666;">//预留接口， 2d的点z轴坐标为0是合理的</span>
<span style="color: #0000ff;">protected</span><span style="color: #008080;">:</span>
	<span style="color: #0000ff;">int</span> _x, _y<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">class</span> Point3d <span style="color: #008080;">:</span> Public Point2d <span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
	<span style="color: #0000ff;">int</span> z<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span> <span style="color: #0000ff;">return</span> z<span style="color: #008080;">;</span> <span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">protected</span><span style="color: #008080;">:</span>
	<span style="color: #0000ff;">int</span> _z<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></pre></div></div>

<p>Point3d的额外开销具体如下：<br />
1.导入一个和Point2d有关的&#8221;虚函数表(virtual table)&#8221;，用来存放它所声明的所有虚成员函数的地址；再加上一两个slot，用于支持运行时“运行时类型信息(RTTI)”。<br />
2.在每一个类对象中导入一个vptr,提供执行期链接,使每一个对象都能找到相应的虚函数表。<br />
3.加强每一个构造函数，使他能够初始化vptr指向对应的虚函数表。<br />
4.加强析够函数，释放vptr。</p>
<p style="text-indent:2em">对于一个简单的Point类来说，这个开销显得非常大了。如</p>
<p style="text-indent:2em">果程序中有数量可观的Point对象，那么这个虚函数的存在将严重拖累程序执行的效率。</p>
<p style="text-indent:2em">所以，在使用虚函数之前，一定要现评估一下为虚函数所带来的设计上便利付出相应的效率代价是否值得。</p>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/200905/note-constructor/" title="构造函数 ——《Inside the C++ Object Model》笔记一 (2009年05月15日)">构造函数 ——《Inside the C++ Object Model》笔记一</a> (0)</li>
	<li><a href="http://jackaldire.com/201001/c-style-cast-issues/" title="[C++]一个由C-Style类型转换引发的血案 (2010年01月11日)">[C++]一个由C-Style类型转换引发的血案</a> (3)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/200906/cost-of-class-inherit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>构造函数 ——《Inside the C++ Object Model》笔记一</title>
		<link>http://jackaldire.com/200905/note-constructor/</link>
		<comments>http://jackaldire.com/200905/note-constructor/#comments</comments>
		<pubDate>Thu, 14 May 2009 16:40:24 +0000</pubDate>
		<dc:creator>JackalDire</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[构造函数]]></category>
		<category><![CDATA[笔记]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=47</guid>
		<description><![CDATA[Inside the C++ Object Model Chapter 2 : The Semantics of Constructor 构造函数语义学 一、默认构造函数 Default Constructor C++编译器会在Class的每一个构造函数中通过添加代码的方法暗中(implicit)做了下面几件事: 如果在这个构造函数没有对所有Member Class Object进行构造, 编译器会调用余下没有构造的Member Class Object的默认构造函数； 如果此构造函数没有提供基类构造函数的参数列表, 编译器会调用基类的默认构造函数； 如果类中含有虚函数,编译器会利用构造函数产生一张virtual function table(vbtl)，并在每个Class Object中添加一个vprt(指向vbtl的指针)； 如果类有Virtual Base Class虚基类，编译器会在构造函数中添加代码, 允许每个虚基类的执行期存取操作。 如果这个Class没有任何构造函数, 而且发生上述四种情况的任意一种, 编译器就会生成一个默认构造函数 有以下两个误区： 任何Class只要没有定义default constructor，就会被合成一个来； 编译器合成出来的default constructor会明确设定class内每一个data member的值。 二、拷贝构造函数 Copy Constructor 拷贝构造函数的情况于默认构造函数类似。编译器对没有声明拷贝构造函数的对象实行Bitwise Copy(位逐次拷贝)，也就是简单的复制类中的每一个成员，此时编译器不会生成默认拷贝构造函数。但若出现以下四种情况的任意一种，编译器将会生成一个拷贝构造函数： 当Class内含一个成员对象，而且这个成员对象声明有一个拷贝构造函数（无论是被设计者声明或是被编译器合成） 编译器会调用基类(Base Class)的拷贝构造函数构造； 如果类中含有虚函数, 编译器会重新设定vptr，而不是简单的拷贝其值。如果将一个对象复制同一种类对象时，复制vptr没有问题。但如果是将一个派生类对象复制给基类对象或者反之，复制vptr的值就是错误的。 如果类对象的继承链上有Virtual Base Class虚基类，当一个基类对象以一个派生类对象为初值的时候，需要重新设定vptr或者offset值 [...]]]></description>
			<content:encoded><![CDATA[<p>Inside the C++ Object Model Chapter 2 : The Semantics of Constructor 构造函数语义学</p>
<h3>一、默认构造函数 Default Constructor</h3>
<p>C++编译器会在Class的每一个构造函数中通过添加代码的方法暗中(implicit)做了下面几件事:</p>
<ol>
<li>如果在这个构造函数没有对所有Member Class Object进行构造, 编译器会调用余下没有构造的Member Class Object的默认构造函数；</li>
<li>如果此构造函数没有提供基类构造函数的参数列表, 编译器会调用基类的默认构造函数；</li>
<li>如果类中含有虚函数,编译器会利用构造函数产生一张virtual function table(vbtl)，并在每个Class Object中添加一个vprt(指向vbtl的指针)；</li>
<li>如果类有Virtual Base Class虚基类，编译器会在构造函数中添加代码, 允许每个虚基类的执行期存取操作。</li>
</ol>
<p><span id="more-47"></span><br />
如果这个Class没有任何构造函数, 而且发生上述四种情况的任意一种, 编译器就会生成一个默认构造函数</p>
<p>有以下两个误区：</p>
<ol>
<li>任何Class只要没有定义default constructor，就会被合成一个来；</li>
<li>编译器合成出来的default constructor会明确设定class内每一个data member的值。</li>
</ol>
<h3>二、拷贝构造函数 Copy Constructor</h3>
<p>拷贝构造函数的情况于默认构造函数类似。编译器对没有声明拷贝构造函数的对象实行Bitwise Copy(位逐次拷贝)，也就是简单的复制类中的每一个成员，此时编译器不会生成默认拷贝构造函数。但若出现以下四种情况的任意一种，编译器将会生成一个拷贝构造函数：</p>
<ol>
<li>当Class内含一个成员对象，而且这个成员对象声明有一个拷贝构造函数（无论是被设计者声明或是被编译器合成）</li>
<li>编译器会调用基类(Base Class)的拷贝构造函数构造；</li>
<li>如果类中含有虚函数, 编译器会重新设定vptr，而不是简单的拷贝其值。如果将一个对象复制同一种类对象时，复制vptr没有问题。但如果是将一个派生类对象复制给基类对象或者反之，复制vptr的值就是错误的。</li>
<li>如果类对象的继承链上有Virtual Base Class虚基类，当一个基类对象以一个派生类对象为初值的时候，需要重新设定vptr或者offset值</li>
</ol>

	<h4>Related Post</h4>
	<ul class="st-related-posts">
	<li><a href="http://jackaldire.com/200906/cost-of-class-inherit/" title="类继承的开销 ——《Inside the C++ Object Model》笔记二 (2009年06月20日)">类继承的开销 ——《Inside the C++ Object Model》笔记二</a> (0)</li>
	<li><a href="http://jackaldire.com/201001/c-style-cast-issues/" title="[C++]一个由C-Style类型转换引发的血案 (2010年01月11日)">[C++]一个由C-Style类型转换引发的血案</a> (3)</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://jackaldire.com/200905/note-constructor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
