<?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>梦幻之星</title>
	<atom:link href="http://www.fancystar.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.fancystar.org</link>
	<description>来创造梦想中的世界吧！</description>
	<lastBuildDate>Sat, 14 Jan 2012 14:47:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>我的2011</title>
		<link>http://www.fancystar.org/2012/01/08/%e6%88%91%e7%9a%842011/</link>
		<comments>http://www.fancystar.org/2012/01/08/%e6%88%91%e7%9a%842011/#comments</comments>
		<pubDate>Sun, 08 Jan 2012 13:06:59 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[其他]]></category>
		<category><![CDATA[游戏开发]]></category>
		<category><![CDATA[编程札记]]></category>
		<category><![CDATA[others]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/?p=129</guid>
		<description><![CDATA[我是一个很懒的bloger，总想着有时间写blog，还不如多写几行代码。但仔细想想，代码是永远写不完的，而blog却不想就此荒废。所以在敷衍公司的年终总结之前，还是坐下来真正地总结一下自己在过去一年中的得失吧。

Fancystar开动
从上海回来后的空档期，研究了很多关于多线程方面的内容，再加上之前Gamebryo的经验，结果是脑袋里各种新的想法开始涌动。于是想要对老SDK进行改造。老SDK大部分是对老余GameBox的模仿，传统的单线程客户端，渲染也只运用到OpenGL 1.x的水平。最终我决定重写这3W多行的客户端代码，采用新的渲染架构，并且可以运用更多的CPU。这就是Fancystar。
多线程和Lock-Free
这是一个严重杀伤脑细胞的领域，在跌跌撞撞摸爬打滚了几个月之后，才终于悟到了一点门道。本来也酝酿过几篇文章想要总结一下这方面的心得，由于各种原因却一直未有成文。
Fancystar的第一个尝试，就是采用多线程架构，以利用更多的CPU核心。这通常有两种选择（其实还有第3种，即1和2的混合）：

将子系统拆分到不同的线程中并行执行 
采用任务（task）/ 调度器（scheduler）/ 工作线程（worker）模型 

开始的时候Fancystar选择了1，试图将输入，逻辑，渲染，物理等各个子系统拆分到不同的线程中并行执行。这会遭遇几个问题：
首先每个object的数据同样要拆分为N个部分，分别由不同的子系统来管理，相当于每个object在不同的子系统中都有proxy（称为代理，或分身）。这些proxy相互之间独立性越强越好，但无论怎样设计，通信总是无法避免的。通信方式要么是传引用，要么是值拷贝。传引用的话，是类似于流水线的方式：proxy1更新数据—&#62;通知proxy2数据更新完毕—&#62;proxy2获得指针并执行自己的操作。在proxy1通知proxy2之后，proxy1（在下一帧之前）就不再允许修改共享数据了，否则就要涉及到线程同步的问题。在实际游戏逻辑中，这条规则是难以维护的。于是很自然就会想到用值拷贝。类似于double buffer （甚至triple buffer），共享数据同时存在多份拷贝，每个proxy独占一份，只在关键点同步一次。这种方法内存占用几乎翻倍，但更安全更容易维护。尽管如此，由于总想着尽量减少同步的次数和数据量，自然就会引入复杂的状态系统，以跟踪每份共享数据的变化。以上2种实现Fancystar都曾尝试使用过，但在写了一些简单的DEMO后就放弃了，因为设计出来的系统实在很难使用。
其次，这种按子系统进行拆分的方式是不利于扩展的。对于游戏来说，子系统的数量是有限的。即使在最优配置的状况下，也只能每个系统分配一个core，更多的core将会被浪费掉。此外，不同的子系统，其负载是极度不相称的。物理系统需要大量的运算，而输入和声音则想到休闲。所以最终整个系统会被最慢的那个子系统所拖累，而最慢的子系统却无法获得更多的运算时间（即使有），与此同时某些子系统所在的core却在idle。
所以最后Fancystar选择了2：整个系统还是在同一个线程中运行，但可以将运算封装成task，由scheduler进行调度，分配到不同的worker thread中去执行。core越多，worker thread就越多。采用这种方式，则主逻辑中无需考虑线程同步的问题，只要在几个热点处将运算分包出去并等待结果就可以了。Gamebryo就是采用这样的方法（虽然早已看过文档，但直到后来才意识到这种设计的优点）。在某篇介绍Capcom新引擎的论文中也提到了类似的技术（但更加彻底，复杂），并用在了 Lost Planet 和 Dead Rising 中。
尽管走了很多弯路，但总算具备了雏形。工作成果包括：线程工具库，提供atomic操作，hazard pointer，各种lock-free队列，lock-free对象池等；重新修改了object系统，提供线程安全的引用计数和回收机制，并全面采用component模式；实现 task / scheduler / worker机制；重写了异步资源管理系统；客户端框架在历经多次修改后也基本确定了下来。
OpenGL3和GLSL
对于本人来说，去年另外一个最重要的进步就是，我开始了GPU编程的旅程。从零开始设计渲染模块，在CPU和GPU间切换，写shader程序，这些都是全新的体验，感觉非常美妙。 Fancystar完全抛弃了老SDK的渲染代码，用OpenGL3 core profile 和 GLSL shader来重写。研究渲染技术，将会是今年的工作重点。

(左) 纪念第一个shader程式&#160; (右) 用shader实现的tile map
游戏
去年主要参与了两个项目，一个是业余时间开发的bombman，以插件的形式将Fancystar跑在浏览器上面，游戏可以native client和web两种形式运行。该项目最后无疾而终了，业余团队在凝聚力上还是有很大的问题。另一个就是工作室的angel，是个很愉快的项目，我的主要工作集中在编辑器和AI上面，下个月就杀青了，敬请期待，呵呵。


读书
在Kindle上速读了一遍 OpenGL Super Bible 5th，整体上对OpenGL3和GLSL有了一定的了解。第5版完全抛弃了老旧的API，从core profile和shader开始阐述新的OpenGL架构，这应该是最好的OpenGL的入门书了吧。第二本读的是 Texturing &#38; Modeling，讲程序纹理和建模技术的。在读了前面几章之后，决定系统地学习现代计算机图形学，于是转而去读 Real-Time Rendering。这是一本超棒的书，作者站在很高的位置来阐述问题，经常给我带来“哦，原来如此”的感觉。第4章之后的内容对我来说逐渐变得艰深起来，往往需要根据书中提供的reference阅读更多的扩展资料才能理解书中所阐述的问题。争取今年能把此书读完，并将其中的理论和技术应用到Fancystar中。
此外我还读了大量关于并发编程，内存模型，以及Lock-Free算法的论文，有时间的话会将这些资料整理出来跟大家分享。
最后
在2011岁末获得可爱LOLI一只，感谢上苍！


]]></description>
			<content:encoded><![CDATA[<p><font size="2" face="微软雅黑">我是一个很懒的bloger，总想着有时间写blog，还不如多写几行代码。但仔细想想，代码是永远写不完的，而blog却不想就此荒废。所以在敷衍公司的年终总结之前，还是坐下来真正地总结一下自己在过去一年中的得失吧。</font></p>
<h3><font size="2" face="微软雅黑"></font></h3>
<h3><font size="2" face="微软雅黑">Fancystar开动</font></h3>
<p><font size="2" face="微软雅黑">从上海回来后的空档期，研究了很多关于多线程方面的内容，再加上之前Gamebryo的经验，结果是脑袋里各种新的想法开始涌动。于是想要对老SDK进行改造。老SDK大部分是对老余GameBox的模仿，传统的单线程客户端，渲染也只运用到OpenGL 1.x的水平。最终我决定重写这3W多行的客户端代码，采用新的渲染架构，并且可以运用更多的CPU。这就是Fancystar。</font></p>
<h3><font size="2" face="微软雅黑">多线程和Lock-Free</font></h3>
<p><font size="2" face="微软雅黑">这是一个严重杀伤脑细胞的领域，在跌跌撞撞摸爬打滚了几个月之后，才终于悟到了一点门道。本来也酝酿过几篇文章想要总结一下这方面的心得，由于各种原因却一直未有成文。</font></p>
<p><font size="2" face="微软雅黑">Fancystar的第一个尝试，就是采用多线程架构，以利用更多的CPU核心。这通常有两种选择（其实还有第3种，即1和2的混合）：</font></p>
<ol>
<li><font size="2" face="微软雅黑">将子系统拆分到不同的线程中并行执行 </font></li>
<li><font size="2" face="微软雅黑">采用任务（task）/ 调度器（scheduler）/ 工作线程（worker）模型 </font></li>
</ol>
<p><font size="2" face="微软雅黑">开始的时候Fancystar选择了1，试图将输入，逻辑，渲染，物理等各个子系统拆分到不同的线程中并行执行。这会遭遇几个问题：</font></p>
<p><font size="2" face="微软雅黑">首先每个object的数据同样要拆分为N个部分，分别由不同的子系统来管理，相当于每个object在不同的子系统中都有proxy（称为代理，或分身）。这些proxy相互之间独立性越强越好，但无论怎样设计，通信总是无法避免的。通信方式要么是传引用，要么是值拷贝。传引用的话，是类似于流水线的方式：proxy1更新数据—&gt;通知proxy2数据更新完毕—&gt;proxy2获得指针并执行自己的操作。在proxy1通知proxy2之后，proxy1（在下一帧之前）就不再允许修改共享数据了，否则就要涉及到线程同步的问题。在实际游戏逻辑中，这条规则是难以维护的。于是很自然就会想到用值拷贝。类似于double buffer （甚至triple buffer），共享数据同时存在多份拷贝，每个proxy独占一份，只在关键点同步一次。这种方法内存占用几乎翻倍，但更安全更容易维护。尽管如此，由于总想着尽量减少同步的次数和数据量，自然就会引入复杂的状态系统，以跟踪每份共享数据的变化。以上2种实现Fancystar都曾尝试使用过，但在写了一些简单的DEMO后就放弃了，因为设计出来的系统实在很难使用。</font></p>
<p><font size="2" face="微软雅黑">其次，这种按子系统进行拆分的方式是不利于扩展的。对于游戏来说，子系统的数量是有限的。即使在最优配置的状况下，也只能每个系统分配一个core，更多的core将会被浪费掉。此外，不同的子系统，其负载是极度不相称的。物理系统需要大量的运算，而输入和声音则想到休闲。所以最终整个系统会被最慢的那个子系统所拖累，而最慢的子系统却无法获得更多的运算时间（即使有），与此同时某些子系统所在的core却在idle。</font></p>
<p><font size="2" face="微软雅黑">所以最后Fancystar选择了2：整个系统还是在同一个线程中运行，但可以将运算封装成task，由scheduler进行调度，分配到不同的worker thread中去执行。core越多，worker thread就越多。采用这种方式，则主逻辑中无需考虑线程同步的问题，只要在几个热点处将运算分包出去并等待结果就可以了。Gamebryo就是采用这样的方法（虽然早已看过文档，但直到后来才意识到这种设计的优点）。在某篇介绍Capcom新引擎的论文中也提到了类似的技术（但更加彻底，复杂），并用在了 <strong><em>Lost Planet</em></strong> 和 <strong><em>Dead Rising</em></strong> 中。</font></p>
<p><font size="2" face="微软雅黑">尽管走了很多弯路，但总算具备了雏形。工作成果包括：线程工具库，提供atomic操作，hazard pointer，各种lock-free队列，lock-free对象池等；重新修改了object系统，提供线程安全的引用计数和回收机制，并全面采用component模式；实现 task / scheduler / worker机制；重写了异步资源管理系统；客户端框架在历经多次修改后也基本确定了下来。</font></p>
<h3><font size="2" face="微软雅黑">OpenGL3和GLSL</font></h3>
<p><font size="2" face="微软雅黑">对于本人来说，去年另外一个最重要的进步就是，我开始了GPU编程的旅程。从零开始设计渲染模块，在CPU和GPU间切换，写shader程序，这些都是全新的体验，感觉非常美妙。 Fancystar完全抛弃了老SDK的渲染代码，用OpenGL3 core profile 和 GLSL shader来重写。研究渲染技术，将会是今年的工作重点。</font></p>
<p align="center"><a href="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/20120108172030s.png"><font size="2" face="微软雅黑"><img style="margin: 0px 20px 0px 0px" title="20120108172030s" border="0" alt="20120108172030s" src="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/20120108172030s_thumb.png" width="204" height="160" /></font></a><a href="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/20120108173539s.png"><font size="2" face="微软雅黑"><img title="20120108173539s" border="0" alt="20120108173539s" src="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/20120108173539s_thumb.png" width="208" height="160" /></font></a></p>
<p align="center"><font size="2" face="微软雅黑"><em>(左) 纪念第一个shader程式&#160; (右) 用shader实现的tile map</em></font></p>
<h3><font size="2" face="微软雅黑">游戏</font></h3>
<p><font size="2" face="微软雅黑">去年主要参与了两个项目，一个是业余时间开发的<em><strong>bombman</strong></em>，以插件的形式将Fancystar跑在浏览器上面，游戏可以native client和web两种形式运行。该项目最后无疾而终了，业余团队在凝聚力上还是有很大的问题。另一个就是工作室的<em><strong>angel</strong></em>，是个很愉快的项目，我的主要工作集中在编辑器和AI上面，下个月就杀青了，敬请期待，呵呵。</font></p>
<p><font size="2" face="微软雅黑"></font></p>
<p><font size="2" face="微软雅黑"></font></p>
<h3><font size="2" face="微软雅黑">读书</font></h3>
<p><font size="2" face="微软雅黑">在Kindle上速读了一遍 <strong><em>OpenGL Super Bible 5th</em></strong>，整体上对OpenGL3和GLSL有了一定的了解。第5版完全抛弃了老旧的API，从core profile和shader开始阐述新的OpenGL架构，这应该是最好的OpenGL的入门书了吧。第二本读的是 <strong><em>Texturing &amp; Modeling</em></strong>，讲程序纹理和建模技术的。在读了前面几章之后，决定系统地学习现代计算机图形学，于是转而去读 <em><strong>Real-Time Rendering</strong></em>。这是一本超棒的书，作者站在很高的位置来阐述问题，经常给我带来“哦，原来如此”的感觉。第4章之后的内容对我来说逐渐变得艰深起来，往往需要根据书中提供的reference阅读更多的扩展资料才能理解书中所阐述的问题。争取今年能把此书读完，并将其中的理论和技术应用到Fancystar中。</font></p>
<p><font size="2" face="微软雅黑">此外我还读了大量关于并发编程，内存模型，以及Lock-Free算法的论文，有时间的话会将这些资料整理出来跟大家分享。</font></p>
<h3><font size="2" face="微软雅黑">最后</font></h3>
<p><font size="2" face="微软雅黑">在2011岁末获得可爱LOLI一只，感谢上苍！</font></p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/S8307561.jpg"><font size="2" face="微软雅黑"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="" border="0" alt="" src="http://www.fancystar.org/wordpress/wp-content/uploads/2012/01/S8307561_thumb.jpg" width="244" height="184" /></font></a></p>
<p><font size="2" face="微软雅黑"></font></p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2012/01/08/%e6%88%91%e7%9a%842011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>游戏架构杂想</title>
		<link>http://www.fancystar.org/2011/09/08/%e6%b8%b8%e6%88%8f%e6%9e%b6%e6%9e%84%e6%9d%82%e6%83%b3/</link>
		<comments>http://www.fancystar.org/2011/09/08/%e6%b8%b8%e6%88%8f%e6%9e%b6%e6%9e%84%e6%9d%82%e6%83%b3/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 18:14:28 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[游戏开发]]></category>
		<category><![CDATA[编程札记]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[game]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/2011/09/08/%e6%b8%b8%e6%88%8f%e6%9e%b6%e6%9e%84%e6%9d%82%e6%83%b3/</guid>
		<description><![CDATA[游戏是一个模拟虚拟世界的实时程序，它接受用户的输入，然后输出图像和声音：

输入
游戏程序的输入是一个有限的消息集合。实际运行中，每条消息都带有timestamp用以记录发送时间。两条消息相等不单内容相等，timestamp也必须相等。因此给定一组初始状态，同样的消息序列必定可以得到唯一确定的运算结果。
不同的游戏所接受的消息集合各不相同。例如：最基本的飞行射击游戏（《1945》），它接受的消息集合为 { 前，后，左，右，射击 }；而只有一个按钮的UI界面，它接受的消息集合为{ 光标位置(x,y)，点击 }。通常需要将各种输入设备的硬件信号映射到游戏所支持的输入集合：

最简单的做法就是直接把所有硬件消息作为游戏的输入集合。例如对于PC来说，就是鼠标移动+所有的鼠标/键盘按键；对于Console来说就是摇杆+按键。
输出
游戏程序的输出为图像和声音，通过专门的API向显示/声音设备发送指令，将逻辑世界以人类的认知方式描绘出来：

Game World
游戏逻辑的核心过程就是以一定的频率，对虚拟世界中的每一件物体执行运算，从而模拟世界的运转。每个运算周期就是一个逻辑帧，其中每个虚拟物体都会依次获得CPU时间来执行如下3个步骤的运算：
 

根据外部输入的消息改变自身的状态 
根据虚拟世界的状况决策将要采取的行为（AI系统） 
在虚拟世界的物理框架内执行特定的行为（物理系统） 

所有的虚拟物体所要执行的运算都不外乎以上3个部分。RPG中的怪物就是包含所有3个步骤的最好的例子。有些物体只需要其中之一二，例如：由玩家操纵的勇者不需要Think，而像桌子这样的静态物体最多只会有Act。
模块拆分和并行化
从程序角度对主要任务进行描述之后，模块的拆分就变得显而易见了。
首先基本框架可以分为Input，GameWorld，Render和Sound四个部分。这四个部分的功能相对独立，并且以类似的模式运作。Input以特定的频率读取硬件输入并映射到游戏输入集合；GameWorld也是以特定的频率对整个世界进行运算；Render同样按照自己的频率从GameWorld采样瞬时数据并转化为多边形/像素输出到图形硬件（类似于照相机）；Sound同理，只不过输出的是声音而已。理想情况下，只要通信条件满足，这四个部分可以任意部署到不同的Client上运行（这里，Client可以是一个线程，一个进程，甚至一台远端机器），而相互之间除了通信协议之外不需要知道任何东西。
其次对于游戏中的虚拟物体，采用Core+Proxy的方式来表示。Core是在GameWorld中足以保证其逻辑的完整表述，而Proxy则是在其他模块中维护专门数据的分身。例如在渲染模块中会存在一个对应的Render Proxy，其持有根据Core生成的多边形和纹理数据。Core和Proxy之间存在通信管道，更新后的数据会由Core广播给各个Proxy（或者由各个Proxy在需要的时候向Core查询？）。嗯，这其实就是经典的Model-View结构
最后值得思考的并行点在Think和Act这两个阶段。如果两个物体的Think（或Act）之间没有关联，是完全可以分配到不同的workflow中并行执行。不同类型物体之间的依赖关系可以通过静态分析得到，这可以作为执行过程中workflow分配的依据。
]]></description>
			<content:encoded><![CDATA[<p>游戏是一个模拟虚拟世界的实时程序，它接受用户的输入，然后输出图像和声音：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image_thumb.png" width="363" height="153" /></a></p>
<h3>输入</h3>
<p>游戏程序的输入是一个有限的消息集合。实际运行中，每条消息都带有timestamp用以记录发送时间。两条消息相等不单内容相等，timestamp也必须相等。因此给定一组初始状态，同样的消息序列必定可以得到唯一确定的运算结果。</p>
<p>不同的游戏所接受的消息集合各不相同。例如：最基本的飞行射击游戏（《1945》），它接受的消息集合为 { <em>前，后，左，右，射击 </em>}；而只有一个按钮的UI界面，它接受的消息集合为<em>{ 光标位置(x,y)，点击 </em>}。通常需要将各种输入设备的硬件信号映射到游戏所支持的输入集合：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image1.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image_thumb1.png" width="444" height="256" /></a></p>
<p>最简单的做法就是直接把所有硬件消息作为游戏的输入集合。例如对于PC来说，就是鼠标移动+所有的鼠标/键盘按键；对于Console来说就是摇杆+按键。</p>
<h3>输出</h3>
<p>游戏程序的输出为图像和声音，通过专门的API向显示/声音设备发送指令，将逻辑世界以人类的认知方式描绘出来：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image2.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image_thumb2.png" width="391" height="192" /></a></p>
<h3>Game World</h3>
<p>游戏逻辑的核心过程就是以一定的频率，对虚拟世界中的每一件物体执行运算，从而模拟世界的运转。每个运算周期就是一个逻辑帧，其中每个虚拟物体都会依次获得CPU时间来执行如下3个步骤的运算：</p>
<p> <a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image3.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/image_thumb3.png" width="393" height="99" /></a>
<ol>
<li>根据外部输入的消息改变自身的状态 </li>
<li>根据虚拟世界的状况决策将要采取的行为（AI系统） </li>
<li>在虚拟世界的物理框架内执行特定的行为（物理系统） </li>
</ol>
<p>所有的虚拟物体所要执行的运算都不外乎以上3个部分。RPG中的怪物就是包含所有3个步骤的最好的例子。有些物体只需要其中之一二，例如：由玩家操纵的勇者不需要Think，而像桌子这样的静态物体最多只会有Act。</p>
<h3>模块拆分和并行化</h3>
<p>从程序角度对主要任务进行描述之后，模块的拆分就变得显而易见了。</p>
<p>首先基本框架可以分为Input，GameWorld，Render和Sound四个部分。这四个部分的功能相对独立，并且以类似的模式运作。Input以特定的频率读取硬件输入并映射到游戏输入集合；GameWorld也是以特定的频率对整个世界进行运算；Render同样按照自己的频率从GameWorld采样瞬时数据并转化为多边形/像素输出到图形硬件（类似于照相机）；Sound同理，只不过输出的是声音而已。理想情况下，只要通信条件满足，这四个部分可以任意部署到不同的Client上运行（这里，Client可以是一个线程，一个进程，甚至一台远端机器），而相互之间除了通信协议之外不需要知道任何东西。</p>
<p>其次对于游戏中的虚拟物体，采用Core+Proxy的方式来表示。Core是在GameWorld中足以保证其逻辑的完整表述，而Proxy则是在其他模块中维护专门数据的分身。例如在渲染模块中会存在一个对应的Render Proxy，其持有根据Core生成的多边形和纹理数据。Core和Proxy之间存在通信管道，更新后的数据会由Core广播给各个Proxy（或者由各个Proxy在需要的时候向Core查询？）。嗯，这其实就是经典的Model-View结构<img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/09/wlEmoticon-smile.png" /></p>
<p>最后值得思考的并行点在Think和Act这两个阶段。如果两个物体的Think（或Act）之间没有关联，是完全可以分配到不同的workflow中并行执行。不同类型物体之间的依赖关系可以通过静态分析得到，这可以作为执行过程中workflow分配的依据。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/09/08/%e6%b8%b8%e6%88%8f%e6%9e%b6%e6%9e%84%e6%9d%82%e6%83%b3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CEGUI渲染流程简析</title>
		<link>http://www.fancystar.org/2011/07/16/cegui%e6%b8%b2%e6%9f%93%e6%b5%81%e7%a8%8b%e7%ae%80%e6%9e%90/</link>
		<comments>http://www.fancystar.org/2011/07/16/cegui%e6%b8%b2%e6%9f%93%e6%b5%81%e7%a8%8b%e7%ae%80%e6%9e%90/#comments</comments>
		<pubDate>Sat, 16 Jul 2011 14:13:16 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[游戏开发]]></category>
		<category><![CDATA[编程札记]]></category>
		<category><![CDATA[CEGUI]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/?p=107</guid>
		<description><![CDATA[粗略分析了CEGUI的渲染流程，总结一下供日后参考。CEGUI版本是0.7.5，OpenGL渲染器。
首先在CEGUI里面每张图片，每个字符都是一个quad，每个quad由2个三角面组成，包括6个顶点的坐标，颜色，纹理坐标，是发送给GPU的最基础的渲染单元。要注意的是，CEGUI并不局限于quad，它可以构造任意多的三角面以生成各种形状。要绘制一个窗口需要很多顶点数据，例如一个简单的button，背景图像需要1个quad（根据背景类型不同可能会更多），有4个文字的话又需要4个quad，总共就有5个quad，30个顶点。所以每个Window对象都有一个GeometryBuffer对象用来缓存自己的顶点数据。
窗口绘制输出的目的地称为RenderingSurface，所以每个窗口都要从属于某个RenderingSurface，否则无法显示。在渲染的时候，CEGUI会遍历所有的窗口，并将该窗口的GeometryBuffer依次提交到该窗口所属surface的渲染队列中去。对于CEGUI来说，这个过程就是“窗口绘制”，同时会触发绘制消息，而真正的绘制操作其实是在这之后。当所有相关的GeometryBuffer都被push进队列，RenderingSurface::draw就会被调用，此时顶点数据才真正提交到Renderer进行渲染输出。Renderer输出的目的地称为RenderTarget，由具体实现而定，可能是Frame buffer，Off-screen buffer，或者Texture object。在当前OpenGLRenderer中，是用纹理对象来实现的。之所以要引入RenderTarget，是为了能缓存surface的输出。
RenderingSurface有两个派生类，一个是RenderingRoot，它是Renderer默认的surface类型，在当前CEGUI的实现中只是对RenderingSurface的简单封装，没有任何额外的功能；第二个是RenderingWindow，它的作用是将渲染队列中的内容绘制到一张纹理图像上面，然后再用该纹理来绘制自己的GeometryBuffer到其他surface上面去。CEGUI 0.7中新增的窗口旋转和各种窗口特效就是通过这种方式来实现的。
在CEGUI中，每个Window对象都有自己的GeometryBuffer，但并不是每个窗口都有自己的surface。对于普通的四平八稳的窗口，它们共享由Renderer创建的RenderingRoot对象；而只有使用了旋转或特效的窗口才会创建属于自己的RenderingWindow（不要忘记，这是一个surface派生类哦）。成员函数Window::getTargetRenderingSurface用于获取窗口对象的surface，从中可以看出surface的从属关系：
RenderingSurface&#38; Window::getTargetRenderingSurface() const
{
    if (d_surface)
	// 优先使用自己的surface
        return *d_surface;
    else if (d_parent)
	// 如果自己没有surface，则使用父窗口的
        return d_parent-&#62;getTargetRenderingSurface();
    else
	// 最后如果自己已经是顶层窗口
	// 则使用Renderer默认的surface
        return System::getSingleton().getRenderer()-&#62;\
		getDefaultRenderingRoot();
}
实际的渲染调用流程从System::RenderGUI开始，它首先清空顶层窗口的surface渲染队列，然后调用顶层窗口的Window::render函数：
void Window::render()
{
	// 是否可见
	if [...]]]></description>
			<content:encoded><![CDATA[<p>粗略分析了CEGUI的渲染流程，总结一下供日后参考。CEGUI版本是0.7.5，OpenGL渲染器。</p>
<p>首先在CEGUI里面每张图片，每个字符都是一个quad，每个quad由2个三角面组成，包括6个顶点的坐标，颜色，纹理坐标，是发送给GPU的最基础的渲染单元。要注意的是，CEGUI并不局限于quad，它可以构造任意多的三角面以生成各种形状。要绘制一个窗口需要很多顶点数据，例如一个简单的button，背景图像需要1个quad（根据背景类型不同可能会更多），有4个文字的话又需要4个quad，总共就有5个quad，30个顶点。所以每个Window对象都有一个GeometryBuffer对象用来缓存自己的顶点数据。</p>
<p>窗口绘制输出的目的地称为RenderingSurface，所以每个窗口都要从属于某个RenderingSurface，否则无法显示。在渲染的时候，CEGUI会遍历所有的窗口，并将该窗口的GeometryBuffer依次提交到该窗口所属surface的渲染队列中去。对于CEGUI来说，这个过程就是“窗口绘制”，同时会触发绘制消息，而真正的绘制操作其实是在这之后。当所有相关的GeometryBuffer都被push进队列，RenderingSurface::draw就会被调用，此时顶点数据才真正提交到Renderer进行渲染输出。Renderer输出的目的地称为RenderTarget，由具体实现而定，可能是Frame buffer，Off-screen buffer，或者Texture object。在当前OpenGLRenderer中，是用纹理对象来实现的。之所以要引入RenderTarget，是为了能缓存surface的输出。</p>
<p>RenderingSurface有两个派生类，一个是RenderingRoot，它是Renderer默认的surface类型，在当前CEGUI的实现中只是对RenderingSurface的简单封装，没有任何额外的功能；第二个是RenderingWindow，它的作用是将渲染队列中的内容绘制到一张纹理图像上面，然后再用该纹理来绘制自己的GeometryBuffer到其他surface上面去。CEGUI 0.7中新增的窗口旋转和各种窗口特效就是通过这种方式来实现的。</p>
<p>在CEGUI中，每个Window对象都有自己的GeometryBuffer，但并不是每个窗口都有自己的surface。对于普通的四平八稳的窗口，它们共享由Renderer创建的RenderingRoot对象；而只有使用了旋转或特效的窗口才会创建属于自己的RenderingWindow（不要忘记，这是一个surface派生类哦）。成员函数Window::getTargetRenderingSurface用于获取窗口对象的surface，从中可以看出surface的从属关系：</p>
<pre class="brush:cpp; collapse:true">RenderingSurface&amp; Window::getTargetRenderingSurface() const
{
    if (d_surface)
	// 优先使用自己的surface
        return *d_surface;
    else if (d_parent)
	// 如果自己没有surface，则使用父窗口的
        return d_parent-&gt;getTargetRenderingSurface();
    else
	// 最后如果自己已经是顶层窗口
	// 则使用Renderer默认的surface
        return System::getSingleton().getRenderer()-&gt;\
		getDefaultRenderingRoot();
}</pre>
<p>实际的渲染调用流程从System::RenderGUI开始，它首先清空顶层窗口的surface渲染队列，然后调用顶层窗口的Window::render函数：</p>
<pre class="brush:cpp; collapse:true">void Window::render()
{
	// 是否可见
	if (!isVisible())
		return;

	// 获取render context，其中包含了当前窗口所从属的surface
	RenderingContext ctx;
	getRenderingContext(ctx);

	// 如果是自己的surface则清空geometry buffer
	if (ctx.owner == this)
		ctx.surface-&gt;clearGeometry();

	// 如果没有surface或者surface被标记为无效
	if (!d_surface || d_surface-&gt;isInvalidated())
	{
		// 绘制自己（生成顶点数据并提交给surface）
		drawSelf(ctx);

		// 递归调用所有子窗口的render
		const size_t child_count = getChildCount();
		for (size_t i = 0; i &lt; child_count; ++i)
			d_drawList[i]-&gt;render();
	}

	// 如果是自己的surface则提交GeometryBuffer到GPU进行渲染输出
	if (ctx.owner == this)
		ctx.surface-&gt;draw();
}</pre>
<p>在Window::drawSelf中: </p>
<pre class="brush:cpp; collapse:true">void Window::drawSelf(const RenderingContext&amp; ctx)
{
	// 构造GeometryBuffer
	bufferGeometry(ctx);
	// 提交GeometryBuffer到surface
	queueGeometry(ctx);
}

void Window::bufferGeometry(const RenderingContext&amp;)
{
	// 仅当需要的时候才重新构造顶点数据
	if (d_needsRedraw)
	{
		// 清空GeometryBuffer
		d_geometry-&gt;reset();

		// 触发相关的CEGUI渲染消息
		WindowEventArgs args(this);
		onRenderingStarted(args);

		// HACK: ensure our rendered string
		// content is up to date
		getRenderedString();

		// 这里才是真正产生顶点数据的地方
		if (d_windowRenderer)
			// 如果有指定的WindowRenderer
			// 则交给它来处理
			d_windowRenderer-&gt;render();
		else
			// 否则调用虚函数让用户自己负责生成
			populateGeometryBuffer();

		// 触发渲染结束消息
		args.handled = 0;
		onRenderingEnded(args);

		// mark ourselves as no longer needed a redraw.
		d_needsRedraw = false;
	}

}</pre>
<p>值得注意的是，CEGUI会记录各种窗口状态，只有在需要的时候才会重新构建窗口对象的GeometryBuffer，同样只有在需要的时候，才会重新绘制RenderingSurface。</p>
<p>上面代码中还提到了WindowRenderer（注意区分RenderingWindow）。这个class的作用是根据looknfeel的描述来生成生成顶点数据。在CEGUIFalagardWRBase工程下面有一大堆Fal开头的class，例如FalButton，FalEditbox等等，就是专门干这些工作的。经过WindowRenderer的层层调用，最终会落到以下三个component上面：</p>
<ol>
<li>FrameComponent：生成边框和背景 </li>
<li>ImageryComponent：生成静态图像 </li>
<li>TextComponent：生成文字 </li>
</ol>
<p>这几个component都是由FalagardComponentBase继承而来，作用是根据各种不同的配置，例如背景的样式，是否是边框，文字的排版方式等等，生成顶点数据。</p>
<p>综上，CEGUI的控件逻辑，控件样式，渲染数据是完全分离的。渲染部分采用两级缓存。第一级缓存用于记录顶点数据（GeometryBuffer）；第二级缓存将渲染结果保存在纹理上面（RenderingSurface）。</p>
<p>（完）</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/07/16/cegui%e6%b8%b2%e6%9f%93%e6%b5%81%e7%a8%8b%e7%ae%80%e6%9e%90/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GAE照片日志</title>
		<link>http://www.fancystar.org/2011/04/23/gae%e7%85%a7%e7%89%87%e6%97%a5%e5%bf%97/</link>
		<comments>http://www.fancystar.org/2011/04/23/gae%e7%85%a7%e7%89%87%e6%97%a5%e5%bf%97/#comments</comments>
		<pubDate>Sat, 23 Apr 2011 07:05:59 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[互联网]]></category>
		<category><![CDATA[编程札记]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[Internet]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/2011/04/23/gae%e7%85%a7%e7%89%87%e6%97%a5%e5%bf%97/</guid>
		<description><![CDATA[一直在写渲染相关的东西，前两天想放松一下，就用GAE搭了个照片日志。基本的设计思路是，每天用照片记录下生活的点滴，然后上传到网络供亲友间分享和日后回味。照片按拍摄日期进行管理和检索，并以日志的形式来显示。
相对于现有的各大门户提供的相册服务，大都是以照片集的方式来进行管理，貌似还没有原生支持日志式发布的产品。而且界面复杂，揉合了各种杂七杂八的社交功能（甚至广告），非常不喜（尤其以QQ空间为最）。所以我将页面简洁作为首要的设计原则。此外，GAE有1G的免费空间，足以媲美大部分的免费相册。借助GAE部署工具，照片更新上传速度非常迅速。而免费用户有1G的下行带宽，在我实际使用中，即使在开着VPN的情况下，访问速度也很令人满意。
但有一个最致命的缺陷，这也是我在部署到GAE服务器之后才发现的，那就是Google App被墙了（Fuck GFW 10,000遍啊！）。我原本是写了命令行发布工具的，结果部署之后死活连不上服务器，枉我还调试了半天。最后只好改为基于HTML的客户端，走浏览器以便使用各种翻墙工具。
具体效果请访问我的相册日志（请开启翻墙模式），而代码则托管在Google Code上。
工程下包括3个目录：

Client：客户端照片更新工具和帐号管理工具，用Python3写的 
Server：部署到GAE的服务器部分，Python2.6 
Tools：照片处理工具（基于FreeImage，目前只有Windows版本） 

第一次使用的时候需要到 client/config.py 和 server/config.py 下设置一些参数，包括本地照片目录，你的APP地址，照片规格等。然后运行 client/user.py，创建管理员帐号。这样初始化就完成了。每天更新照片的时候就执行 client/upadate_photos.py，它会自动搜索指定日期的照片并调整尺寸，然后复制到Server目录下。完成之后会弹出一个页面，你可以为每一张照片添加说明，并提交到服务器。最后就是运行GAE部署工具，将照片文件实际上传到GAE的服务器。
整个过程还是不够简单。如果对网络通信熟悉，应该可以再写一个脚本来自动化整个流程（原本我也是这样做的，但由于墙的存在…）。对于普通用户来说，可能还需要一个漂亮简洁的客户端。但作为我的玩具，暂时这样也就足够了。当然，我是欢迎有兴趣的朋友加入进来继续完善的。
]]></description>
			<content:encoded><![CDATA[<p>一直在写渲染相关的东西，前两天想放松一下，就用GAE搭了个照片日志。基本的设计思路是，每天用照片记录下生活的点滴，然后上传到网络供亲友间分享和日后回味。照片按拍摄日期进行管理和检索，并以日志的形式来显示。</p>
<p>相对于现有的各大门户提供的相册服务，大都是以照片集的方式来进行管理，貌似还没有原生支持日志式发布的产品。而且界面复杂，揉合了各种杂七杂八的社交功能（甚至广告），非常不喜（尤其以QQ空间为最）。所以我将页面简洁作为首要的设计原则。此外，GAE有1G的免费空间，足以媲美大部分的免费相册。借助GAE部署工具，照片更新上传速度非常迅速。而免费用户有1G的下行带宽，在我实际使用中，即使在开着VPN的情况下，访问速度也很令人满意。</p>
<p>但有一个<strong><font color="#c0504d">最致命的缺陷</font></strong>，这也是我在部署到GAE服务器之后才发现的，那就是<strong><font color="#c0504d">Google App被墙了</font></strong>（Fuck GFW 10,000遍啊！）。我原本是写了命令行发布工具的，结果部署之后死活连不上服务器，枉我还调试了半天。最后只好改为基于HTML的客户端，走浏览器以便使用各种翻墙工具。</p>
<p>具体效果请访问<a href="http://fancy-isle.appspot.com/photodiary/index.html">我的相册日志</a>（请开启翻墙模式），而代码则托管在<a href="http://code.google.com/p/fancy-isle/">Google Code</a>上。</p>
<p>工程下包括3个目录：</p>
<ol>
<li>Client：客户端照片更新工具和帐号管理工具，用Python3写的 </li>
<li>Server：部署到GAE的服务器部分，Python2.6 </li>
<li>Tools：照片处理工具（基于FreeImage，目前只有Windows版本） </li>
</ol>
<p>第一次使用的时候需要到 <em><strong>client/config.py</strong> </em>和 <em><strong>server/config.py</strong> </em>下设置一些参数，包括本地照片目录，你的APP地址，照片规格等。然后运行 <em><strong>client/user.py</strong></em>，创建管理员帐号。这样初始化就完成了。每天更新照片的时候就执行 <em><strong>client/upadate_photos.py</strong></em>，它会自动搜索指定日期的照片并调整尺寸，然后复制到Server目录下。完成之后会弹出一个页面，你可以为每一张照片添加说明，并提交到服务器。最后就是运行GAE部署工具，将照片文件实际上传到GAE的服务器。</p>
<p>整个过程还是不够简单。如果对网络通信熟悉，应该可以再写一个脚本来自动化整个流程（原本我也是这样做的，但由于墙的存在…）。对于普通用户来说，可能还需要一个漂亮简洁的客户端。但作为我的玩具，暂时这样也就足够了<img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/wlEmoticon-smile.png" />。当然，我是欢迎有兴趣的朋友加入进来继续完善的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/04/23/gae%e7%85%a7%e7%89%87%e6%97%a5%e5%bf%97/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VPN服务推荐：Astrill.com</title>
		<link>http://www.fancystar.org/2011/04/10/vpn%e6%9c%8d%e5%8a%a1%e6%8e%a8%e8%8d%90%ef%bc%9aastrill-com/</link>
		<comments>http://www.fancystar.org/2011/04/10/vpn%e6%9c%8d%e5%8a%a1%e6%8e%a8%e8%8d%90%ef%bc%9aastrill-com/#comments</comments>
		<pubDate>Sun, 10 Apr 2011 05:32:46 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[互联网]]></category>
		<category><![CDATA[Internet]]></category>
		<category><![CDATA[VPN]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/2011/04/10/vpn%e6%9c%8d%e5%8a%a1%e6%8e%a8%e8%8d%90%ef%bc%9aastrill-com/</guid>
		<description><![CDATA[国内的互联网是怎样一个糟糕的情况，就不用多说了。自Google选择离开之后，其所有的服务都遭到了严重的干扰，以至俺日常的工作都受到极大的影响。相信IT行业的朋友都会有同感。当时Google事件的时候，我就预感到会有今天的情形，于是开始使用付费的VPN服务。在试了一轮之后，其中一个我用到了现在，这就是Astrill.com。
Astrill只有月费，季度和年费三种方式，是不限流量的（对，你没有看错，不限流量！）。月费最贵，大概60RMB左右。但如果包年的话，那么每月折合大概也就35RMB左右。相比其他很多VPN，Astrill的费用确实比较昂贵，但其在稳定性，易用性和服务上却也优秀得多。
Astrill的使用非常简单。首先到官网上下载客户端：

可以看到，所有的通用平台都支持了。下载完成后安装，一路点“Next”，它会自动为你搞掂所有的网络设置，其中可能还需要给你的浏览器安装插件。完成后运行客户端，是这个样子：

这时你需要一个Astrill的帐号。有两种方式，一是直接到官网上注册；二是由Astrill的付费用户给你发邀请邮件。选择后者你可以获得7天的免费试用期。想要获得邀请的同学可以给我发邮件：wangyongcong#gmail.com（请把#改为@）
完成注册之后登录，是这个样子：

看到那个大大的&#34;ON&#34;按钮了么，那就是关闭/开启VPN的按钮。不想用的时候点一下就关闭了。就这么简单。你不需要折腾任何东西，不用设置Windows网络，也不用安装OpenVPN什么的。我刚开始使用VPN的时候，当时所在的公司网络环境比较复杂，试用过好几个VPN，或者无法使用，或者需要执行非常复杂的设置，只有Astrill能够无需任何设置就正常工作。这也是我当时转向Astrill的重要原因之一。
Astrill提供了一大串的服务器列表可以供你选择：

服务器很多，所在地遍布欧美，还有香港和中国的服务器，甚至还有一些服务器后面直接注明”Optimized for China”（但实际只是ping值大大降低，速度改善不明显…）。在选择了一个服务器之后，你可以测试速度：

这个只能做参考，根据你的使用情况选择一个合适的。通常欧美的服务器ping值很高，但download速度很快。而像香港的服务器，ping值通常可以维持在20MS以下，但速度则只有40KB/S左右（这个download speed估计就是带宽相关）。
根据我实际使用的经验，选择美国的服务器，像Facebook，twitter这样的，基本和访问国内的站点没啥区别。但youtube还是不能流畅播放，需要缓冲。对于P2P下载，Astrill则完全没有影响，照样满速。
另外Astrill还有很多高级的设置，包括针对视频和Flash进行优化，对于指定的网站不使用VPN而是直接访问等：

有过两次不知怎么回事，速度变得很糟很不稳定，让我大为火光，投诉之（在Help菜单里就有“Contact Us”按钮）。然后很快就会有客服邮件联系，并发起一个Ticket，你可以不断跟踪和反馈处理的结果，还可以给客服的回答打分。通常刚开始都是让你确认并安装最新版本的客户端之类（我怀疑是不是机器人…）。如果你不依不饶的话，就会有个家伙出来告诉你在Advance选项里应该怎样设置会更好。我上一回的投诉到这里就结束了，按照他提供的设置（就是上图所示），速度马上有了很大的改善。
OK，就介绍到这里了。具体还是自己体会吧，有兴趣的朋友给我发email，邀请注册可以获得7天的免费使用哦。如果你最终成为了Astrill的付费用户，作为邀请人的我则可以获得30天的续期，呵呵。另外如果哪位朋友有用过更优秀的VPN，也希望能告知分享一下，谢谢。
]]></description>
			<content:encoded><![CDATA[<p>国内的互联网是怎样一个糟糕的情况，就不用多说了。自Google选择离开之后，其所有的服务都遭到了严重的干扰，以至俺日常的工作都受到极大的影响。相信IT行业的朋友都会有同感。当时Google事件的时候，我就预感到会有今天的情形，于是开始使用付费的VPN服务。在试了一轮之后，其中一个我用到了现在，这就是<a href="http://www.astrill.com">Astrill.com</a>。</p>
<p><strong>Astrill只有月费，季度和年费三种方式，是不限流量的</strong>（对，你没有看错，不限流量！）。月费最贵，大概60RMB左右。但如果包年的话，那么每月折合大概也就35RMB左右。相比其他很多VPN，Astrill的费用确实比较昂贵，但其在稳定性，易用性和服务上却也优秀得多。</p>
<p>Astrill的使用非常简单。首先到官网上下载客户端：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/MQIOV7NJ7KBRED5KMNA.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="MQIO$V7NJ7KBR~}E[D5KMNA" border="0" alt="MQIO$V7NJ7KBR~}E[D5KMNA" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/MQIOV7NJ7KBRED5KMNA_thumb.jpg" width="326" height="172" /></a></p>
<p>可以看到，所有的通用平台都支持了。下载完成后安装，一路点“Next”，它会自动为你搞掂所有的网络设置，其中可能还需要给你的浏览器安装插件。完成后运行客户端，是这个样子：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/AAW_16CM_O4QWIW88SH.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="AAW_16CM]$_$O4(QWIW88SH" border="0" alt="AAW_16CM]$_$O4(QWIW88SH" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/AAW_16CM_O4QWIW88SH_thumb.jpg" width="198" height="244" /></a></p>
<p>这时你需要一个Astrill的帐号。有两种方式，一是直接到官网上注册；二是由Astrill的付费用户给你发邀请邮件。选择后者你可以获得7天的免费试用期。想要获得邀请的同学可以给我发邮件：wangyongcong#gmail.com（请把#改为@）</p>
<p>完成注册之后登录，是这个样子：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/0VGEN1A8CMNEGUPCER5.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="0VGE}N1A8CMNEG~]]U%PCER[5]" border="0" alt="0VGE}N1A8CMNEG~]]U%PCER[5]" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/0VGEN1A8CMNEGUPCER5_thumb.jpg" width="194" height="244" /></a></p>
<p>看到那个大大的&quot;ON&quot;按钮了么，那就是关闭/开启VPN的按钮。不想用的时候点一下就关闭了。就这么简单。你不需要折腾任何东西，不用设置Windows网络，也不用安装OpenVPN什么的。我刚开始使用VPN的时候，当时所在的公司网络环境比较复杂，试用过好几个VPN，或者无法使用，或者需要执行非常复杂的设置，只有Astrill能够无需任何设置就正常工作。这也是我当时转向Astrill的重要原因之一。</p>
<p>Astrill提供了一大串的服务器列表可以供你选择：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image_thumb.png" width="194" height="244" /></a></p>
<p>服务器很多，所在地遍布欧美，还有香港和中国的服务器，甚至还有一些服务器后面直接注明”Optimized for China”（但实际只是ping值大大降低，速度改善不明显…）。在选择了一个服务器之后，你可以测试速度：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image1.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image_thumb1.png" width="418" height="273" /></a></p>
<p>这个只能做参考，根据你的使用情况选择一个合适的。通常欧美的服务器ping值很高，但download速度很快。而像香港的服务器，ping值通常可以维持在20MS以下，但速度则只有40KB/S左右（这个download speed估计就是带宽相关）。</p>
<p>根据我实际使用的经验，选择美国的服务器，像Facebook，twitter这样的，基本和访问国内的站点没啥区别。但youtube还是不能流畅播放，需要缓冲。对于P2P下载，Astrill则完全没有影响，照样满速。</p>
<p>另外Astrill还有很多高级的设置，包括针对视频和Flash进行优化，对于指定的网站不使用VPN而是直接访问等：</p>
<p><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image2.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/04/image_thumb2.png" width="194" height="244" /></a></p>
<p>有过两次不知怎么回事，速度变得很糟很不稳定，让我大为火光，投诉之（在Help菜单里就有“Contact Us”按钮）。然后很快就会有客服邮件联系，并发起一个Ticket，你可以不断跟踪和反馈处理的结果，还可以给客服的回答打分。通常刚开始都是让你确认并安装最新版本的客户端之类（我怀疑是不是机器人…）。如果你不依不饶的话，就会有个家伙出来告诉你在Advance选项里应该怎样设置会更好。我上一回的投诉到这里就结束了，按照他提供的设置（就是上图所示），速度马上有了很大的改善。</p>
<p>OK，就介绍到这里了。具体还是自己体会吧，有兴趣的朋友给我发email，邀请注册可以获得7天的免费使用哦。如果你最终成为了Astrill的付费用户，作为邀请人的我则可以获得30天的续期，呵呵。另外如果哪位朋友有用过更优秀的VPN，也希望能告知分享一下，谢谢。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/04/10/vpn%e6%9c%8d%e5%8a%a1%e6%8e%a8%e8%8d%90%ef%bc%9aastrill-com/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>C++向量数学库</title>
		<link>http://www.fancystar.org/2011/03/27/c%e5%90%91%e9%87%8f%e6%95%b0%e5%ad%a6%e5%ba%93/</link>
		<comments>http://www.fancystar.org/2011/03/27/c%e5%90%91%e9%87%8f%e6%95%b0%e5%ad%a6%e5%ba%93/#comments</comments>
		<pubDate>Sun, 27 Mar 2011 15:05:43 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[编程札记]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[math]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/?p=84</guid>
		<description><![CDATA[这两天重新测试了Fancystar数学库中的向量和矩阵的部分。由于当时写的时候并没有执行过全面的测试，所以心里一直都不踏实。结果证明我的担心是对的。尽管有些类已经用了好几年，还是揪出了一些BUG，再次印证了：任何没有跑过的分支都是不可靠的。
挑选了两个轻量级的线性代数库作为测试基准，选它们的原因，一是库比较小，而且都是专注于矩阵的运算，效率较好；二是文档全，容易使用：
Eigen：引用官网的描述就是：Fast，Reliable，Elegen。尽管有点黄婆卖瓜，但经过我这两天使用下来，感觉确实不虚。
Armadillo：澳大利亚人写的（看名字就很澳大利亚），也是非常容易使用。
二者都是基于template的C++库，除了基本的向量和矩阵数学之外，还支持很多其他的数学运算。矩阵都支持固定大小和动态大小两种。前者是在template参数中传入行列数，速度较快，用于3&#215;3，4&#215;4之类的小型矩阵，这正是我所需要的。后者动态分配内存，可在执行时动态改变大小，用于科学计算中的大型矩阵。
另外还有一些其他的向量数学库，包括：
LAPACK：非常著名的线性代数库，Fortran写的，C版本为CLAPACK。Eigen和Armadillo中的有些功能也都是借助LAPACK来实现的，比较重型，压缩包超过4M
Matrix TCL Pro：Pro版是收费的，免费的Lite版只有1个头文件，绝对是最轻型的矩阵库了（当然，Fancystar除外）
Intel Math Kernel Library：Intel的数学库，价格不菲，评估版足有300M之巨，针对多线程和自家的CPU高度优化，据说是”incredibel fast”。对其性能我是相当感兴趣，有机会要试用一下
测试中比较麻烦的是浮点数比较的问题。不同的浮点数运算顺序产生的结果可能会有微小的偏差，直接按位来比较是不行的。这里有篇关于浮点数比较的经典文章：Comparing floating point numbers by Bruce Dawson。StackOverflow上也有一篇不错的帖子：Most effective way for float and double comparison，里面提到了 Knuth 和 Google Test 的算法。
我采用的策略是，首先检查绝对误差是否在阀值之内，如果不通过，再检查相对误差是否在阀值之内，同时统计误差数和最大误差值。我设置的绝对误差的阀值是1E-6，相对误差的阀值为0.005。多轮测试下来，主要问题集中在逆阵的算法上。由于Fancystar的4&#215;4以下的矩阵都是用克拉默法则来算逆阵，在行列式的值比较小的情况下（&#60;1E-4），有可能会产生很大的浮点误差。根据测试的结果，100W级数下，3&#215;3方阵通常都会有2～3次的比较失败，而4&#215;4方阵则达到10次之多。如果采用Gauss-Jordan Eliminant，失败的次数会显著减少，但速度会比较慢，在这方面Fancystar还有待改善。
排除突发的大误差，跟Eigen库的平均误差值和最大误差值如下（100W次运算，测试数据范围：-1.0 ~ 1.0）：



&#160;
Matrix 3&#215;3 
Matrix 4&#215;4


绝对误差
0.000034
0.000014 


最大绝对误差
55.332031
55.332031


相对误差
0.000175%
0.000250%


最大相对误差
0.137957%
0.150691%



&#160;
速度方面，Eigen的评价表现是最好的，而Armadillo则欠佳。Fancystar的3&#215;3矩阵通过模板特化来专门优化，大部分的运算都是在线展开的，相较之下表现非常优秀。而4&#215;4矩阵原先是采用通用模板，除了求逆，其余均介于Eigen和Armadillo之间。而求逆算法耗费的时间却是Eigen的3～4倍。后来当4&#215;4矩阵也特化了之后，加法和乘法运算采用在线展开的方式，效率均优于Eigen。这有利于super-scalar和out of order，但生成的代码却会变大。而逆阵改用克拉默法则来计算，效率提升了一倍，但还是低于Eigen很多。看来3&#215;3是克拉默算法的效率拐点。
速度测试结果如下（100W次运算，数据范围：-1.0 ~ 1.0）：




Matrix 3&#215;3
Fancystar
Eigen
Armadillo


矩阵加法
0.016710
0.017571
0.021266


矩阵乘法
0.032410
0.040037
0.103627


计算逆阵
0.040567
0.049416
0.050305






Matrix 4&#215;4
Fancystar
Eigen
Armadillo


矩阵加法
0.020215
0.021257
0.024995


矩阵乘法
0.057874
0.086725
0.188035


计算逆阵
0.264499
0.167397
0.199870



&#160;
话说我看了下Eigen的代码，顿时泪流满面：无数的模板参数，类型来来回回地折腾，可读性约等于0，只能说作者太牛逼了… 相比之下，Armadillo则要清晰得多。 
在Numerical Recipes上看到一种新的逆阵算法Strassen&#8217;s Method，我实现了4&#215;4矩阵的版本，速度相当快，平均为0.06左右（Eigen为0.16）。但该算法的缺点是，浮点误差会被累积放大（在关键变量上使用double可以一定程度降低累积误差的影响），尽管对于3D游戏的精度要求来说影响不大。
]]></description>
			<content:encoded><![CDATA[<p>这两天重新测试了Fancystar数学库中的向量和矩阵的部分。由于当时写的时候并没有执行过全面的测试，所以心里一直都不踏实。结果证明我的担心是对的。尽管有些类已经用了好几年，还是揪出了一些BUG，再次印证了：任何没有跑过的分支都是不可靠的。</p>
<p>挑选了两个轻量级的线性代数库作为测试基准，选它们的原因，一是库比较小，而且都是专注于矩阵的运算，效率较好；二是文档全，容易使用：</p>
<p><strong><a href="http://eigen.tuxfamily.org/index.php?title=Main_Page">Eigen</a></strong>：引用官网的描述就是：Fast，Reliable，Elegen。尽管有点黄婆卖瓜，但经过我这两天使用下来，感觉确实不虚。</p>
<p><strong><a href="http://arma.sourceforge.net/">Armadillo</a></strong>：澳大利亚人写的（看名字就很澳大利亚），也是非常容易使用。</p>
<p>二者都是基于template的C++库，除了基本的向量和矩阵数学之外，还支持很多其他的数学运算。矩阵都支持固定大小和动态大小两种。前者是在template参数中传入行列数，速度较快，用于3&#215;3，4&#215;4之类的小型矩阵，这正是我所需要的。后者动态分配内存，可在执行时动态改变大小，用于科学计算中的大型矩阵。</p>
<p>另外还有一些其他的向量数学库，包括：</p>
<p><a href="http://www.netlib.org/lapack/">LAPACK</a>：非常著名的线性代数库，Fortran写的，C版本为<a href="http://www.netlib.org/clapack/">CLAPACK</a>。Eigen和Armadillo中的有些功能也都是借助LAPACK来实现的，比较重型，压缩包超过4M</p>
<p><a href="http://www.techsoftpl.com/matrix/index.htm">Matrix TCL Pro</a>：Pro版是收费的，免费的Lite版只有1个头文件，绝对是最轻型的矩阵库了（当然，Fancystar除外<img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="微笑" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/03/wlEmoticon-smile.png" />）</p>
<p><a href="http://www.intel.com/cd/software/products/apac/zho/329191.htm">Intel Math Kernel Library</a>：Intel的数学库，价格不菲，评估版足有300M之巨，针对多线程和自家的CPU高度优化，据说是<strong>”incredibel fast”</strong>。对其性能我是相当感兴趣，有机会要试用一下</p>
<p>测试中比较麻烦的是浮点数比较的问题。不同的浮点数运算顺序产生的结果可能会有微小的偏差，直接按位来比较是不行的。这里有篇关于浮点数比较的经典文章：<a href="http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm"><strong>Comparing floating point numbers</strong></a><strong> <em>by Bruce Dawson</em></strong>。StackOverflow上也有一篇不错的帖子：<a href="http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison">Most effective way for float and double comparison</a>，里面提到了 Knuth 和 Google Test 的算法。</p>
<p>我采用的策略是，首先检查绝对误差是否在阀值之内，如果不通过，再检查相对误差是否在阀值之内，同时统计误差数和最大误差值。我设置的绝对误差的阀值是<strong><em>1E-6</em></strong>，相对误差的阀值为<strong><em>0.005</em></strong>。多轮测试下来，主要问题集中在逆阵的算法上。由于Fancystar的4&#215;4以下的矩阵都是用克拉默法则来算逆阵，在行列式的值比较小的情况下（&lt;1E-4），有可能会产生很大的浮点误差。根据测试的结果，100W级数下，3&#215;3方阵通常都会有2～3次的比较失败，而4&#215;4方阵则达到10次之多。如果采用Gauss-Jordan Eliminant，失败的次数会显著减少，但速度会比较慢，在这方面Fancystar还有待改善。</p>
<p>排除突发的大误差，跟Eigen库的平均误差值和最大误差值如下（100W次运算，测试数据范围：-1.0 ~ 1.0）：</p>
<table border="0" cellspacing="0" cellpadding="2" width="400">
<tbody>
<tr>
<td valign="top" width="133">&#160;</td>
<td valign="top" width="133">Matrix 3&#215;3 </td>
<td valign="top" width="133">Matrix 4&#215;4</td>
</tr>
<tr>
<td valign="top" width="133">绝对误差</td>
<td valign="top" width="133">0.000034</td>
<td valign="top" width="133">0.000014 </td>
</tr>
<tr>
<td valign="top" width="133">最大绝对误差</td>
<td valign="top" width="133">55.332031</td>
<td valign="top" width="133">55.332031</td>
</tr>
<tr>
<td valign="top" width="133">相对误差</td>
<td valign="top" width="133">0.000175%</td>
<td valign="top" width="133">0.000250%</td>
</tr>
<tr>
<td valign="top" width="133">最大相对误差</td>
<td valign="top" width="133">0.137957%</td>
<td valign="top" width="133">0.150691%</td>
</tr>
</tbody>
</table>
<p>&#160;</p>
<p>速度方面，Eigen的评价表现是最好的，而Armadillo则欠佳。Fancystar的3&#215;3矩阵通过模板特化来专门优化，大部分的运算都是在线展开的，相较之下表现非常优秀。而4&#215;4矩阵原先是采用通用模板，除了求逆，其余均介于Eigen和Armadillo之间。而求逆算法耗费的时间却是Eigen的3～4倍。后来当4&#215;4矩阵也特化了之后，加法和乘法运算采用在线展开的方式，效率均优于Eigen。这有利于super-scalar和out of order，但生成的代码却会变大。而逆阵改用克拉默法则来计算，效率提升了一倍，但还是低于Eigen很多。看来3&#215;3是克拉默算法的效率拐点。</p>
<p>速度测试结果如下（100W次运算，数据范围：-1.0 ~ 1.0）：</p>
<p>
<table border="0" cellspacing="0" cellpadding="2" width="400">
<tbody>
<tr>
<td valign="top" width="100">Matrix 3&#215;3</td>
<td valign="top" width="100"><strong>Fancystar</strong></td>
<td valign="top" width="100"><strong>Eigen</strong></td>
<td valign="top" width="100"><strong>Armadillo</strong></td>
</tr>
<tr>
<td valign="top" width="100"><strong>矩阵加法</strong></td>
<td valign="top" width="100">0.016710</td>
<td valign="top" width="100">0.017571</td>
<td valign="top" width="100">0.021266</td>
</tr>
<tr>
<td valign="top" width="100"><strong>矩阵乘法</strong></td>
<td valign="top" width="100">0.032410</td>
<td valign="top" width="100">0.040037</td>
<td valign="top" width="100">0.103627</td>
</tr>
<tr>
<td valign="top" width="100"><strong>计算逆阵</strong></td>
<td valign="top" width="100">0.040567</td>
<td valign="top" width="100">0.049416</td>
<td valign="top" width="100">0.050305</td>
</tr>
</tbody>
</table>
<table border="0" cellspacing="0" cellpadding="2" width="400">
<tbody>
<tr>
<td valign="top" width="100">Matrix 4&#215;4</td>
<td valign="top" width="100"><strong>Fancystar</strong></td>
<td valign="top" width="100"><strong>Eigen</strong></td>
<td valign="top" width="100"><strong>Armadillo</strong></td>
</tr>
<tr>
<td valign="top" width="100"><strong>矩阵加法</strong></td>
<td valign="top" width="100">0.020215</td>
<td valign="top" width="100">0.021257</td>
<td valign="top" width="100">0.024995</td>
</tr>
<tr>
<td valign="top" width="100"><strong>矩阵乘法</strong></td>
<td valign="top" width="100">0.057874</td>
<td valign="top" width="100">0.086725</td>
<td valign="top" width="100">0.188035</td>
</tr>
<tr>
<td valign="top" width="100"><strong>计算逆阵</strong></td>
<td valign="top" width="100">0.264499</td>
<td valign="top" width="100">0.167397</td>
<td valign="top" width="100">0.199870</td>
</tr>
</tbody>
</table>
<p>&#160;</p>
<p>话说我看了下Eigen的代码，顿时泪流满面：无数的模板参数，类型来来回回地折腾，可读性约等于0，只能说作者太牛逼了… 相比之下，Armadillo则要清晰得多。 </p>
<hr />在<a href="http://www.amazon.com/Numerical-Recipes-3rd-Scientific-Computing/dp/0521880688">Numerical Recipes</a>上看到一种新的逆阵算法Strassen&#8217;s Method，我实现了4&#215;4矩阵的版本，速度相当快，平均为0.06左右（Eigen为0.16）。但该算法的缺点是，浮点误差会被累积放大（在关键变量上使用double可以一定程度降低累积误差的影响），尽管对于3D游戏的精度要求来说影响不大。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/03/27/c%e5%90%91%e9%87%8f%e6%95%b0%e5%ad%a6%e5%ba%93/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于C++对象的创建和删除</title>
		<link>http://www.fancystar.org/2011/03/13/%e5%85%b3%e4%ba%8ec%e5%af%b9%e8%b1%a1%e7%9a%84%e5%88%9b%e5%bb%ba%e5%92%8c%e5%88%a0%e9%99%a4/</link>
		<comments>http://www.fancystar.org/2011/03/13/%e5%85%b3%e4%ba%8ec%e5%af%b9%e8%b1%a1%e7%9a%84%e5%88%9b%e5%bb%ba%e5%92%8c%e5%88%a0%e9%99%a4/#comments</comments>
		<pubDate>Sun, 13 Mar 2011 15:03:19 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[编程札记]]></category>
		<category><![CDATA[C++]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/2011/03/13/%e5%85%b3%e4%ba%8ec%e5%af%b9%e8%b1%a1%e7%9a%84%e5%88%9b%e5%bb%ba%e5%92%8c%e5%88%a0%e9%99%a4%e7%9a%84%e4%b8%80%e4%ba%9b%e5%8f%8d%e6%80%9d/</guid>
		<description><![CDATA[如果需要通过base class的指针来删除一个derived class，那么该base class必须拥有一个virtual 析构函数，否则会导致未定义的行为。
这里的“未定义行为”通常是部分析构，内存泄漏。这条约束无论是对于单继承，还是多重继承，都是适用的，尽管多重继承在C++是一个需要避免的麻烦。考虑以下这个比较极端的多重继承的例子：

如果要通过base01的指针来删除my class对象，则base01必须要有virtual析构函数；如果只需要通过derived01的指针来删除my class对象，则virtual析构函数可以仅是在derived01中定义（此时base01是没有的，但my class会有）。base02，base03也同理，仅当需要通过其指针删除my class对象的时候，才需要定义virtual析构函数。
通常，对于继承体系的设计应遵循以下的原则：

尽量避免多重继承 
如果无法避免，则尽量只有一个实基类，其他都是不带数据的纯虚基类 
多重继承下，从base class指针转换到derived class指针必须使用dynamic_cast，这也意味着必须开启RTTI 

另外还想说下operator new和operator delete（以及它们的数组版本）的重载。这两个运算符均可在class级别进行重载，用于定制new和delete的行为，起作用包括更高效的内存分配策略，以及追踪记录内存的使用情况等。
operator new最基本的形式是：
void* operator new (size_t size);
其实除了第一个参数必须是待分配的内存数量之外，可以有任意的重载版本：
class myclass
{
public:
	static void* operator new (size_t size, int arg1, int arg2);
};
myclass *pobj=new(arg1,arg2) myclass;
其中一个比较常见的重载版本就是placement new，在标准库的&#60;new&#62;中定义：
// 第二个参数指定内存的位置
void* operator new (size_t size, void* place);
而operator delete的基本形式则有两种：
void operator delete (void* ptr);
//size为ptr所指内存的大小
void operator delete (void* ptr, size_t size);
但在同一个作用域内二者不能共存，只能选择实现其中一种形式。
每种operator new的重载，都必须要有一个对应的operator delete的实现（具有相同的参数列表），它们各有其使用的场合：
class myclass
{
public:
	static [...]]]></description>
			<content:encoded><![CDATA[<p><font face="微软雅黑"><em><strong>如果需要通过base class的指针来删除一个derived class，那么该base class必须拥有一个virtual 析构函数，否则会导致未定义的行为。</strong></em></font></p>
<p><font face="微软雅黑">这里的“未定义行为”通常是部分析构，内存泄漏。这条约束无论是对于单继承，还是多重继承，都是适用的，尽管多重继承在C++是一个需要避免的麻烦。</font><font face="微软雅黑">考虑以下这个比较极端的多重继承的例子：</font></p>
<p><font face="微软雅黑"><a class="thickbox" href="http://www.fancystar.org/wordpress/wp-content/uploads/2011/03/565c8791.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="565c8791" border="0" alt="565c8791" src="http://www.fancystar.org/wordpress/wp-content/uploads/2011/03/565c8791_thumb.png" width="260" height="244" /></a></font></p>
<p><font face="微软雅黑">如果要通过base01的指针来删除my class对象，则base01必须要有virtual析构函数；如果只需要通过derived01的指针来删除my class对象，则virtual析构函数可以仅是在derived01中定义（此时base01是没有的，但my class会有）。base02，base03也同理，仅当需要通过其指针删除my class对象的时候，才需要定义virtual析构函数。</font></p>
<p><font face="微软雅黑">通常，对于继承体系的设计应遵循以下的原则：</font></p>
<ul>
<li><font face="微软雅黑">尽量避免多重继承</font> </li>
<li><font face="微软雅黑">如果无法避免，则尽量只有一个实基类，其他都是不带数据的纯虚基类</font> </li>
<li><font face="微软雅黑">多重继承下，从base class指针转换到derived class指针必须使用dynamic_cast，这也意味着必须开启RTTI</font> </li>
</ul>
<p><font face="微软雅黑">另外还想说下<em><strong>operator new</strong></em>和<strong><em>operator delete</em></strong>（以及它们的数组版本）的重载。这两个运算符均可在class级别进行重载，用于定制new和delete的行为，起作用包括更高效的内存分配策略，以及追踪记录内存的使用情况等。</font></p>
<p><font face="微软雅黑"><em><strong>operator new</strong></em>最基本的形式是：</font></p>
<pre class="brush:cpp">void* operator new (size_t size);</pre>
<p>其实除了第一个参数必须是待分配的内存数量之外，可以有任意的重载版本：</p>
<pre class="brush:cpp">class myclass
{
public:
	static void* operator new (size_t size, int arg1, int arg2);
};
myclass *pobj=new(arg1,arg2) myclass;</pre>
<p>其中一个比较常见的重载版本就是<em><strong>placement new</strong></em>，在标准库的<font color="#0000ff">&lt;new&gt;</font>中定义：</p>
<pre class="brush:cpp">// 第二个参数指定内存的位置
void* operator new (size_t size, void* place);</pre>
<p>而<em><strong>operator delete</strong></em>的基本形式则有两种：</p>
<pre class="brush:cpp">void operator delete (void* ptr);
//size为ptr所指内存的大小
void operator delete (void* ptr, size_t size);</pre>
<p>但在同一个作用域内二者不能共存，只能选择实现其中一种形式。</p>
<p>每种<em><strong>operator new</strong></em>的重载，都必须要有一个对应的<em><strong>operator delete</strong></em>的实现（具有相同的参数列表），它们各有其使用的场合：</p>
<pre class="brush:cpp">class myclass
{
public:
	static void* operator new (size_t size);
	static void operator delete (void *ptr);
	static void* operator new (size_t size, int arg1, int arg2);
	static void operator delete (void *ptr, int arg1, int arg2);
};
myclass *pobj=0;
//调用 new (size_t size)
pobj=new myclass;
//调用 delete (void *ptr)
delete pobj;
//调用 new (size_t size, int arg1, int arg2)
pobj=new(arg1,arg2) myclass;
/*
  如果myclass的构造函数抛出了异常，系统内部就会
  调用 delete(void *ptr, int arg1, int arg2)
  来尝试回滚到之前的状态
*/
delete pobj; //调用 delete(void *ptr)</pre>
<p>数组版本的<em><strong>operator new[]</strong></em>和<em><strong>operaotr delete[]</strong></em>也遵循同样的规则。</p>
<p>在重载<em><strong>operator new</strong></em>和<em><strong>operator delete</strong></em>的时候，也需要遵循一些惯例，以使其行为跟标准的new和delete保持一致。这方面最好的参考资料就是<font color="#0000ff">《Effective C++》<em>by Scott Meyers</em></font></p>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2011/03/13/%e5%85%b3%e4%ba%8ec%e5%af%b9%e8%b1%a1%e7%9a%84%e5%88%9b%e5%bb%ba%e5%92%8c%e5%88%a0%e9%99%a4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC的RTTI选项</title>
		<link>http://www.fancystar.org/2010/10/26/vc%e7%9a%84rtti%e9%80%89%e9%a1%b9/</link>
		<comments>http://www.fancystar.org/2010/10/26/vc%e7%9a%84rtti%e9%80%89%e9%a1%b9/#comments</comments>
		<pubDate>Tue, 26 Oct 2010 14:20:39 +0000</pubDate>
		<dc:creator>黑鸟</dc:creator>
				<category><![CDATA[编程札记]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://www.fancystar.org/2010/10/26/%e9%93%be%e6%8e%a5%e5%bc%80%e5%90%af%e5%85%b3%e9%97%adrtti%e9%80%89%e9%a1%b9%e7%9a%84%e4%bb%a3%e7%a0%81/</guid>
		<description><![CDATA[liba为开启了RTTI选项的static library，b.cpp使用liba里的类（带有虚函数），并且编译的时候关闭RTTI，结果会如何呢？
C++ standard没有说明，那就只能依赖各家编译器。我在VC2008 Express上做了测试，包括在开启和关闭优化，以及多继承的情况，结果都是：
无论liba在编译的时候是否有开启RTTI，只要编译b.cpp的时候打开RTTI，则dynamic_cast和typeid就能正常工作，反之则会抛出no_rtti异常
补充说明一下，即使在liba中调用dynamic_cast，只要b.cpp关闭RTTI，也一样会抛异常。
对此结果有点疑惑。如果VC的RTTI实现是在虚函数表中插入typeinfo结构的指针的话，那么在编译liba的时候如果关闭RTTI，显然不会生成这些东西，那么b.cpp即使开启了RTTI，也无法使liba中的class的RTTI机制能正常工作。难道是typeinfo和虚函数表都是链接的时候生成的？
当liba为dll的时候又会如何呢？stack overflow上有个帖子也提到了这个问题：Is it allowed to link rtti enabled DLL within rtti disabled application? 作者写了个dll，里面调用了dynamic_cast，打开RTTI编译输出；然后在另一个程序中调用该dll的函数，并关闭RTTI。结果可以正常工作。
这其实是一个相当无趣的语言问题，但却又无法忽略。C++在这点上确实非常令人讨厌！对于这些undefined的事情，还是少碰为妙。所以：
1，存在多态的地方，要么全部RTTI，要么全部non-RTTI；
2，如果你的lib开启了RTTI，则意味着你的client也必须开启RTTI；
3，或者，干脆对C++的RTTI SAY NO，可以省掉不少麻烦:)
最后附上测试代码：

//==================
// File：a.h
//==================
class base
{
	int m_ival;
public:
	base() {
		m_ival=0;
	}
	virtual ~base() {
		wyc_print("destroy base");
	}
};

class derived : public base
{
	int m_ival;
public:
	derived() {
		m_ival=0;
	}
	virtual ~derived() {
		wyc_print("destroy derived");
	}
}; 

class sum_interface
{
	int m_cnt;
public:
	sum_interface() {
		m_cnt=0;
	}
	virtual ~sum_interface() {
		wyc_print("destroy sum");
	}
	virtual void sum() {
		m_cnt+=1;
	} 

}; 

class third : public derived, public sum_interface
{
	float m_fval;
public:
	third() {
		m_fval=0;
	}
	virtual [...]]]></description>
			<content:encoded><![CDATA[<p>liba为开启了RTTI选项的static library，b.cpp使用liba里的类（带有虚函数），并且编译的时候关闭RTTI，结果会如何呢？</p>
<p>C++ standard没有说明，那就只能依赖各家编译器。我在VC2008 Express上做了测试，包括在开启和关闭优化，以及多继承的情况，结果都是：</p>
<blockquote><p><strong>无论liba在编译的时候是否有开启RTTI，只要编译b.cpp的时候打开RTTI，则dynamic_cast和typeid就能正常工作，反之则会抛出no_rtti异常</strong></p></blockquote>
<p>补充说明一下，即使在liba中调用dynamic_cast，只要b.cpp关闭RTTI，也一样会抛异常。</p>
<p>对此结果有点疑惑。如果VC的RTTI实现是在虚函数表中插入typeinfo结构的指针的话，那么在编译liba的时候如果关闭RTTI，显然不会生成这些东西，那么b.cpp即使开启了RTTI，也无法使liba中的class的RTTI机制能正常工作。难道是typeinfo和虚函数表都是链接的时候生成的？</p>
<p>当liba为dll的时候又会如何呢？stack overflow上有个帖子也提到了这个问题：<a href="http://stackoverflow.com/questions/1238325/is-it-allowed-to-link-rtti-enabled-dll-within-rtti-disabled-application">Is it allowed to link rtti enabled DLL within rtti disabled application?</a> 作者写了个dll，里面调用了dynamic_cast，打开RTTI编译输出；然后在另一个程序中调用该dll的函数，并关闭RTTI。结果可以正常工作。</p>
<p>这其实是一个相当无趣的语言问题，但却又无法忽略。C++在这点上确实非常令人讨厌！对于这些undefined的事情，还是少碰为妙。所以：</p>
<blockquote><p><strong>1，存在多态的地方，要么全部RTTI，要么全部non-RTTI；</strong></p>
<p><strong>2，如果你的lib开启了RTTI，则意味着你的client也必须开启RTTI；</strong></p>
<p><strong>3，或者，干脆对C++的RTTI SAY NO，可以省掉不少麻烦:)</strong></p></blockquote>
<p><span><em>最后附上测试代码：</em></span></p>
<pre class="brush:cpp; collapse:true">
//==================
// File：a.h
//==================
class base
{
	int m_ival;
public:
	base() {
		m_ival=0;
	}
	virtual ~base() {
		wyc_print("destroy base");
	}
};

class derived : public base
{
	int m_ival;
public:
	derived() {
		m_ival=0;
	}
	virtual ~derived() {
		wyc_print("destroy derived");
	}
}; 

class sum_interface
{
	int m_cnt;
public:
	sum_interface() {
		m_cnt=0;
	}
	virtual ~sum_interface() {
		wyc_print("destroy sum");
	}
	virtual void sum() {
		m_cnt+=1;
	} 

}; 

class third : public derived, public sum_interface
{
	float m_fval;
public:
	third() {
		m_fval=0;
	}
	virtual ~third() {
		wyc_print("destroy third");
	}
	virtual void sum() {
		wyc_print("i am third");
	}
}; 

class translater
{
public:
	static third* trans(base *pbase);
};

//==================
// File：a.cpp
//==================
third* translater::trans(base *pbase){
	return dynamic_cast
(pbase);
}

//==================
// File：b.cpp
//==================
int main(int argc, char **argv)
{
	size_t s1=sizeof(base);
	size_t s2=sizeof(derived);
	size_t s3=sizeof(third);
	base *pbase=new third;
	third *pthird=translater::trans(pbase);
	derived *pderived=dynamic_cast(pbase);
	sum_interface *psum=dynamic_cast(pbase);
	delete psum;
	return 0;
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.fancystar.org/2010/10/26/vc%e7%9a%84rtti%e9%80%89%e9%a1%b9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

