﻿<?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++博客-Graceful Code and Clean Soul</title><link>http://www.cppblog.com/josh/</link><description>Seeking for a leap from greenhorn to guru in C++</description><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 10:48:31 GMT</lastBuildDate><pubDate>Mon, 13 Apr 2026 10:48:31 GMT</pubDate><ttl>60</ttl><item><title>C++: Under the Hood(zh-CN)</title><link>http://www.cppblog.com/josh/archive/2009/08/02/91960.html</link><dc:creator>Josh</dc:creator><author>Josh</author><pubDate>Sun, 02 Aug 2009 08:02:00 GMT</pubDate><guid>http://www.cppblog.com/josh/archive/2009/08/02/91960.html</guid><wfw:comment>http://www.cppblog.com/josh/comments/91960.html</wfw:comment><comments>http://www.cppblog.com/josh/archive/2009/08/02/91960.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/josh/comments/commentRss/91960.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/josh/services/trackbacks/91960.html</trackback:ping><description><![CDATA[<p>这是Jan Gray在1994的一篇文章，是以Visual C++作为实现来讲解C++对象布局、继承、多态等的实现</p> <p>下面是我的阅读笔记：</p> <h2>Intruduction</h2> <p>理解编程语言如何实现是非常重要的。这些关于语言实现的知识驱散了类似“编译器究竟在这里做了什么手脚？”之类的恐惧和疑问；给予了使用新特性的信心；并提供了调试和学习其他语言特性时的领悟力。它也提供了每天写最有效率代码的感觉。</p> <p>本篇认真探索C++的底层，解释“运行时”实现细节，如类布局技术和virtual函数调用机制。将解释的问题包括：</p> <ul> <li>类如何内存布局？  <li>数据成员如何访问？  <li>成员函数如何调用？  <li>什么是adjuster thunk?  <li>代价如何：  <ul> <li>单继承、多继承、虚继承  <li>虚函数和虚函数调用  <li>cast到基类或虚基类  <li>异常处理</li></ul></li></ul> <h2>Class Layout</h2> <p>本节，我们将考虑不同类型继承的内存布局</p> <h3>C-like Structs</h3> <p>和C是兼容的，struct遵循简单的结构体布局规则：按成员声明的顺序布局，并受制于实现定义的对齐填补</p> <p>所有的C++厂商确保有效的struct由他们的C++编译器存储保持相同</p> <p>例子：</p> <p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_2.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="78" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb.png" width="207" border="0"></a> </p> <div class="csharpcode-wrapper"> <div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> A {</pre><pre class="alteven">    <span class="kwrd">char</span> c;</pre><pre class="alt">    <span class="kwrd">char</span> i;</pre><pre class="alteven">};</pre></div></div>
<p></p>
<h3>C-like Structs with C++ feature</h3>
<p>这里B是一个带C++风格的的struct：有public/protected/private 访问控制符，成员函数，静态成员和嵌套的类型声明。</p>
<p>只有那些non-virtual 的数据成员在每个实例占有空间</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_4.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="111" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_1.png" width="202" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> B {</pre><pre class="alteven"><span class="kwrd">public</span>:</pre><pre class="alt">    <span class="kwrd">int</span> bm1;</pre><pre class="alteven"><span class="kwrd">protected</span>:</pre><pre class="alt">    <span class="kwrd">int</span> bm2;</pre><pre class="alteven"><span class="kwrd">private</span>:</pre><pre class="alt">    <span class="kwrd">int</span> bm3;</pre><pre class="alteven">    <span class="kwrd">static</span> <span class="kwrd">int</span> bsm;</pre><pre class="alt">    <span class="kwrd">void</span> bf();</pre><pre class="alteven">    typedef <span class="kwrd">void</span> * bpv;</pre><pre class="alt">    <span class="kwrd">struct</span> N {};</pre><pre class="alteven">};</pre></div></div>
<h3>Single Inheritance</h3>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_6.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="48" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_2.png" width="202" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> C {</pre><pre class="alteven">    <span class="kwrd">int</span> c1;</pre><pre class="alt">    <span class="kwrd">void</span> cf();</pre><pre class="alteven">};</pre></div></div>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_8.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="86" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_3.png" width="236" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> D : C {</pre><pre class="alteven">    <span class="kwrd">int</span> d1;</pre><pre class="alt">    <span class="kwrd">void</span> df();</pre><pre class="alteven">};</pre></div></div>
<p>在D中，虽然没有要求说c的实例数据必须优先于D的实例数据，但是语言实现这样布局可以确保D中的基类子对象c的起始地址和D实例对象的起始地址一样，这样当我们需要从D*得到子对象地址时可以减少开销。</p>
<p>于是，在单继承的类层次中，新的实例数据成员仅仅是简单的追加到基类布局的后面。</p>
<h3>Multiple Inheritance</h3>
<p>在大多数设计中，单继承已经有足够表达力来表达继承关系。但是有时，我们希望继承类能取得两个或更多类的行为，多继承正好满足了这个要求。</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_10.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="51" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_4.png" width="209" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> E {</pre><pre class="alteven">    <span class="kwrd">int</span> e1;</pre><pre class="alt">    <span class="kwrd">void</span> ef();</pre><pre class="alteven">};</pre></div></div>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_12.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="113" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_5.png" width="247" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> F : <span class="kwrd">public</span> C, E {</pre><pre class="alteven">    inf f1; </pre><pre class="alt">    <span class="kwrd">void</span> ef();</pre><pre class="alteven">};</pre></div></div>
<p>F继承自C和E，并且F的实例包含了每个基类的实例。但是不像单继承，不可能使得每个基类子对象的地址都和继承类的实例地址保持一样，其中只能有一个一样，其他的得做偏移。</p>
<p>既然存在地址偏移，我们注意到当我们做cast的时，对于需要地址偏移的cast显然有效率代价。</p>
<p>当然了，这种偏移对于语言使用者来说是透明的，偏移计算由编译器来实现。</p>
<h3>Virtual Inheritance</h3>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> G : <span class="kwrd">virtual</span> C {</pre><pre class="alteven">    <span class="kwrd">int</span> g1;</pre><pre class="alt">    <span class="kwrd">void</span> gf();</pre><pre class="alteven">}</pre></div></div>
<p></p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_14.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="119" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_6.png" width="441" border="0"></a> 
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> H : <span class="kwrd">virtual</span> C {</pre><pre class="alteven">    <span class="kwrd">int</span> h1;</pre><pre class="alt">    <span class="kwrd">void</span> hf();</pre><pre class="alteven">}</pre></div></div>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_16.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="121" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_7.png" width="437" border="0"></a> </p>
<div class="csharpcode-wrapper">
<div class="csharpcode"><pre class="alt"><span class="kwrd">struct</span> I : G, H {</pre><pre class="alteven">    <span class="kwrd">int</span> i1;</pre><pre class="alt">    <span class="kwrd">void</span> _if();</pre><pre class="alteven">}</pre></div></div>
<p><a href="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_18.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="211" alt="image" src="http://www.cppblog.com/images/cppblog_com/josh/WindowsLiveWriter/CUndertheHoodzhCN_96D6/image_thumb_8.png" width="527" border="0"></a> </p>
<p>在G和H，C子对象的数据成员都是紧随各自数据成员之后。</p>
<p>但是当布局I时，我们不可能保留原有的位置关系，在上面例子中，对于G实例对象中，G到C的偏移与在I实例中的G到C的偏移并不相同。由于编译后，并不知道其从哪个类继承，于是必须有一种方法来计算继承类到虚基类的地址偏移。</p>
<p>在VC++中，这是通过添加一个隐藏的指针vbptr（虚基表指针）来实现的。它指向了一个类共享的地址偏移表。通常指向的虚基表中的第二项就是到虚基类的偏移</p>
<p>如图中的IdGvbptrC和IdHvbptrC</p>
<p>&nbsp;</p>
<h2>Data Member Access</h2><img src ="http://www.cppblog.com/josh/aggbug/91960.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/josh/" target="_blank">Josh</a> 2009-08-02 16:02 <a href="http://www.cppblog.com/josh/archive/2009/08/02/91960.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新blog地址</title><link>http://www.cppblog.com/josh/archive/2009/08/01/91924.html</link><dc:creator>Josh</dc:creator><author>Josh</author><pubDate>Sat, 01 Aug 2009 15:16:00 GMT</pubDate><guid>http://www.cppblog.com/josh/archive/2009/08/01/91924.html</guid><wfw:comment>http://www.cppblog.com/josh/comments/91924.html</wfw:comment><comments>http://www.cppblog.com/josh/archive/2009/08/01/91924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/josh/comments/commentRss/91924.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/josh/services/trackbacks/91924.html</trackback:ping><description><![CDATA[<p>终于注册到了我的英文名，Josh</p>
<p>本来想安家到cnblogs.com上，但是Josh却被人注掉了，无他，只好来到C++博客。</p>
<p>以下声明：</p>
<p>此博客关注范围为 - </p>
<ul>
    <li>C/C++ 语言层面/实现层面/标准库/第三方库
    <li>C++应用：GUI/网络编程等
    <li>Linux应用编程：shell/C++/软件安装使用，GNOME/GTK+/QT4
    <li>Linux内核：驱动/内核研究
    <li>网络：计算机网络基础理论/协议栈分析/psocket, winsocket网络编程
    <li>编译器/语言虚拟机：编译器理论，虚拟机研究
    <li>汇编与机器：IA-32汇编，intel x86 CPU知识
    <li>数据结构与算法
    <li>Windows编程</li>
</ul>
<img src ="http://www.cppblog.com/josh/aggbug/91924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/josh/" target="_blank">Josh</a> 2009-08-01 23:16 <a href="http://www.cppblog.com/josh/archive/2009/08/01/91924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>