﻿<?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++博客-漫步者-文章分类-源码学习</title><link>http://www.cppblog.com/Walker/category/16501.html</link><description>先学会转文章，在仔细读文章，最后自己写点东西........</description><language>zh-cn</language><lastBuildDate>Thu, 26 May 2011 18:51:06 GMT</lastBuildDate><pubDate>Thu, 26 May 2011 18:51:06 GMT</pubDate><ttl>60</ttl><item><title>一些开源项目网址</title><link>http://www.cppblog.com/Walker/articles/145927.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sun, 08 May 2011 01:35:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/145927.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/145927.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/145927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/145927.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/145927.html</trackback:ping><description><![CDATA[<p><a href="http://code.ijinshan.com/">http://code.ijinshan.com/</a> <br><a href="http://search.csdn.net/">http://search.csdn.net/</a><br>CSDN搜索，CSDN还是有非常多的编程资源的，用它的搜索能搜出不少东西。代码类别也比较全面。</p>
<p><a href="http://snippets.org/">http://snippets.org/</a><br>简单实用的代码收集网站，强力推荐。比如你要找个DES加密，要找个数据压缩，找个INI文件操作的C代码等，均能手到擒来。</p>
<p><a href="http://www.codase.com/index.html">http://www.codase.com/index.html</a><br>它是一个代码搜索引擎，特别是搜索c/c++的开源代码，可以通过函数名、类名等搜索，很酷噢</p>
<p><a href="http://sourceforge.net/">http://sourceforge.net</a><br>有名的开源代码库，只要能想到的功能，上面都有对应的源码</p>
<p><a href="http://www.tigris.org/">http://www.tigris.org/</a><br>和上面的sourceforge一样的开源代码库，不过sourceforge用CVS，而这个用SVN版本管理。</p>
<p><a href="http://cosoft.org.cn/">http://cosoft.org.cn/</a><br>中文版的开源代码库，好像是由sourceforge直接翻译过来的。其同步性等尚未考察。</p>
<p><a href="http://codeguru.com/">http://codeguru.com</a><br><a href="http://codeproject.com/">http://codeproject.com</a><br>这两个站点比较类似，放一起吧。以WINDOWS下的Visual studio编程代码、教程为主。在以前可是使用MFC的人的必经之地。</p>
<p><a href="http://www.experts-exchange.com/Programming/">http://www.experts-exchange.com/Programming/</a><br>这是专家问答的网站，在编程方面能够解决不少问题。</p>
<p><a href="http://www.koders.com/">http://www.koders.com/</a><br>也是一个代码搜索引擎，与codase差不多，并且能查找指定许可的代码</p>
<p><a href="http://groups.google.com/">http://groups.google.com/</a><br>找代码，决不能忘记google的groups。一定要去噢。国内的兄弟可能访问时不稳定，就用代理吧。如果你用firefox，可以使用我开发的xyzproxy切换代理，很方便噢:)</p>
<p><a href="http://www.thefreecountry.com/sourcecode/index.shtml">http://www.thefreecountry.com/sourcecode/index.shtml</a><br>这也是个源码码网站。这个网站的资源还是很多的。</p>
<p><a href="http://www.vckbase.com/">http://www.vckbase.com/</a><br>VC知识库，国内比较好的VC资源站　</p>
<p><a href="http://www.programmersheaven.com/">http://www.programmersheaven.com/</a><br>开发者天堂？ 有一些教程好像不错。</p>
<p><a href="http://www.cprogramming.com/">http://www.cprogramming.com/</a><br>收集C/C++编程方面资源的网站</p>
<p><a href="http://csourcesearch.net/">http://csourcesearch.net</a><br>又一个代码搜索网站，大家试试？</p>
<p><a href="http://www.netlib.org/">http://www.netlib.org/</a><br>源代库索引，有很多数学方面的库，很好用。要找库，以此网站着手较好</p>
<p>50个c/c++源代码网站<br>C/C++是最主要的编程语言。这里列出了50名优秀网站和网页清单，这些网站提供c/c++源代码。这份清单提供了源代码的链接以及它们的小说明。我已尽力包括最佳的C/C++源代码的网站。这不是一个完整的清单，您有建议可以联系我，我将欢迎您的建议，以进一步加强这方面的清单。</p>
<p>1、<a href="http://snippets.dzone.com/tag/c/">http://snippets.dzone.com/tag/c/</a> --数以千计的有用的C语言源代码片段<br>2、<a href="http://www.hotscripts.com/category/c-cpp/scripts-programs/">http://www.hotscripts.com/category/c-cpp/scripts-programs/</a> Hotscripts --提供数以百计的C和C++脚本和程序。所有程序都分为不同的类别。<br>3、<a href="http://www.planetsourcecode.com/vb/default.asp?lngWId=3">http://www.planetsourcecode.com/vb/default.asp?lngWId=3</a> --超过万行C和C++免费的源代码<br>4、<a href="http://freshmeat.net/browse/164/">http://freshmeat.net/browse/164/</a> --超过9000个C编写的项目。<br>5、<a href="http://www.daniweb.com/code/c.html">http://www.daniweb.com/code/c.html</a> --DANIWEB提供的实用代码段。<br>6、<a href="http://www.programmersheaven.com/tags/C/">http://www.programmersheaven.com/tags/C/</a> --programmersheaven.com上的C编程资源。<br>7、<a href="http://www.ddj.com/code/ddj.html">http://www.ddj.com/code/ddj.html</a> --Dr. Dobb&#8217;s Journal的源代码。<br>8、<a href="http://www.cprogramming.com/cgi-bin/source/source.cgi">http://www.cprogramming.com/cgi-bin/source/source.cgi</a> --C和C + +编程资源。<br>9、<a href="http://www.codecogs.com/">http://www.codecogs.com/</a> --CodeCogs是一项协作的开放源码库，C/C++的数值方面的组件。<br>10、[URL=http://www.google.com /codesearch?q=programming++lang:c&amp;cs_r=lang:c ]http://www.google.com/codesearch?q=programming++lang:c&amp;cs_r=lang:c [/URL] --谷歌代码的C源代码。<br>11、<a href="http://www.codepedia.com/1/C">http://www.codepedia.com/1/C</a> --CodePedia是一个开放的关于系统编程和其他与电脑有关的议题。<br>12、<a href="http://www.cis.temple.edu/~ingargio/cis71/code/">http://www.cis.temple.edu/~ingargio/cis71/code/</a> --为学生提供的一个简单的C语言程序的列表。<br>13、<a href="http://www.codeproject.com/?cat=2">http://www.codeproject.com/?cat=2</a> --codeproject提供的C/C++资源代码项目。<br>14、<a href="http://www.thefreecountry.com/sourcecode/cpp.shtml">http://www.thefreecountry.com/sourcecode/cpp.shtml</a> --以下是一些C和C++库的DLL，VCLs，源代码，元件，模块，应用程序框架，类库，源代码片段等，你可以在您的项目中使用而不需要支付费用和版税。<br>15、[URL=http://people.sc.fsu.edu /~burkardt/cpp_src/cpp_src.html ]http://people.sc.fsu.edu/~burkardt/cpp_src/cpp_src.html [/URL] --这是一个全面的关于C++的345个源代码清单。<br>16、<a href="http://www.cplusplus.com/src/">http://www.cplusplus.com/src/</a> --C++写的通用控制台程序和Windows程序代码清单。<br>17、<a href="http://users.cs.fiu.edu/~weiss/dsaa_c++/code/">http://users.cs.fiu.edu/~weiss/dsaa_c++/code/</a> --C++语言数据结构与算法分析（第二版）的源代码。<br>18、<a href="http://c.snippets.org/">http://c.snippets.org/</a> --C源代码片段。<br>19、<a href="http://www.bbdsoft.com/downloads.html">http://www.bbdsoft.com/downloads.html</a> --C++源代码。<br>20、<a href="http://www.moshier.net/">http://www.moshier.net/</a> 天文学和数值软件源代码<br>21、<a href="http://cplus.about.com/od/cgames/C_Games_with_Source_Code.htm">http://cplus.about.com/od/cgames/C_Games_with_Source_Code.htm</a> --游戏有关的C++源代码。<br>22、[URL=http://cliodhna.cop.uop.edu /~hetrick/c-sources.html ]http://cliodhna.cop.uop.edu/~hetrick/c-sources.html [/URL] --免费的C/C++数值计算源代码。<br>23、<a href="http://www.mathtools.net/C_C__/Utilities/index.html">http://www.mathtools.net/C_C__/Utilities/index.html</a> --C/C++工具。<br>24、<a href="http://www.programmerworld.net/resources/c_library.htm">http://www.programmerworld.net/resources/c_library.htm</a> --免费C++源代码和其它有用的工具。<br>25、<a href="http://www.cmcrossroads.com/bradapp/links/cplusplus-links.html">http://www.cmcrossroads.com/bradapp/links/cplusplus-links.html</a> --布拉德阿普尔顿的C++链接-资源，项目，图书馆，教学和编码。<br>26、<a href="http://www.robertnz.net/cpp_site.html">http://www.robertnz.net/cpp_site.html</a> --这是一个收集了数C/C++网站链接列表的网页。<br>27、<a href="http://www.josuttis.com/libbook/examples.html">http://www.josuttis.com/libbook/examples.html</a> --在这里，你可以看到并下载所有的本书的C++标准库例子 。<br>28、<a href="ftp://66.77.27.238/sourcecode/cuj/">ftp://66.77.27.238/sourcecode/cuj/</a> --C/C++用户杂志<br>29、<a href="ftp://66.77.27.238/sourcecode/wd/">ftp://66.77.27.238/sourcecode/wd/</a> --Windows开发者网络<br>30、<a href="http://www.einet.net/directory/65892/Developers.htm">http://www.einet.net/directory/65892/Developers.htm</a> --C程序<br>31、<a href="http://www.daniweb.com/code/cplusplus.html">http://www.daniweb.com/code/cplusplus.html</a> --实用代码段。<br>32、<a href="http://snippets.dzone.com/tag/c">http://snippets.dzone.com/tag/c</a> --C++源代码<br>33、<a href="http://www.programmersheaven.com/tags/C">http://www.programmersheaven.com/tags/C</a> --C++编程资源，programmersheaven.com<br>34、<a href="http://www.google.com/codesearch?hl=en&amp;lr=&amp;q=programming">http://www.google.com/codesearch?hl=en&amp;lr=&amp;q=programming</a> --谷歌代码搜索-C++编程语言<br>35、<a href="http://www.codepedia.com/1/Cpp">http://www.codepedia.com/1/Cpp</a> --CodePedia是一个开放的关于系统编程和其他与电脑有关的议题的网站。<br>36、<a href="http://www.codebeach.com/index.asp?TabID=1&amp;CategoryID=3">http://www.codebeach.com/index.asp?TabID=1&amp;CategoryID=3</a> --C++源代码，Codebeach提供<br>37、<a href="http://freshmeat.net/browse/165/">http://freshmeat.net/browse/165/</a> --5000项目写的C++编程语言<br>38、<a href="http://cplus.about.com/od/codelibrary/Code_Library_for_C_C_and_C.htm">http://cplus.about.com/od/codelibrary/Code_Library_for_C_C_and_C.htm</a> --代码库C、C + +和C＃。<br>39、<a href="http://www.c.happycodings.com/">http://www.c.happycodings.com/</a> --Visual Basic、PHP、ASP技术、C、C++大全。<br>40、<a href="http://www.blueparrots.com/">http://www.blueparrots.com/</a> --Borland C游戏，图像和声音源代码范例。<br>41、<a href="http://www.java2s.com/Code/Cpp/CatalogCpp.htm">http://www.java2s.com/Code/Cpp/CatalogCpp.htm</a> --C++源代码。<br>42、<a href="http://www.yeohhs.com/modules/mydownloads/">http://www.yeohhs.com/modules/mydownloads/</a> --C与C++电子书和源代码示例。<br>43、<a href="http://www.brpreiss.com/books/opus4/programs/index.html">http://www.brpreiss.com/books/opus4/programs/index.html</a> C++的数学方程和公式源代码。<br>44、[URL=http://users.cs.fiu.edu][/URL]http://users.cs.fiu.edu/ C++。<br>45、[URL=http://www.josuttis.com/libbook/examples.html][/URL]http://www.josuttis.com/libbook/examples.html --C++标准库-教程和参考资料。<br>46、<a href="http://emr.cs.uiuc.edu/~reingold/calendars.shtml">http://emr.cs.uiuc.edu/~reingold/calendars.shtml</a> Edward M. Reingold's Calendar Book, Papers, and Code。<br>47、<a href="http://cpp.snippets.org/">http://cpp.snippets.org/</a> --c++源代码档案。<br>48、<a href="http://ubiety.uwaterloo.ca/~tveldhui/papers/techniques/">http://ubiety.uwaterloo.ca/~tveldhui/papers/techniques/</a> --用C和C++的解决科学问题。<br>49、<a href="http://c.ittoolbox.com/topics/core-c/">http://c.ittoolbox.com/topics/core-c/</a> --C/C++的IT工具框。<br>50、<a href="http://www.le.ac.uk/cc/tutorials/c/ccccdbas.html">http://www.le.ac.uk/cc/tutorials/c/ccccdbas.html</a> --本文件中包含有大量的C示例程序。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/fisher_jiang/archive/2010/05/06/5561556.aspx">http://blog.csdn.net/fisher_jiang/archive/2010/05/06/5561556.aspx</a></p>
<img src ="http://www.cppblog.com/Walker/aggbug/145927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-08 09:35 <a href="http://www.cppblog.com/Walker/articles/145927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>信号(signals)和槽(slots) 精讲</title><link>http://www.cppblog.com/Walker/articles/145870.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sat, 07 May 2011 00:47:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/145870.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/145870.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/145870.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/145870.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/145870.html</trackback:ping><description><![CDATA[<div class=tit>信号(signals)和槽(slots) 精讲</div>
<div class=date>2010-11-01 22:54</div>
<table style="WIDTH: 100%; TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div id=blog_text class=cnt><span class=Apple-style-span><span style="LINE-HEIGHT: 20px; LETTER-SPACING: 1px; COLOR: rgb(89,89,89); FONT-SIZE: 12px" class=Apple-style-span>
            <p style="LINE-HEIGHT: normal">信号(signals)和槽(slots)</p>
            <p style="LINE-HEIGHT: normal">信号和信号槽被用于对象(object)之间的通信。信号和槽机制是QT的重要特征并且也许是QT与其他框架最不相同的部分。</p>
            <p style="LINE-HEIGHT: normal">前言</p>
            <p style="LINE-HEIGHT: normal">在GUI程序设计中，通常我们希望当对一个窗口部件(widget)进行改变时能告知另一个对此改变感兴趣的窗口部件。更一般的，我们希望任何一类的对象(object)都能和其他对象进行通信。例如，如果用户单击一个关闭按钮，我们可能就希望窗口的 close() 函数被调用。</p>
            <p style="LINE-HEIGHT: normal">早期的工具包用回调(backcalls)的方式实现上面所提到的对象间的通信。回调是指一个函数的指针，因此如果你希望一个处理函数通知你一些事情，你可以传递另一个函数（回调函数）指针给这个处理函数。这个处理函数就会在适当的时候调用回调函数。回调有两个重要的缺陷：首先，它们不是类型安全的。我们无法确定处理函数是用正确的参数调用这个回调函数。其次，回调与处理函数紧密的联系在一起以致处理函数必须知道调用哪个回调。</p>
            <p style="LINE-HEIGHT: normal">消息和槽</p>
            <p style="LINE-HEIGHT: normal">在QT中，我们使用一种可替代回调的技术：信号和槽机制。当一个特别的事件产生时则发出一个信号。QT的窗口部件有很多已经预定义好的信号，我们也可以通过继承，给窗口部件的子类添加他们自己信号。槽就是一个可以被调用处理特定信号的函数。QT的窗口部件有很多预定义好的槽，但是通常的做法是给子类窗口部件添加自己的信号，这样就可以操纵自己加入的信号了。</p>
            <p style="LINE-HEIGHT: normal">&nbsp;&nbsp;</p>
            <div style="FILTER: none; LINE-HEIGHT: normal; WORD-WRAP: break-word; VISIBILITY: visible !important; FONT-SIZE: 12px"><img style="LINE-HEIGHT: normal" class=blogimg border=0 src="http://hiphotos.baidu.com/kxw102/pic/item/85a59b08e0af60cbd0581bdd.jpg"><br><font style="BACKGROUND-COLOR: rgb(255,255,0)" color=#ff0000 size=4><strong><br><br>上面这个图一定要好好理解，每个signal和Slot都是一个Object的属性，不同Object的signal可以对应不用的Object的Slot。</strong></font></div>
            <p style="LINE-HEIGHT: normal"><br style="LINE-HEIGHT: normal">信号和槽机制是类型安全的：一个信号的签名必须和该信号接受槽的签名相匹配。（事实上以一个槽的签名可以比他可接受的信号的签名少，因为它可以忽略一些签名）。因此签名是一致的，编译器就可以帮助我们检测类型匹配。信号和槽是松耦合的：一个类不知道也不关心哪个槽接受了它所发出的信号。QT的信号和槽机制确保他们自生的正确连接，槽会在正确的时间使用信号参数而被调用。信号和槽可以使用任何数量、任何类型的参数。他们完全是类型安全的。</p>
            <p style="LINE-HEIGHT: normal">所有继承至QObject或是其子类（如 QWidget）的类都可包含信号和槽。当对象改变它们自身状态的时候，信号被发送，从某种意义上讲，它们也许对外面的世界感兴趣。这就是所有对象在通讯时所做的一切。它不知道也不关心有没有其他的东西接受它发出的信号。这就是真正的消息封装，并且确保对象可用作一个软件组件。</p>
            <p style="LINE-HEIGHT: normal">槽被用于接收信号，但是他们也是正常的成员函数。正如一个对象不知道是否有东西接受了他信号，一个槽也不知道它是否被某个信号连接。这就确保QT能创建真正独立的组件。</p>
            <p style="LINE-HEIGHT: normal">你可以将任意个信号连接到你想连接的信号槽，并且在需要时可将一个信号连接到多个槽。将信号直接连接到另一个信号也是可能的（这样无论何时当第一个信号被发出后会立即发出第二个）。</p>
            <p style="LINE-HEIGHT: normal">总体来看，信号和槽构成了一个强有力的组件编程机制。</p>
            <p style="LINE-HEIGHT: normal">简单示例</p>
            <p style="LINE-HEIGHT: normal">一个极小的 C++ 类 声明如下：</p>
            <p style="LINE-HEIGHT: normal">class Counter<br style="LINE-HEIGHT: normal">{<br style="LINE-HEIGHT: normal">public:<br style="LINE-HEIGHT: normal">Counter() {m_value = 0;}</p>
            <p style="LINE-HEIGHT: normal">int value() const {return m_value;}<br style="LINE-HEIGHT: normal">void setValue(int Value);<br style="LINE-HEIGHT: normal">private:<br style="LINE-HEIGHT: normal">int m_value;<br style="LINE-HEIGHT: normal">};</p>
            <p style="LINE-HEIGHT: normal"><br style="LINE-HEIGHT: normal">一个小型的 QObject 子类声明为：</p>
            <p style="LINE-HEIGHT: normal">#include &lt;QObject&gt;</p>
            <p style="LINE-HEIGHT: normal">class Counter : public QObject<br style="LINE-HEIGHT: normal">{<br style="LINE-HEIGHT: normal">Q_OBJECT</p>
            <p style="LINE-HEIGHT: normal">public:<br style="LINE-HEIGHT: normal">Counter() {m_value = 0;}</p>
            <p style="LINE-HEIGHT: normal">int value() const {return m_value;}</p>
            <p style="LINE-HEIGHT: normal">public slots:<br style="LINE-HEIGHT: normal">void setValue(int value);<br style="LINE-HEIGHT: normal">signals:<br style="LINE-HEIGHT: normal">void valueChanged(int newValue);</p>
            <p style="LINE-HEIGHT: normal">private:<br style="LINE-HEIGHT: normal">int m_value;<br style="LINE-HEIGHT: normal">};</p>
            <p style="LINE-HEIGHT: normal">QObject版本的类与前一个C++类有着相同的域，并且提供公有函数接受这个域，但是它还增加了对信号和槽(signals-slots)组件编程的支持。这个类可以通过valueChanged()发送信号告诉外部世界他的域发生了改变,并且它有一个可以接受来自其他对象发出信号的槽。</p>
            <p style="LINE-HEIGHT: normal">所有包含信号和槽的类都必须在他们声明中的最开始提到Q_OBJECT。并且他们必须继承至（直接或间接）QObject。</p>
            <p style="LINE-HEIGHT: normal">槽可以由应用程序的编写者来实现。这里是Counter::setVaule()的一个可能的实现：</p>
            <p style="LINE-HEIGHT: normal">void Counter::setValue(int value)<br style="LINE-HEIGHT: normal">{<br style="LINE-HEIGHT: normal">if(value != m_value)<br style="LINE-HEIGHT: normal">{<br style="LINE-HEIGHT: normal">m_value = value;<br style="LINE-HEIGHT: normal">emit valueChanged(value);<br style="LINE-HEIGHT: normal">}<br style="LINE-HEIGHT: normal">}</p>
            <p style="LINE-HEIGHT: normal">emit所在的这一行从对象发出valueChanged信号，并使用新值做为参数。</p>
            <p style="LINE-HEIGHT: normal">在下面的代码片段中，我们创建两个Counter对象并且使用QObject::connect()函数将第一个对象的valueChanged()信号连接到第二个对象的setValue()槽。</p>
            <p style="LINE-HEIGHT: normal">Counter a, b;<br style="LINE-HEIGHT: normal">QObject::connect (&amp;a, SIGNAL(valueChanged(int)),<br style="LINE-HEIGHT: normal">&amp;b, SLOT(setValue(int)));<br style="LINE-HEIGHT: normal">a.setValue(12);&nbsp;&nbsp;&nbsp; // a.value() == 12, b.value() == 12<br style="LINE-HEIGHT: normal">b.setValue(48);&nbsp;&nbsp;&nbsp; // a.value() == 12, b.value() == 48</p>
            <p style="LINE-HEIGHT: normal">函数a.setValue(12)的调用导致信号valueChange(12)被发出,对象b的setValue()槽接受该信号，即函数setValue()被调用。然后b同样发出信号valueChange()，但是由于没有槽连接到b到valueChange()信号，所以该信号被忽略。</p>
            <p style="LINE-HEIGHT: normal">注意，只有当 value != m_value 时，函数 setValue() 才会设置新值并发出信号。这样就避免了在循环连接的情况下（比如b.valueChanged() 和a.setValue()连接在一起）出现无休止的循环的情况。</p>
            <p style="LINE-HEIGHT: normal">信号将被发送给任何你建立了连接的槽；如果重复连接，将会发送两个信号。总是可以使用QObject::disconnect()函数断开一个连接。</p>
            <p style="LINE-HEIGHT: normal">这个例子说明了对象之间可以不需要知道相互间的任何信息而系协同工作。为了实现这一目的，只需要将对象通过函数QObject::connect()的调用相连接（connect）,或者利用uic的automatic connections的特性。</p>
            <p style="LINE-HEIGHT: normal">编译这个示例</p>
            <p style="LINE-HEIGHT: normal">C++预编译器会改变或去除关键字signals,slots,和emit，这样就可以使用标准的C++编译器。</p>
            <p style="LINE-HEIGHT: normal">在一个定义有信号和槽的类上运行moc，这样就会生成一个可以和其它对象文件编译和连接成应用程序的C++源文件。如果使用qmake工具，将会在你的makefile文件里加入自动调用moc的规则。</p>
            <p style="LINE-HEIGHT: normal">信号</p>
            <p style="LINE-HEIGHT: normal">当对象的内部状态发生改变，信号就被发射，在某些方面对于对象代理或者所有者也许是很有趣的。只有定义了信号的对象或其子对象才能发射该信号。</p>
            <p style="LINE-HEIGHT: normal">当一个信号被发出，被连接的槽通常会立刻运行，就像执行一个普通的函数调用。当这一切发生时，信号和槽机制是完全独立于任何GUI事件循环之外的。槽会在emit域下定义的代码执行完后返回。当使用队列连接（queued connections）时会有一些不同；这种情况下，关键字emit后的代码会继续执行，而槽在此之后执行。</p>
            <p style="LINE-HEIGHT: normal">如果几个槽被连接到一个信号，当信号被发出后，槽会以任意顺序一个接一个的执行。</p>
            <p style="LINE-HEIGHT: normal">关于参数需要注意：我们的经验显示如果信号和槽不使用特殊的类型将会变得更具重用性。如果QScrollBar::valueChanged() 使用了一个特殊的类型，比如hypothetical QRangeControl::Range，它就只能被连接到被设计成可以处理QRangeControl的槽。再没有象教程5这样简单的例子。</p>
            <p style="LINE-HEIGHT: normal">槽<br style="LINE-HEIGHT: normal"><br style="LINE-HEIGHT: normal">当一个信号被发出时连接他的槽被调用。槽是一个普通的C++函数并按普通方式调用；他的特点仅仅是可以被信号连接。</p>
            <p style="LINE-HEIGHT: normal">由于槽只是普通的成员函数，当调用时直接遵循C++规则。然而，对于槽，他们可以被任何组件通过一个信号-槽连接（signal-slot connection）调用，而不管其访问权限。也就是说，一个从任意的类的实例发出的信号可导致一个不与此类相关的另一个类的实例的私有槽被调用。</p>
            <p style="LINE-HEIGHT: normal">你还可以定义一个虚拟槽，在实践中被发现也是非常有用的。</p>
            <p style="LINE-HEIGHT: normal">由于增加来灵活性，与回调相比，信号和槽稍微慢一些，尽管这对真实的应用程序来说是可以忽略掉的。通常，发出一连接了某个槽的信号，比直接调用那些非虚拟调用的接受器要慢十倍。这是定位连接对象所需的开销，可以安全地重复所有地连接（例如在发射期间检查并发接收器是否被破坏）并且可以按一般的方式安排任何参数。当十个非虚函数调用听起来很多时，实际上他比任何new和delete操作的开销都少，例如，当你执行一个字符串、矢量或列表操作时，就需要用到new和delete，而信号和槽的开销只是全部函数调用花费的一小部分。</p>
            <p style="LINE-HEIGHT: normal">无论何时你用槽进行一个系统调用和间接的调用超过10个以上的函数时间都是一样的。在i586-500机器上，每秒钟你可以发送超过2,000,000个信号给一个接受者，或者每秒发送1,200,000个信号给两个接受者。相对于信号和槽机制的简洁性和灵活性，他的时间开销是完全值得的，你的用户甚至察觉不出来。</p>
            <p style="LINE-HEIGHT: normal">注意：若其他的库将变量定义为signals和slots,可能导致编译器在连接基于QT的应用程序时出错或警告。为了解决这个问题，请使用#undef预处理符号。</p>
            <p style="LINE-HEIGHT: normal">元对象信息</p>
            <p style="LINE-HEIGHT: normal">元对象编译器（moc）解析一个C++文件中的类声明并且生成初始化元对象的C++代码。元对象包括信号和槽的名字，和指向这些函数的指针。</p>
            <p style="LINE-HEIGHT: normal">if (widget-&gt;inherits("QAbstractButton")) {<br style="LINE-HEIGHT: normal">QAbstractButton *button = static_cast&lt;QAbstractButton *&gt;(widget);<br style="LINE-HEIGHT: normal">button-&gt;toggle();<br style="LINE-HEIGHT: normal">}</p>
            <p style="LINE-HEIGHT: normal">元对象信息的使用也可以是qobject_cast&lt;T&gt;(), 他和QObject::inherits() 相似，但更不容易出错。</p>
            <p style="LINE-HEIGHT: normal">if (QAbstractButton *button = qobject_cast&lt;QAbstractButton *&gt;(widget))<br style="LINE-HEIGHT: normal">button-&gt;toggle();</p>
            <p style="LINE-HEIGHT: normal">查看Meta-Object系统可获取更多信息。</p>
            <p style="LINE-HEIGHT: normal">一个实例</p>
            <p style="LINE-HEIGHT: normal">这是一个注释过的简单的例子（代码片断选自qlcdnumber.h）。</p>
            <p style="LINE-HEIGHT: normal">#ifndef LCDNUMBER_H<br style="LINE-HEIGHT: normal">#define LCDNUMBER_H</p>
            <p style="LINE-HEIGHT: normal">#include &lt;QFrame&gt;</p>
            <p style="LINE-HEIGHT: normal">class LcdNumber : public QFrame<br style="LINE-HEIGHT: normal">{<br style="LINE-HEIGHT: normal">Q_OBJECT</p>
            <p style="LINE-HEIGHT: normal">LcdNumber通过QFrame和QWiget继承至QObject，它包含了大部分signal-slot知识。这是有点类似于内置的QLCDNumber部件。</p>
            <p style="LINE-HEIGHT: normal">Q_OBJECT宏由预处理器展开，用来声明由moc实现的机个成员函数；如果你的编译器出现错误如下"undefined reference to vtable for LcdNumber", 你可能忘了运行moc或者没有用连接命令包含moc输出。</p>
            <p style="LINE-HEIGHT: normal">public:<br style="LINE-HEIGHT: normal">LcdNumber(QWidget *parent = 0);</p>
            <p style="LINE-HEIGHT: normal">LcdNumber并不明显的与moc相关，但是如果你继承了QWidege，那么可以几乎肯定在你的构造函数中有父对象的变量，并且希望把它传给基类的构造函数。</p>
            <p style="LINE-HEIGHT: normal">析构函数和一些成员函数在这里省略；moc会忽视成员函数。</p>
            <p style="LINE-HEIGHT: normal">signals:<br style="LINE-HEIGHT: normal">void overflow();</p>
            <p style="LINE-HEIGHT: normal">当LcdNumbe被要求显示一个不可能的值时，便发出信号。</p>
            <p style="LINE-HEIGHT: normal">如果你没有留意溢出，或者你知道溢出不会出现，你可以忽略overflow()信号，比如不将其连接到任何槽。</p>
            <p style="LINE-HEIGHT: normal">如果另一方面，当有数字溢出时你想调用两个不同的错误处理函数，可以将这个信号简单的连接到两个不同的槽。QT将调用两个函数（无序的）。</p>
            <p style="LINE-HEIGHT: normal">public slots:<br style="LINE-HEIGHT: normal">void display(int num);<br style="LINE-HEIGHT: normal">void display(double num);<br style="LINE-HEIGHT: normal">void display(const QString &amp;str);<br style="LINE-HEIGHT: normal">void setHexMode();<br style="LINE-HEIGHT: normal">void setDecMode();<br style="LINE-HEIGHT: normal">void setOctMode();<br style="LINE-HEIGHT: normal">void setBinMode();<br style="LINE-HEIGHT: normal">void setSmallDecimalPoint(bool point);<br style="LINE-HEIGHT: normal">};</p>
            <p style="LINE-HEIGHT: normal">#endif</p>
            <p style="LINE-HEIGHT: normal">一个槽是一个接受函数，用于获得其他窗口部件的信息变化。LcdNumber使用它，就像上面的代码一样，来设置显示的数字。因为display()是这个类和程序的其它的部分的一个接口，所以这个槽是公有的。</p>
            <p style="LINE-HEIGHT: normal">几个例程把QScrollBar的valueChanged()信号连接到display()槽，所以LCD数字可以继续显示滚动条的值。</p>
            <p style="LINE-HEIGHT: normal">请注意display()被重载了，当将一个信号连接到槽时QT将选择一个最适合的一个。而对于回调，你会发现五个不同的名字并且自己来跟踪类型。</p>
            <p style="LINE-HEIGHT: normal">一个不相干的成员函数在例子中被忽略。</p>
            <p style="LINE-HEIGHT: normal">高级信号和槽的使用</p>
            <p style="LINE-HEIGHT: normal">在当你需要信号发送者的信息时，QT提供了一个函数QObject::sender()，他返回指向一个信号发送对象的指针。</p>
            <p style="LINE-HEIGHT: normal">当有几个信号被连接到同一槽上，并且槽需要处理每个不同的信号，可使用 QSignalMapper类。</p>
            <p style="LINE-HEIGHT: normal">假设你用三个按钮来决定打开哪个文件：Tax File", "Accounts File", or "Report File"。</p>
            <p style="LINE-HEIGHT: normal">为了能打开真确的文件，你需要分别将它们的信号 QPushButton::clicked()连接到 readFile()。然后用QSignalMapper 的 setMapping()来映射所有 clicked()信号到一个 QSignalMapper对象。</p>
            <p style="LINE-HEIGHT: normal">signalMapper = new QSignalMapper(this);<br style="LINE-HEIGHT: normal">signalMapper-&gt;setMapping(taxFileButton, QString("taxfile.txt"));<br style="LINE-HEIGHT: normal">signalMapper-&gt;setMapping(accountFileButton, QString("accountsfile.txt"));<br style="LINE-HEIGHT: normal">signalMapper-&gt;setMapping(reportFileButton, QString("reportfile.txt"));</p>
            <p style="LINE-HEIGHT: normal">connect(taxFileButton, SIGNAL(clicked()),<br style="LINE-HEIGHT: normal">signalMapper, SLOT (map()));<br style="LINE-HEIGHT: normal">connect(accountFileButton, SIGNAL(clicked()),<br style="LINE-HEIGHT: normal">signalMapper, SLOT (map()));<br style="LINE-HEIGHT: normal">connect(reportFileButton, SIGNAL(clicked()),<br style="LINE-HEIGHT: normal">signalMapper, SLOT (map()));</p>
            <p style="LINE-HEIGHT: normal">然后，连接信号 mapped()到 readFile() ，根据被按下的按钮，就可以打开不同的文件。</p>
            <p style="LINE-HEIGHT: normal">connect(signalMapper, SIGNAL(mapped(const QString &amp;)),<br style="LINE-HEIGHT: normal">this, SLOT(readFile(const QString &amp;)));</p>
            <p style="LINE-HEIGHT: normal">在QT中使用第三方signals slots</p>
            <p style="LINE-HEIGHT: normal">在QT中使用第三方signals slots是可能的。你甚至可以在同一类中使用两种机制。仅仅需要在你的qmake工程文件(.pro)中加入下面语句：</p>
            <p style="LINE-HEIGHT: normal">CONFIG += no_keywords</p>
            <p style="LINE-HEIGHT: normal">它告诉QT不要定义moc关键字signals,slots和emit，因为这些名字可能将被用于第三方库，例如Boost。你只需简单的用QT宏将他们替换为 Q_SIGNALS, Q_SLOTS，和 Q_EMIT，就可以继续使用信号和槽了。</p>
            </span></span></div>
            </td>
        </tr>
    </tbody>
