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

<channel>
	<title>Wut I am &#187; c++</title>
	<atom:link href="http://wutiam.net/tag/cpp/feed/" rel="self" type="application/rss+xml" />
	<link>http://wutiam.net</link>
	<description>I&#039;m islet8, I&#039;m what I am</description>
	<lastBuildDate>Wed, 25 Jan 2012 14:11:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>游戏引擎设计 之 内存管理框架</title>
		<link>http://wutiam.net/2011/10/game-engine-design-memory-management-framework/</link>
		<comments>http://wutiam.net/2011/10/game-engine-design-memory-management-framework/#comments</comments>
		<pubDate>Mon, 17 Oct 2011 12:46:31 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[游戏开发]]></category>
		<category><![CDATA[游戏引擎]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=14</guid>
		<description><![CDATA[内存管理对于大型游戏来说是至关重要的一环，这里我们所说的“内存”指的是动态分配的内存。一个好的内存管理框架，能显著提升程序执行效率，也能大大提高内存问题的调试效率。所以，我们设计内存管理框架时必须能够满足以下两大需求： 自定义内存分配器 malloc/free（包括间接由new/delete等调过来的）等函数本身的开销其实并不大，但由于其功能太过基础了，没有任何策略可言，从而导致反复分配释放带来的开销、大量内存碎片以及多线程内存分配引起的效率问题。所以自定义一个高效的内存分配器就显得必不可少了，对于一般情况，我们可以使用 nedmalloc、jemalloc 等第三方多线程内存分配器，也可以根据具体需求自己实现一套（例如 Nebula3 中的内存池以及 Heap 对象机制），甚至是基于栈的动态内存分配。而具体的内存分配/释放策略是另一个话题了，以后新开一篇讨论。 内存统计及内存泄漏跟踪 虽然 CRT 有 dump 内存泄漏的功能，但最大的问题就在于——仅限于 DEBUG 环境下，而游戏由于其实时计算的复杂性，在后期很多情况下开 DEBUG 模式已经无法满足正常调试的需求了，所以 Release with debug info 模式其实才是我们更常用的，这样我们就必须自己实现内存统计及泄漏跟踪的功能，甚至提供对动态内存泄漏（运行时大量已无用的内存未释放，直到游戏退出时才一并释放，最常见的就是由智能指针引起的动态内存泄漏）的检查。 先说说第一点，我们需要把 malloc/free 和 new/delete 的分配/释放部分重定向到我们自己的分配/释放函数（handler）里，然后在我们的函数里调用指定的内存分配器的分配/释放函数（allocator），这就完成了。 第二点，正好可以利用第一点定义出来的我们自己的分配/释放函数，除了调用分配器之外，再通知内存分配统计模块更新统计信息（记录新分配的内存的相关信息或删掉相关的信息），这样内存分配统计模块就完成了统计和泄漏报告两大功能了。 来看看具体的伪码吧： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
<li><a href='http://wutiam.net/2006/08/const-int-ptr/' rel='bookmark' title='(const int * i || int const * i) &amp;&amp; int * const i'>(const int * i || int const * i) &#038;& int * const i</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>内存管理对于大型游戏来说是至关重要的一环，这里我们所说的“内存”指的是<strong>动态分配</strong>的内存。一个好的内存管理框架，能显著提升程序执行效率，也能大大提高内存问题的调试效率。所以，我们设计内存管理框架时必须能够满足以下两大需求：</p>
<ol>
<li>自定义内存分配器</li>
<p>malloc/free（包括间接由new/delete等调过来的）等函数本身的开销其实并不大，但由于其功能太过基础了，没有任何策略可言，从而导致反复分配释放带来的开销、大量内存碎片以及多线程内存分配引起的效率问题。所以自定义一个高效的内存分配器就显得必不可少了，对于一般情况，我们可以使用 <a href="http://www.nedprod.com/programs/portable/nedmalloc/" target="_blank">nedmalloc</a>、<a href="http://www.canonware.com/jemalloc/" target="_blank">jemalloc</a> 等第三方多线程内存分配器，也可以根据具体需求自己实现一套（例如 <a href="http://code.google.com/p/nebula3/" target="_blank">Nebula3</a> 中的内存池以及 Heap 对象机制），甚至是<a title="Stack-based memory allocation" href="http://en.wikipedia.org/wiki/Stack-based_memory_allocation" target="_blank">基于栈的动态内存分配</a>。而具体的内存分配/释放策略是另一个话题了，以后新开一篇讨论。</p>
<li>内存统计及内存泄漏跟踪</li>
<p>虽然 CRT 有 dump 内存泄漏的功能，但最大的问题就在于——仅限于 DEBUG 环境下，而游戏由于其实时计算的复杂性，在后期很多情况下开 DEBUG 模式已经无法满足正常调试的需求了，所以 Release with debug info 模式其实才是我们更常用的，这样我们就必须自己实现内存统计及泄漏跟踪的功能，甚至提供对动态内存泄漏（运行时大量已无用的内存未释放，直到游戏退出时才一并释放，最常见的就是由智能指针引起的动态内存泄漏）的检查。
</ol>
<p><span id="more-14"></span>先说说第一点，我们需要把 malloc/free 和 new/delete 的分配/释放部分重定向到我们自己的分配/释放函数（handler）里，然后在我们的函数里调用指定的内存分配器的分配/释放函数（allocator），这就完成了。</p>
<p>第二点，正好可以利用第一点定义出来的我们自己的分配/释放函数，除了调用分配器之外，再通知内存分配统计模块更新统计信息（记录新分配的内存的相关信息或删掉相关的信息），这样内存分配统计模块就完成了统计和泄漏报告两大功能了。</p>
<p>来看看具体的伪码吧：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #666666;">// Handle allocation/deallocation</span>
<span style="color: #0000ff;">class</span> AllocationHandler
<span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
<span style="color: #339900;">#ifdef MEMORY_STATS</span>
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> Allocate<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> size, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> fileName, <span style="color: #0000ff;">int</span> lineNum, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> funcName<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        AllocStats<span style="color: #008080;">::</span><span style="color: #007788;">Alloc</span><span style="color: #008000;">&#40;</span>size, fileName, lineNum, funcName<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">return</span> Allocator<span style="color: #008080;">::</span><span style="color: #007788;">Allocate</span><span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#else</span>
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> Allocate<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> size<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> Allocator<span style="color: #008080;">::</span><span style="color: #007788;">Allocate</span><span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#endif // MEMORY_STATS</span>
&nbsp;
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Deallocate<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        Allocator<span style="color: #008080;">::</span><span style="color: #007788;">Deallocate</span><span style="color: #008000;">&#40;</span>ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">private</span><span style="color: #008080;">:</span>
    <span style="color: #666666;">// re-define to your own allocator</span>
    <span style="color: #0000ff;">typedef</span> DefaultAllocator Allocator<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #666666;">// Allocator using default CRT malloc</span>
<span style="color: #0000ff;">class</span> MM_DefaultAllocator
<span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> Allocate<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> size<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">malloc</span><span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> Deallocate<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000dd;">free</span><span style="color: #008000;">&#40;</span>ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span></pre></td></tr></table></div>

<p>基本原理非常简单，但“现实总是残酷的”！为了方便的传入调试信息（__FILE__、__LINE__、 __FUNCTION__ 等），我们使用宏来定义我们自己的分配函数：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#ifdef MEMORY_STATS</span>
<span style="color: #339900;">#define MALLOC(size)        AllocationHandler::Allocate(size, __FILE__, __LINE__, __FUNCTION__)</span>
<span style="color: #339900;">#define ALLOC(T, count)     static_cast&lt;T*&gt;(AllocationHandler::Allocate(sizeof(T)*(count), __FILE__, __LINE__, __FUNCTION__))</span>
<span style="color: #339900;">#define T_FREE(ptr)         AllocationHandler::Deallocate(ptr)</span>
<span style="color: #339900;">#else</span>
<span style="color: #339900;">#define MALLOC(size)        AllocationHandler::Allocate(size)</span>
<span style="color: #339900;">#define ALLOC(T, count)     (T*)AllocationHandler::Allocate(sizeof(T)*(count))</span>
<span style="color: #339900;">#define FREE(ptr)           AllocationHandler::Deallocate(ptr)</span>
<span style="color: #339900;">#endif // MEMORY_STATS</span></pre></td></tr></table></div>

<p>很好，可是 new/delete 呢？我也很想一起把它们定义出来，就像这样：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #ff0000; font-style: italic;">/*
#define GE_NEW(type)                new(__FILE__, __LINE__, __FUNCTION__) type
#define GE_DELETE(ptr)              delete ptr
#define GE_NEW_ARRAY(type, count)   new(__FILE__, __LINE__, __FUNCTION__) type[count]
#define GE_DELETE_ARRAY(ptr)        delete[] ptr
&nbsp;
inline void* operator new(size_t size, const char* fileName, int lineNum, const char* funcName)
{
    return AllocationHandler::Allocate(size, fileName, lineNum, funcName);
}
inline void operator delete(void* ptr)
{
    return AllocationHandler::Deallocate(ptr);
}
// only called if there is an exception in corresponding 'new'
inline void operator delete(void* ptr, const char*, int, const char*)
{
    return AllocationHandler::Deallocate(ptr);
}
inline void* operator new[](size_t size, const char* fileName, int lineNum, const char* funcName)
{
    return AllocationHandler::Allocate(size, fileName, lineNum, funcName);
}
inline void operator delete[](void* ptr)
{
    return AllocationHandler::Deallocate(ptr);
}
// only called if there is an exception in corresponding 'new'
inline void operator delete[](void* ptr, const char*, int, const char*)
{
    return AllocationHandler::Deallocate(ptr);
}
*/</span></pre></td></tr></table></div>

<p>通过重定义全局的 new/delete operators 来重定向，看起来很统一、很完美。可是，除了程序自己的所有代码的 new/delete 被重定向了以外，连第三方库里的所有 new/delete 操作也都被重定向到我们自己的分配器来了（这里所说的第三方库指的是以源码方式发布并合入我们的工程的），而这些第三方库里的内存分配被我们接管后，可能会导致一些意想不到的异常结果。此路不通。</p>
<p>既然不能替换全局的 new/delete 操作符，但至少可以替换掉一部分 class 的 new/delete 操作符吧，即，凡是从 MemoryObject 继承的子类，将其 new/delete 都接管到我们的分配器上（这也是 <a href="http://www.ogre3d.org/" target="_blank">ORGE</a> 和 <a href="http://www.gamebryo.com/" target="_blank">Gamebryo</a> 所采用的方法）：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#ifdef MEMORY_STATS</span>
<span style="color: #339900;">#define NEW(T)          new(__FILE__, __LINE__, __FUNCTION__) T</span>
<span style="color: #339900;">#else</span>
<span style="color: #339900;">#define NEW(T)          new T</span>
<span style="color: #339900;">#endif // MEMORY_STATS</span>
<span style="color: #339900;">#define T_DELETE(ptr)   delete ptr</span>
&nbsp;
<span style="color: #666666;">// Override MemoryObject-derived classes' new/delete operators to redirect to AllocationHandler</span>
<span style="color: #339900;">#ifdef MEMORY_STATS</span>
<span style="color: #0000ff;">class</span> MemoryObject
<span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
    <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> operator <span style="color: #0000dd;">new</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">size_t</span> size, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> fileName, <span style="color: #0000ff;">int</span> lineNum, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> funcName<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">void</span> operator <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #666666;">// only called if there is an exception in corresponding 'new'</span>
    <span style="color: #0000ff;">void</span> operator <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span>, <span style="color: #0000ff;">int</span>, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> MemoryObject<span style="color: #008080;">::</span><span style="color: #007788;">operator</span> <span style="color: #0000dd;">new</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">size_t</span> size, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> fileName, <span style="color: #0000ff;">int</span> lineNum, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span> funcName<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> AllocationHandler<span style="color: #008080;">::</span><span style="color: #007788;">Allocate</span><span style="color: #008000;">&#40;</span>size, fileName, lineNum, funcName<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> MemoryObject<span style="color: #008080;">::</span><span style="color: #007788;">operator</span> <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    AllocationHandler<span style="color: #008080;">::</span><span style="color: #007788;">Deallocate</span><span style="color: #008000;">&#40;</span>ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> MemoryObject<span style="color: #008080;">::</span><span style="color: #007788;">operator</span> <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span>, <span style="color: #0000ff;">int</span>, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span><span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    AllocationHandler<span style="color: #008080;">::</span><span style="color: #007788;">Deallocate</span><span style="color: #008000;">&#40;</span>ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#else</span>
<span style="color: #0000ff;">class</span> MemoryObject
<span style="color: #008000;">&#123;</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
    <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> operator <span style="color: #0000dd;">new</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">size_t</span> size<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">void</span> operator <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> MemoryObject<span style="color: #008080;">::</span><span style="color: #007788;">operator</span> <span style="color: #0000dd;">new</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">size_t</span> size<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> AllocationHandler<span style="color: #008080;">::</span><span style="color: #007788;">Allocate</span><span style="color: #008000;">&#40;</span>size<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">void</span> MemoryObject<span style="color: #008080;">::</span><span style="color: #007788;">operator</span> <span style="color: #0000dd;">delete</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> ptr<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    AllocationHandler<span style="color: #008080;">::</span><span style="color: #007788;">Deallocate</span><span style="color: #008000;">&#40;</span>ptr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#endif // MEMORY_STATS</span></pre></td></tr></table></div>

<p>然而，毕竟还有那么多 <a href="http://en.wikipedia.org/wiki/Plain_old_data_structure" target="_blank">POD</a> 类型、自定义的简单结构体的数据，都用 MemoryObject 包起来既不现实又没效率，那只能为它们再单独制定一套 new/delete 策略了，类似 OGRE 的实现：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#ifdef MEMORY_STATS</span>
<span style="color: #339900;">#define EXTERNAL_NEW(T)     new(AllocationHandler::Allocate(sizeof(T), __FILE__, __LINE__, __FUNCTION__)) T</span>
<span style="color: #339900;">#else</span>
<span style="color: #339900;">#define EXTERNAL_NEW(T)     new(AllocationHandler::Allocate(sizeof(T))) T</span>
<span style="color: #339900;">#endif // MEMORY_STATS</span>
<span style="color: #339900;">#define EXTERNAL_DELETE(ptr, T) if (ptr != NULL_PTR) { (ptr)-&gt;~T(); AllocationHandler::Deallocate(ptr); }</span></pre></td></tr></table></div>

<p>但这里我们去掉了对数组的支持，原因是一方面自定义数组的 new/delete 处理代码比较丑陋，另一方面也不支持多维数组的创建，而且我们对引擎设计的其中一条底线就是禁止上层应用直接使用原生数组（越界引起的各种崩溃，太让人崩溃了），要玩数组，用我们自己封的数组对象吧。顺便说一句，说实话我个人觉得 OGRE 的内存分配 category 和 policy 有过度设计的嫌疑，而且其分类和策略的设计并不能为多分配器提供支持，所以我直接让分配请求和分配器对接了。未来如果需要对不同的对象采用不同的分配器，可以考虑 <a href="http://heliumproject.org/" target="_blank">Helium</a> 的设计思路，通过模板将分配器直接传入类定义。</p>
<p>好了，内存管理的框架设计就是这样，看起来很简单，但为什么设计成了这样，也许只有等你也走过一遍才会有深刻的理解，也许你的需求，可以产生更优秀的框架。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
<li><a href='http://wutiam.net/2006/08/const-int-ptr/' rel='bookmark' title='(const int * i || int const * i) &amp;&amp; int * const i'>(const int * i || int const * i) &#038;& int * const i</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2011/10/game-engine-design-memory-management-framework/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>代码之初，性本丑？(下)</title>
		<link>http://wutiam.net/2011/08/ugly-code-at-birth-part3/</link>
		<comments>http://wutiam.net/2011/08/ugly-code-at-birth-part3/#comments</comments>
		<pubDate>Sat, 27 Aug 2011 03:49:55 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=154</guid>
		<description><![CDATA[《代码之丑（六）——分家的声明和使用》 变量声明的就近原则，除了该文中提到的几点好处之外，还有一点就是能够提高发现声明了却未使用的变量的概率。别小看这个问题，以为编译器会给优化掉，我就碰到过一坨很烂的代码，这个函数里做了大量的计算，比如先经过复杂计算得到 a，然后 a 经过计算得到 b，b 再经过计算得到 c，c 再计算后得到 d，在一团乱麻的代码里，谁也没发现，其实 d 根本没被用到，函数最后直接拿 e 返回了…… 《代码之丑（七）——你的语言》 这篇有点各抒己见的意思，不过我觉得有一点是明确的，学一门语言，主要是学习它的思维方式，而不仅仅学会语法就算完了。至于具体怎么用，这是每个 team 自己定编码规范的事了。 《代码之丑（八）——不一致的困惑》 这个例子都老的快成寓言故事了，不过就算如此，我们还是有同学把四种字符串类型随心所欲地混用的代码，其中一种是 STL 的、一种是 MFC 的、一种是第三方引擎库的、一种是他不知道从哪个网页里抄来的。原因也正如该文里提到的，每种字符串类型都有他想用的功能，但有时又缺点什么。于是乎 ，呵呵，debug 这样的代码是痛苦的，大量的类型转换，大量的字符串内存分配，仅仅是为了满足他不同函数间定义的形参或返回值类型的不同。 我觉得，不仅仅是软件设计要尽可能的保证一致性和唯一性（就像《人月神话》中所说的外科手术团队模式，也是我非常赞同的），编码等具体事物也需要有一致性，这不仅仅是效率和可维护性的问题，而是一个人、一个团队的态度问题，一个“大心脏”的程序员（见第五篇），他的代码能给谁带来安全感？ 《代码之丑（九）——退让的缩进》 缩进是个永恒的话题。两个空格、四个空格、一个 tab，{} 跟上一层对齐还是跟下一层对齐，其实这些都不重要，重要的是明白缩进是为了什么？可读性！当然是可读性！就算 Python 的缩进含有控制的功能，那也是因为看明白了可读性的重要性。 从理论上讲，一个函数不能太长，一个条件也不能太长，一个循环更不能太长，所以缩进也不能有太多层，谁不希望是这样呢？大部分的冗长代码确实可以通过改变设计来拆分甚至是消除丑陋，可是现实总是残酷的（比纸上谈兵残酷得多），有些情况从逻辑上讲已经是原子操作的了，但从代码角度却依然有大量跳转计算和前后依赖，这就存在一个如何折衷的问题了。把连续依赖的 10 小段代码分别挪到 10 个独立的函数中去？想想假如在看书的时候全文都是“参见 XX 页 xx 段”的情况，这样的书有人爱看么？ 所以，缩进不是可读性的唯一，连贯的思路也一样非常重要。 《代码之丑（十）——条件编译那些事儿​》 该文基本都说得差不多了，这也是这几年比较流行的改进方法，最近看了几个引擎的代码，确实舒服多了。但有一点要注意，是否拆分到文件，还是得看具体的功能是否强依赖于平台，如果依赖度较低，虽然拆到独立文件后可读性很高了，但维护性却大大降低了。为什么？DRY —— Don't Repeat Yourself！ 后话 说得够多了，类似《代码大全》上的观点本来没必要那么多人反复的说。但是，就算已经有那么多人反复的说过了，工作中大部分人还是坚定地认为（至少潜意识里认为），“程序么，能跑就好了，你看，功能好得很呢”。嗨！真的是大部分人呢。所以只好再多说一次，多说一次，总可能会多影响一个人，也是好事。 费那么大劲说，费那么大劲做，图什么，无非是想要明天的日子好过一些。说来说去，还是一句老话：性格决定命运，气度决定格局，细节决定成败，态度决定一切。 Related posts: 代码之初，性本丑？(中) [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2011/01/ugly-code-at-birth-part2/' rel='bookmark' title='代码之初，性本丑？(中)'>代码之初，性本丑？(中)</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-6" target="_blank">代码之丑（六）——分家的声明和使用</a>》</strong></p>
<p>变量声明的就近原则，除了该文中提到的几点好处之外，还有一点就是能够提高发现声明了却未使用的变量的概率。别小看这个问题，以为编译器会给优化掉，我就碰到过一坨很烂的代码，这个函数里做了大量的计算，比如先经过复杂计算得到 a，然后 a 经过计算得到 b，b 再经过计算得到 c，c 再计算后得到 d，在一团乱麻的代码里，谁也没发现，其实 d 根本没被用到，函数最后直接拿 e 返回了……</p>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/12/ugly-code-7" target="_blank">代码之丑（七）——你的语言</a>》</strong></p>
<p>这篇有点各抒己见的意思，不过我觉得有一点是明确的，学一门语言，主要是学习它的思维方式，而不仅仅学会语法就算完了。至于具体怎么用，这是每个 team 自己定编码规范的事了。</p>
<p><span id="more-154"></span><strong>《<a href="http://www.infoq.com/cn/news/2010/12/ugly-code-8" target="_blank">代码之丑（八）——不一致的困惑</a>》</strong></p>
<p>这个例子都老的快成寓言故事了，不过就算如此，我们还是有同学把四种字符串类型随心所欲地混用的代码，其中一种是 STL 的、一种是 MFC 的、一种是第三方引擎库的、一种是他不知道从哪个网页里抄来的。原因也正如该文里提到的，每种字符串类型都有他想用的功能，但有时又缺点什么。于是乎 ，呵呵，debug 这样的代码是痛苦的，大量的类型转换，大量的字符串内存分配，仅仅是为了满足他不同函数间定义的形参或返回值类型的不同。</p>
<p>我觉得，不仅仅是软件设计要尽可能的保证一致性和唯一性（就像《人月神话》中所说的外科手术团队模式，也是我非常赞同的），编码等具体事物也需要有一致性，这不仅仅是效率和可维护性的问题，而是一个人、一个团队的态度问题，一个“大心脏”的程序员（见第五篇），他的代码能给谁带来安全感？</p>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/12/ugly-code-9" target="_blank">代码之丑（九）——退让的缩进</a>》</strong></p>
<p>缩进是个永恒的话题。两个空格、四个空格、一个 tab，{} 跟上一层对齐还是跟下一层对齐，其实这些都不重要，重要的是明白缩进是为了什么？可读性！当然是可读性！就算 Python 的缩进含有控制的功能，那也是因为看明白了可读性的重要性。</p>
<p>从理论上讲，一个函数不能太长，一个条件也不能太长，一个循环更不能太长，所以缩进也不能有太多层，谁不希望是这样呢？大部分的冗长代码确实可以通过改变设计来拆分甚至是消除丑陋，可是现实总是残酷的（比纸上谈兵残酷得多），有些情况从逻辑上讲已经是原子操作的了，但从代码角度却依然有大量跳转计算和前后依赖，这就存在一个如何折衷的问题了。把连续依赖的 10 小段代码分别挪到 10 个独立的函数中去？想想假如在看书的时候全文都是“参见 XX 页 xx 段”的情况，这样的书有人爱看么？</p>
<p>所以，缩进不是可读性的唯一，连贯的思路也一样非常重要。</p>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/12/ugly-code-10" target="_blank">代码之丑（十）——条件编译那些事儿​</a>》</strong></p>
<p>该文基本都说得差不多了，这也是这几年比较流行的改进方法，最近看了几个引擎的代码，确实舒服多了。但有一点要注意，是否拆分到文件，还是得看具体的功能是否强依赖于平台，如果依赖度较低，虽然拆到独立文件后可读性很高了，但维护性却大大降低了。为什么？DRY —— Don't Repeat Yourself！</p>
<p><strong>后话</strong></p>
<p>说得够多了，类似《代码大全》上的观点本来没必要那么多人反复的说。但是，就算已经有那么多人反复的说过了，工作中大部分人还是坚定地认为（至少潜意识里认为），“程序么，能跑就好了，你看，功能好得很呢”。嗨！真的是大部分人呢。所以只好再多说一次，多说一次，总可能会多影响一个人，也是好事。</p>
<p>费那么大劲说，费那么大劲做，图什么，无非是想要明天的日子好过一些。说来说去，还是一句老话：性格决定命运，气度决定格局，细节决定成败，<strong>态度决定一切</strong>。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2011/01/ugly-code-at-birth-part2/' rel='bookmark' title='代码之初，性本丑？(中)'>代码之初，性本丑？(中)</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2011/08/ugly-code-at-birth-part3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>代码之初，性本丑？(中)</title>
		<link>http://wutiam.net/2011/01/ugly-code-at-birth-part2/</link>
		<comments>http://wutiam.net/2011/01/ugly-code-at-birth-part2/#comments</comments>
		<pubDate>Sat, 08 Jan 2011 08:58:32 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=144</guid>
		<description><![CDATA[《代码之丑（三）——switch陷阱》 可能是因为篇幅关系，这篇文章里的例子不够有代表性，而且还有越改越没有可读性和可维护性之嫌。曾经我在公司的一个比较有历史的程序里看到过一段伟岸的 switch/case 代码，每个 case 下的代码各不相同且长短不一也就算了，可能你无法想象的是，它里面竟然有几千个 case，总共 10000+ 行的代码！你没看错，一个函数一万多行！这就是从一开始放任 switch 陷阱，经过漫长地演化得到的后果。 其实我并不认为该把 switch/case 当作唯恐避之不及的恶魔，它只是一把双刃剑，就像没必要一味地反对使用 if 一样。在正确的环境下正确使用它的话，只会提高可读性，但如果滥用它（就像 C++ 很多其他特性那样），那的确是一场噩梦。 对于本身结构很简单的逻辑，使用 switch/case 能很好地保持其可读性（显然比一堆的 if/else 好，更不用说多态了），而且也正因为其逻辑简单，条件不多，也不会对系统性能产生明显影响（代码并非越短越快就越好，它首先是给人看的，顺便附带了能让机器执行的功能）。而对于我上面提到的超大的逻辑结构，倒是可以用多态或者状态机来解决，定位问题显然会方便不少（假如这个逻辑架构本身没法改变的话）。 《代码之丑（四）——代码找茬游戏》 “代码重复是 bug 之母”，没错，这就是传说中的 DRY 原则（Don't Repeat Yourself）。可悲的是，现实开发中，重复的代码比比皆是，无论是大拿还是小菜鸟，因为这是人的本性，在拷贝代码的时候说服自己，写完功能就把这儿重构掉，然而一旦功能完成，就不想再动这块代码了，否则做完了重构还得重新测一遍，太麻烦了。三个月后，因为业务逻辑的改动或者 bugs，另一位同事只改了其中一个代码的拷贝（他可不知道你的这些小秘密，甚至其实你自己也已经不记得了），完蛋了，又要花上一整天的时间来查到底出了什么事。可有几个人能坚持为以后的自己负责呢？“说不定再也不会暴露出来，先管它呢”。 唉。 人会犯错，想偷懒，本来都不可怕。对于大多数程序员，引入交叉 code review 机制，真是一个不错的选择，毕竟总是做得比别人差是很难堪的。但对于少数缺乏羞耻心的人来说，真没什么好办法，如果我是老板，别让我的团队被一颗老鼠屎糟蹋了。 《代码之丑（五）——不受欢迎的大心脏》 先局限在该文的例子说说，这个代码的问题得根据其真正的用意来定。如果它真的是为了克隆 oldRule 的所有属性并加入到规则池中去，那更好的做法应该是为 ColdRule 类提供一个拷贝构造函数： rules-&#62;Add&#40;new ColdRule&#40;oldRule&#41;&#41;; 能封装到自己内部的操作为什么要让外面了解这么多细节呢。但如果它的目的只是把 oldRule 中的一部分属性设给新的 rule，然后“可能”还要做些额外的设置（或者只是保留构造函数中的初始化设置），那就又得斟酌有没有必要提出一个公共函数来封装这份操作，以及这个函数是放在类内呢还是放到一个公共文件里了。 这就是我想说的，写代码很大条的确不是个受欢迎的性格，但有些代码的业务逻辑实在太繁琐了，它就是需要几十个步骤来完成，每个步骤还会有若干个分支，而这本身又只是一个很小的功能点（比如读取 DDS 图片文件头），为了代码“优雅”所付出的成本远高于其价值，那这样的重构是否还有必要呢？我个人认为很没有必要，倒不如花点小力气，写些注释，加些换行，把这块又长又复杂的代码弄得阅读起来顺溜些，总比把各个小秘密藏得到处都是好多了。 Related posts: 代码之初，性本丑？(下) [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2011/08/ugly-code-at-birth-part3/' rel='bookmark' title='代码之初，性本丑？(下)'>代码之初，性本丑？(下)</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-3">代码之丑（三）——switch陷阱</a>》</strong></p>
<p>可能是因为篇幅关系，这篇文章里的例子不够有代表性，而且还有越改越没有可读性和可维护性之嫌。曾经我在公司的一个比较有历史的程序里看到过一段伟岸的 switch/case 代码，每个 case 下的代码各不相同且长短不一也就算了，可能你无法想象的是，它里面竟然有几千个 case，总共 10000+ 行的代码！你没看错，一个函数<strong>一万多</strong>行！这就是从一开始放任 switch 陷阱，经过漫长地演化得到的后果。</p>
<p>其实我并不认为该把 switch/case 当作唯恐避之不及的恶魔，它只是一把双刃剑，就像没必要一味地<a title="The Anti-IF Campaign" href="http://www.antiifcampaign.com/">反对使用 if</a> 一样。在正确的环境下正确使用它的话，只会提高可读性，但如果滥用它（就像 C++ 很多其他特性那样），那的确是一场噩梦。</p>
<p>对于本身结构很简单的逻辑，使用 switch/case 能很好地保持其可读性（显然比一堆的 if/else 好，更不用说多态了），而且也正因为其逻辑简单，条件不多，也不会对系统性能产生明显影响（代码并非越短越快就越好，它首先是给人看的，顺便附带了能让机器执行的功能）。而对于我上面提到的超大的逻辑结构，倒是可以用多态或者状态机来解决，定位问题显然会方便不少（假如这个逻辑架构本身没法改变的话）。</p>
<p><span id="more-144"></span><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-4">代码之丑（四）——代码找茬游戏</a>》</strong></p>
<p>“代码重复是 bug 之母”，没错，这就是传说中的 <a title="Don't repeat yourself" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> 原则（Don't Repeat Yourself）。可悲的是，现实开发中，重复的代码比比皆是，无论是大拿还是小菜鸟，因为这是人的本性，在拷贝代码的时候说服自己，写完功能就把这儿重构掉，然而一旦功能完成，就不想再动这块代码了，否则做完了重构还得重新测一遍，太麻烦了。三个月后，因为业务逻辑的改动或者 bugs，另一位同事只改了其中一个代码的拷贝（他可不知道你的这些小秘密，甚至其实你自己也已经不记得了），完蛋了，又要花上一整天的时间来查到底出了什么事。可有几个人能坚持为以后的自己负责呢？“说不定再也不会暴露出来，先管它呢”。</p>
<p>唉。</p>
<p>人会犯错，想偷懒，本来都不可怕。对于大多数程序员，引入交叉 code review 机制，真是一个不错的选择，毕竟总是做得比别人差是很难堪的。但对于少数缺乏羞耻心的人来说，真没什么好办法，如果我是老板，别让我的团队被一颗老鼠屎糟蹋了。</p>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-5">代码之丑（五）——不受欢迎的大心脏</a>》</strong></p>
<p>先局限在该文的例子说说，这个代码的问题得根据其真正的用意来定。如果它真的是为了克隆 oldRule 的所有属性并加入到规则池中去，那更好的做法应该是为 ColdRule 类提供一个拷贝构造函数：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">rules<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>Add<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">new</span> ColdRule<span style="color: #008000;">&#40;</span>oldRule<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span></pre></div></div>

<p>能封装到自己内部的操作为什么要让外面了解这么多细节呢。但如果它的目的只是把 oldRule 中的一部分属性设给新的 rule，然后“可能”还要做些额外的设置（或者只是保留构造函数中的初始化设置），那就又得斟酌有没有必要提出一个公共函数来封装这份操作，以及这个函数是放在类内呢还是放到一个公共文件里了。</p>
<p>这就是我想说的，写代码很大条的确不是个受欢迎的性格，但有些代码的业务逻辑实在太繁琐了，它就是需要几十个步骤来完成，每个步骤还会有若干个分支，而这本身又只是一个很小的功能点（比如读取 DDS 图片文件头），为了代码“优雅”所付出的成本远高于其价值，那这样的重构是否还有必要呢？我个人认为很没有必要，倒不如花点小力气，写些注释，加些换行，把这块又长又复杂的代码弄得阅读起来顺溜些，总比把各个小秘密藏得到处都是好多了。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2011/08/ugly-code-at-birth-part3/' rel='bookmark' title='代码之初，性本丑？(下)'>代码之初，性本丑？(下)</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2011/01/ugly-code-at-birth-part2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>代码之初，性本丑？(上)</title>
		<link>http://wutiam.net/2010/12/ugly-code-at-birth-part1/</link>
		<comments>http://wutiam.net/2010/12/ugly-code-at-birth-part1/#comments</comments>
		<pubDate>Fri, 17 Dec 2010 00:58:39 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=143</guid>
		<description><![CDATA[我是一个很在意代码质量的人，因为我相信软件开发，细节决定成败（追求细节和拥有宏观视野本身并不矛盾，而且这是另一个话题了）。虽然我并不赞成将团队甚至公司中的每个人的编码风格都硬性规范到某种统一格式，那样不仅会扼杀程序员们的创造力，而且从成本收益来看太不划算了。但是，这并不意味着代码就可以随心所欲地写，所谓“规范”不该是“法律”上的，但一定是“道德”上的：你现在写的代码，要对半年后的你负责，更要对团队里其他同事们负责，甚至可能还有你的客户。而这种责任，不仅仅是代码的功能完整度，更包括可读、健壮和效率。 这几个月有幸参与了我们游戏提升稳定性和优化性能的工作，review了很多代码，这期间时长被各种“富有创造性的”代码雷得外焦里嫩的。碰巧正好看到了《代码之丑》系列文章，感触颇深，有些观点也有些自己的看法，记录下来，与大家探讨。 《代码之丑——开篇》 功能完整的代码，仅仅指的是“能按流程跑起来、不会崩、可以得到正确结果”的代码，这其中的代码质量依然可谓千差万别。在大多数情况下，这些代码可以定义其美丑。不过该文中举的例子可能不够典型，不够丑陋，结果被大家揪住了小尾巴。的确，两种写法都有各自大量的拥趸，也都有各自的理由：比如用 if/else 分别 return 的同学更在意其可 debug 性，而且如果今后要在 if 或 else 的 {} 里加入更多的处理（不仅仅是 return）也很方便；而采用表达式直接 return 的同学更喜欢代码的简洁性。这种时候，你很难让人信服地把这类代码归为丑陋，这只是个人口味不同。 但另一种类似的情况就截然不同了，比如这个函数： inline bool Foo&#40;void&#41; &#123; // 整个函数代码很少，几乎没什么额外功能 ... &#160; if&#40;db.HasNext&#40;&#41;&#41; &#123; return true; &#125; else &#123; return false; &#125; 甚至判断条件简单到只有“db != NULL”之类的语义本身就是纯条件判断的，那这时傻乎乎的用 if/else 就可能贻笑大方了，有人会写出 if (true) return true; else return false; 的代码么？ 《代码之丑（一）——让判断条件做真正的选择》 这篇中提到的问题其实非常常见，因为正常人一般都是先写完了某个函数的调用，然后改吧改吧才发现在不同情况下需要使用不同的参数调这个函数，或者当时写代码时只是想验证这条路是否可行，没太在意避免重复的代码，验证通过后又懒的再去修改已经稳定了的代码。该文中的例子算是客气的，我们有不少代码是在多层 if/else 的每个 [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2011/08/ugly-code-at-birth-part3/' rel='bookmark' title='代码之初，性本丑？(下)'>代码之初，性本丑？(下)</a></li>
<li><a href='http://wutiam.net/2011/01/ugly-code-at-birth-part2/' rel='bookmark' title='代码之初，性本丑？(中)'>代码之初，性本丑？(中)</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>我是一个很在意代码质量的人，因为我相信软件开发，细节决定成败（追求细节和拥有宏观视野本身并不矛盾，而且这是另一个话题了）。虽然我并不赞成将团队甚至公司中的每个人的编码风格都硬性规范到某种统一格式，那样不仅会扼杀程序员们的创造力，而且从成本收益来看太不划算了。但是，这并不意味着代码就可以随心所欲地写，所谓“规范”不该是“法律”上的，但一定是“道德”上的：你现在写的代码，要对半年后的你负责，更要对团队里其他同事们负责，甚至可能还有你的客户。而这种责任，不仅仅是代码的<strong>功能完整度</strong>，更包括<strong>可读</strong>、<strong>健壮</strong>和<strong>效率</strong>。</p>
<p>这几个月有幸参与了我们游戏提升稳定性和优化性能的工作，review了很多代码，这期间时长被各种“富有创造性的”代码雷得外焦里嫩的。碰巧正好看到了《<a href="http://www.infoq.com/cn/ugly-code">代码之丑</a>》系列文章，感触颇深，有些观点也有些自己的看法，记录下来，与大家探讨。</p>
<p><span id="more-143"></span><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-0">代码之丑——开篇</a>》</strong></p>
<p>功能完整的代码，仅仅指的是“能按流程跑起来、不会崩、可以得到正确结果”的代码，这其中的代码质量依然可谓千差万别。在大多数情况下，这些代码可以定义其美丑。不过该文中举的例子可能不够典型，不够丑陋，结果被大家揪住了小尾巴。的确，两种写法都有各自大量的拥趸，也都有各自的理由：比如用 if/else 分别 return 的同学更在意其可 debug 性，而且如果今后要在 if 或 else 的 {} 里加入更多的处理（不仅仅是 return）也很方便；而采用表达式直接 return 的同学更喜欢代码的简洁性。这种时候，你很难让人信服地把这类代码归为丑陋，这只是个人口味不同。</p>
<p>但另一种类似的情况就截然不同了，比如这个函数：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">inline</span> <span style="color: #0000ff;">bool</span> Foo<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">void</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #666666;">// 整个函数代码很少，几乎没什么额外功能</span>
    ...
&nbsp;
    <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>db.<span style="color: #007788;">HasNext</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #008000;">&#123;</span>
       <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></div></div>

<p>甚至判断条件简单到只有“db != NULL”之类的<strong>语义本身就是纯条件判断</strong>的，那这时傻乎乎的用 if/else 就可能贻笑大方了，有人会写出 if (true) return true; else return false; 的代码么？</p>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-1">代码之丑（一）——让判断条件做真正的选择</a>》</strong></p>
<p>这篇中提到的问题其实非常常见，因为正常人一般都是先写完了某个函数的调用，然后改吧改吧才发现在不同情况下需要使用不同的参数调这个函数，或者当时写代码时只是想验证这条路是否可行，没太在意避免重复的代码，验证通过后又懒的再去修改已经稳定了的代码。该文中的例子算是客气的，我们有不少代码是在多层 if/else 的每个 {} 中都复制了代码，函数调用本身就很长，那么多参数，有差别的只有几个字母，如果哪天这个函数的输入输出改了，想必维护这段代码的同学很难不改出 bug。</p>
<p>程序中最忌讳的陋习之一就是复制代码，这对软件的影响不仅仅是增大程序体积，真正严重的是每当需要修改这段代码，所有它的副本都得记得去改，如果漏掉了一个，哼哼，一个小时内找到问题就算不错了。可又有多少程序员在编码的时候会提醒自己这个问题呢？我想恐怕不超过10%，至少真的能要求自己做到的不会超过这个比例。聪明的懒人，是努力让自己日后更轻松，而不是偷一时之快让自己逐渐进入越忙越乱越乱越忙的恶性循环。</p>
<p>不过，话又要说回来，任何的重构都要有个度，也要有个合适的环境才行。重构的前提是要保持语义清晰，或者是为了语义更清晰而重构，而不能为了重构而重构。对于该文的这个最最简单的例子来说，下面 Shichao Liu 的补充给得很强力：</p>
<blockquote><p>对于仅仅参数不同分支， 也要从分支的业务意义上加以区分：<br />
1.是恰巧逻辑相同，实则业务意义上完全独立的分支。<br />
2.或是业务意义上就应该有完全相同的逻辑操作，而仅是数据有差异。</p>
<p>在需求发生演变时会有所不同。</p>
<p>举个例子，网游， 一次攻击可能是“普通伤害”或者“致命一击”。可能在第一个版本中仅仅是伤害数值有所差异， 而在第二个版本中需要对致命一击后追击一些额外的操作（统计次数，累积点数等等策划能想得到的点子）。</p></blockquote>
<p><strong>《<a href="http://www.infoq.com/cn/news/2010/11/ugly-code-2">代码之丑（二）——长长的条件</a>》</strong></p>
<p>这篇我的认同度就比较低了，的确，该文所述的方法是各大软件公司惯用的技巧（MFC 里就有好多），重构出来的代码<strong>看似</strong>也非常清晰。但，对于第一次阅读或者修改这个代码的人，第一反应估计就是“STR_PREDICATE_ITEM 这个宏都干了啥？靠谱么？后面要不要加分号？BEGIN 和 END 之间加上 {} 会有问题么？太可怕了，又得记住一堆宏定义和用法，而且其实我总是记不起来要去用它们……”这还不是最糟糕的。你不能要求所有的同事都有这种归纳重构的能力，而且就算大家都是重构大拿，在一个项目中把各自写的所有复杂判断条件都这么折腾一趟，要么每个人得给每个判断单独写一套宏（很快就不会有人遵守了，今天还有五个功能等着交付呢），要么有神人来写了套相当泛型的通用宏，哇哦，原本为了可读性而重构出来的代码变得完全没法维护了。最严酷的是，如果条件并不是简单而统一的判断，比如：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>IsOperator<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
    <span style="color: #000040;">||</span> <span style="color: #008000;">&#40;</span>IsOtherPlayer<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;&amp;</span> GetID<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&lt;</span> <span style="color: #0000dd;">100</span><span style="color: #008000;">&#41;</span>
    <span style="color: #000040;">||</span> <span style="color: #008000;">&#40;</span>IsNpc<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>IsMonster<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">||</span> strResName <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #FF0000;">&quot;NNN&quot;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
    <span style="color: #000040;">||</span> strResName <span style="color: #000080;">==</span> <span style="color: #FF0000;">&quot;RRR&quot;</span>
    <span style="color: #000040;">||</span> strResName.<span style="color: #007788;">Contain</span><span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;XY&quot;</span><span style="color: #008000;">&#41;</span>
    ...<span style="color: #008000;">&#41;</span></pre></div></div>

<p>怎么办？更复杂的多层嵌套呢？应该说出现这类判断的代码，背后的设计一定是有问题的，但如果我们没法解决设计的缺陷，只能努力找一个平衡点，别累死写代码的人，也别搞懵维护代码的人，更别为了追求代码看起来很上流而让所有人都感到反感。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2011/08/ugly-code-at-birth-part3/' rel='bookmark' title='代码之初，性本丑？(下)'>代码之初，性本丑？(下)</a></li>
<li><a href='http://wutiam.net/2011/01/ugly-code-at-birth-part2/' rel='bookmark' title='代码之初，性本丑？(中)'>代码之初，性本丑？(中)</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/12/ugly-code-at-birth-part1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>#include</title>
		<link>http://wutiam.net/2010/12/include-rules-of-cpp/</link>
		<comments>http://wutiam.net/2010/12/include-rules-of-cpp/#comments</comments>
		<pubDate>Mon, 13 Dec 2010 08:31:51 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=148</guid>
		<description><![CDATA[译自 zeuxcg 的《#include 》（墙外），有删改。请勿转载。 若不听劝告非要转载，请注明出处，谢谢。 我们没法摆脱 C++，至少短期内不太可能。我真希望 C++ 里没有那么多恶心的特性，但目前也没有真正能取代 C++ 的玩意。现代编程语言往往采用大宗编译（bulk compilation）和/或智能链接器，但 C++ 仍然受困于头文件分离的设计思想（当然从另一方面说，C++ 的这种设计也使得其构建过程可以是增量的、高度并行的），使得鱼与熊掌不可兼得。 这种分离头文件并保持代码清晰的策略很显而易见，但我却很纳闷为啥还有那么多人没搞明白到底该怎么使用头文件，希望这篇文章能够进一步理清这团乱麻。本文也适用于 C，但有幸使用其他语言的同学们请绕行。 包含头文件的问题在于预编译器很笨：你的代码告诉它要包含某个文件，那么它就会“递归地”包含整个文件内容；如果你不想包含那个文件却又想用该文件中定义的一个符号呢，那你只有面对编译错误的份了；但你如果把所有头文件都包含进来，哼哼，编译的时候你就去数星星吧。 一般情况下，一个头文件被包含的次数越多（包括递推的包含，比如 A 包含了 B，B 又包含了 C，那么 A 也就包含了 C），那么改动这个头文件就会导致越多的代码文件需要被重编。迭代周期的时间很紧（这是关于时间的另一个话题了），所以我们要尽可能地减少头文件被包含的次数。这就是第一条重要规范：每个文件仅包含其真正需要显式包含的头文件，尽可能地减少包含的数量。遵守这条规范能确保你代码的构建速度不会像拖拉机一样。 现在，假设我们的某个头文件里声明了一个类 A。按照 C++ 的原则，这个类声明在没有声明其他某些符号前不会被编译。例如，A 继承自类 B 并且包含了一个类型是 C 的成员，那么你就需要在声明 A 之前同时将 B 和 C 的声明提供给编译器。有两种方法：在 A 的头文件里包含 B 和 C 的头文件，或者强制 类A 的使用者们每次在使用前都手动地引入 B 和 C 的头文件。问题是有时使用者没法知道这些内在的依赖关系，有时依赖关系又变了，这会让使用者们抓狂的，更崩溃的是，为了包含你写的这么一个头文件，还得莫名其妙地包含一大堆看起来完全无关的头文件。正因如此，所有的头文件都该自给自足——任何人都可以在任一 cpp [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p><em>译自 <a href="http://zeuxcg.org/">zeuxcg</a> 的《<a href="http://zeuxcg.org/2010/11/15/include-rules/">#include</a> 》（墙外），有删改。请勿转载。<br />
若不听劝告非要转载，请注明出处，谢谢。</em></p>
<p>我们没法摆脱 C++，至少短期内不太可能。我真希望 C++ 里没有那么多恶心的特性，但目前也没有真正能取代 C++ 的玩意。现代编程语言往往采用大宗编译（bulk compilation）和/或智能链接器，但 C++ 仍然受困于头文件分离的设计思想（当然从另一方面说，C++ 的这种设计也使得其构建过程可以是增量的、高度并行的），使得鱼与熊掌不可兼得。 这种分离头文件并保持代码清晰的策略很显而易见，但我却很纳闷为啥还有那么多人没搞明白到底该怎么使用头文件，希望这篇文章能够进一步理清这团乱麻。本文也适用于 C，但有幸使用其他语言的同学们请绕行。</p>
<p>包含头文件的问题在于预编译器很笨：你的代码告诉它要包含某个文件，那么它就会“递归地”包含整个文件内容；如果你不想包含那个文件却又想用该文件中定义的一个符号呢，那你只有面对编译错误的份了；但你如果把所有头文件都包含进来，哼哼，编译的时候你就去数星星吧。</p>
<p><span id="more-148"></span>一般情况下，一个头文件被包含的次数越多（包括递推的包含，比如 A 包含了 B，B 又包含了 C，那么 A 也就包含了 C），那么改动这个头文件就会导致越多的代码文件需要被重编。迭代周期的时间很紧（这是关于时间的另一个话题了），所以我们要尽可能地减少头文件被包含的次数。这就是第一条重要规范：<strong>每个文件仅包含其真正需要显式包含的头文件，尽可能地减少包含的数量</strong>。遵守这条规范能确保你代码的构建速度不会像拖拉机一样。</p>
<p>现在，假设我们的某个头文件里声明了一个类 A。按照 C++ 的原则，这个类声明在没有声明其他某些符号前不会被编译。例如，A 继承自类 B 并且包含了一个类型是 C 的成员，那么你就需要在声明 A 之前同时将 B 和 C 的声明提供给编译器。有两种方法：在 A 的头文件里包含 B 和 C 的头文件，或者强制 类A 的使用者们每次在使用前都手动地引入 B 和 C 的头文件。问题是有时使用者没法知道这些内在的依赖关系，有时依赖关系又变了，这会让使用者们抓狂的，更崩溃的是，为了包含你写的这么一个头文件，还得莫名其妙地包含一大堆看起来完全无关的头文件。正因如此，所有的头文件都该自给自足——任何人都可以在任一 cpp 中包含任何一个头文件而不会产生编译错误。这就是第二条重要规范：<strong>每个文件都必须包含所有依赖的头文件</strong>。遵守这条规范能确保程序员们不会被混乱的依赖关系逼疯。</p>
<p>以上这两大规范共同框定了如何正确编写头文件的方法：</p>
<ul>
<li>为每个需要的声明包含相应的头文件到你的头文件里；</li>
<li>但千万不要包含别的头文件了。</li>
</ul>
<p>也就是说，如果能用<a href="http://software.intel.com/zh-cn/blogs/2010/05/04/c-2/">前置声明</a>，千万别把整个头文件都包含进来——尽量使用前置声明。花一些时间和精力去解除头文件依赖关系有时是值得的，例如使用 <a href="http://www.codeproject.com/KB/tips/PIMPL.aspx">PIMPL 模式</a>（这个得视情况而定，并非套上了模式就上流了；不过得避免包含重量级的平台文件，比如 windows.h 或 d3d9.h）（关于怎么给 D3D9 的编译瘦身，作者去年的<a href="http://zeuxcg.org/2009/03/22/miscellanea/">这篇文章</a>最后一段有讲，供参考）。</p>
<p>还有一件事，由于疏忽或者意外，我们可能把同一个头文件包含了两次（例如 A 依赖于 B 和 C，而 B 又依赖于 C，所以 C 的头文件就被 A 包含了两次），所以我们需要一种机制来确保不会出现这种问题。我们得让每个文件都能够检测到多重包含，这里有两个办法：使用 #pragma once 或者使用。#pragma once 是一种非标准的技巧，它明确地告知预处理器“不要重复包含当前这个头文件”。而 header guard （也叫做 #include guard 或 macro guard）则通过预编译器的定义来模拟这样一种行为：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#ifndef FILE_NAME_H</span>
<span style="color: #339900;">#define FILE_NAME_H</span>
...
<span style="color: #339900;">#endif</span></pre></div></div>

<p>可能很多人不知道这种方法，但 #pragma once 已经被现代编译器广泛支持了。它有两点比 header guard 更好：</p>
<ul>
<li>更快（MSVC 不会第二次读取标记有 #pragma once 的文件，但却会读若干遍用 header guard 的文件）；</li>
<li>更简单（不需要再为每个文件的 header guard 取名了，也就不会再把名字搞混了）。</li>
</ul>
<p>所以，<strong>尽量使用 #pragma once，就算用不了，至少也得使用 header guard</strong>。如果你所使用的编译器不支持 #pragma once（而且你也没法说服开发商添加这个功能），那得<strong>确保你使用了一套能够生成永不重名 header guard 的方法</strong>。例如，把文件名连同路径一起转成以下划线（代表子文件夹）分隔的大写字符串，如 THEGAME_RENDER_LIGHTING_POINTLIGHT_H。<strong>不要</strong>只使用文件名，它可能<strong>不是</strong>唯一的！</p>
<p>如果你使用的是 header guard，理论上你可以在任何地方判断当前是否已经包含了某个头文件。但是，别通过判断是否已包含了某个头文件来改变后续代码的走势！这么干就等于让程序依赖于包含头文件的顺序，不但违反了一般性认知，而且由于没有预编译器的输出而无从 debug。如果你觉得你需要干诸如“如果当前包含了渲染器接口，那么我就该加入光照渲染器类，否则就不该让这堆用不到的代码被加进来”，那你应该把你的头文件分成两个类，第二个文件应该明确地包含渲染器接口，因为就是和它有关嘛。</p>
<p>至少在开发游戏的过程中，。最常见的可能就是断言的宏定义了（由于标准的 assert 实在太烂了，你得定义一套符合你们开发需求的），当然其实还有更多：日志接口、min/max函数、跨平台的定义（“当前系统是高位在前的字节顺序么？”）、内存管理相关的宏定义等等。一般惯例是把这些都放到一个单独的通用头文件里。所以你得控制这个文件的 size（当然，你知道我这里的 size 指的是累计包含进该头文件里的所有头文件的大小），而且你得<strong>确保所有文件都是在第一行先包含了这个头文件</strong>——否则你就有麻烦了（可能你得花上几个小时来查原因）。</p>
<p>呼，我能想到的关于头文件的就是这些了。额，还得再罗嗦几句，关于包含路径的问题。包含头文件时，你得指定是相对于当前文件的路径呢，还是相对于某个 include directories 全局设置项：</p>
<ul>
<li><strong>如果你是在写库代码</strong>，当然我说的是相对较小的库啦（像虚幻引擎这种巨人级的平台直接忽略），应该尽量降低配置门槛，也就是理论上不要强迫使用者把你的库加到 include directories 配置列表里去。对于这类工程，<strong>建议使用相对当前文件的的包含路径</strong>。</li>
<li>其他情况下，包含路径应该便于查找，也就是所有被包含的文件应该在同一个路径下。这时<strong>建议使用相对 include directory 配置项的包含路径</strong>，同时还要确保包含<strong>路径没有二义性</strong>。</li>
<li>不管用哪种包含路径的方式，请<strong>尽力保持项目间的一致性</strong>。理论上讲，每个项目的 include directories 配置都应该是一样的，比如引擎工程的设置应该是游戏工程的严格子集。</li>
</ul>
<p>噢，我是不是没提到良好的头文件依赖关系还能加快工程的链接速度？</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/12/include-rules-of-cpp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</title>
		<link>http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/</link>
		<comments>http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/#comments</comments>
		<pubDate>Tue, 28 Sep 2010 09:02:14 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[vs/vc++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=136</guid>
		<description><![CDATA[众所周知，在 STL 中，对于有序的 vector 容器，使用 binary_search、lower_bound/upper_bound 等搜索算法要比直接 find 高效得多。但是由于各个 STL 实现版本没有统一的标准，在 DEBUG 环境下各自的校验机制千差万别，这就导致可能出现一些让人郁闷的情况，比如这次的主角，微软的 STL。 现在有一个有序容器 kTasks，比如是 vector 或 deque，容器中保存的是 task 对象，task 对象中有一个 priority 的属性，容器以 task 的 priority 值降序排序。我们需要频繁查询当前该容器中有多少个 task 的 priority 高于一个给定的值，最高效的办法之一是通过 std::lower_bound() 直接拿到区间，再用 std::distance() 直接统计就行了。 1 2 3 4 5 6 7 8 9 10 11 bool PriorityLesser&#40;CTask* pkTask, int nPriority&#41; &#123; return pkTask-&#62;GetPriority&#40;&#41; [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2009/05/ctreectrl-traversal/' rel='bookmark' title='遍历 CTreeCtrl'>遍历 CTreeCtrl</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>众所周知，在 STL 中，对于有序的 vector 容器，使用 binary_search、lower_bound/upper_bound 等搜索算法要比直接 find 高效得多。但是由于各个 STL 实现版本没有统一的标准，在 DEBUG 环境下各自的校验机制千差万别，这就导致可能出现一些让人郁闷的情况，比如这次的主角，微软的 STL。</p>
<p><span id="more-136"></span>现在有一个有序容器 kTasks，比如是 vector 或 deque，容器中保存的是 task 对象，task 对象中有一个 priority 的属性，容器以 task 的 priority 值降序排序。我们需要频繁查询当前该容器中有多少个 task 的 priority 高于一个给定的值，最高效的办法之一是通过 std::lower_bound() 直接拿到区间，再用 std::distance() 直接统计就行了。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">bool</span> PriorityLesser<span style="color: #008000;">&#40;</span>CTask<span style="color: #000040;">*</span> pkTask, <span style="color: #0000ff;">int</span> nPriority<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> pkTask<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>GetPriority<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&gt;=</span> nPriority<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> CountIfPriorityIsGE<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nPriority<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> kTasks.<span style="color: #007788;">empty</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> 
        <span style="color: #008080;">?</span> <span style="color: #0000dd;">0</span>
        <span style="color: #008080;">:</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span>std<span style="color: #008080;">::</span><span style="color: #007788;">distance</span><span style="color: #008000;">&#40;</span>kTasks.<span style="color: #007788;">begin</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, std<span style="color: #008080;">::</span><span style="color: #007788;">lower_bound</span><span style="color: #008000;">&#40;</span>kTasks.<span style="color: #007788;">begin</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, kTasks.<span style="color: #007788;">end</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, nPriority, PriorityLesser<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>杯具就这么来了，由于这里使用的 std::lower_bound() 是带比较器的，在 DEBUG 模式下编译不过（RELEASE 下没问题），错误很莫名其妙，类似这样：</p>

<div class="wp_syntax"><div class="code"><pre class="txt" style="font-family:monospace;">error C2664: 'bool (CTask *,int)' : cannot convert parameter 2 from 'CTask *' to 'int'
...
see reference to function template instantiation 'bool std::_Debug_lt_pred&lt;_Pr,CTask*,_Ty&gt;(_Pr,_Ty1 &amp;,const _Ty2 &amp;,const wchar_t *,unsigned int)' being compiled</pre></div></div>

<p>这是啥破玩意儿呀？！MSDN 里的例子很简单，也没说有这种情况，难道是微软 STL 的 bug？没看到有人报过啊。只好硬着头皮看 MS STL 天书般的源代码了。</p>
<p>先来看看 lower_bound() 的代码（知道啥是天书了吧）：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;">		<span style="color: #666666;">// TEMPLATE FUNCTION lower_bound WITH PRED</span>
<span style="color: #0000ff;">template</span><span style="color: #000080;">&lt;</span><span style="color: #0000ff;">class</span> _FwdIt,
	<span style="color: #0000ff;">class</span> _Ty,
	<span style="color: #0000ff;">class</span> _Diff,
	<span style="color: #0000ff;">class</span> _Pr<span style="color: #000080;">&gt;</span> <span style="color: #0000ff;">inline</span>
	_FwdIt _Lower_bound<span style="color: #008000;">&#40;</span>_FwdIt _First, _FwdIt _Last,
		<span style="color: #0000ff;">const</span> _Ty<span style="color: #000040;">&amp;</span> _Val, _Pr _Pred, _Diff <span style="color: #000040;">*</span><span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>	<span style="color: #666666;">// find first element not before _Val, using _Pred</span>
	_DEBUG_POINTER<span style="color: #008000;">&#40;</span>_Pred<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	_DEBUG_ORDER_SINGLE_PRED<span style="color: #008000;">&#40;</span>_First, _Last, _Pred, <span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	_Diff _Count <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
	_Distance<span style="color: #008000;">&#40;</span>_First, _Last, _Count<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #008080;">;</span> <span style="color: #0000dd;">0</span> <span style="color: #000080;">&lt;</span> _Count<span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
		<span style="color: #008000;">&#123;</span>	<span style="color: #666666;">// divide and conquer, find half that contains answer</span>
		_Diff _Count2 <span style="color: #000080;">=</span> _Count <span style="color: #000040;">/</span> <span style="color: #0000dd;">2</span><span style="color: #008080;">;</span>
		_FwdIt _Mid <span style="color: #000080;">=</span> _First<span style="color: #008080;">;</span>
		std<span style="color: #008080;">::</span><span style="color: #007788;">advance</span><span style="color: #008000;">&#40;</span>_Mid, _Count2<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
		_DEBUG_ORDER_SINGLE_PRED<span style="color: #008000;">&#40;</span>_Mid, _Last, _Pred, <span style="color: #0000ff;">false</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
		<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>_DEBUG_LT_PRED<span style="color: #008000;">&#40;</span>_Pred, <span style="color: #000040;">*</span>_Mid, _Val<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
			_First <span style="color: #000080;">=</span> <span style="color: #000040;">++</span>_Mid, _Count <span style="color: #000040;">-</span><span style="color: #000080;">=</span> _Count2 <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
		<span style="color: #0000ff;">else</span>
			_Count <span style="color: #000080;">=</span> _Count2<span style="color: #008080;">;</span>
		<span style="color: #008000;">&#125;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #008000;">&#40;</span>_First<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>先注意第 10 行和第 18 行的这两个 _DEBUG_ORDER_SINGLE_PRED()，再往里跟，这个宏在 DEBUG 模式下实际执行的代码基本就是：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">template</span><span style="color: #000080;">&lt;</span><span style="color: #0000ff;">class</span> _Pr, <span style="color: #0000ff;">class</span> _Ty1, <span style="color: #0000ff;">class</span> _Ty2<span style="color: #000080;">&gt;</span> <span style="color: #0000ff;">inline</span>
	<span style="color: #0000ff;">bool</span> __CLRCALL_OR_CDECL _Debug_lt_pred<span style="color: #008000;">&#40;</span>_Pr _Pred, <span style="color: #0000ff;">const</span> _Ty1<span style="color: #000040;">&amp;</span> _Left, _Ty2<span style="color: #000040;">&amp;</span> _Right,
		<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">wchar_t</span> <span style="color: #000040;">*</span>_Where, <span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> _Line<span style="color: #008000;">&#41;</span>
	<span style="color: #008000;">&#123;</span>	<span style="color: #666666;">// test if _Pred(_Left, _Right) and _Pred is strict weak ordering</span>
	<span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>_Pred<span style="color: #008000;">&#40;</span>_Left, _Right<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
		<span style="color: #0000ff;">return</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">false</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>_Pred<span style="color: #008000;">&#40;</span>_Right, _Left<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
		_DEBUG_ERROR2<span style="color: #008000;">&#40;</span><span style="color: #FF0000;">&quot;invalid operator&lt;&quot;</span>, _Where, _Line<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #0000ff;">return</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
	<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>因为这时传入的都是容器的 iterator，所以实际上是在检查当前容器是否真的有序。。。</p>
<p>这还不算完，再看 lower_bound() 第 20 行的 _DEBUG_LT_PRED()，这个宏在 RELEASE 下是直接调用比较器，而在 DEBUG 下，又是去调用了上面那个 _Debug_lt_pred()，但传入的参数是一个迭代器和一个比较值，也就是既要做正常判断，又要检查反向比较时得到的结果是否正好相反。</p>
<p>终于明白了 DEBUG 下 lower_bound() 都干了些啥，那问题就好解决了。我们的比较器只提供了正常的比较操作（左项为迭代器、右项为比较值），还缺少用于检查容器是否有序的比较器（左右项都为迭代器）和用于检查反向比较的比较器（左项为比较值、右项为迭代器），这么一来，比较器就不能再使用普通函数了，必须以仿函数类的形式来提供所需的三种功能：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">struct</span> PriorityLesser
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">bool</span> operator<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>CTask<span style="color: #000040;">*</span> pkTask, <span style="color: #0000ff;">int</span> nPriority<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> pkTask<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>GetPriority<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&gt;=</span> nPriority<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#ifdef _DEBUG</span>
    <span style="color: #0000ff;">bool</span> operator<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nPriority, CTask<span style="color: #000040;">*</span> pkTask<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> pkTask<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>GetPriority<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&gt;=</span> nPriority<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">bool</span> operator<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#40;</span>CTask<span style="color: #000040;">*</span> pkTask1, CTask<span style="color: #000040;">*</span> pkTask2<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">const</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> pkTask1<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>GetPriority<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span> <span style="color: #000080;">&gt;</span> pkTask2<span style="color: #000040;">-</span><span style="color: #000080;">&gt;</span>GetPriority<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #339900;">#endif // _DEBUG</span>
<span style="color: #008000;">&#125;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span> CountIfPriorityIsGE<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> nPriority<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>kTasks.<span style="color: #007788;">empty</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
&nbsp;
    OperationQueue<span style="color: #008080;">::</span><span style="color: #007788;">iterator</span> itr <span style="color: #000080;">=</span> std<span style="color: #008080;">::</span><span style="color: #007788;">lower_bound</span><span style="color: #008000;">&#40;</span>kTasks.<span style="color: #007788;">begin</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, kTasks.<span style="color: #007788;">end</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, nPriority, PriorityLesser<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    OperationQueue<span style="color: #008080;">::</span><span style="color: #007788;">difference_type</span> diff <span style="color: #000080;">=</span> std<span style="color: #008080;">::</span><span style="color: #007788;">distance</span><span style="color: #008000;">&#40;</span>kTasks.<span style="color: #007788;">begin</span><span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>, itr<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">unsigned</span> <span style="color: #0000ff;">int</span><span style="color: #008000;">&#41;</span>diff<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>呼～</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2009/05/ctreectrl-traversal/' rel='bookmark' title='遍历 CTreeCtrl'>遍历 CTreeCtrl</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>避免在 C++ 类结构中出现私有虚成员函数</title>
		<link>http://wutiam.net/2010/07/avoid-private-virtual-member-function-in-cpp-class/</link>
		<comments>http://wutiam.net/2010/07/avoid-private-virtual-member-function-in-cpp-class/#comments</comments>
		<pubDate>Tue, 27 Jul 2010 12:34:44 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=134</guid>
		<description><![CDATA[最近在重构 C++ 代码时突然想起，如果一个基类的虚成员函数被设为 private，有没有意义？又是否合理？ 当然，有其一定的意义，那就是不希望子类在其他地方调用父类的这个函数，包括在子类的实现中；如果需要这个功能，应该使用其 public 的接口去使用该功能。而子类可以提供自己的实现，以提供多态。但是，如果子类觉得需要，还可以把这个 private 的虚成员函数重定义成 protected（虽然这会让人迷惑），从而使子类的子类们调用它。 有同学可能会问，如果是一个 private 的纯虚成员函数（语法上当然合理），那语义合理么？嗯，我觉得这的确是个问题——也许是为了告诉写子类的其他同学，这个虚函数，我不希望你们在除了基类已有的接口里调用之外还来用——仅仅是一个道德约束？！ 嗯，C++ 就是灵活得过头了，什么都让程序员自己去把控，可是别忘了，“太多的选择比没有选择糟糕得多”。所以，我决定，为了自己也为了别人不犯迷糊，避免使用 private 的虚函数，private 的成员函数仅包含当前类自己使用的函数。 BTW，类的成员变量正好相反，能设为 private 的尽量不要 protected 更不要public，否则后期维护，嗯嗯，就太痛苦了。 Related posts: 游戏引擎设计 之 内存管理框架 用 前置声明，还是用 #include？ #ifdef 中的逻辑与或操作
Related posts:<ul>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
<li><a href='http://wutiam.net/2009/08/logical-and-or-operation-in-ifdef/' rel='bookmark' title='#ifdef 中的逻辑与或操作'>#ifdef 中的逻辑与或操作</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>最近在重构 C++ 代码时突然想起，如果一个基类的虚成员函数被设为 private，有没有意义？又是否合理？</p>
<p>当然，有其一定的意义，那就是不希望子类在其他地方调用父类的这个函数，包括在子类的实现中；如果需要这个功能，应该使用其 public 的接口去使用该功能。而子类可以提供自己的实现，以提供多态。但是，如果子类觉得需要，还可以把这个 private 的虚成员函数重定义成 protected（虽然这会让人迷惑），从而使子类的子类们调用它。</p>
<p>有同学可能会问，如果是一个 private 的纯虚成员函数（语法上当然合理），那语义合理么？嗯，我觉得这的确是个问题——也许是为了告诉写子类的其他同学，这个虚函数，我不希望你们在除了基类已有的接口里调用之外还来用——仅仅是一个道德约束？！</p>
<p>嗯，C++ 就是灵活得过头了，什么都让程序员自己去把控，可是别忘了，“太多的选择比没有选择糟糕得多”。所以，我决定，为了自己也为了别人不犯迷糊，避免使用 private 的虚函数，private 的成员函数仅包含当前类自己使用的函数。</p>
<p>BTW，类的成员变量正好相反，能设为 private 的尽量不要 protected 更不要public，否则后期维护，嗯嗯，就太痛苦了。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
<li><a href='http://wutiam.net/2009/08/logical-and-or-operation-in-ifdef/' rel='bookmark' title='#ifdef 中的逻辑与或操作'>#ifdef 中的逻辑与或操作</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/07/avoid-private-virtual-member-function-in-cpp-class/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>乱用 STL 是地狱</title>
		<link>http://wutiam.net/2010/04/stl-on-the-wrong-way-may-lead-to-hell/</link>
		<comments>http://wutiam.net/2010/04/stl-on-the-wrong-way-may-lead-to-hell/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 05:43:21 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[stl]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=128</guid>
		<description><![CDATA[根据《Effective STL》条款21中的例子，建立一个比较类型为 less_equal 的 set 容器： set&#60; int, less_equal&#60;int&#62; &#62; s; 然后连续插入两个10： s.insert&#40;10&#41;; // 10a s.insert&#40;10&#41;; // 10b 会得到什么？ 在debug下，可能会给出一个assert报比较符号不合法，第二次插入失败，但在release下，这个动作很可能是未定义的，而通常的结果是，set中存在了两个键值同为10的项，也就是说，set被悄声无息地变成了multiset！太可怕了！ 所以，为正确的容器挑选正确的比较函数，很重要。用好 STL 其实并不容易，用错了不仅执行效率狂低，而且还可能出现这些难以想象的意外…… Related posts: 代码之初，性本丑？(上) #include 用 前置声明，还是用 #include？
Related posts:<ul>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2010/12/include-rules-of-cpp/' rel='bookmark' title='#include'>#include</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>根据《Effective STL》条款21中的例子，建立一个比较类型为 less_equal 的 set 容器：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">    set<span style="color: #000080;">&lt;</span> <span style="color: #0000ff;">int</span>, less_equal<span style="color: #000080;">&lt;</span><span style="color: #0000ff;">int</span><span style="color: #000080;">&gt;</span> <span style="color: #000080;">&gt;</span> s<span style="color: #008080;">;</span></pre></div></div>

<p>然后连续插入两个10：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;">    s.<span style="color: #007788;">insert</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">10</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// 10a</span>
    s.<span style="color: #007788;">insert</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">10</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">// 10b</span></pre></div></div>

<p>会得到什么？</p>
<p>在debug下，可能会给出一个assert报比较符号不合法，第二次插入失败，但在release下，这个动作很可能是未定义的，而通常的结果是，set中存在了两个键值同为10的项，也就是说，set被悄声无息地变成了multiset！太可怕了！</p>
<p>所以，为正确的容器挑选正确的比较函数，很重要。用好 STL 其实并不容易，用错了不仅执行效率狂低，而且还可能出现这些难以想象的意外……</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2010/12/include-rules-of-cpp/' rel='bookmark' title='#include'>#include</a></li>
<li><a href='http://wutiam.net/2009/05/forward-declaration-or-include/' rel='bookmark' title='用 前置声明，还是用 #include？'>用 前置声明，还是用 #include？</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/04/stl-on-the-wrong-way-may-lead-to-hell/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>do...while(0) 的妙用</title>
		<link>http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/</link>
		<comments>http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 01:52:10 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=127</guid>
		<description><![CDATA[在 C++ 中，有三种类型的循环语句：for、while 和 do...while，但是在一般应用中作循环时，我们可能用 for 和 while 要多一些，do...while 相对不受重视。 但是，最近在读我们项目的代码时，却发现了 do...while 的一些十分聪明的用法，不是用来做循环，而是用作其他来提高代码的健壮性。 转载自：http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html 1. do...while(0) 消除 goto 语句 通常，如果在一个函数中开始要分配一些资源，然后在中途执行过程中如果遇到错误则退出函数，当然，退出前先释放资源，我们的代码可能是这样： [ Version 1 ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [...]
Related posts:<ul>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>在 C++ 中，有三种类型的循环语句：for、while 和 do...while，但是在一般应用中作循环时，我们可能用 for 和 while 要多一些，do...while 相对不受重视。<br />
但是，最近在读我们项目的代码时，却发现了 do...while 的一些十分聪明的用法，不是用来做循环，而是用作其他来提高代码的健壮性。</p>
<p><span id="more-127"></span>转载自：<a href="http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html">http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html</a></p>
<p><strong>1. do...while(0) 消除 goto 语句</strong></p>
<p>通常，如果在一个函数中开始要分配一些资源，然后在中途执行过程中如果遇到错误则退出函数，当然，退出前先释放资源，我们的代码可能是这样：</p>
<p>[ Version 1 ]</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">bool</span> Execute<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
   <span style="color: #666666;">// 分配资源</span>
   <span style="color: #0000ff;">int</span> <span style="color: #000040;">*</span>p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> <span style="color: #0000ff;">int</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">bool</span> bOk<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
   <span style="color: #666666;">// 执行并进行错误处理</span>
   bOk <span style="color: #000080;">=</span> func1<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> 
   <span style="color: #008000;">&#123;</span>
      <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
      p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
   <span style="color: #008000;">&#125;</span>
&nbsp;
   bOk <span style="color: #000080;">=</span> func2<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> 
   <span style="color: #008000;">&#123;</span>
      <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
      p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
   <span style="color: #008000;">&#125;</span>
&nbsp;
   bOk <span style="color: #000080;">=</span> func3<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> 
   <span style="color: #008000;">&#123;</span>
      <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
      p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
   <span style="color: #008000;">&#125;</span>
&nbsp;
   <span style="color: #666666;">// ..........</span>
&nbsp;
   <span style="color: #666666;">// 执行成功，释放资源并返回</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
    p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>这里一个最大的问题就是代码的冗余，而且我每增加一个操作，就需要做相应的错误处理，非常不灵活。于是我们想到了 goto:</p>
<p>[ Version 2 ]</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">bool</span> Execute<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
   <span style="color: #666666;">// 分配资源</span>
   <span style="color: #0000ff;">int</span> <span style="color: #000040;">*</span>p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> <span style="color: #0000ff;">int</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">bool</span> bOk<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
   <span style="color: #666666;">// 执行并进行错误处理</span>
   bOk <span style="color: #000080;">=</span> func1<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">goto</span> errorhandle<span style="color: #008080;">;</span>
&nbsp;
   bOk <span style="color: #000080;">=</span> func2<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">goto</span> errorhandle<span style="color: #008080;">;</span>
&nbsp;
   bOk <span style="color: #000080;">=</span> func3<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">goto</span> errorhandle<span style="color: #008080;">;</span>
&nbsp;
   <span style="color: #666666;">// ..........</span>
&nbsp;
   <span style="color: #666666;">// 执行成功，释放资源并返回</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
    p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
&nbsp;
errorhandle<span style="color: #008080;">:</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
    p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>代码冗余是消除了，但是我们引入了 C++ 中身份比较微妙的 goto 语句，虽然正确的使用 goto 可以大大提高程序的灵活性与简洁性，但太灵活的东西往往是很危险的，它会让我们的程序捉摸不定，那么怎么才能避免使用 goto 语句，又能消除代码冗余呢，请看 do...while(0) 循环：</p>
<p>[ Version3 ]</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">bool</span> Execute<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
   <span style="color: #666666;">// 分配资源</span>
   <span style="color: #0000ff;">int</span> <span style="color: #000040;">*</span>p <span style="color: #000080;">=</span> <span style="color: #0000dd;">new</span> <span style="color: #0000ff;">int</span><span style="color: #008080;">;</span>
&nbsp;
   <span style="color: #0000ff;">bool</span> bOk<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">true</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
   <span style="color: #0000ff;">do</span>
   <span style="color: #008000;">&#123;</span>
      <span style="color: #666666;">// 执行并进行错误处理</span>
      bOk <span style="color: #000080;">=</span> func1<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
&nbsp;
      bOk <span style="color: #000080;">=</span> func2<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
&nbsp;
      bOk <span style="color: #000080;">=</span> func3<span style="color: #008000;">&#40;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
      <span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>bOk<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
&nbsp;
      <span style="color: #666666;">// ..........</span>
&nbsp;
   <span style="color: #008000;">&#125;</span><span style="color: #0000ff;">while</span><span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
&nbsp;
    <span style="color: #666666;">// 释放资源</span>
    <span style="color: #0000dd;">delete</span> p<span style="color: #008080;">;</span>   
    p <span style="color: #000080;">=</span> <span style="color: #0000ff;">NULL</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> bOk<span style="color: #008080;">;</span>
&nbsp;
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>“漂亮！”， 看代码就行了，啥都不用说了...</p>
<p><strong>2. 宏定义中的 do...while(0)</strong></p>
<p>如果你是 C++ 程序员，我有理由相信你用过，或者接触过，至少听说过MFC, 在 MFC的afx.h 文件里面， 你会发现很多宏定义都是用了 do...while(0) 或 do...while(false)， 比如说：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#define AFXASSUME(cond) do { bool __afx_condVal=!!(cond); ASSERT(__afx_condVal); __analysis_assume(__afx_condVal); } while(0)</span></pre></div></div>

<p>粗看我们就会觉得很奇怪，既然循环里面只执行了一次，我要这个看似多余的 do...while(0) 有什么意义呢？<br />
当然有！<br />
为了看起来更清晰，这里用一个简单点的宏来演示：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#define SAFE_DELETE(p) do{ delete p; p = NULL} while(0)</span></pre></div></div>

<p>假设这里去掉 do...while(0),</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#define SAFE_DELETE(p) delete p; p = NULL;</span></pre></div></div>

<p>那么以下代码：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span> <span style="color: #000040;">!</span><span style="color: #000080;">=</span> p<span style="color: #008000;">&#41;</span> SAFE_DELETE<span style="color: #008000;">&#40;</span>p<span style="color: #008000;">&#41;</span>
<span style="color: #0000ff;">else</span>   ...<span style="color: #0000ff;">do</span> sth...</pre></td></tr></table></div>

<p>就有两个问题：<br />
1) 因为if分支后有两个语句，else分支没有对应的if，编译失败；<br />
2) 假设没有else, SAFE_DELETE中的第二个语句无论if测试是否通过，会永远执行。<br />
你可能发现，为了避免这两个问题，我不一定要用这个令人费解的do...while,  我直接用{}括起来就可以了：</p>

<div class="wp_syntax"><div class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#define SAFE_DELETE(p) { delete p; p = NULL;}</span></pre></div></div>

<p>的确，这样的话上面的问题是不存在了，但是我想对于C++程序员来讲，在每个语句后面加分号是一种约定俗成的习惯，这样的话，以下代码:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span><span style="color: #0000ff;">NULL</span> <span style="color: #000040;">!</span><span style="color: #000080;">=</span> p<span style="color: #008000;">&#41;</span> SAFE_DELETE<span style="color: #008000;">&#40;</span>p<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">else</span>   ...<span style="color: #0000ff;">do</span> sth...</pre></td></tr></table></div>

<p>其 else 分支就无法通过编译了（原因同上），所以采用 do...while(0) 是做好的选择了。<br />
也许你会说，我们代码的习惯是在每个判断后面加上{}, 就不会有这种问题了，也就不需要 do...while 了，如：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">if</span><span style="color: #008000;">&#40;</span>...<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
<span style="color: #008000;">&#125;</span>
<span style="color: #0000ff;">else</span>
<span style="color: #008000;">&#123;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>诚然，这是一个好的，应该提倡的编程习惯，但一般这样的宏都是作为 library 的一部分出现的，而对于一个 library 的作者，他所要做的就是让其库具有通用性，强壮性，因此他不能有任何对库的使用者的假设，如其编码规范，技术水平等。 </p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
<li><a href='http://wutiam.net/2010/09/lower_bound-strange-compiling-error-in-ms-stl/' rel='bookmark' title='微软 STL lower_bound() 在 DEBUG 下的诡异编译错误'>微软 STL lower_bound() 在 DEBUG 下的诡异编译错误</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>让 C++ 的 new 操作失败时返回空指针</title>
		<link>http://wutiam.net/2010/02/let-cpp-new-operator-return-null-when-failed/</link>
		<comments>http://wutiam.net/2010/02/let-cpp-new-operator-return-null-when-failed/#comments</comments>
		<pubDate>Thu, 25 Feb 2010 01:33:52 +0000</pubDate>
		<dc:creator>islet8</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c++]]></category>

		<guid isPermaLink="false">http://wutiam.net/?p=124</guid>
		<description><![CDATA[C 中如果创建一个对象失败，就会返回空指针。但是对于 C++ 就不一样了，new 是不应返回空指针的，书上的推荐做法是在构造函数里抛异常。 当不想引入异常机制的时候，一般的做法是在构造器里啥都不做（最多做个变量初始化），加一个 Init() 函数来完成真正的初始化工作。 然而这样就使得每次创建一个对象，都要执行两步（new+init），总不是太方便，其实 C++ 的 new 操作符是带参的，可以通过“new(std::nothrow) CXxx”的方式让 new 失败时返回 null 指针，来标记失败（而不是抛出异常）。 Related posts: do...while(0) 的妙用 代码之初，性本丑？(上) 游戏引擎设计 之 内存管理框架
Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
</ul>]]></description>
			<content:encoded><![CDATA[<p>C 中如果创建一个对象失败，就会返回空指针。但是对于 C++ 就不一样了，new 是不应返回空指针的，书上的推荐做法是在构造函数里抛异常。<br />
当不想引入异常机制的时候，一般的做法是在构造器里啥都不做（最多做个变量初始化），加一个 Init() 函数来完成真正的初始化工作。<br />
然而这样就使得每次创建一个对象，都要执行两步（new+init），总不是太方便，其实 C++ 的 new 操作符是带参的，可以通过“<strong>new(std::nothrow) CXxx</strong>”的方式让 new 失败时返回 null 指针，来标记失败（而不是抛出异常）。</p>
<p>Related posts:<ul>
<li><a href='http://wutiam.net/2010/03/ingenious-usage-of-do-while-0/' rel='bookmark' title='do...while(0) 的妙用'>do...while(0) 的妙用</a></li>
<li><a href='http://wutiam.net/2010/12/ugly-code-at-birth-part1/' rel='bookmark' title='代码之初，性本丑？(上)'>代码之初，性本丑？(上)</a></li>
<li><a href='http://wutiam.net/2011/10/game-engine-design-memory-management-framework/' rel='bookmark' title='游戏引擎设计 之 内存管理框架'>游戏引擎设计 之 内存管理框架</a></li>
</ul></p>]]></content:encoded>
			<wfw:commentRss>http://wutiam.net/2010/02/let-cpp-new-operator-return-null-when-failed/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

