﻿<?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++博客-C++之旅</title><link>http://www.cppblog.com/wangjt/</link><description /><language>zh-cn</language><lastBuildDate>Sat, 30 Aug 2008 03:31:19 GMT</lastBuildDate><pubDate>Sat, 30 Aug 2008 03:31:19 GMT</pubDate><ttl>60</ttl><item><title>Windows同步API （二）—— Semaphore</title><link>http://www.cppblog.com/wangjt/archive/2008/02/01/42362.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Fri, 01 Feb 2008 10:11:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/02/01/42362.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/42362.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/02/01/42362.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/42362.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/42362.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Semaphore是旗语的意思，在Windows中，Semaphore对象用来控制对资源的并发访问数。Semaphore对象具有一个计数值，当值大于0时，Semaphore被置信号，当计数值等于0时，Semaphore被清除信号。每次针对Semaphore的wait functions返回时，计数值被减1，调用ReleaseSemaphore可以将计数值增加 lReleaseCount 参数值指定的值。&nbsp;&nbsp;<a href='http://www.cppblog.com/wangjt/archive/2008/02/01/42362.html'>阅读全文</a><img src ="http://www.cppblog.com/wangjt/aggbug/42362.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-02-01 18:11 <a href="http://www.cppblog.com/wangjt/archive/2008/02/01/42362.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows同步API （二）—— Event</title><link>http://www.cppblog.com/wangjt/archive/2008/02/01/42312.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Thu, 31 Jan 2008 16:18:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/02/01/42312.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/42312.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/02/01/42312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/42312.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/42312.html</trackback:ping><description><![CDATA[<p>　　Event即事件是一种用于进行线程/进程间同步的对象，事件有置位和复位两种状态，当线程通过waiting functions等待Event对象置位时该线程将进入阻塞状态，当该Event对象被置位或等待超时后，等待的线程将恢复执行。Event可以用在一个线程要等待其它线程时。<br><br>可以使用CreateEvent创建Event对象<br>HANDLE WINAPI CreateEvent(<br>&nbsp;&nbsp;&nbsp; LPSECURITY_ATTRIBUTES lpEventAttributes,<br>&nbsp;&nbsp;&nbsp; BOOL bManualReset,<br>&nbsp;&nbsp;&nbsp; BOOL bInitialState,<br>&nbsp;&nbsp;&nbsp; LPCTSTR lpName<br>);<br><br>lpEventAttributes用于指定Event对象的安全属性，包括句柄是否可被子进程继承和对象的安全描述符。可设置NULL取默认安全属性。<br>bManualReset表明Event对象是否需要手动复位。如果该参数为TRUE，则Event对象需要通过ResetEvent函数手动复位。如果该参数为FALSE，则Event被创建为自动复位的Event，任何等待的线程被恢复执行后，该Event将被系统自动复位。打个比方，如果有10个线程在等待一个Event，这时将Event置位，如果这是个手动复位Event，那么这10个线程将被依次唤醒直到通过ResetEvent调用将该Event复位；如果Event为自动复位Event，那么10个线程中的第一个被唤醒后Event被自动复位，其它线程将继续等待。<br>bInitialState参数表明Event对象被创建后默认是否置位。<br>lpName参数是Event的名字，可以为空表明将创建匿名Event。<br><br>CreateEvent函数在调用成功后返回Event句柄。如果同名Event已经存在，则返回这个已经存在了的Event的句柄，此时调用GetLastError函数将返回 ERROR_ALREADY_EXISTS。<br><br>还可以通过OpenEvent打开一个已经创建的非匿名Event<br>HANDLE WINAPI OpenEvent(<br>&nbsp;&nbsp;&nbsp; DWORD dwDesiredAccess,<br>&nbsp;&nbsp;&nbsp; BOOL bInheritHandle,<br>&nbsp;&nbsp;&nbsp; LPCTSTR lpName<br>);<br><br><br>在创建或打开了Event对象之后，可以使用SetEvent和ResetEvent函数来置位或复位一个Event对象。<br>BOOL WINAPI SetEvent(<br>&nbsp;&nbsp;&nbsp; HANDLE hEvent<br>);<br>BOOL WINAPI ResetEvent(<br>&nbsp;&nbsp;&nbsp; HANDLE hEvent<br>);<br><br>要等待一个或多个Event对象置位可以使用wait functions。<br><br>简单示例，一个线程不停读取用户输入并放入message列表，另一个线程模拟将message发送出去，如果没有消息，则发送线程处于阻塞状态等待，一旦有消息录入，输入线程将event置位，发送线程即被激活并逐个发送消息。<br><br>#include "stdafx.h"<br>#include &lt;windows.h&gt;<br>#include &lt;tchar.h&gt;<br>#include &lt;iostream&gt;<br>#include &lt;list&gt;<br>#include &lt;string&gt;<br>using namespace std;</p>
<p>#ifdef _UNICODE<br>typedef wstring tstring;<br>#define tcout wcout<br>#define tcin wcin<br>#else<br>typedef string tstring;<br>#define tcout cout<br>#define tcin cin<br>#endif /* _UNICODE */</p>
<p>typedef list&lt;tstring&gt; StringList;</p>
<p>HANDLE hMutex = NULL;<br>HANDLE hEvent = NULL;<br>HANDLE hSendThread = NULL;<br>StringList messages;</p>
<p>bool isRunning;</p>
<p>DWORD WINAPI SendThreadProc(LPVOID lpThreadParameter)<br>{<br>&nbsp;DWORD dw;<br>&nbsp;while(isRunning)<br>&nbsp;{<br>&nbsp;&nbsp;dw = WaitForSingleObject(hEvent, INFINITE);<br>&nbsp;&nbsp;if(dw != WAIT_OBJECT_0)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;tcout &lt;&lt; _T("Wait error.") &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;dw = WaitForSingleObject(hMutex, INFINITE);<br>&nbsp;&nbsp;if(WAIT_OBJECT_0 != dw &amp;&amp; WAIT_ABANDONED != dw)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;tcout &lt;&lt; _T("Wait error.") &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp;return -2;<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;StringList list(messages);<br>&nbsp;&nbsp;messages.clear();</p>
<p>&nbsp;&nbsp;ReleaseMutex(hMutex);</p>
<p>&nbsp;&nbsp;for(StringList::iterator i = list.begin(); i != list.end(); i++)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;Sleep(1000); //休眠1秒模拟发送所耗时间</p>
<p>&nbsp;&nbsp;&nbsp;tcout &lt;&lt; _T("/* Send Message:") &lt;&lt; *i &lt;&lt; _T(" */");<br>&nbsp;&nbsp;}</p>
<p>&nbsp;}</p>
<p>&nbsp;return 0;<br>}</p>
<p>int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;hMutex = CreateMutex(NULL, FALSE, NULL);<br>&nbsp;hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);<br>&nbsp;isRunning = true;</p>
<p>&nbsp;hSendThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendThreadProc, NULL, 0, NULL);</p>
<p>&nbsp;while(isRunning)<br>&nbsp;{<br>&nbsp;&nbsp;tstring s;<br>&nbsp;&nbsp;tcin &gt;&gt; s;<br>&nbsp;&nbsp;if(s == _T("quit"))<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;isRunning = true;<br>&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;DWORD dw = WaitForSingleObject(hMutex, INFINITE);<br>&nbsp;&nbsp;if(WAIT_OBJECT_0 != dw &amp;&amp; WAIT_ABANDONED != dw)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;tcout &lt;&lt; _T("Wait error.") &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp;return -1;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;messages.push_back(s);<br>&nbsp;&nbsp;ReleaseMutex(hMutex);<br>&nbsp;&nbsp;SetEvent(hEvent);<br>&nbsp;}</p>
<p><br>&nbsp;CloseHandle(hMutex);<br>&nbsp;CloseHandle(hEvent);<br>&nbsp;CloseHandle(hSendThread);</p>
<p>&nbsp;return 0;</p>
<p>}</p>
<p><br>&nbsp;</p>
<img src ="http://www.cppblog.com/wangjt/aggbug/42312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-02-01 00:18 <a href="http://www.cppblog.com/wangjt/archive/2008/02/01/42312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows同步API （一）—— Mutex</title><link>http://www.cppblog.com/wangjt/archive/2008/01/30/42235.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Wed, 30 Jan 2008 08:42:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/01/30/42235.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/42235.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/01/30/42235.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/42235.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/42235.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Mutex是互斥体的意思，当一个线程持有一个Mutex时，其它线程申请持有同一个Mutex会被阻塞，因此可以通过Mutex来保证对某一资源的互斥访问（即同一时间最多只有一个线程访问）。&nbsp;&nbsp;<a href='http://www.cppblog.com/wangjt/archive/2008/01/30/42235.html'>阅读全文</a><img src ="http://www.cppblog.com/wangjt/aggbug/42235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-01-30 16:42 <a href="http://www.cppblog.com/wangjt/archive/2008/01/30/42235.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过Thunk将类的非静态成员函数设置为WindowProc</title><link>http://www.cppblog.com/wangjt/archive/2008/01/27/41939.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Sun, 27 Jan 2008 10:13:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/01/27/41939.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/41939.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/01/27/41939.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/41939.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/41939.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">当注册窗口类时，WNDCLASSEX结构的lpfnWndProc成员应设置为窗口过程函数的地址，这是一个C风格的函数指针，所以我们只能使用全局或静态函数的地址，这在我们将窗口封装为C++类时会很麻烦，因为我们无法在一个全局或静态的WindowProc函数中访问类实例，这时Thunk技术可以大显身手了<br><br>　　我们先分析一下其它的解决方案：一种是建立一个HWND到C++类实例的映射表，在WindowProc中通过这个映射表从HWND得到C++类实例。如果我们不考虑那一点点性能损失的话（每个消息在处理时都要查一次映射表）我想这也是个不错的办法（我们不考虑WM_NCDESTROY消息出现两次的变态情况）<br>　　而另有一种方式是通过WndExtra来存放类实例指针，这样就可以在WindowProc中通过调用GetWindowLong来获取类实例指针了，缺点就是当别人使用你的C++类时如果不知道你用的WndExtra，他可能会把其它数据放到WndExtra中，于是后果不可设想<br>　　现在让我们来看看Thunk方案吧，据我了解MFC/ATL都是基于这种方案实现。Thunk在这里是指一小段代码，可惜的是这段代码无法用C/C++来表示（因为是动态代码），只能用机器码写（汇编都不好使），这也就造成本方案应对多平台有点名义上的不完美。这里仅以i386体系来讨论。<br>　　我们先假设一个C++窗口类<br><br>struct _THUNK;<br>class CWnd<br>{<br>&nbsp;&nbsp;&nbsp; m_hWnd;<br>&nbsp;&nbsp;&nbsp; HRESULT WINAPI static&nbsp;StdWindowProc(HWND, UINT, WPARAM, LPARAM);<br>&nbsp;&nbsp;&nbsp; HRESULT WINAPI WindowProc(UINT, WPARAM, LPARAM);<br>&nbsp;&nbsp;&nbsp; _THUNK* pThunk;<br>}<br><br>　　_THUNK结构定义如下：<br><br>#pragma pack(push,1)<br>struct _THUNK<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; m_mov;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)<br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; m_this;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br>&nbsp;&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp; m_jmp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // jmp WndProc<br>&nbsp;&nbsp;&nbsp;&nbsp; DWORD&nbsp;&nbsp; m_relproc;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // relative jmp<br>};<br>#pragma pack(pop)<br><br>该结构实质是一段CPU指令，我将来要将其设置为窗口过程，这段指令将传入的第一个参数改为m_this（m_this是CWnd实例的this指针），然后由一个jmp指令跳转到代码地址m_relproc，这里m_relproc的值将是静态WindowProc函数的地址，当WindowProc将第一个参数取出时，这个参数已经不再是HWND，而是CWnd*了，这时就可以间接调用CWnd的实例成员了。<br><br>实例化_THUNK结构的代码如下：其中pThis指CWnd实例的指针，proc指静态StdWindowProc的地址，pThunk指_THUNK结构实例地址<br>&nbsp;&nbsp;m_mov = 0x042444C7;&nbsp; //C7 44 24 0C<br>&nbsp;&nbsp;m_this = PtrToUlong(pThis);<br>&nbsp;&nbsp;m_jmp = 0xe9;<br>&nbsp;&nbsp;m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)pThunk+sizeof(_THUNK)));<br>&nbsp;&nbsp;FlushInstructionCache(GetCurrentProcess(), pThunk, sizeof(_THUNK));<br>最后这句是告诉CPU清除当前指令缓存，因为我们更改了&#8220;代码&#8221;。m_relproc的值是proc到thunk下一条指令（也就是thunk后边紧挨着的那个地址）的相对地址偏移。m_mov与m_this合起来是：mov dword ptr[esp+0x4], pThis，意思是将 [栈顶+4] 地址处的4字节（DWORD）的值为pThis，也就是将第一上参数hWnd的值改为pThis。下面是栈的结构<br><br>栈底<br>......<br>......<br>栈顶 + 7<br>栈顶 + 6<br>栈顶 + 5<br>栈顶 + 4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4-7 原本存放着hWnd参数，在执行完Thunk后，其值为CWnd实例地址<br><span style="COLOR: red">-------------------------------------------<br></span>栈顶 + 3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0-3 存放着窗口过程的返回地址，<br>栈顶 + 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WindowProc里在return之后会返回到该地址继续运行<br>栈顶 + 1<br>栈顶 + 0<br><br>因为Thunk执行后栈结构只是第一个参数被更改，返回地址并没有被破坏，所以StdWindowProc并不知道Thunk的存在而错误的认为自己是被直接调用，其内部的return语句会按栈顶的返回地址返回。<br><br>理论解决了，看一下流程，<br>创建并初始化CWnd::m_pThunk<br>先注册一个windows窗口类其中WNDCLASSEX的lpfnWindowProcx字段为CWnd::StdWindowProc的地址<br>然后用CreateWindowEx创建窗口，最后一个参数设置为当前CWnd实例的this指针的值<br>在StdWindowProc中我们响应WM_NCCREATE消息，从lParam参数指向的CREATESTRUCT结构体的lpCreateParams字段中取出CWnd的实例指针，通过SetWindowLong将窗口过程更改为CWnd实例的m_pThunk的值，然后手动调用CWnd实例的WindowProc<br>在StdWindowProc中响应任何其它消息时，我们将参数hWnd强制类型转换为CWnd*，将通过这个指针调用CWnd::WindowProc。<br><br>最扣补充一句，因为新版Windows或最新的Server Packs都加入了数据执行保护功能，按上面的方法弄很可能出现异常，因为Thunk是数据结构，会被放在数据段中并被标记不不可执行。解决办法之一是使用VirtualAlloc方法动态为thunk分配内在，并使用PAGE_EXECUTE_READWRITE标志，记得最后使用VirtualFree释放该内在。</p>
<img src ="http://www.cppblog.com/wangjt/aggbug/41939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-01-27 18:13 <a href="http://www.cppblog.com/wangjt/archive/2008/01/27/41939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Type traits简单应用：__uuidof的模拟</title><link>http://www.cppblog.com/wangjt/archive/2008/01/21/41584.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Mon, 21 Jan 2008 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/01/21/41584.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/41584.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/01/21/41584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/41584.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/41584.html</trackback:ping><description><![CDATA[&nbsp; 在VC++中，__uuidof是一个扩展操作符用于取得组件或接口的uuid，在COM编程中经常用到。那么在其它编译器中我们如何实现这个很有用的功能呢？<br>&nbsp; 方法也许不只一种，这介绍用type traits的方式来实现，为了简单起见，并不使用GUID结构来表示uuid以省去GUID的定义和其繁琐的初始化操作，这里用一个字符串来表示一个类的uuid，有点类似于COM编程中的ProgID（Programme ID，编程ID，COM编程中指表示接口或组件的容易记忆的名字，以下uuid都是指这个ID）<br>&nbsp; 首先来定义一个模板类来存储uuid<br>&nbsp; <br>template&lt;typename T&gt;<br>struct _UuidTraits<br>{<br>};<br>&nbsp;<br>这个类没有任何实现，uuid数据将存储于其特化版本，看以下的代码<br>class ClassA;<br>template&lt;&gt;<br>_UuidTraits&lt;ClassA&gt;<br>{<br>&nbsp;&nbsp;&nbsp; static const char * uuid = "ClassA";<br>};<br>class ClassA<br>{<br>//any code of ClassA<br>};<br>&nbsp;<br>现在我们可以通过 _UuidTraits&lt;TheClass&gt;::uuid 来取类ClassA的uuid来，方便使用我们定义一个宏<br>#define __uuidof(x) (_UuidTraits&lt;x&gt;::uuid)<br>&nbsp;<br>但这样只实现了ClassA的uuid，如果为每个类都写一个_UuidTraits的特化版本有点累，但我们可以用宏来简化<br>#define DEFINE_UUID(x, uuidofx) \<br>class x; \<br>template&lt;&gt; \<br>_UuidTraits&lt;x&gt; \<br>{ \<br>static const char * uuid = #uuidofx; \<br>}; \<br>这样我们就可以方便的为类定义uuid了，比如：<br>DEFINE_UUID(ClassB,&nbsp; ClassB) <br>class ClassB<br>{<br>//any code of ClassB<br>};<br>&nbsp;<br>OK，现在试试__uuidof(ClassB)，应该返回字符串"ClassB"了。<br>还有一点要说明的是，为了能兼容UNICODE，可以定义两个版本的DEFINE_UUID：<br>#ifdef UNICODE<br>#define DEFINE_UUID(x, uuidofx) \<br>class x; \<br>template&lt;&gt; \<br>_UuidTraits&lt;x&gt; \<br>{ \<br>static const wchar_t * uuid = L#uuidofx; \<br>}; \<br>#else<br>#define DEFINE_UUID(x, uuidofx) \<br>class x; \<br>template&lt;&gt; \<br>_UuidTraits&lt;x&gt; \<br>{ \<br>static const char * uuid = #uuidofx; \<br>}; \<br>#endif<br>&nbsp;<br>这样在使用UNICODE字符编码编译时，__uuidof返回的就是unicode字符串了 
<img src ="http://www.cppblog.com/wangjt/aggbug/41584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-01-21 20:40 <a href="http://www.cppblog.com/wangjt/archive/2008/01/21/41584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>类型特化 -- Type-traits</title><link>http://www.cppblog.com/wangjt/archive/2008/01/19/41449.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Fri, 18 Jan 2008 17:15:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/01/19/41449.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/41449.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/01/19/41449.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/41449.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/41449.html</trackback:ping><description><![CDATA[<p>　　个人理解这个东西说白了就是当模板类（或函数）的类型参数为某特定值时用对应的特化定义代之。<br>　　看个例子吧<br><br>#include &lt;iostream&gt;<br>using namespace std;</p>
<p>template&lt;typename T&gt;<br>struct is_void<br>{<br>&nbsp;&nbsp;&nbsp;static const bool value = false;<br>};<br>/* 上面的代码定义了一个简单的模板结构is_void的主版本，无论类型参数T是何值，<br>&nbsp;* 结构体的静态常量成员value的值都是false，这当然是无意义的，我们需要当且<br>&nbsp;* 仅当类型参数T为void时，value成员的值为true，于是我们定义下面的特化版本<br>&nbsp;* /<br></p>
<p>template&lt;&gt;<br>struct is_void&lt;void&gt;<br>{<br>&nbsp;&nbsp;&nbsp;static const bool value = true;<br>};<br></p>
<p>/*&nbsp;这里定义了一个is_void结构体的特化版本，将类型参数T固定为void，此时value<br>&nbsp;*&nbsp;成员的值被设定为true<br>&nbsp;* /<br><br>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;&nbsp;cout &lt;&lt; is_void&lt;void&gt;::value &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp;cout &lt;&lt; is_void&lt;int&gt;::value &lt;&lt; endl;<br>&nbsp;&nbsp;&nbsp;cout &lt;&lt; is_void&lt;bool&gt;::value &lt;&lt; endl;</p>
<p>&nbsp;&nbsp;&nbsp;return 0;<br>}<br><br>这段代码输出为：<br>1<br>0<br>0</p>
<p><br>以上代码中，当is_void结构体的模板类型参数T被指定时，编译器将检查该结构体的主版本及所有特化版本，如果类型参数与某一特化版本匹配，则该特化版本将被调用，如果没有匹配的特化版本，则调用主版本。也就是说如果T的实参为void，与我们定义的特化版本的参数一至，则此特化版本的定义被应用，如果T不是void则按主版本的定义展开。<br><br>上面的例子属于全特化（full-specialisation），也就是所有的类型参数都被确定为实际类型，但有时我们可以需要对参数进行部分而不是完全的限定，这便是偏特化（partial template-class specialisation）。<br>看下面的例子<br><br>template&lt;typename T&gt;<br>struct is_pointer<br>{<br>&nbsp;&nbsp;&nbsp;static const bool value = false;<br>};<br>template&lt;typename T&gt;<br>struct is_pointer&lt;T*&gt;<br>{<br>&nbsp;&nbsp;&nbsp;static const bool value = true;<br>};<br><br>这个例子中我们试图判断模板参数T是否是指针类型，如果是则value成员设为true，如果不是则置为false。然而无论T是不是指针都不可能被一一列举，也就是我们不能像第一个例子一样对诸如void*, int*, long*, char* .... 等等等等一一特化，这工作量太吓人了，我要需要一种方式来将指针的情况一次性特化，这就是例子中应用的方式：偏特化。偏特化的写法我想不难理解，但注意两个版本中的T是不一样的，我们可以把特化版本的T换成T2是没有问题的，这时对于is_pointer&lt;int*&gt;来讲，T代表int*，而T2代表的只是int，大家可以揣摩一下。这也就间接说明了主版本和特化版本允许不同个数的模板参数的原因。<br><br>再来一个怪异一点的例子<br>template &lt;typename T&gt; <br>struct remove_bounds<br>{<br>&nbsp;&nbsp;&nbsp;typedef T type; <br>};</p>
<p>template &lt;typename T, size_t N&gt; <br>struct remove_bounds&lt;T[N]&gt;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;typedef T type; <br>};<br>remove_bounds&lt;int&gt;::type a;<br>remove_bounds&lt;int[5]&gt;::type b;<br>remove_bounds&lt;int[5][6]&gt;::type c;<br><br>remove_bounds也就是将一个数组类型的[n]去掉即得到其基础类型，如果是非数组类型则返回原类型。于是上面的例子中，a是个int类型的变量，而b也是int类型，c有点怪，它不是int[5]类型，而是int[6]类型。这个例子也说明一个问题，特化版本与主版本的类型参数不一定一样，但typename修饰的类型参数个数是一致的。<br></p>
<img src ="http://www.cppblog.com/wangjt/aggbug/41449.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-01-19 01:15 <a href="http://www.cppblog.com/wangjt/archive/2008/01/19/41449.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++之旅 -- 来自一位新cppbloger的报到帖</title><link>http://www.cppblog.com/wangjt/archive/2008/01/18/41388.html</link><dc:creator>汪江涛</dc:creator><author>汪江涛</author><pubDate>Thu, 17 Jan 2008 17:10:00 GMT</pubDate><guid>http://www.cppblog.com/wangjt/archive/2008/01/18/41388.html</guid><wfw:comment>http://www.cppblog.com/wangjt/comments/41388.html</wfw:comment><comments>http://www.cppblog.com/wangjt/archive/2008/01/18/41388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wangjt/comments/commentRss/41388.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wangjt/services/trackbacks/41388.html</trackback:ping><description><![CDATA[　　怀着一种崇高而虔诚的向往，终于决定在C++博客上写写文章了。一直以来，这里都是我心中圣洁的殿堂，是全中国乃至全球最专业的C++中文博客网。日后希望在此能与各位博友一同切磋技艺，为中国的C++乃至软件行业尽一份薄力。<br>　　昨天晚上8、9点钟提交的申请，本以为要到今天天亮以后才有结果，不想这么快就批复了，不免有些&#8220;激动&#8221;，深更半夜特写此文权作报到。<br>　　<br>　　C++的经久不衰在于其强大的功能，更在于其优雅的几近完美的语法！<br>
<img src ="http://www.cppblog.com/wangjt/aggbug/41388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wangjt/" target="_blank">汪江涛</a> 2008-01-18 01:10 <a href="http://www.cppblog.com/wangjt/archive/2008/01/18/41388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>