</table>
<div class=tit>Qt源码分析之信号和槽机制</div>
<div class=date>2009/09/17 13:21</div>
<table style="WIDTH: 100%; TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div id=blog_text class=cnt>
            <p>Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的<br>说法,简单点说就是如何在一个类的一个函数中触发另一个类的另一个函数调用,而且还要把相关的参数传递过去.好像这和回调函数也有点关系,但是消息机制可比回调函数有用<br>多了,也复杂多了</p>
            <p>MFC中的消息机制没有采用C++中的虚函数机制,原因是消息太多,虚函数开销太大.在Qt中也没有采用C++中的虚函数机制,原因与此相同.其实这里还有更深层次上的原因,大体说来,<br>多态的底层实现机制只有两种,一种是按照名称查表,一种是按照位置查表,两种方式各有利弊,而C++的虚函数机制无条件的采用了后者,导致的问题就是在子类很少重载基类实现<br>的时候开销太大,再加上象界面编程这样子类众多的情况,基本上C++的虚函数机制就废掉了,于是各家库的编写者就只好自谋生路了,说到底,这确实是C++语言本身的缺陷</p>
            <p>示例代码：<br>#include &lt;QApplication&gt;<br>#include &lt;QPushButton&gt;<br>#include &lt;QPointer&gt;</p>
            <p>int main(int argc, char *argv[])<br>{<br>QApplication app(argc, argv);</p>
            <p>&nbsp;&nbsp;&nbsp; QPushButton quit("Quit");<br>quit.resize(100, 30);<br>quit.show();<br>QObject::connect(&amp;quit, SIGNAL(clicked()), &amp;app, SLOT(quit()));<br><br>return app.exec();<br>}</p>
            <p>这里主要是看QPushButton的clicked()信号和app的quit()槽如何连接?又是如何响应?<br>前面已经说过了,Qt的信号槽机制其实就是按照名称查表,因此这里的首要问题是如何构造这个表?<br>和C++虚函数表机制类似的,在Qt中,这个表就是元数据表,Qt中的元数据表最大的作用就是支持信号槽机制,当然,也可以在此基础上扩展出更多的功能,因为<br>元数据是我们可以直接访问到的,不再是象虚函数表那样被编译器遮遮掩掩的藏了起来,不过Qt似乎还没有完全发挥元数据的能力,动态属性,反射之类的机制好像还没有</p>
            <p>任何从QObject派生的类都包含了自己的元数据模型，一般是通过宏Q_OBJECT定义的<br>#define Q_OBJECT \<br>public: \<br>static const QMetaObject staticMetaObject; \<br>virtual const QMetaObject *metaObject() const; \<br>virtual void *qt_metacast(const char *); \<br>QT_TR_FUNCTIONS \<br>virtual int qt_metacall(QMetaObject::Call, int, void **); \<br>private:</p>
            <p>首先声明了一个QMetaObject类型的静态成员变量,这就是元数据的数据结构</p>
            <p>struct Q_CORE_EXPORT QMetaObject<br>{<br>...<br>struct { // private data<br>const QMetaObject *superdata;<br>const char *stringdata;<br>const uint *data;<br>const QMetaObject **extradata;<br>} d;<br>}<br>QMetaObject中有一个嵌套类封装了所有的数据<br>const QMetaObject *superdata;//这是元数据代表的类的基类的元数据<br>const char *stringdata;//这是元数据的签名标记<br>const uint *data;//这是元数据的索引数组的指针<br>const QMetaObject **extradata;//这是扩展元数据表的指针,一般是不用的&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
            <p>这里的三个虚函数metaObject，qt_metacast，qt_metacall是在moc文件中定义的<br>metaObject的作用是得到元数据表指针<br>qt_metacast的作用是根据签名得到相关结构的指针,注意它返回的可是void*指针<br>qt_metacall的作用是查表然后调用调用相关的函数</p>
            <p>宏QT_TR_FUNCTIONS是和翻译相关的<br>#&nbsp; define QT_TR_FUNCTIONS \<br>static inline QString tr(const char *s, const char *c = 0) \<br>{ return staticMetaObject.tr(s, c); }</p>
            <p>好了,看看实际的例子吧:</p>
            <p>QPushButton的元数据表如下:<br>static const uint qt_meta_data_QPushButton[] = {</p>
            <p>&nbsp;// content:<br>1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // revision<br>0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // classname<br>0,&nbsp;&nbsp;&nbsp; 0, // classinfo<br>2,&nbsp;&nbsp; 10, // methods<br>3,&nbsp;&nbsp; 20, // properties<br>0,&nbsp;&nbsp;&nbsp; 0, // enums/sets</p>
            <p>&nbsp;// slots: signature, parameters, type, tag, flags<br>13,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12, 0x0a,<br>24,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12, 0x08,</p>
            <p>&nbsp;// properties: name, type, flags<br>44,&nbsp;&nbsp; 39, 0x01095103,<br>56,&nbsp;&nbsp; 39, 0x01095103,<br>64,&nbsp;&nbsp; 39, 0x01095103,</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // eod<br>};</p>
            <p>static const char qt_meta_stringdata_QPushButton[] = {<br>"QPushButton\0\0showMenu()\0popupPressed()\0bool\0autoDefault\0default\0"<br>"flat\0"<br>};</p>
            <p>const QMetaObject QPushButton::staticMetaObject = {<br>{ &amp;QAbstractButton::staticMetaObject, qt_meta_stringdata_QPushButton,<br>qt_meta_data_QPushButton, 0 }<br>};</p>
            <p>在这里我们看到了静态成员staticMetaObject被填充了<br>const QMetaObject *superdata;//这是元数据代表的类的基类的元数据,被填充为基类的元数据指针&amp;QAbstractButton::staticMetaObject<br>const char *stringdata;//这是元数据的签名标记,被填充为qt_meta_stringdata_QPushButton<br>const uint *data;//这是元数据的索引数组的指针,被填充为qt_meta_data_QPushButton<br>const QMetaObject **extradata;//这是扩展元数据表的指针,一般是不用的,被填充为0</p>
            <p>首先应该看qt_meta_data_QPushButton,因为这里是元数据的主要数据,它被填充为一个整数数组,正因为这里只有整数,不能有任何字符串存在,因此才有<br>qt_meta_stringdata_QPushButton发挥作用的机会,可以说真正的元数据应该是qt_meta_data_QPushButton加上qt_meta_stringdata_QPushButton,那么为什么<br>不把这两个东西合在一起呢?估计是两者都不是定长的结构,合在一起反而麻烦吧</p>
            <p>qt_meta_data_QPushButton实际上是以以下结构开头的</p>
            <p>struct QMetaObjectPrivate<br>{<br>int revision;<br>int className;<br>int classInfoCount, classInfoData;<br>int methodCount, methodData;<br>int propertyCount, propertyData;<br>int enumeratorCount, enumeratorData;<br>};</p>
            <p>一般使用中是直接使用以下函数做个转换<br>static inline const QMetaObjectPrivate *priv(const uint* data)<br>{ return reinterpret_cast&lt;const QMetaObjectPrivate*&gt;(data); }</p>
            <p>这种转换怎么看都有些黑客的味道,这确实是十足的C风格</p>
            <p>再结合实际的数据看一看<br>static const uint qt_meta_data_QPushButton[] = {</p>
            <p>&nbsp;// content:<br>1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // revision&nbsp;&nbsp;版本号是1<br>0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // classname&nbsp;类名存储在qt_meta_stringdata_QPushButton中,索引是0,因此就是QPushButton了<br>0,&nbsp;&nbsp;&nbsp; 0, // classinfo&nbsp; 类信息数量为0,数据也是0<br>2,&nbsp;&nbsp; 10, // methods&nbsp;&nbsp;QPushButton有2个自定义方法,方法数据存储在qt_meta_data_QPushButton中,索引是10,就是下面的slots:开始的地方<br>3,&nbsp;&nbsp; 20, // properties QPushButton有3个自定义属性,属性数据存储在qt_meta_data_QPushButton中,索引是20,就是下面的properties:开始的地方<br>0,&nbsp;&nbsp;&nbsp; 0, // enums/sets QPushButton没有自定义的枚举</p>
            <p>&nbsp;// slots: signature, parameters, type, tag, flags<br>13,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12, 0x0a,<br>第一个自定义方法的签名存储在qt_meta_data_QPushButton中,索引是13,就是showMenu()了<br>24,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12,&nbsp;&nbsp; 12, 0x08,<br>第二个自定义方法的签名存储在qt_meta_data_QPushButton中,索引是24,popupPressed()了</p>
            <p>&nbsp;// properties: name, type, flags<br>44,&nbsp;&nbsp; 39, 0x01095103,&nbsp;&nbsp; <br>第一个自定义属性的签名存储在qt_meta_data_QPushButton中,索引是44,就是autoDefault了<br>第一个自定义属性的类型存储在qt_meta_data_QPushButton中,索引是39,就是bool<br>56,&nbsp;&nbsp; 39, 0x01095103,&nbsp;&nbsp; <br>第二个自定义属性的签名存储在qt_meta_data_QPushButton中,索引是56,就是default了<br>第二个自定义属性的类型存储在qt_meta_data_QPushButton中,索引是39,就是bool<br>64,&nbsp;&nbsp; 39, 0x01095103,&nbsp;&nbsp; <br>第三个自定义属性的签名存储在qt_meta_data_QPushButton中,索引是64,就是flat了<br>第三个自定义属性的类型存储在qt_meta_data_QPushButton中,索引是39,就是bool</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // eod&nbsp;元数据的结束标记<br>};</p>
            <p>static const char qt_meta_stringdata_QPushButton[] = {<br>"QPushButton\0\0showMenu()\0popupPressed()\0bool\0autoDefault\0default\0"<br>"flat\0"<br>};</p>
            <p>QPushButton\\showMenu()\popupPressed()\bool\autoDefault\default\flat\<br>这里把\0直接替换为\是为了数数的方便</p>
            <p>当然我们还可以看看QPushButton的基类QAbstractButton的元数据<br>static const uint qt_meta_data_QAbstractButton[] = {</p>
            <p>&nbsp;// content:<br>1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // revision<br>0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // classname<br>0,&nbsp;&nbsp;&nbsp; 0, // classinfo<br>12,&nbsp;&nbsp; 10, // methods<br>9,&nbsp;&nbsp; 70, // properties<br>0,&nbsp;&nbsp;&nbsp; 0, // enums/sets</p>
            <p>&nbsp;// signals: signature, parameters, type, tag, flags<br>17,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x05,<br>27,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x05,<br>46,&nbsp;&nbsp; 38,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x05,<br>60,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x25,<br>70,&nbsp;&nbsp; 38,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x05,</p>
            <p>&nbsp;// slots: signature, parameters, type, tag, flags<br>89,&nbsp;&nbsp; 84,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x0a,<br>113,&nbsp; 108,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x0a,<br>131,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x2a,<br>146,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x0a,<br>154,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x0a,<br>163,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x0a,<br>182,&nbsp; 180,&nbsp;&nbsp; 16,&nbsp;&nbsp; 16, 0x1a,</p>
            <p>&nbsp;// properties: name, type, flags<br>202,&nbsp; 194, 0x0a095103,<br>213,&nbsp; 207, 0x45095103,<br>224,&nbsp; 218, 0x15095103,<br>246,&nbsp; 233, 0x4c095103,<br>260,&nbsp; 255, 0x01095103,<br>38,&nbsp; 255, 0x01195103,<br>270,&nbsp; 255, 0x01095103,<br>281,&nbsp; 255, 0x01095103,<br>295,&nbsp; 255, 0x01094103,</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // eod<br>};</p>
            <p>static const char qt_meta_stringdata_QAbstractButton[] = {<br>"QAbstractButton\0\0pressed()\0released()\0checked\0clicked(bool)\0"<br>"clicked()\0toggled(bool)\0size\0setIconSize(QSize)\0msec\0"<br>"animateClick(int)\0animateClick()\0click()\0toggle()\0setChecked(bool)\0"<br>"b\0setOn(bool)\0QString\0text\0QIcon\0icon\0QSize\0iconSize\0"<br>"QKeySequence\0shortcut\0bool\0checkable\0autoRepeat\0autoExclusive\0"<br>"down\0"<br>};</p>
            <p>QAbstractButton00pressed()0released()0checked0clicked(bool)0clicked()0toggled(bool)0size0setIconSize(QSize)0msec0animateClick(int)0animateClick()0click()0toggle()0setChecked(bool)0b0setOn(bool)0QString0text0QIcon0icon0QSize0iconSize0QKeySequence0shortcut0bool0checkable0autoRepeat0autoExclusive0down0</p>
            <p>基本上都是大同小异的</p>
            <p>&nbsp; QObject::connect(&amp;quit, SIGNAL(clicked()), &amp;app, SLOT(quit()));<br>下面开始看信号和槽连接的源码了</p>
            <p>// connect的源码<br>connect函数是连接信号和槽的桥梁，非常关键<br>bool QObject::connect(const QObject *sender, const char *signal,<br>const QObject *receiver, const char *method,<br>Qt::ConnectionType type)<br>{<br>#ifndef QT_NO_DEBUG<br>bool warnCompat = true;<br>#endif<br>if (type == Qt::AutoCompatConnection) {<br>type = Qt::AutoConnection;<br>#ifndef QT_NO_DEBUG<br>warnCompat = false;<br>#endif<br>}</p>
            <p>&nbsp;// 不允许空输入<br>if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {<br>#ifndef QT_NO_DEBUG<br>qWarning("Object::connect: Cannot connect %s::%s to %s::%s",<br>sender ? sender-&gt;metaObject()-&gt;className() : "(null)",<br>signal ? signal+1 : "(null)",<br>receiver ? receiver-&gt;metaObject()-&gt;className() : "(null)",<br>method ? method+1 : "(null)");<br>#endif<br>return false;<br>}<br>QByteArray tmp_signal_name;</p>
            <p>#ifndef QT_NO_DEBUG<br>// 检查是否是信号标记<br>if (!check_signal_macro(sender, signal, "connect", "bind"))<br>return false;<br>#endif<br>// 得到元数据类<br>const QMetaObject *smeta = sender-&gt;metaObject();<br>++signal; //skip code跳过信号标记,直接得到信号标识<br>// 得到信号的索引<br>int signal_index = smeta-&gt;indexOfSignal(signal);<br>if (signal_index &lt; 0) {<br>// check for normalized signatures<br>tmp_signal_name = QMetaObject::normalizedSignature(signal).prepend(*(signal - 1));<br>signal = tmp_signal_name.constData() + 1;<br>signal_index = smeta-&gt;indexOfSignal(signal);<br>if (signal_index &lt; 0) {<br>#ifndef QT_NO_DEBUG<br>err_method_notfound(QSIGNAL_CODE, sender, signal, "connect");<br>err_info_about_objects("connect", sender, receiver);<br>#endif<br>return false;<br>}<br>}</p>
            <p>&nbsp;&nbsp;&nbsp; QByteArray tmp_method_name;<br>int membcode = method[0] - '0';</p>
            <p>#ifndef QT_NO_DEBUG<br>// 检查是否是槽,用QSLOT_CODE 1标记<br>if (!check_method_code(membcode, receiver, method, "connect"))<br>return false;<br>#endif<br>++method; // skip code</p>
            <p>&nbsp;&nbsp;// 得到元数据类<br>const QMetaObject *rmeta = receiver-&gt;metaObject();<br>int method_index = -1;<br>// 这里是一个case,信号即可以和信号连接也可以和槽连接<br>switch (membcode) {<br>case QSLOT_CODE:<br>// 得到槽的索引<br>method_index = rmeta-&gt;indexOfSlot(method);<br>break;<br>case QSIGNAL_CODE:<br>// 得到信号的索引<br>method_index = rmeta-&gt;indexOfSignal(method);<br>break;<br>}<br>if (method_index &lt; 0) {<br>// check for normalized methods<br>tmp_method_name = QMetaObject::normalizedSignature(method);<br>method = tmp_method_name.constData();<br>switch (membcode) {<br>case QSLOT_CODE:<br>method_index = rmeta-&gt;indexOfSlot(method);<br>break;<br>case QSIGNAL_CODE:<br>method_index = rmeta-&gt;indexOfSignal(method);<br>break;<br>}<br>}</p>
            <p>&nbsp;&nbsp;&nbsp; if (method_index &lt; 0) {<br>#ifndef QT_NO_DEBUG<br>err_method_notfound(membcode, receiver, method, "connect");<br>err_info_about_objects("connect", sender, receiver);<br>#endif<br>return false;<br>}<br>#ifndef QT_NO_DEBUG<br>// 检查参数,信号和槽的参数必须一致,槽的参数也可以小于信号的参数<br>if (!QMetaObject::checkConnectArgs(signal, method)) {<br>qWarning("Object::connect: Incompatible sender/receiver arguments"<br>"\n\t%s::%s --&gt; %s::%s",<br>sender-&gt;metaObject()-&gt;className(), signal,<br>receiver-&gt;metaObject()-&gt;className(), method);<br>return false;<br>}<br>#endif</p>
            <p>&nbsp;&nbsp;&nbsp; int *types = 0;<br>if (type == Qt::QueuedConnection<br>&amp;&amp; !(types = ::queuedConnectionTypes(smeta-&gt;method(signal_index).parameterTypes())))<br>return false;</p>
            <p>#ifndef QT_NO_DEBUG<br>{<br>// 得到方法的元数据<br>QMetaMethod smethod = smeta-&gt;method(signal_index);<br>QMetaMethod rmethod = rmeta-&gt;method(method_index);<br>if (warnCompat) {<br>if(smethod.attributes() &amp; QMetaMethod::Compatibility) {<br>if (!(rmethod.attributes() &amp; QMetaMethod::Compatibility))<br>qWarning("Object::connect: Connecting from COMPAT signal (%s::%s).", smeta-&gt;className(), signal);<br>} else if(rmethod.attributes() &amp; QMetaMethod::Compatibility &amp;&amp; membcode != QSIGNAL_CODE) {<br>qWarning("Object::connect: Connecting from %s::%s to COMPAT slot (%s::%s).",<br>smeta-&gt;className(), signal, rmeta-&gt;className(), method);<br>}<br>}<br>}<br>#endif<br>// 调用元数据类的连接<br>QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);<br>// 发送连接的通知,现在的实现是空的<br>const_cast&lt;QObject*&gt;(sender)-&gt;connectNotify(signal - 1);<br>return true;<br>}</p>
            <p>检查信号标记其实比较简单,就是用signal的第一个字符和用QSIGNAL_CODE=2的标记比较而已<br>static bool check_signal_macro(const QObject *sender, const char *signal,<br>const char *func, const char *op)<br>{<br>int sigcode = (int)(*signal) - '0';<br>if (sigcode != QSIGNAL_CODE) {<br>if (sigcode == QSLOT_CODE)<br>qWarning("Object::%s: Attempt to %s non-signal %s::%s",<br>func, op, sender-&gt;metaObject()-&gt;className(), signal+1);<br>else<br>qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s",<br>func, op, sender-&gt;metaObject()-&gt;className(), signal);<br>return false;<br>}<br>return true;<br>}</p>
            <p>得到信号的索引实际上要依次找每个基类的元数据,得到的偏移也是所有元数据表加在一起后的一个索引<br>int QMetaObject::indexOfSignal(const char *signal) const<br>{<br>int i = -1;<br>const QMetaObject *m = this;<br>while (m &amp;&amp; i &lt; 0) {<br>// 根据方法的数目倒序的查找<br>for (i = priv(m-&gt;d.data)-&gt;methodCount-1; i &gt;= 0; --i)<br>// 得到该方法的类型<br>if ((m-&gt;d.data[priv(m-&gt;d.data)-&gt;methodData + 5*i + 4] &amp; MethodTypeMask) == MethodSignal<br>&amp;&amp; strcmp(signal, m-&gt;d.stringdata<br>// 得到方法名称的偏移<br>+ m-&gt;d.data[priv(m-&gt;d.data)-&gt;methodData + 5*i]) == 0) {<br>//如果找到了正确的方法,再增加所有基类的方法偏移量<br>i += m-&gt;methodOffset();<br>break;<br>}<br>// 在父类中继续找<br>m = m-&gt;d.superdata;<br>}<br>#ifndef QT_NO_DEBUG<br>// 判断是否于基类中的冲突<br>if (i &gt;= 0 &amp;&amp; m &amp;&amp; m-&gt;d.superdata) {<br>int conflict = m-&gt;d.superdata-&gt;indexOfMethod(signal);<br>if (conflict &gt;= 0)<br>qWarning("QMetaObject::indexOfSignal:%s: Conflict with %s::%s",<br>m-&gt;d.stringdata, m-&gt;d.superdata-&gt;d.stringdata, signal);<br>}<br>#endif<br>return i;<br>}</p>
            <p>// 这里是所有基类的方法偏移量算法,就是累加基类所有的方法数目<br>int QMetaObject::methodOffset() const<br>{<br>int offset = 0;<br>const QMetaObject *m = d.superdata;<br>while (m) {<br>offset += priv(m-&gt;d.data)-&gt;methodCount;<br>m = m-&gt;d.superdata;<br>}<br>return offset;<br>}</p>
            <p>// 得到方法的元数据<br>QMetaMethod QMetaObject::method(int index) const<br>{<br>int i = index;<br>// 要减去基类的偏移<br>i -= methodOffset();<br>// 如果本类找不到,就到基类中去找<br>if (i &lt; 0 &amp;&amp; d.superdata)<br>return d.superdata-&gt;method(index);</p>
            <p>&nbsp;// 如果找到了,就填充QMetaMethod结构<br>QMetaMethod result;<br>if (i &gt;= 0 &amp;&amp; i &lt; priv(d.data)-&gt;methodCount) {<br>// 这里是类的元数据<br>result.mobj = this;<br>// 这里是方法相关数据在data数组中的偏移量<br>result.handle = priv(d.data)-&gt;methodData + 5*i;<br>}<br>return result;<br>}</p>
            <p>bool QMetaObject::connect(const QObject *sender, int signal_index,<br>const QObject *receiver, int method_index, int type, int *types)<br>{<br>// 得到全局的连接列表<br>QConnectionList *list = ::connectionList();<br>if (!list)<br>return false;<br>QWriteLocker locker(&amp;list-&gt;lock);<br>// 增加一个连接<br>list-&gt;addConnection(const_cast&lt;QObject *&gt;(sender), signal_index,<br>const_cast&lt;QObject *&gt;(receiver), method_index, type, types);<br>return true;<br>}</p>
            <p>void QConnectionList::addConnection(QObject *sender, int signal,<br>QObject *receiver, int method,<br>int type, int *types)<br>{<br>// 构造一个连接<br>QConnection c = { sender, signal, receiver, method, 0, 0, types };<br>c.type = type; // don't warn on VC++6<br>int at = -1;<br>// 如果有中间被删除的连接,可以重用这个空间<br>for (int i = 0; i &lt; unusedConnections.size(); ++i) {<br>if (!connections.at(unusedConnections.at(i)).inUse) {<br>// reuse an unused connection<br>at = unusedConnections.takeAt(i);<br>connections[at] = c;<br>break;<br>}<br>}<br>if (at == -1) {<br>// append new connection<br>at = connections.size();<br>// 加入一个连接<br>connections &lt;&lt; c;<br>}<br>// 构造sender,receiver连接的哈希表,加速搜索速度<br>sendersHash.insert(sender, at);<br>receiversHash.insert(receiver, at);<br>}</p>
            <p>通过connect函数,我们建立了信号和槽的连接,并且把信号类+信号索引+槽类,槽索引作为记录写到了全局的connect列表中</p>
            <p>一旦我们发送了信号,就应该调用相关槽中的方法了,这个过程其实就是查找全局的connect列表的过程,当然还要注意其中要对相关的参数打包和解包</p>
            <p>// emit是发送信号的代码<br>void Foo::setValue(int v)<br>{<br>if (v != val)<br>{<br>val = v;<br>emit valueChanged(v);<br>}<br>}</p>
            <p>// 发送信号的真正实现在moc里面<br>// SIGNAL 0<br>void Foo::valueChanged(int _t1)<br>{<br>// 首先把参数打包<br>void *_a[] = { 0, const_cast&lt;void*&gt;(reinterpret_cast&lt;const void*&gt;(&amp;_t1)) };<br>// 调用元数据类的激活<br>QMetaObject::activate(this, &amp;staticMetaObject, 0, _a);<br>}</p>
            <p>void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,<br>void **argv)<br>{<br>// 增加一个基类偏移量<br>int offset = m-&gt;methodOffset();<br>activate(sender, offset + local_signal_index, offset + local_signal_index, argv);<br>}</p>
            <p>void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)<br>{<br>// 这里得到的是QObject的数据,首先判断是否为阻塞设置<br>if (sender-&gt;d_func()-&gt;blockSig)<br>return;</p>
            <p>&nbsp;// 得到全局链表<br>QConnectionList * const list = ::connectionList();<br>if (!list)<br>return;</p>
            <p>&nbsp;&nbsp;&nbsp; QReadLocker locker(&amp;list-&gt;lock);</p>
            <p>&nbsp;&nbsp;&nbsp; void *empty_argv[] = { 0 };<br>if (qt_signal_spy_callback_set.signal_begin_callback != 0) {<br>locker.unlock();<br>qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,<br>argv ? argv : empty_argv);<br>locker.relock();<br>}</p>
            <p>&nbsp;// 在sender的哈希表中得到sender的连接<br>QConnectionList::Hash::const_iterator it = list-&gt;sendersHash.find(sender);<br>const QConnectionList::Hash::const_iterator end = list-&gt;sendersHash.constEnd();</p>
            <p>&nbsp;&nbsp;&nbsp; if (it == end) {<br>if (qt_signal_spy_callback_set.signal_end_callback != 0) {<br>locker.unlock();<br>qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);<br>locker.relock();<br>}<br>return;<br>}</p>
            <p>&nbsp;&nbsp;&nbsp; QThread * const currentThread = QThread::currentThread();<br>const int currentQThreadId = currentThread ? QThreadData::get(currentThread)-&gt;id : -1;</p>
            <p>&nbsp;// 记录sender连接的索引<br>QVarLengthArray&lt;int&gt; connections;<br>for (; it != end &amp;&amp; it.key() == sender; ++it) {<br>connections.append(it.value());<br>// 打上使用标记,因为可能是放在队列中<br>list-&gt;connections[it.value()].inUse = 1;<br>}</p>
            <p>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; connections.size(); ++i) {<br>const int at = connections.constData()[connections.size() - (i + 1)];<br>QConnectionList * const list = ::connectionList();<br>// 得到连接<br>QConnection &amp;c = list-&gt;connections[at];<br>c.inUse = 0;<br>if (!c.receiver || (c.signal &lt; from_signal_index || c.signal &gt; to_signal_index))<br>continue;</p>
            <p>&nbsp;&nbsp;// 判断是否放到队列中<br>// determine if this connection should be sent immediately or<br>// put into the event queue<br>if ((c.type == Qt::AutoConnection<br>&amp;&amp; (currentQThreadId != sender-&gt;d_func()-&gt;thread<br>|| c.receiver-&gt;d_func()-&gt;thread != sender-&gt;d_func()-&gt;thread))<br>|| (c.type == Qt::QueuedConnection)) {<br>::queued_activate(sender, c, argv);<br>continue;<br>}</p>
            <p>&nbsp;&nbsp;// 为receiver设置当前发送者<br>const int method = c.method;<br>QObject * const previousSender = c.receiver-&gt;d_func()-&gt;currentSender;<br>c.receiver-&gt;d_func()-&gt;currentSender = sender;<br>list-&gt;lock.unlock();</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (qt_signal_spy_callback_set.slot_begin_callback != 0)<br>qt_signal_spy_callback_set.slot_begin_callback(c.receiver, method, argv ? argv : empty_argv);</p>
            <p>#if defined(QT_NO_EXCEPTIONS)<br>c.receiver-&gt;qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);<br>#else<br>try {<br>// 调用receiver的方法<br>c.receiver-&gt;qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);<br>} catch (...) {<br>list-&gt;lock.lockForRead();<br>if (c.receiver)<br>c.receiver-&gt;d_func()-&gt;currentSender = previousSender;<br>throw;<br>}<br>#endif</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (qt_signal_spy_callback_set.slot_end_callback != 0)<br>qt_signal_spy_callback_set.slot_end_callback(c.receiver, method);</p>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list-&gt;lock.lockForRead();<br>if (c.receiver)<br>c.receiver-&gt;d_func()-&gt;currentSender = previousSender;<br>}</p>
            <p>&nbsp;&nbsp;&nbsp; if (qt_signal_spy_callback_set.signal_end_callback != 0) {<br>locker.unlock();<br>qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);<br>locker.relock();<br>}<br>}</p>
            // 响应信号也是在moc里实现的<br>int Foo::qt_metacall(QMetaObject::Call _c, int _id, void **_a)<br>{<br>// 首先在基类中调用方法,返回的id已经变成当前类的方法id了<br>_id = QObject::qt_metacall(_c, _id, _a);<br>if (_id &lt; 0)<br>return _id;<br>if (_c == QMetaObject::InvokeMetaMethod) {<br>switch (_id) {<br>// 这里就是真正的调用方法了,注意参数的解包用法<br>case 0: valueChanged(*reinterpret_cast&lt; int(*)&gt;(_a[1])); break;<br>case 1: setValue(*reinterpret_cast&lt; int(*)&gt;(_a[1])); break;<br>}<br>_id -= 2;<br>}<br>return _id;<br>}</div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cppblog.com/Walker/aggbug/145870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-07 08:47 <a href="http://www.cppblog.com/Walker/articles/145870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Android 的消息队列模型（转）</title><link>http://www.cppblog.com/Walker/articles/145869.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Fri, 06 May 2011 23:53:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/145869.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/145869.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/145869.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/145869.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/145869.html</trackback:ping><description><![CDATA[<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/ghj1976/archive/2011/05/06/6398896.aspx">http://blog.csdn.net/ghj1976/archive/2011/05/06/6398896.aspx</a></p>
<p>Android是参考Windows的消息循环机制来实现Android自身的消息循环的。 <br>Android通过Looper、Handler来实现消息循环机制，Android消息循环是针对线程的（每个线程都可以有自己的消息队列和消息循环）。 <br>Android系统中，Looper负责管理线程的消息队列和消息循环。我们可以通过Loop.myLooper()得到当前线程的Looper对象，通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。 <br>一个线程可以存在（当然也可以不存在）一个消息队列和一个消息循环（Looper）。 <br>Activity是一个UI线程，运行于主线程中，Android系统在启动的时候会为Activity创建一个消息队列和消息循环（Looper）。 <br>Handler的作用是把消息加入特定的（Looper）消息队列中，并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象，如果不指定则利用当前线程的Looper创建。 <br>Activity、Looper、Handler，Thread的关系如下图所示：</p>
<p>&nbsp;</p>
<p>一个Activity中可以创建多个工作线程或者其他的组件，如果这些线程或者组件把他们的消息放入Activity的主线程消息队列，那么该消息就会在主线程中处理了。</p>
<p>因为主线程一般负责界面的更新操作，并且Android系统中的widget不是线程安全的，所以这种方式可以很好的实现Android界面更新。在Android系统中这种方式有着广泛的运用。 </p>
<p>那么一个线程怎样把消息放入主线程的消息队列呢？答案是通过Handle对象，只要Handler对象以主线程的Looper创建，那么调用Handler的sendMessage等接口，将会把消息放入队列都将是放入主线程的消息队列。并且将会在Handler主线程中调用该handler的handleMessage接口来处理消息。</p>
<p>更多Android消息队列的信息请参看： <a href="http://my.unix-center.net/~Simon_fu/?p=652">http://my.unix-center.net/~Simon_fu/?p=652</a></p>
<p>下面这个图从另外一个角度描述了他们的关系：</p>
<p>&nbsp;</p>
<p>参考资料：</p>
<p>Android异步加载图像小结 <br><a href="http://blog.csdn.net/sgl870927/archive/2011/03/29/6285535.aspx">http://blog.csdn.net/sgl870927/archive/2011/03/29/6285535.aspx</a></p>
<p>深入理解Android消息处理系统——Looper、Handler、Thread</p>
<p><a href="http://my.unix-center.net/~Simon_fu/?p=652">http://my.unix-center.net/~Simon_fu/?p=652</a></p>
<p>Android线程模型（Painless Threading） <br><a href="http://android.group.iteye.com/group/blog/382683">http://android.group.iteye.com/group/blog/382683</a></p>
<p>android线程控制UI更新（Handler 、post()、postDelayed()、postAtTime） <br><a href="http://lepeng.net/blogger/?p=21">http://lepeng.net/blogger/?p=21</a></p>
<p>Android &#8211; Multithreading in a UI environment <br><a href="http://www.aviyehuda.com/2010/12/android-multithreading-in-a-ui-environment/">http://www.aviyehuda.com/2010/12/android-multithreading-in-a-ui-environment/</a></p>
<p>Android中的Handler, Looper, MessageQueue和Thread <br><a href="http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html">http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html</a></p>
<p>Android Runnable <br><a href="http://blog.csdn.net/michaelpp/archive/2010/06/30/5704682.aspx">http://blog.csdn.net/michaelpp/archive/2010/06/30/5704682.aspx</a></p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/Walker/aggbug/145869.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-07 07:53 <a href="http://www.cppblog.com/Walker/articles/145869.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Chrome的小胡瓜(Courgette) </title><link>http://www.cppblog.com/Walker/articles/145114.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Tue, 26 Apr 2011 23:54:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/145114.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/145114.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/145114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/145114.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/145114.html</trackback:ping><description><![CDATA[<p>Chrome的小胡瓜(Courgette) 收藏 <br></p>
<p>转自<a href="http://blog.csdn.net/xingtian713/archive/2009/08/25/4483810.aspx">http://blog.csdn.net/xingtian713/archive/2009/08/25/4483810.aspx</a></p>
<p><br>在Chrome中有一个很有意思的工具courgette，翻译成中文是小胡瓜的意思。我很难把这个单词和这个小工具联系在一起，也许作者比较偏爱这个蔬菜吧！ </p>
<p>背景 <br>我们用C++编写程序时，经常会出现修改了一行代码，重新编译一下之后，再去对比一下新的二进制文件，就可以发现千差万别了。往往出现我们要发布一个修改了一行代码的补丁，需要替换整个DLL或者EXE。这对于Chrome中类似于Chrome.dll（10M左右）文件来说，升级一次的代价还是挺高的。本人在去年下半年负责的一个升级服务器项目中，就曾动过念头，是否有一种方法可以寻找出两个二进制文件的差异，将这个差异打成一个补丁，用户下载后，可以通过这个补丁，可以自动生成新的DLL。可惜功力不够，最后放弃了。最近读Chrome的代码时，发现Chrome已经实现了该功能。这个就是Courgette的功劳了。 </p>
<p>Courgette做什么的？ <br>Courgette主要用于Chrome的升级过程，他的主要作用是，针对两个版本不同的二进制文件(Binary File)，寻找其中区别，生成补丁文件；另外就是根据这个补丁文件加上旧版本的二进制文件生成新版本的二进制文件的还原过程了。类似于加解密流程。 </p>
<p>Courgette的实现原理？ <br>Courgette构建在一个开源代码bsdiff和bspatch 基础上的。并在bsdiff的基础上做了一些优化。 </p>
<p>本人是半路出家的人，对编译原理和汇编了解不深，按照我对bsdiff的算法 理解，一个二进制文件里面，包含了代码部分（函数，数据），指向这些函数的指针列表（编译链接产生，包含了如何定位函数等信息），由于这些地址是内部的相对地址，即使更改了一小行代码，重新编译后，函数的地址将发生变化了，指向这些函数的指针值也全部变化了。因此，即使更改了一个小小的变量，也会导致很多部分的修改。bsdiff的原理就是对二进制文件进行反汇编，将上面所说的两部分进行分别处理，对于代码部分，其实就和普通的文本文件类似了，改变不会太大，这部分体积基本上占去了整个二进制文件的80%左右。然后对动态指针部分进行一些更新处理，就基本上达到了打补丁的目标了。 </p>
<p>&nbsp;&nbsp;&nbsp; server:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; diff = bsdiff(original, update)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transmit diff </p>
<p><br>&nbsp;&nbsp;&nbsp; client:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; receive diff</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; update = bspatch(original, diff) <br>大致流程如上，制作补丁时调用bsdiff函数，根据两个不同版本的二进制文件，生成补丁文件。客户端下载补丁后，调用bspatch函数，根据旧版本二进制文件和补丁生成新的二进制文件。</p>
<p>Chrome在bsdiff的基础上做了一些优化，主要体现在动态指针列表部分，chrome对代码部分的每一个模块地址分配了一个标签（Label），这些标签都是整数，并把这些标签保存到一个数组中，然后指针列表中映射的地址改为指向数组的索引，由于一些函数地址被调用多次，通过这种方法确实可以减少一些体积。做了一些优化后，在bsdiff的基础上又减少了30%左右吧（这是google的说法，我没验证过）。 <br>&nbsp;&nbsp;&nbsp; server: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_old = disassemble(original) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_new = disassemble(update) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_new_adjusted = adjust(asm_new, asm_old) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_diff = bsdiff(asm_old, asm_new_adjusted) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transmit asm_diff </p>
<p><br>&nbsp;&nbsp;&nbsp; client: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; receive asm_diff <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_old = disassemble(original) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; asm_new_adjusted = bspatch(asm_old, asm_diff) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; update = assemble(asm_new_adjusted) <br>Chrome的大致处理流程如上，和bsdiff流程类似，多了 asm_new_adjusted = adjust(asm_new, asm_old)这一个步骤，这个步骤主要就是上面说的对地址标签化的过程。</p>
<img src ="http://www.cppblog.com/Walker/aggbug/145114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-04-27 07:54 <a href="http://www.cppblog.com/Walker/articles/145114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>chrome 源码分析</title><link>http://www.cppblog.com/Walker/articles/145113.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Tue, 26 Apr 2011 23:52:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/145113.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/145113.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/145113.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/145113.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/145113.html</trackback:ping><description><![CDATA[chrome源代码目录结构简介（版本4.1.249.1059）<br><br>为了对庞大的源码项目进行分析，先对源码目录树作一个简单的介绍，粗略的了解一下各个模块的功能分布情况，chrome源代码src目录下的结构如下图：
<p><img alt="" src="http://pic001.cnblogs.com/images/2010/155795/2010100921265893.gif"></p>
<p>　　app：该目录下的代码主要是和各个操作系统平台相关的应用上层代码的提炼。不同操作系统可能对应不同的c++实现文件。比如裁剪板操作、操作系统数据交换接口、资源管理等。代码量不大。</p>
<p>　　base：基础设施代码，该目录下的代码对理解chrome的基础架构设计是必不可少的，这里面是大量的工具性、框架性代码实现，比如对进程、线程、消息循环的统一封装，对字符串处理、c++对象生命周期管理、json解析、路径服务、日期时间、日志框架等。</p>
<p>　　breakpad：崩溃服务框架库，在程序发生异常时，对异常进行捕获后可以将崩溃现场数据发送给google进行分析。</p>
<p>　　build：编译构建相关的工具支持。</p>
<p>　　chrome：浏览器主程序实现代码，包括了UI实现和Render部分两大部分，当然这两部分又是以大量的其他基础设施代码为基础的，比如Render部分是对webkit的封装。这部分代码量很大，google自产代码，频繁的改动代码主要集中在这里。</p>
<p>　　chrome_frame：这是google针对IE开发的一个插件，使得IE可以使用chrome的渲染引擎来显示网页。</p>
<p>　　courgette：小胡瓜，这个项目是一个针对升级使用的，目的是减少升级过程中数据下载的大小。比如版本升级可能需要更新某个DLL文件，而这个文件可能有10M大小，而新版本可能只是对该DLL改动了一行代码。通过courgette可以找出这两个DLL之间的差异部分，使得不需要下载10M大小，而可能只需要下载几十K的差异描述数据即可完成升级。</p>
<p>　　gears：是一个用来开发离线网络应用的工具，是一个JavaScript应用编程接口，通过Google&nbsp;Gears可以允许多种Web应用程序脱机运行，可以让用户在上线或者离线状态下运行网络程序。离线就需要作本地存储，而在html5中就有本地存储相关的接口规范，因此google将放弃gears而采用html5的方式。</p>
<p>　　google_update：google更新，用于自动升级。</p>
<p>　　googleurl：google实现的URL解析辅助工具库。</p>
<p>　　ipc：非常重要的进程通信基础设施库。chrome是多进程架构，而进程间的通信就是以ipc库作为基础支持的。具体在windows下的实现方式是命名管道、异步IO（完成端口）、共享内存来实现进程间高效的数据传输。ipc不仅封装了IO机制，而且还定义了统一的消息传输格式。</p>
<p>　　media：多媒体音频视频解码相关的内容。</p>
<p>　　native_client：在浏览器中运行native代码的技术，是一个插件。native_client项目被视为微软ActiveX技术的继任者。项目具体细节可参考<a href="http://code.google.com/p/nativeclient/" target=_blank>native client官网</a>。</p>
<p>　　net：网络协议实现基础库，包括ftp、http等客户端协议栈的实现代码。</p>
<p>　　o3d：一个插件，可在浏览器中创建丰富的交互式三维应用程序，以后在浏览器中玩3D游戏将不再遥远。具体细节参考<a href="http://code.google.com/intl/zh-CN/apis/o3d/" target=_blank>o3d项目官网</a>。</p>
<p>　　printing：打印方面的内容。</p>
<p>　　rlz：用户行为追踪，这个没有源码，这个库的目的就是将用户行为收集报告给google。虽然这对产品的改善有很大的帮助，但也存在隐私问题。</p>
<p>　　sandbox：沙盒安全技术，在浏览网页的时候，保护计算机不被恶意代码侵入。</p>
<p>　　sdch：一种新的压缩技术。浏览器在http请求时可以写成Accept-Encoding: sdch, gzip。服务器如果支持的话，就可以返回sdch格式的压缩数据给浏览器。</p>
<p>　　site_scons：一个工具，里面是一个python脚本文件，具体用处还未深入了解。</p>
<p>　　skia：google收购的一家公司提供的2D图形渲染库，图形库的优劣决定了浏览器的显示效果。据说IE9将采用GPU显卡渲染，估计浏览器采用GPU渲染将很快普及。</p>
<p>　　testing：c++单元测试框架库。</p>
<p>　　third_party：该目录下是大量的第三方开源支持库，最重要的当然是webkit内核了。</p>
<p>　　v8：google开发的高效的javascript引擎，是chrome的重要内核库。</p>
<p>　　views：界面控件元素库，对不同操作系统平台的UI事件交互机制、各种控件如按钮、菜单、树、checkbox等进行了统一的封装。界面绘制采用skia来实现。</p>
<p>　　webkit：google对webkit内核的封装层，其目的是在webkit内核和上层调用之间提供一个中间层。该目录下有一个重要的glue工程。是名副其实的&#8216;胶水&#8217;层。</p>
<p>　　整个源码工程虽然庞大，但其结构是非常清晰的，代码风格很统一，就象是一个人写的一样。借助vs2008强大的可视化调试，我们只要掌握好粒度，从粗到细，从整体到局部逐渐深入，带着问题去跟踪调试，很快就会上手进入状态。</p>
<img src ="http://www.cppblog.com/Walker/aggbug/145113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-04-27 07:52 <a href="http://www.cppblog.com/Walker/articles/145113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>源代码分析之如何实现自定义的标题栏</title><link>http://www.cppblog.com/Walker/articles/143886.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sun, 10 Apr 2011 13:06:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/143886.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/143886.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/143886.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/143886.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/143886.html</trackback:ping><description><![CDATA[<p>本文主要分析Visual Studio Samples\1033\C++\MFC\Visual C++ 2008 Feature Pack\MSMoneyDemo这个Sample<br>一般窗口的标题栏上面都是只有固定的最小化，恢复，最大化按钮，这些按钮的大小，图标都是系统自定义的，<br>本文分析VS2008 sp1 的事例代码实现自己的标题栏。<br>CMSMCaptionBar实现了，自定义的标题栏窗口，类定义如下。<br>class CMSMCaptionBar : public CPane<br>{<br>&nbsp;DECLARE_DYNCREATE(CMSMCaptionBar)</p>
<p>// Construction<br>public:<br>&nbsp;CMSMCaptionBar ();</p>
<p>&nbsp;virtual ~CMSMCaptionBar ();<br>&nbsp;<br>&nbsp;virtual void SetIcon (HICON hIcon);</p>
<p>&nbsp;void SetCaptionHeight (int nHeight);</p>
<p>&nbsp;int GetCaptionHeight () const;</p>
<p>&nbsp;void SetCaptionFont (const LOGFONT&amp; lf);</p>
<p>&nbsp;HFONT GetCaptionFont () const;</p>
<p>&nbsp;virtual COLORREF GetCaptionTextColor () const;</p>
<p>&nbsp;void SetParentActive (BOOL bParentActive = true);</p>
<p>&nbsp;BOOL IsParentActive () const;</p>
<p>&nbsp;void SetParentMaximize (BOOL bParentMaximize = true);</p>
<p>&nbsp;BOOL IsParentMaximize () const;</p>
<p>// Attributes<br>public:</p>
<p>// Operations<br>public:</p>
<p>// Overrides<br>public:<br>&nbsp;virtual BOOL Create(CWnd* pParentWnd, UINT nID = uiCaprionBarID);<br>&nbsp;virtual BOOL CreateEx(CWnd* pParentWnd, UINT nID = uiCaprionBarID);<br>&nbsp;virtual CSize CalcFixedLayout(BOOL, BOOL);<br>protected:<br>&nbsp;virtual BOOL PreCreateWindow(CREATESTRUCT&amp; cs);<br>&nbsp;virtual void DoPaint(CDC* pDCPaint);<br>&nbsp;virtual BOOL PreTranslateMessage(MSG* pMsg);<br>&nbsp;virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);</p>
<p>protected:<br>&nbsp;afx_msg LRESULT OnSetText(WPARAM, LPARAM lParam);<br>&nbsp;afx_msg void OnSize(UINT nType, int cx, int cy);<br>&nbsp;afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);<br>&nbsp;afx_msg void OnSysCommand(UINT nID, LPARAM lParam);</p>
<p>&nbsp;DECLARE_MESSAGE_MAP()</p>
<p>&nbsp;virtual UINT HitTest (const CPoint&amp; pt) const;<br>&nbsp;virtual void ShowSysMenu (const CPoint&amp; point);</p>
<p>public:<br>&nbsp;CString &nbsp;&nbsp;&nbsp;&nbsp; m_strCaption;</p>
<p>&nbsp;HICON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hIcon;<br>&nbsp;CSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_szIcon;</p>
<p>&nbsp;BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_bParentActive;<br>&nbsp;BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_bParentMaximize;</p>
<p>&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_SystemHeight;<br>&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_CaptionHeight;<br>&nbsp;CFont&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_CaptionFont;</p>
<p>&nbsp;CMSMCaptionBarButton m_BtnMinimize;<br>&nbsp;CMSMCaptionBarButton m_BtnMaximize;<br>&nbsp;CMSMCaptionBarButton m_BtnClose;<br>};</p>
<p>下面是类的派生关系图：<br>CObject <br>&nbsp;&nbsp; CCmdTarget<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CWnd<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CBasePane<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CPane<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CMSMCaptionBar<br>从这个图上可以看出：CMSMCaptionBar也是一个窗口，（从CWnd类派生的都是一个窗口WIndow）。</p>
<p>2 如何设计设计一个Caption Window<br>一个窗口主要由UI部分以及消息响应部分组成。<br>UI部分通俗的说就是窗口的外观描述，通过外观描述可以绘制出窗口。<br>消息响应部分就是该窗口会处理哪些消息。比如双击时最大化还是恢复，点击上面的关闭按钮，程序关闭等等，下面会详细描述。<br>2.1 Caption Window的组成<br>1）元素组成：标题图标，窗口标题，最小化按钮，恢复按钮，最大化按钮。<br>描述这些元素需要相应的成员变量，也就是CMSMCaptionBar的public成员：<br>&nbsp;CString &nbsp;&nbsp;&nbsp;&nbsp; m_strCaption;//窗口标题<br>&nbsp;CFont&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_CaptionFont;//标题字体<br>&nbsp;<br>&nbsp;HICON&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hIcon;//标题图标<br>&nbsp;CSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_szIcon;//图标大小</p>
<p>&nbsp;</p>
<p>&nbsp;//最小化按钮，恢复按钮，最大化按钮。<br>&nbsp;CMSMCaptionBarButton m_BtnMinimize;<br>&nbsp;CMSMCaptionBarButton m_BtnMaximize;<br>&nbsp;CMSMCaptionBarButton m_BtnClose;</p>
<p>对于Caption Window，还有自己的一些属性，比如窗口高度。</p>
<p>&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_SystemHeight;<br>&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_CaptionHeight;//标题栏高度<br>&nbsp;<br>2）如何绘制元素<br>为了保证窗口接收双击事件，需要组成S_DBLCLKS风格的窗口。<br>LPCTSTR lpszClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW),<br>&nbsp;&nbsp;(HBRUSH)(COLOR_BTNFACE+1), NULL);</p>
<p>Create-&gt;CreateEx<br>创建窗口<br>CWnd::Create(lpszClass, NULL, dwStyle | WS_CLIPSIBLINGS, rect, pParentWnd, nID)<br>创建好窗口之后还必须加入到窗口的一个Pane的列表中去<br>if (pParentWnd-&gt;IsKindOf (RUNTIME_CLASS (CFrameWndEx)))<br>&nbsp;{<br>&nbsp;&nbsp;((CFrameWndEx*) pParentWnd)-&gt;AddPane (this);<br>&nbsp;}<br>&nbsp;<br>&nbsp;<br>加载Capation Icon，设置Caption 标题<br>SetIcon (hIcon);<br>SetWindowText (strCaption);<br>创建最小化按钮，恢复按钮，最大化按钮<br>&nbsp;m_BtnClose.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt, this, SC_CLOSE);<br>&nbsp;m_BtnClose.SetTooltip (_T("Close"));</p>
<p>&nbsp;m_BtnMaximize.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt, this, SC_MAXIMIZE);<br>&nbsp;m_BtnMaximize.SetTooltip (_T("Maximize"));</p>
<p>&nbsp;m_BtnMinimize.Create (_T(""), BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rt, this, SC_MINIMIZE);<br>&nbsp;m_BtnMinimize.SetTooltip (_T("Minimize"));</p>
<p>对于按钮上图片的设置与加载是在CMSMVisualManager中设置的，这是在VS2008 Sp1中有的可以设置多种UI主题风格，类似于换皮肤。<br>下面列出了与Caption Window相关的函数。<br>class CMSMVisualManager : public CMFCVisualManagerOffice2003<br>{<br>BOOL CMSMVisualManager::LoadMSMCaptionButtonsIcons (LPCTSTR lpszID);<br>&nbsp;virtual void MSMDrawCaptionButton (CDC* pDC, CRect rect, AFX_BUTTON_STATE state, UINT id);<br>&nbsp;BOOL LoadMSMCaptionButtonsIcons (LPCTSTR lpszID);</p>
<p>&nbsp;const CSize&amp; GetMSMCaptionButtonsSize () const;<br>&nbsp;<br>&nbsp;virtual void OnFillBarBackground (CDC* pDC, CBasePane* pBar,<br>&nbsp;CRect rectClient, CRect rectClip, BOOL bNCArea = FALSE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;CImageList m_CaptionButtonIconst;<br>&nbsp;CSize&nbsp;&nbsp;&nbsp; m_CaptionButtonSize;<br>}<br>通过这些函数可以设置Button的图片以及Caption Windowd的背景。</p>
<p><br>通过上面就把窗口UI元素画好了，下面介绍消息的处理</p>
<p>3）Caption Windows处理的消息又哪些？<br>&nbsp;鼠标左键单击（需要判断是最小化，最大化，恢复，还是关闭）；<br>&nbsp;鼠标右键单击（弹出帮助菜单）<br>&nbsp;鼠标左键双击（最大化或者恢复）<br>&nbsp;WM_SIZE消息，当窗口大小变化时，需要移动最小化，最大化，恢复按钮的位置。<br>&nbsp;<br>&nbsp;也就是下面的一些消息。<br>&nbsp;ON_MESSAGE(WM_SETTEXT, OnSetText)<br>&nbsp;ON_WM_SIZE()<br>&nbsp;ON_WM_CONTEXTMENU()<br>&nbsp;ON_WM_SYSCOMMAND()<br>&nbsp;<br>&nbsp;<br>4）对于按钮消息的处理WM_XXX应当转化成WM_NCXXX，也就是说客户端消息要转化成非客户端消息。因为对于单文档程序来说，<br>标题栏属于非客户区。</p>
<p>BOOL CMSMCaptionBar::PreTranslateMessage (MSG* pMsg) <br>在其中调用HitTest函数判断鼠标的位置，如果是在Caption window中点击，则<br>判断uiHit = HTCAPTION;还是uiHit = HTSYSMENU;<br>再<br>&nbsp;&nbsp;&nbsp;switch (pMsg-&gt;message)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;case WM_LBUTTONDOWN:<br>&nbsp;&nbsp;&nbsp;&nbsp;message = WM_NCLBUTTONDOWN;<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;case WM_LBUTTONUP:<br>&nbsp;&nbsp;&nbsp;&nbsp;message = WM_NCLBUTTONUP;<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;case WM_LBUTTONDBLCLK:<br>&nbsp;&nbsp;&nbsp;&nbsp;message = WM_NCLBUTTONDBLCLK;<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;if (message != 0)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;if (message == WM_NCLBUTTONDOWN &amp;&amp; uiHit == HTSYSMENU)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CRect rt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetWindowRect (rt);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowSysMenu (CPoint (rt.left, rt.bottom));<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pParentWnd-&gt;SendMessage (message, wParam, lParam);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;}</p>
<p>对于Caption Window不感兴趣的消息会发送给父窗口处理。<br>&nbsp;<br>5）WM_SIZE消息的处理</p>
<p>对WM_SIZE的消息处理就是移动最下化，最大化，以及恢复按钮。</p>
<p><br>3）使用Caption Window<br>int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) <br>设置风格，去掉WS_CAPTION | FWS_ADDTOTITLE，即创建不带Caption的Mainframe,<br>因为Caption是我们自己要创建的，而不是自动创建</p>
<p>&nbsp;ModifyStyle (WS_CAPTION | FWS_ADDTOTITLE, 0);<br>去掉窗口的边框<br>&nbsp;ModifyStyleEx (WS_EX_CLIENTEDGE, 0);<br>设置窗口风格<br>&nbsp;CMFCVisualManager::SetDefaultManager (RUNTIME_CLASS (CMSMVisualManager));</p>
<img src ="http://www.cppblog.com/Walker/aggbug/143886.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-04-10 21:06 <a href="http://www.cppblog.com/Walker/articles/143886.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）strlen源码剖析 http://www.cppblog.com/ant/archive/2007/10/12/32886.html</title><link>http://www.cppblog.com/Walker/articles/82347.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sat, 09 May 2009 01:50:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/82347.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/82347.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/82347.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/82347.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/82347.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 学习高效编程的有效途径之一就是阅读高手写的源代码，CRT(C/C++ Runtime Library)作为底层的函数库，实现必然高效。恰好手中就有glibc和VC的CRT源代码，于是挑了一个相对简单的函数strlen研究了一下，并对各种实现作了简单的效率测试。strlen的函数原形如下：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_t strlen(const ...&nbsp;&nbsp;<a href='http://www.cppblog.com/Walker/articles/82347.html'>阅读全文</a><img src ="http://www.cppblog.com/Walker/aggbug/82347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2009-05-09 09:50 <a href="http://www.cppblog.com/Walker/articles/82347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>