﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-eXile 的专栏</title><link>http://www.cppblog.com/eXile/</link><description /><language>zh-cn</language><lastBuildDate>Thu, 23 Apr 2026 04:10:00 GMT</lastBuildDate><pubDate>Thu, 23 Apr 2026 04:10:00 GMT</pubDate><ttl>60</ttl><item><title>好玩的Go语言</title><link>http://www.cppblog.com/eXile/archive/2010/01/11/105422.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Mon, 11 Jan 2010 11:48:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2010/01/11/105422.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/105422.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2010/01/11/105422.html#Feedback</comments><slash:comments>39</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/105422.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/105422.html</trackback:ping><description><![CDATA[
<span style="FONT-WEIGHT: bold">&nbsp;&nbsp;&nbsp;&nbsp; 1.什么是Go语言</span><br><br>&nbsp;&nbsp;&nbsp; Go语言是Google推出的新的一个致力于系统级的编程语言。很多人说它是 C + Python，既有c的灵活高效，又有Python的简单易用，它的原则是 Simple &amp;&amp; Fast。它的语法规则很简单。其官方网站上有一个《三天学会Go语言》的教程，包括三部分：基本结构，面向对象，并发。一个周末的时间就差不多可以掌握其基本面貌。相对来说，第三部分&#8220;并发&#8221;似乎难接受一些。但是如果你对多线程，同步，消息队列这些东西很有经验的话，对它的概念也会容易理解。<br><br style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: bold">&nbsp;&nbsp;&nbsp;&nbsp; 2.Go语言的面向对象</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp; Go语言在C语言语法的基础上，以最简洁的形式加入了面向对象。<br>&nbsp;&nbsp;&nbsp; 关于继承：Go语言没有继承。但是它支持嵌入，这个有点类似于其它语言的mixin，可以用来模拟继承。<br>&nbsp;&nbsp;&nbsp; 关于多态：Go 语言的最大特点是它的接口定义。所谓&#8220;接口&#8221;，就是一组方法的集合。任何一个类只要实现了一个接口的所有方法，则是该接口的实现者，不需要显式声明实现该接口。所以一个没有任何方法的空接口可以代表任何类型。<br style="FONT-WEIGHT: bold">&nbsp;&nbsp;&nbsp; <br>&nbsp; &nbsp;&nbsp; <span style="FONT-WEIGHT: bold">3.Go语言的并发</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp; Go语言提出一个新的概念—Go例程，有点类似于线程，但是更加轻量，更省资源。Go例程之间的通信方式 ——信道，这是GO 语言的核心概念，有点类似于UNIX的Pipe。在Go语言中，不需要接触线程，锁这些低阶概念。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="FONT-WEIGHT: bold"> 4.Go语言可以用来做什么</span><br><br>&nbsp;&nbsp;&nbsp;&nbsp; Go语言目前最强的是它的网络功能。它的package中已经实现了最常见的网络协议和编码处理。Go的官方网站使用的就是Go语言，实际上它就是Go的文档系统godoc。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 还没有官方对数据库支持，不过因为通过某种办法可以在Go语言中直接调用C函数，所以很容易的实现对MYSQL 或者 Sqlite 这些数据库的支持。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 至于GUI，这估计目前还不在设计者的考虑范围之内，因为他们连Windows都不舍得支持。不过可以通过它的http包和template包等等，已经构成了一个WEB框架，可以用来实现WEB GUI的开发，使用Go写一个带有路径分派的Http服务器也就几行代码的事。<br>&nbsp;&nbsp;&nbsp;&nbsp; <br><br><br><img src ="http://www.cppblog.com/eXile/aggbug/105422.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2010-01-11 19:48 <a href="http://www.cppblog.com/eXile/archive/2010/01/11/105422.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>说说C++智能指针(2): 标准库中的智能指针</title><link>http://www.cppblog.com/eXile/archive/2009/11/16/101062.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Mon, 16 Nov 2009 05:57:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/11/16/101062.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/101062.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/11/16/101062.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/101062.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/101062.html</trackback:ping><description><![CDATA[

<p><span style="FONT-FAMILY: courier new; FONT-SIZE: 12pt">&nbsp; <span style="FONT-FAMILY: Courier">使用智能指针是C++中常用的管理内存的方式。关于智能指针的设计，各路C++高手也是各展神通。<br><br>&nbsp; 在1994年. Greg Colvin向C++标准委员会提出了自己设计的智能指针：auto_ptr和counted_ptr。auto_ptr实现基本的RAII管理，不可复制；counted_ptr采用引用计数实现了一个可复制的智能指针。两者用于不同的场合。<br>&nbsp; 但是标准委员会最终只通过了auto_ptr，并且对auto_ptr加入了一个古怪的&#8220;所有权转移&#8221;语义。后来auto_ptr和counted_ptr进入了Boost C++ 库，改名为scoped_ptr和shared_ptr。<br>&nbsp;&nbsp;<br>&nbsp; std::auto_ptr只所以设计为可拷贝的，也许是出于以下考虑，比如下例函数：</span></span></p>
<font size="4">
<div style="border-left-color: rgb(204, 204, 204); padding-bottom: 4px; background-color: rgb(238, 238, 238); padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; padding-top: 4px; "><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f1(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;ptr);<br></span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;f2();</span></div>
</font><p><font size="4">&nbsp; </font><span style="FONT-SIZE: 12pt">&nbsp;f1中的参数所指向的对象应该由谁来删除呢？调用者还是被调用者？如果不看程序文档的话，无法知道这一点。f2函数也存在同样的问题。<br><br>&nbsp;&nbsp; 用auto_ptr可以消除这种歧义性：</span></p>
<font size="4">
<div style="border-left-color: rgb(204, 204, 204); padding-bottom: 4px; background-color: rgb(238, 238, 238); padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; padding-top: 4px; "><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f1(auto_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;ptr);<br>auto_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;f2();</span></div>
</font><p><font size="4">&nbsp;&nbsp;<br></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="FONT-FAMILY: courier new">尽管如此，auto_ptr的&#8220;所有权转移&#8221;语义还是会带来副作用，因为会修改原值的常量拷贝违背了一般的设计原则，它也许会在你意想不到的情况下就把对象转移了。它也不能用于标准容器中。<br>&nbsp;&nbsp; 所以auto_ptr在新的标准库已经不再推荐使用。取而代之的是unique_ptr。unique_ptr与auto_ptr类似，但限制了auto_ptr的拷贝行为。同时，像上面举的例子一样，unique_ptr可以作为函数的参数和返回值使用。这是因为C++增加了一个新的特征：右值引用。<br><br>&nbsp;&nbsp; shared_ptr也进入了标准库。对于引用计数的智能指针而言，循环引用是一个大问题。标准库为此把shared_ptr定义为强引用指针，它还实现了一个弱引用指针weak_ptr。显然，标准库并没有从根本上解决循环引用的问题，它把这个问题交给了程序员。在一个简单的系统中，你可以区分使用shared_ptr和weak_ptr，以此来避免出现循环引用。但是在一个大的对象系统中，有时还是容易出错。循环引用的问题，严重减弱了shared_ptr的可用性。<br><br>&nbsp;&nbsp; 那么能不能自动检测是否出现循环引用呢？事实上，对于shared_ptr这种使用非侵入式策略实现的智能指针，是很难实现自动检测的。但是如果采用侵入式设计，我们可以引入一些接口，来解决这个问题。循环引用的检测，实际上是图论中的回路检测问题。<br></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文由<a style="FONT-SIZE: 12pt" title="eXile" href="http://www.cppblog.com/eXile/">eXile</a><span style="FONT-SIZE: 12pt">&nbsp;原创，转载请表明原贴地址。&nbsp;</span><a href="http://www.cppblog.com/eXile/">http://www.cppblog.com/eXile/</a>。</p><img src ="http://www.cppblog.com/eXile/aggbug/101062.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-11-16 13:57 <a href="http://www.cppblog.com/eXile/archive/2009/11/16/101062.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>说说C++智能指针(1): 关于shared_ptr</title><link>http://www.cppblog.com/eXile/archive/2009/11/02/99995.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Mon, 02 Nov 2009 09:38:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/11/02/99995.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/99995.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/11/02/99995.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/99995.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/99995.html</trackback:ping><description><![CDATA[<span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>&nbsp;&nbsp; shared_ptr是新的标准库的一个主要成员，作为一个非嵌入式的智能指针，其设计可谓已经是绞尽脑汁。当然，还有很多人对它提出了不满。没有完美的设计，只有合适的设计。<br><br>1. </span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>shared_ptr最大的特点是接口的简单性与实现的灵活性。<br>&nbsp;&nbsp;&nbsp; 对于shared_ptr&lt;Object&gt;,object的内存管理是可定制的，甚至可以定制引用计数结点的内存分配，以满足对内存有特殊要求的情况。而这一切，都被</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>Object的实现者隐藏起来，</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span></span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>使用Object的客户类是不用关心的。这和以前标准库的组件实现策略有些不同。比如说, vector&lt;int, A1&gt;和vector&lt;int, A2&gt;，由于内存分配策略的不同，而变成类型的不同，造成接口的改变。</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>这一点在shared_ptr的设计时被避免了，当然以一定的性能代价。shared_ptr作为C++面向对象设计的一个重要组件，</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>接口的简单性是很重要的，必须要有</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span></span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>接口和实现的分离。与此相似的还有tr1::function的设计。<br><br>2.在同一体系中，各种类型的智能指针可以互相转换。<br><br>&nbsp;&nbsp; 如下例：<br><br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">struct</span><span style="COLOR: #000000">&nbsp;Object&nbsp;:&nbsp;InterfaceA,&nbsp;InterfaceB&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;MemberA&nbsp;memberA;<br>};<br><br><br>shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;obj(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;Object);<br>shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">InterfaceA</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;a&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;obj;<br>shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">InterfaceB</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;b&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;obj;<br></span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span><span style="COLOR: #000000">shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;static_pointer_cast</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(b);<br></span></span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span><span style="COLOR: #000000">shared_ptr</span><span style="COLOR: #000000">&lt;void</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;p2 </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> obj;</span></span><span style="COLOR: #000000"><br><br></span></div>
<br>&nbsp; 甚至还可以取得数据成员的智能指针：<br><br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">Object</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;obj(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;Object);<br>shared_ptr</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">MemberA</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;memberA(obj,&nbsp;</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">obj</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">memberA);</span></div>
<br><br>&nbsp; 再来说说shared_ptr的缺点。<br></span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span><br>1.对于使用引用计数的智能指针来说，必须要小心出现循环引用。<br>&nbsp;&nbsp;&nbsp;&nbsp; 在重度使用</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>shared_ptr的系统中，你必须一开始就明确类与类的关系，以决定哪里使用</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>shared_ptr，哪里使用</span>weak_ptr，否则就会出现内存泄露。而<span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>shared_ptr的接口</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>转换</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>的灵活性，也很容易导致</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>智能指针被滥用。</span>内存自动管理的问题并没有得到解决，它只是被转移了。<br><br><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>2.shared_ptr使用非嵌入式设计，这样可以使用于基本类型，比如 shared_ptr&lt;int&gt;。但是根据个人经验，这种情况在很少使用。大部分情况还是使用自己设计的类。这有一个问题，就是没有很方便的办法实现this指针和</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>智能指针的转换。标准库中提供了enable_shared_from_this类来解决这个问题。但这</span>已经使所谓的<span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>非嵌入式设计徒有虚名</span><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span></span>。而假如一开始采用<span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span>嵌入式设计的话，则在性能代价和多线程设计方面具有更大的灵活性。</span><br><span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; COLOR: #000000; WORD-SPACING: 0px" class=Apple-style-span><br></span>
<img src ="http://www.cppblog.com/eXile/aggbug/99995.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-11-02 17:38 <a href="http://www.cppblog.com/eXile/archive/2009/11/02/99995.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TDD for GUI</title><link>http://www.cppblog.com/eXile/archive/2009/06/22/88271.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Mon, 22 Jun 2009 05:15:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/06/22/88271.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/88271.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/06/22/88271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/88271.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/88271.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 如何对GUI应用进行单元测试和TDD。&nbsp;&nbsp;<a href='http://www.cppblog.com/eXile/archive/2009/06/22/88271.html'>阅读全文</a><img src ="http://www.cppblog.com/eXile/aggbug/88271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-06-22 13:15 <a href="http://www.cppblog.com/eXile/archive/2009/06/22/88271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最简单的foreach实现(VC &amp; GCC)</title><link>http://www.cppblog.com/eXile/archive/2009/05/08/82201.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Thu, 07 May 2009 17:25:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/05/08/82201.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/82201.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/05/08/82201.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/82201.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/82201.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 对于VC 和GCC 分别用最简单方法实现了foreach遍历。&nbsp;&nbsp;<a href='http://www.cppblog.com/eXile/archive/2009/05/08/82201.html'>阅读全文</a><img src ="http://www.cppblog.com/eXile/aggbug/82201.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-05-08 01:25 <a href="http://www.cppblog.com/eXile/archive/2009/05/08/82201.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[T] ICE实例学习：Let's Chat!  (2) 实现服务器</title><link>http://www.cppblog.com/eXile/archive/2009/03/26/77897.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Wed, 25 Mar 2009 16:54:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/03/26/77897.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/77897.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/03/26/77897.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/77897.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/77897.html</trackback:ping><description><![CDATA[<br><span style="FONT-SIZE: 18pt"><strong>服务器实现：</strong></span><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 服务器使用C++。注意它的结构：类 ChatRoom 实现了大部分的应用逻辑。为了支持推模型与拉模型，服务器实现了类ChatSession 和类 PollingChatSession。 ChatRoom 调用 ChatRoomCallbackAdapter 对象的 send 函数来传递客户消息，该对象隐藏了两种模型之间的差异。<br><br>ChatRoom 实现：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChatRoom是一个普通的C++对象，而不是一个Servant.<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;C++&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapter&nbsp;{&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif">&nbsp;</span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;};&nbsp;<br>typedef&nbsp;IceUtil::Handle</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">ChatRoomCallbackAdapter</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapterPtr;&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ChatRoom&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;IceUtil::Shared&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;reserve(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;unreserve(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;join(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapterPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;leave(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;Ice::Long&nbsp;send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;map</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">,&nbsp;ChatRoomCallbackAdapterPtr</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackMap;&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ChatRoomCallbackMap&nbsp;_members;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;_reserved;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex&nbsp;_mutex;&nbsp;<br>};&nbsp;<br>typedef&nbsp;IceUtil::Handle</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">ChatRoom</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;ChatRoomPtr;</span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 成员_reserverd是一个字符串集合，它存储已经建立回话，但是还没有加入聊天室的客户名。_members存储当前聊天室的所有用户（已经调用过join函数的用户）。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; 成员函数 reserve 和 unreserve 维护 _reserved 集合。</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;C++&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;<br>ChatRoom::reserve(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;name)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_reserved.find(name)&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;_reserved.end()&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;_members.find(name)&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;_members.end())&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">The&nbsp;name&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;name&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;is&nbsp;already&nbsp;in&nbsp;use.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_reserved.insert(name);&nbsp;<br>}&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;<br>ChatRoom::unreserve(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;name)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_reserved.erase(name);&nbsp;<br>}</span></div>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp; join操作添加用户到聊天室。</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;C++&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;<br>ChatRoom::join(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;name,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapterPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;callback)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Int64&nbsp;timestamp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;IceUtil::Time::now().toMilliSeconds();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_reserved.erase(name);&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;Ice::StringSeq&nbsp;names;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ChatRoomCallbackMap::const_iterator&nbsp;q;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(q&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;_members.begin();&nbsp;q&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;_members.end();&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">q)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;names.push_back((</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">q).first);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">init(names);&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_members[name]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;callback;&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;UserJoinedEventPtr&nbsp;e&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;UserJoinedEvent(timestamp,&nbsp;name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(q&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;_members.begin();&nbsp;q&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;_members.end();&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">q)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">second</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">join(e);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>}<br></span></div>
<p><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send实现，同join实现非常类似：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;C++&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">Ice::Long&nbsp;<br>ChatRoom::send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;name,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;message)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Int64&nbsp;timestamp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;IceUtil::Time::now().toMilliSeconds();&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;MessageEventPtr&nbsp;e&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;MessageEvent(timestamp,&nbsp;name,&nbsp;message);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(ChatRoomCallbackMap::iterator&nbsp;q&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;_members.begin();&nbsp;q&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;_members.end();&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">q)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;q</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">second</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">send(e);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;timestamp;&nbsp;<br>}</span></div>
<p>&nbsp;</p>
<p>&nbsp;类 ChatRoomCallbackAdapter<br><br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;C++&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapter&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;IceUtil::Shared&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;init(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::StringSeq</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;join(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;UserJoinedEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;leave(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;UserLeftEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;MessageEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;<br>};</span></div>
<br>推模式 CallbackAdapter 实现：
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;SessionCallbackAdapter&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackAdapter&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;SessionCallbackAdapter(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;callback,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatSessionPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;session)&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;_callback(callback),&nbsp;_session(session)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;init(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::StringSeq</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;users)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">init_async(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;AMICallback</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AMI_ChatRoomCallback_init</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(_session),&nbsp;users);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;join(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;UserJoinedEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;e)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">join_async(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;AMICallback</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AMI_ChatRoomCallback_join</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(_session),&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timestamp,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;leave(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;UserLeftEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;e)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">leave_async(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;AMICallback</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AMI_ChatRoomCallback_leave</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(_session),&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timestamp,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;MessageEventPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;e)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">send_async(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;AMICallback</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">AMI_ChatRoomCallback_send</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">(_session),&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">timestamp,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">name,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">message);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackPrx&nbsp;_callback;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatSessionPrx&nbsp;_session;&nbsp;<br>};<br></span></div>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看一下SessionCallbackAdapter的四个成员函数，当异步调用完成时，都使用类AMICallback来接收通知。它的定义如下：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">template</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;T</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;AMICallback&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;T&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;AMICallback(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatSessionPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;session)&nbsp;:&nbsp;_session(session)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ice_response()&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ice_exception(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Exception</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_session</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">destroy();&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Collocated&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::LocalException</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatSessionPrx&nbsp;_session;&nbsp;<br>};<br></span></div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当用户回调操作抛出异常，服务器立即销毁客户会话，即把该用户赶出聊天室。这是因为，一旦客户的回调对象出现了一次异常，它以后也就不可能再正常。<br><br><br><span style="FONT-SIZE: 14pt">推模式会话创建</span>：<br><br>&nbsp;&nbsp;&nbsp;&nbsp; 现在来看一下会话创建。推模式的客户使用Glacier2，所以要使用Glacier2的会话创建机制。Glacier2 允许用户通过提供一个Glacier2::SessionManager对象的代理来自定义会话创建机制。通过设置Glacier2.SessionManager属性来配置Gloacier2，就可以使用自己的会话管理器。会话管理器除了一个trivial构造函数（设置聊天室指针），只有一个操作，create，Glacier2调用它来代理应用的会话创建。 create 操作必须返回一个会话代理（类型为Glacier2::Session*）。实现如下：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">Glacier2::SessionPrx&nbsp;<br>ChatSessionManagerI::create(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;name,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Glacier2::SessionControlPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;c)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;vname;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vname&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;validateName(name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_chatRoom</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">reserve(vname);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;reason)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;CannotCreateSessionException(reason);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;Glacier2::SessionPrx&nbsp;proxy;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChatSessionIPtr&nbsp;session&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;ChatSessionI(_chatRoom,&nbsp;vname);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;SessionPrx::uncheckedCast(c.adapter</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">addWithUUID(session));&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ice::IdentitySeq&nbsp;ids;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ids.push_back(proxy</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ice_getIdentity());&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sessionControl</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">identities()</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">add(ids);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::LocalException</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(proxy)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;proxy</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">destroy();&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;CannotCreateSessionException(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Internal&nbsp;server&nbsp;error</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;proxy;&nbsp;<br>}</span></div>
<br>&nbsp;&nbsp;&nbsp;&nbsp; 首先调用一个简单的帮助函数 validateName, 来检查传递的用户名是否包含非法字符，并把它转为大写，然后调用 reserver函数把它加到聊天室的_reserved集合中。我们要监视这些操作抛出的消息，并把它转化为Glacide2::CannotCreateSessionException异常，即在create操作的异常规范声明的异常。<br>&nbsp;&nbsp;&nbsp;&nbsp; 接着实例化一个ChatSessionI对象（见下面）来创建会话。注意这个会话使用UUID作为对象标识，所以保证标识符唯一。<br>&nbsp;&nbsp;&nbsp; 最后，添加这个新创建的会话标识，Gllacier2只通过它来转发经过这个会话的请求。实际上，&#8220;只转发经过这个会话的并且只到这个会话的请求&#8221;，这是一种安全的办法：如果有恶意客户能猜出另一个客户会话的标识，它也不能向别的对象发送请求（可能在除了聊天服务器之外的服务器上）。如果出错，就销毁刚创建的会话对象，这样避免了资源泄露。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这就是利用Glacier2创建会话的全部。如果你希望使用Glacier2的认证机制，可以设置属性Glacier2.PermissionsVerifier为执行认证的对象代理。（Glacier2提供一个内置的权限验证器，NullPermissionsVerifier，可以检查用户名和密码）。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图：会话创建交互图（略）<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ChatSessionI类实现了ChatSession接口。<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;ChatSessionI&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;ChatSession&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ChatSessionI(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomPtr</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;setCallback(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;Ice::Long&nbsp;send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;destroy(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomPtr&nbsp;_chatRoom;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;_name;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ChatRoomCallbackAdapterPtr&nbsp;_callback;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">bool</span><span style="COLOR: #000000">&nbsp;_destroy;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex&nbsp;_mutex;&nbsp;<br>};&nbsp;<br>typedef&nbsp;IceUtil::Handle</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">ChatSessionI</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;ChatSessionIPtr;</span></div>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;构造函数设置聊天室和用户名，并把_destroy设置为False.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 由于Glacier2::create操作不允许传递代理，必须把创建会话和设置回调分成两步。这是setCallback的实现；<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;<br>ChatSessionI::setCallback(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ChatRoomCallbackPrx</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;callback,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;c)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_destroy)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;Ice::ObjectNotExistException(__FILE__,&nbsp;__LINE__);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_callback&nbsp;</span><span style="COLOR: #000000">||</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">callback)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;Ice::Context&nbsp;ctx;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ctx[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_fwd</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">o</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_callback&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;SessionCallbackAdapter(callback</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">ice_context(ctx),&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ChatSessionPrx::uncheckedCast(&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.adapter</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">createProxy(c.id)));&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_chatRoom</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">join(_name,&nbsp;_callback);&nbsp;<br>}</span></div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意，在使用join传递代理之前，向客户代理添加了一个值为 "o" 的_fwd上下文。它提示Glacier使用单向调用来转发客户回调。这样比双向调用更加有效。因为所有的回调操作均为void返回值，所以可以单向调用。<br>&nbsp;&nbsp;&nbsp;&nbsp; 服务器的回调为普通的双向调用。这样当出错时可以通知服务器。当客户端出错时，这个对结束客户会话很有用。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; 一旦客户调用了setCallback，就可以接收聊天室的各种行为通知。下为send实现：</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">Ice::Long&nbsp;<br>ChatSessionI::send(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;message,&nbsp;</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_destroy)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;Ice::ObjectNotExistException(__FILE__,&nbsp;__LINE__);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">_callback)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;InvalidMessageException(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">You&nbsp;cannot&nbsp;send&nbsp;messages&nbsp;until&nbsp;you&nbsp;joined&nbsp;the&nbsp;chat.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;msg&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;validateMessage(message);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;reason)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;InvalidMessageException(reason);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;_chatRoom</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">send(_name,&nbsp;msg);&nbsp;<br>}</span></div>
<p>&nbsp;<br>&nbsp;&nbsp;&nbsp; 客户要离开聊天室，只要调用 destory.</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;<br>ChatSessionI::destroy(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::Current</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">&nbsp;c)&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;IceUtil::Mutex::Lock&nbsp;sync(_mutex);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_destroy)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000">&nbsp;Ice::ObjectNotExistException(__FILE__,&nbsp;__LINE__);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">try</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c.adapter</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">remove(c.id);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(_callback&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_chatRoom</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">unreserve(_name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_chatRoom</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">leave(_name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;Ice::ObjectAdapterDeactivatedException</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">)&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;No&nbsp;need&nbsp;to&nbsp;clean&nbsp;up,&nbsp;the&nbsp;server&nbsp;is&nbsp;shutting&nbsp;down.&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;_destroy&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">;&nbsp;<br>}</span></div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eXile/aggbug/77897.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-03-26 00:54 <a href="http://www.cppblog.com/eXile/archive/2009/03/26/77897.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[T] ICE实例学习：Let's Chat! 节译 (1)</title><link>http://www.cppblog.com/eXile/archive/2009/03/25/77890.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Wed, 25 Mar 2009 15:52:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/03/25/77890.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/77890.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/03/25/77890.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/77890.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/77890.html</trackback:ping><description><![CDATA[节译，原文地址：<a href="http://www.zeroc.com/articles/index.html"><u><font color=#0000ff>http://www.zeroc.com/articles/index.html</font></u></a><br><br><span style="FONT-SIZE: 18pt"><strong>需求<br></strong></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）一个典型的聊天室应用，使用客户器/服务器架构，客户发送消息到中心服务器，然后，消息发送给其它客户。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）尽量减少服务器管理，甚至可以不需要。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3）通信必须安全，通过公共网络时必须要保护个人隐私。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4）当客户端和服务器有防火墙保护时也能正常运行，客户端不用修改它的网络或者防火墙设置。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5）客户端可以在各种平台上利用多种语言实现，比如说利用Web浏览器作为客户端。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6）客户端可能网络带宽有限，所以应该尽量减少网络流量。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只支持单个聊天室。（多个聊天室只是代码多了点，并没有增加任何难度）。<br><br><span style="FONT-SIZE: 18pt"><strong>设计<br></strong></span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在本文中，将会演示多种客户端的设计和实现。包括：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）C++命令行客户端；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）JAVA SWing GUI客户端；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3）.NET WPF客户端；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4）PHP网页客户端；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5）Silverlight 网页客户端；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; 注意，对于ICE3.3, PHP和Ruby只提供了客户端的Run Time。我们在考虑当连接的客户如何从服务器获得消息时，必须要想到这一点。对于消息发布，有两种通信模型：<br>&nbsp;&nbsp;&nbsp;&nbsp; 1）推模型：略<br>&nbsp;&nbsp;&nbsp;&nbsp; 2）拉模型：略<br>&nbsp;&nbsp;&nbsp;&nbsp; 推模型比较简单，容易实现，我们的Chat 客户端中，C++，Java, .Net, Silverlight(0.3)都使用该模型。PHP客户端使用拉模型。<br><br><span style="FONT-SIZE: 18pt"></span><strong><font size=5>推模型定义<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp; 每个客户端中提供一个 ChatRoomCallback 类型的ICE对象到服务器。当发生事件时，服务器调用该对象的操作通知客户。SLICE定义如下：<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Slice&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">module&nbsp;Chat&nbsp;<br>{&nbsp;<br></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Implemented&nbsp;by&nbsp;clients&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #0000ff">interface</span><span style="COLOR: #000000">&nbsp;ChatRoomCallback&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ami</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;init(Ice::StringSeq&nbsp;users);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ami</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;join(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;timestamp,&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ami</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;leave(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;timestamp,&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;name);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ami</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;send(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;timestamp,&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;name,&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;message);&nbsp;<br>};&nbsp;<br>};<br></span></div>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1）当用户首次连接到聊天室时，服务器调用 init 操作.&nbsp;&nbsp;&nbsp;&nbsp; users参数告诉用户目前连接到聊天室的所有用户信息。<br>&nbsp;&nbsp;&nbsp;&nbsp; 2）有用户连接到聊天室时，服务器调用 join 操作。<br>&nbsp;&nbsp;&nbsp;&nbsp; 3）有用户断开连接时，服务器调用&nbsp;leave 操作。<br>&nbsp;&nbsp;&nbsp;&nbsp; 4）有用户发送消息时，服务器调用 send 操作。<br>&nbsp;&nbsp;&nbsp;&nbsp; 注意设计使用异步事件。元数据指令 ["ami"] 标明服务器异步调用回调操作。当客户端行为异常时，这对服务器是一个保护：客户端可能长时间阻塞，服务器调用期间不会因此失去对线程的控制。<br><br><strong><font size=5>与防火墙协作<br></font></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 略......<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Glacer2是ICE针对这种情况的预建的解决方案，它扮演一个服务器前端。Glacer2具有以下特征：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）支持会话概念，API支持认证机制，可实现自定义的会话创建和认证。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）单个Glacer2可进行任意数量的服务器和客户端转发。服务器只要有一个端口接受外来连接，而不用管具体服务器个数。<br>&nbsp;&nbsp;&nbsp;&nbsp; 3）对于具有防火墙的客户端，服务器也可调用其提供的回调。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因为 Glacer2会话概念是面向连接的，只有当客户端同Glacer2的连接打开时，更精确的说，同Glacer2保持一个激活的会话时，服务器才可以对客户进行回调。换句话说，当客户端同Glacer2失去连接，Glacer2自动销毁会话。为了阻止客户端到Glacer2的连接被意外关闭，客户端必须要禁用ACM（Automic Connection Management, 自动连接管理）。而且，Glacer2通常对长时间空闲的会话设置超时。当聊天室长时间没有动作时，为了防止Glacer2销毁会话，客户端必须周期性进行激活，比如，调用 ice_ping, 来对Glacer2的会话超时进行重置。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; Chat客户端通过服务器提供的 ChatSession接口来和服务器通信。 ChatSession 从Glacer2::Session派生。<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Slice&nbsp;</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">module&nbsp;Chat&nbsp;<br>{&nbsp;<br>exception&nbsp;InvalidMessageException&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;reason;&nbsp;<br>};&nbsp;<br>&nbsp;<br></span><span style="COLOR: #0000ff">interface</span><span style="COLOR: #000000">&nbsp;ChatSession&nbsp;extends&nbsp;Glacier2::Session&nbsp;<br>{&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;setCallback(ChatRoomCallback</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;cb);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ami</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">]&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;send(</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">&nbsp;message)&nbsp;throws&nbsp;InvalidMessageException;&nbsp;<br>};&nbsp;<br>};<br></span></div>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这就是推模型，Chat客户端调用ChatSession的send来发送消息，服务器调用每一个客户的ChatRoomCallback的send操作进行分发。<br><br><strong><font size=5>拉模型定义<br></font></strong><br>TODO<br><br><br>
<img src ="http://www.cppblog.com/eXile/aggbug/77890.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-03-25 23:52 <a href="http://www.cppblog.com/eXile/archive/2009/03/25/77890.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[T] 利用QT进行web与本地混合应用开发 </title><link>http://www.cppblog.com/eXile/archive/2009/03/09/75997.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Mon, 09 Mar 2009 08:41:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/03/09/75997.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/75997.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/03/09/75997.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/75997.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/75997.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Qt提供了本地 C++对象与JavaScript的无缝集成，是进行本地与web混合应用开发的理想平台。&nbsp;&nbsp;<a href='http://www.cppblog.com/eXile/archive/2009/03/09/75997.html'>阅读全文</a><img src ="http://www.cppblog.com/eXile/aggbug/75997.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-03-09 16:41 <a href="http://www.cppblog.com/eXile/archive/2009/03/09/75997.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(Z)正则表达式30分钟入门教程</title><link>http://www.cppblog.com/eXile/archive/2009/01/15/72092.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Thu, 15 Jan 2009 06:58:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/01/15/72092.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/72092.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/01/15/72092.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/72092.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/72092.html</trackback:ping><description><![CDATA[<div class="postbody">
<h1>正则表达式30分钟入门教程</h1>
<p id="meta">版本：v2.3 (2008-4-13) 作者：<a  href="http://www.unibetter.com/members/deerchao.aspx"><u><font color="#0000ff">deerchao</font></u></a> 转载请注明<a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm"><u><font color="#0000ff">来源</font></u></a></p>
<h2 id="contents">目录</h2>
<p class="note" id="skipContents"><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#mission" title="转到正文内容"><u><font color="#0000ff">跳过目录</font></u></a></p>
<ol>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#mission"><u><font color="#0000ff">本文目标</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#howtouse"><u><font color="#0000ff">如何使用本教程</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#introduction"><u><font color="#0000ff">正则表达式到底是什么东西？</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#getstarted"><u><font color="#0000ff">入门</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#testing"><u><font color="#0000ff">测试正则表达式</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#metacode"><u><font color="#0000ff">元字符</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#escape"><u><font color="#0000ff">字符转义</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#repeat"><u><font color="#0000ff">重复</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#charclass"><u><font color="#0000ff">字符类</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#alternative"><u><font color="#0000ff">分枝条件</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#negation"><u><font color="#0000ff">反义</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#grouping"><u><font color="#0000ff">分组</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#backreference"><u><font color="#0000ff">后向引用</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#lookaround"><u><font color="#0000ff">零宽断言</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#negativelookaround"><u><font color="#0000ff">负向零宽断言</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#commenting"><u><font color="#0000ff">注释</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#greedyandlazy"><u><font color="#0000ff">贪婪与懒惰</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#regexoptions"><u><font color="#0000ff">处理选项</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#balancedgroup"><u><font color="#0000ff">平衡组/递归匹配</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#more"><u><font color="#0000ff">还有些什么东西没提到</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#contact"><u><font color="#0000ff">联系作者</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#ad"><u><font color="#0000ff">最后,来点广告...</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#resources"><u><font color="#0000ff">网上的资源及本文参考文献</font></u></a>
    </li>
    <li><a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#updatelog"><u><font color="#0000ff">更新说明</font></u></a></li>
</ol>
<h2 id="mission">本文目标</h2>
<p>30分钟内让你明白正则表达式是什么，并对它有一些基本的了解，让你可以在自己的程序或网页里使用它。</p>
<h2 id="howtouse">如何使用本教程</h2>
<p class="important note" id="giveMe30Minutes">最重要的是——请给我<em>30分钟</em>，如果你没有使用正则表达式的经验，请不要试图在30<em>秒</em>内入门——除非你是超人 :)</p>
<p>别被下面那些复杂的表达式吓倒，只要跟着我一步一步来，你会发现正则表达式其实并<span lang="zh-cn">没有</span>你想像
中的那么困难。当然，如果你看完了这篇教程之后，发现自己明白了很多，却又几乎什么都记不得，那也是很正常的——我认为，没接触过正则表达式的人在看完这
篇教程后，能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理，以后你还需要多练习，多使用，才能熟练掌握正则表达式。</p>
<p>除了作为入门教程之外，本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说，这个目标还是完成得不错的——你看，我自己也没能把所有的东西记下来，不是吗？</p>
<p><a  href="http://www.unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm" id="clearButton" onclick="return clearFormats();"><u><font color="#0000ff">清除格式</font></u></a>&nbsp;文本格式约定：<span class="name">专业术语</span>&nbsp;<span class="code">元字符/语法格式</span>&nbsp;<span class="regex">正则表达式</span>&nbsp;<span class="part">正则表达式中的一部分(用于分析)</span>&nbsp;<span class="string">对其进行匹配的源字符串</span>&nbsp;<span class="desc">对正则表达式或其中一部分的说明</span></p>
<p><a  href="http://www.unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm" id="hideButton" onclick="return hideNotes();"><u><font color="#0000ff">隐藏边注</font></u></a>&nbsp;本文右边有一些注释，主要是用来提供一些相关信息，或者给没有程序员背景的读者解释一些基本概念，通常可以忽略。</p>
<h2 id="introduction">正则表达式到底是什么东西？</h2>
<p class="note"><span class="name">字符</span>是计算机软件处理文字时最基本的单位，可能是字母，数字，标点符号，空格，换行符，汉字等等。<span class="name">字符串</span>是0个或更多个字符的序列。<span class="name">文本</span>也就是文字，字符串。说某个字符串<span class="name">匹配</span>某个正则表达式，通常是指这个字符串里有一部分（或几部分分别）能满足表达式给出的条件。</p>
<p>在编写处理字符串的程序或网页时，经常会有查找符合某些复杂规则的字符串的需要。<span class="name">正则表达式</span>就是用于描述这些规则的工具。换句话说，正则表达式就是记录文本规则的代码。</p>
<p>很可能你使用过Windows/Dos下用于文件查找的<span class="name">通配符(wildcard)</span>，也就是<span class="code">*</span>和<span class="code">?</span>。如果你想查找某个目录下的所有的Word文档的话，你会搜索<span style="color: red;">*.doc</span>。在这里，<span class="code">*</span>会被解释成任意的字符串。和通配符类似，正则表达式也是用来进行文本匹配的工具，只不过比起通配符，它能更精确地描述你的需求——当然，代价就是更复杂——比如你可以编写一个正则表达式，用来查找<span class="desc">所有以0开头，后面跟着2-3个数字，然后是一个连字号&#8220;-&#8221;，最后是7或8位数字的字符串</span>(像<span class="string">010-12345678</span>或<span class="string">0376-7654321</span>)。</p>
<h2 id="getstarted">入门</h2>
<p>学习正则表达式的最好方法是从例子开始，理解例子之后再自己对例子进行修改，实验。下面给出了不少简单的例子，并对它们作了详细的说明。</p>
<p>假设你在一篇英文小说里查找<span class="desc">hi</span>，你可以使用正则表达式<span class="regex">hi</span>。</p>
<p>这几乎是最简单的正则表达式了，它可以精确匹配这样的字符串：<span class="desc">由两个字符组成，前一个字符是h,后一个是i</span>。通常，处理正则表达式的工具会提供一个忽略大小写的选项，如果选中了这个选项，它可以匹配<span class="string">hi</span>,<span class="string">HI</span>,<span class="string">Hi</span>,<span class="string">hI</span>这四种情况中的任意一种。</p>
<p>不幸的是，很多单词里包含<span class="string">hi</span>这两个连续的字符，比如<span class="string">him</span>,<span class="string">history</span>,<span class="string">high</span>等等。用<span class="regex">hi</span>来查找的话，这里边的<span class="string">hi</span>也会被找出来。如果要<span class="desc">精确地查找hi这个单词</span>的话，我们应该使用<span class="regex">\bhi\b</span>。</p>
<p><span class="part">\b</span>是正则表达式规定的一个特殊代码（好吧，某些人叫它<span class="name">元字符，metacharacter</span>），代表着<span class="desc">单词的开头或结尾，也就是单词的分界处</span>。虽然通常英文的单词是由空格，标点符号或者换行来分隔的，但是<span class="code">\b</span>并不匹配这些单词分隔字符中的任何一个，它<strong>只匹配一个位置</strong>。</p>
<p class="note">如果需要更精确的说法，<span class="code">\b</span>匹配这样的位置：它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)<span class="code">\w</span>。</p>
<p>假如你要找的是<span class="desc">hi后面不远处跟着一个Lucy</span>，你应该用<span class="regex">\bhi\b.*\bLucy\b</span>。</p>
<p>这里，<span class="part">.</span>是另一个元字符，匹配<span class="desc">除了换行符以外的任意字符</span>。<span class="part">*</span>同样是元字符，不过它代表的不是字符，也不是位置，而是数量——它指定*<span class="desc">前边的内容可以连续重复出现任意次以使整个表达式得到匹配</span>。因此，<span class="part">.*</span>连在一起就意味着<span class="desc">任意数量的不包含换行的字符</span>。现在<span class="regex">\bhi\b.*\bLucy\b</span>的意思就很明显了：<span class="desc">先是一个单词hi,然后是任意个任意字符(但不能是换行)，最后是Lucy这个单词</span>。</p>
<p class="note">换行符就是'\n',ASCII编码为10(十六进制0x0A)的字符。</p>
<p>如果同时使用其它元字符，我们就能构造出功能更强大的正则表达式。比如下面这个例子：</p>
<p><span class="regex">0\d\d-\d\d\d\d\d\d\d\d</span>匹配这样的字符串：<span class="desc">以0开头，然后是两个数字，然后是一个连字号&#8220;-&#8221;，最后是8个数字</span>(也就是中国的电话号码。当然，这个例子只能匹配区号为3位的情形)。</p>
<p>这里的<span class="part">\d</span>是个新的元字符，匹配<span class="desc">一位数字(0，或1，或2，或&#8230;&#8230;)</span>。<span class="part">-</span>不是元字符，只匹配它本身——连字符或者减号。</p>
<p>为了避免那么多烦人的重复，我们也可以这样写这个表达式：<span class="regex">0\d{2}-\d{8}</span>。 这里<span class="part">\d</span>后面的<span class="part">{2}</span>(<span class="part">{8}</span>)的意思是前面<span class="part">\d</span><span class="desc">必须连续重复匹配2次(8次)</span>。</p>
<h2 id="testing">测试正则表达式</h2>
<div class="note">
<p>其它可用的测试工具:</p>
<ul>
    <li><a  href="http://www.regexbuddy.com/"><u><font color="#0000ff">RegexBuddy</font></u></a>
    </li>
    <li><a  href="http://regexpal.com/"><u><font color="#0000ff">Javascript正则表达式在线测试工具</font></u></a></li>
</ul>
</div>
<p>如果你不觉得正则表达式很难读写的话，要么你是一个天才，要么，你不是地球人。正则表达式的语法很令人头疼，即使对经常使用它的人来说也是如此。由于难于读写，容易出错，所以找一种工具对正则表达式进行测试是很有必要的。</p>
<p>由于在不同的环境下正则表达式的一些细节是不相同的，本教程介绍的是微软 .Net Framework 2.0下正则表达式的行为，所以，我向你介绍一个.Net下的工具<a  href="http://www.dotnet2themax.com/blogs/fbalena/PermaLink,guid,13bce26d-7755-441e-92b3-1eb5f9e859f9.aspx" title="转到RegexTester的官方网站（英文）"><u><font color="#0000ff">Regex Tester</font></u></a>。首先你确保已经安装了<a  href="http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&amp;FamilyID=0856eacb-4362-4b0d-8edd-aab15c5e04f5" title="转到下载.Net Framework 2.0的页面"><u><font color="#0000ff">.Net Framework 2.0</font></u></a>，然后<a  href="http://www.unibetter.com/deerchao/downloads/RegexTester.zip" title="从www.unibetter.com下载Regex Tester, 75KB"><u><font color="#0000ff">下载Regex Tester</font></u></a>。这是个绿色软件，下载完后打开压缩包,直接运行RegexTester.exe就可以了。</p>
<p>下面是Regex Tester运行时的截图：</p>
<p><img  src="http://unibetter.com/deerchao/images/RegexTester.jpg" alt="Regex Tester运行时的截图"></p>
<h2 id="metacode">元字符</h2>
<p>现在你已经知道几个很有用的元字符了，如<span class="code">\b</span>,<span class="code">.</span>,<span class="code">*</span>，还有<span class="code">\d</span>.正则表达式里还有更多的元字符，比如<span class="code">\s</span>匹配<span class="desc">任意的空白符，包括空格，制表符(Tab)，换行符，中文全角空格等</span>。<span class="code">\w</span>匹配<span class="desc">字母或数字或下划线或汉字等</span>。</p>
<p class="note">对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的，其它环境下的具体情况请查看相关文档。</p>
<p>下面来看看更多的例子：</p>
<p><span class="regex">\ba\w*\b</span>匹配<span class="desc">以字母<span class="part">a</span>开头的单词——先是某个单词开始处(<span class="part">\b</span>)，然后是字母<span class="part">a</span>,然后是任意数量的字母或数字(<span class="part">\w*</span>)，最后是单词结束处(<span class="part">\b</span>)</span>。</p>
<p class="note">好吧，现在我们说说正则表达式里的单词是什么意思吧：就是多于一个的连续的<span class="code">\w</span>。不错，这与学习英文时要背的成千上万个同名的东西的确关系不大 :)</p>
<p><span class="regex">\d+</span>匹配<span class="desc">1个或更多连续的数字</span>。这里的<span class="part">+</span>是和<span class="code">*</span>类似的元字符，不同的是<span class="code">*</span>匹配<span class="desc">重复任意次(可能是0次)</span>，而<span class="code">+</span>则匹配<span class="desc">重复1次或更多次</span>。</p>
<p><span class="regex">\b\w{6}\b</span> 匹配<span class="desc">刚好6个字母/数字的单词</span>。</p>
<table cellspacing="0">
    <caption>表1.常用的元字符</caption>
    <thead>
        <tr>
            <th scope="col">代码</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><span class="code">.</span></td>
            <td><span class="desc">匹配除换行符以外的任意字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\w</span></td>
            <td><span class="desc">匹配字母或数字或下划线或汉字</span></td>
        </tr>
        <tr>
            <td><span class="code">\s</span></td>
            <td><span class="desc">匹配任意的空白符</span></td>
        </tr>
        <tr>
            <td><span class="code">\d</span></td>
            <td><span class="desc">匹配数字</span></td>
        </tr>
        <tr>
            <td><span class="code">\b</span></td>
            <td><span class="desc">匹配单词的开始或结束</span></td>
        </tr>
        <tr>
            <td><span class="code">^</span></td>
            <td><span class="desc">匹配字符串的开始</span></td>
        </tr>
        <tr>
            <td><span class="code">$</span></td>
            <td><span class="desc">匹配字符串的结束</span></td>
        </tr>
    </tbody>
</table>
<p>元字符<span class="code">^</span>（和数字6在同一个键位上的符号）和<span class="code">$</span>都匹配一个位置，这和<span class="code">\b</span>有点类似。<span class="code">^</span>匹配你要用来查找的字符串的开头，<span class="code">$</span>匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的QQ号必须为5位到12位数字时，可以使用：<span class="regex">^\d{5,12}$</span>。</p>
<p>这里的<span class="part">{5,12}</span>和前面介绍过的<span class="part">{2}</span>是类似的，只不过<span class="part">{2}</span>匹配<span class="desc">只能不多不少重复2次</span>，<span class="part">{5,12}</span>则是<span class="desc">重复的次数不能少于5次，不能多于12次</span>，否则都不匹配。</p>
<p>因为使用了<span class="part">^</span>和<span class="part">$</span>，所以输入的整个字符串都要用来和<span class="part">\d{5,12}</span>来匹配，也就是说整个输入<span class="desc">必须是5到12个数字</span>，因此如果输入的QQ号能匹配这个正则表达式的话，那就符合要求了。</p>
<p>和忽略大小写的选项类似，有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项，<span class="code">^</span>和<span class="code">$</span>的意义就变成了<span class="desc">匹配行的开始处和结束处</span>。</p>
<h2 id="escape">字符转义</h2>
<p>如果你想查找元字符本身的话，比如你查找<span class="desc">.</span>,或者<span class="desc">*</span>,就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用<span class="code">\</span>来取消这些字符的特殊意义。因此，你应该使用<span class="regex">\.</span>和<span class="regex">\*</span>。当然，要查找<span class="desc">\</span>本身，你也得用<span class="regex">\\</span>.</p>
<p>例如：<span class="regex">unibetter\.com</span>匹配<span class="desc">unibetter.com</span>，<span class="regex">C:\\Windows</span>匹配<span class="desc">C:\Windows</span>。</p>
<h2 id="repeat">重复</h2>
<p>你已经看过了前面的<span class="code">*</span>,<span class="code">+</span>,<span class="code">{2}</span>,<span class="code">{5,12}</span>这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码，例如*,{5,12}等)：</p>
<table cellspacing="0">
    <caption>表2.常用的限定符</caption>
    <thead>
        <tr>
            <th scope="col">代码/语法</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><span class="code">*</span></td>
            <td><span class="desc">重复零次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">+</span></td>
            <td><span class="desc">重复一次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">?</span></td>
            <td><span class="desc">重复零次或一次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n}</span></td>
            <td><span class="desc">重复n次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,}</span></td>
            <td><span class="desc">重复n次或更多次</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,m}</span></td>
            <td><span class="desc">重复n到m次</span></td>
        </tr>
    </tbody>
</table>
<p>下面是一些使用重复的例子：</p>
<p><span class="regex">Windows\d+</span>匹配<span class="desc">Windows后面跟1个或更多数字</span></p>
<p><span class="regex">^\w+</span>匹配<span class="desc">一行的第一个单词(或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)</span></p>
<h2 id="charclass">字符类</h2>
<p>要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？</p>
<p>很简单，你只需要在方括号里列出它们就行了，像<span class="regex">[aeiou]</span>就匹配<span class="desc">任何一个英文元音字母</span>，<span class="regex">[.?!]</span>匹配<span class="desc">标点符号(.或?或!)</span>。</p>
<p>我们也可以轻松地指定一个字符<span class="name">范围</span>，像<span class="regex">[0-9]</span>代表的含意与<span class="regex">\d</span>就是完全一致的：<span class="desc">一位数字</span>；同理<span class="regex">[a-z0-9A-Z_]</span>也完全等同于<span class="code">\w</span>（如果只考虑英文的话）。</p>
<p>下面是一个更复杂的表达式：<span class="regex">\(?0\d{2}[) -]?\d{8}</span>。</p>
<p class="note">&#8220;(&#8221;和&#8220;)&#8221;也是元字符，后面的<a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#grouping"><u><font color="#0000ff">分组节</font></u></a>里会提到，所以在这里需要使用<a  href="http://unibetter.com/deerchao/zhengzhe-biaodashi-jiaocheng-se.htm#escape"><u><font color="#0000ff">转义</font></u></a>。</p>
<p>这个表达式可以匹配<span class="desc">几种格式的电话号码</span>，像<span class="string">(010)88886666</span>，或<span class="string">022-22334455</span>，或<span class="string">02912345678</span>等。我们对它进行一些分析吧：首先是一个转义字符<span class="part">\(</span>,它能出现0次或1次(<span class="part">?</span>),然后是一个<span class="part">0</span>，后面跟着2个数字(<span class="part">\d{2}</span>)，然后是<span class="part">)</span>或<span class="part">-</span>或<span class="part">空格</span>中的一个，它出现1次或不出现(<span class="part">?</span>)，最后是8个数字(<span class="part">\d{8}</span>)。</p>
<h2 id="alternative">分枝条件</h2>
<p>不幸的是，刚才那个表达式也能匹配<span class="string">010)12345678</span>或<span class="string">(022-87654321</span>这样的&#8220;不正确&#8221;的格式。要解决这个问题，我们需要用到<span class="name">分枝条件</span>。正则表达式里的<span class="name">分枝条件</span>指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用<span class="code">|</span>把不同的规则分隔开。听不明白？没关系，看例子：</p>
<p><span class="regex">0\d{2}-\d{8}|0\d{3}-\d{7}</span>这个表达式能<span class="desc">匹配两种以连字号分隔的电话号码：一种是三位区号，8位本地号(如010-12345678)，一种是4位区号，7位本地号(0376-2233445)</span>。</p>
<p><span class="regex">\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}</span>这个表达式<span class="desc">匹配3位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔</span>。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。</p>
<p><span class="regex">\d{5}-\d{4}|\d{5}</span>这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字，或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题：<strong>使用分枝条件时，要注意各个条件的顺序</strong>。如果你把它改成<span class="regex">\d{5}|\d{5}-\d{4}</span>的话，那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。</p>
<h2 id="grouping">分组</h2>
<p>我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定<span class="name">子表达式</span>(也叫做<span class="name">分组</span>)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。</p>
<p><span class="regex">(\d{1,3}\.){3}\d{1,3}</span>是一个<span class="desc">简单的IP地址匹配</span>表达式。要理解这个表达式，请按下列顺序分析它：<span class="part">\d{1,3}</span>匹配<span class="desc">1到3位的数字</span>，<span class="part">(\d{1,3}\.){3}</span>匹配<span class="desc">三位数字加上一个英文句号(这个整体也就是这个<span class="name">分组</span>)重复3次</span>，最后再加上<span class="desc">一个一到三位的数字</span>(<span class="part">\d{1,3}</span>)。</p>
<p class="note">IP地址中每个数字都不能大于255，大家千万不要被《24》第三季的编剧给忽悠了...</p>
<p>不幸的是，它也将匹配<span class="string">256.300.888.999</span>这种不可能存在的IP地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的IP地址：<span class="regex">((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)</span>。</p>
<p>理解这个表达式的关键是理解<span class="part">2[0-4]\d|25[0-5]|[01]?\d\d?</span>，这里我就不细说了，你自己应该能分析得出来它的意义。</p>
<h2 id="negation">反义</h2>
<p>有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到<span class="name">反义</span>：</p>
<table cellspacing="0">
    <caption>表3.常用的反义代码</caption>
    <thead>
        <tr>
            <th scope="col">代码/语法</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><span class="code">\W</span></td>
            <td><span class="desc">匹配任意不是字母，数字，下划线，汉字的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\S</span></td>
            <td><span class="desc">匹配任意不是空白符的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\D</span></td>
            <td><span class="desc">匹配任意非数字的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\B</span></td>
            <td><span class="desc">匹配不是单词开头或结束的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">[^x]</span></td>
            <td><span class="desc">匹配除了x以外的任意字符</span></td>
        </tr>
        <tr>
            <td><span class="code">[^aeiou]</span></td>
            <td><span class="desc">匹配除了aeiou这几个字母以外的任意字符</span></td>
        </tr>
    </tbody>
</table>
<p>例子：<span class="regex">\S+</span>匹配<span class="desc">不包含空白符的字符串</span>。</p>
<p><span class="regex">&lt;a[^&gt;]+&gt;</span>匹配<span class="desc">用尖括号括起来的以a开头的字符串</span>。</p>
<h2 id="backreference">后向引用</h2>
<p>使用小括号指定一个子表达式后，<strong>匹配这个子表达式的文本</strong>(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下，每个分组会自动拥有一个<span class="name">组号</span>，规则是：从左向右，以分组的左括号为标志，第一个出现的分组的组号为1，第二个为2，以此类推。</p>
<p><span class="name">后向引用</span>用于重复搜索前面某个分组匹配的文本。例如，<span class="part">\1</span>代表<span class="desc">分组1匹配的文本</span>。难以理解？请看示例：</p>
<p><span class="regex">\b(\w+)\b\s+\1\b</span>可以用来匹配<span class="desc">重复的单词</span>，像<span class="string">go go</span>, 或者<span class="string">kitty kitty</span>。这个表达式首先是<span class="desc">一个单词</span>，也就是<span class="desc">单词开始处和结束处之间的多于一个的字母或数字</span>(<span class="part">\b(\w+)\b</span>)，这个单词会被捕获到编号为1的分组中，然后是<span class="desc">1个或几个空白符</span>(<span class="part">\s+</span>)，最后是<span class="desc">分组1中捕获的内容（也就是前面匹配的那个单词）</span>(<span class="part">\1</span>)。</p>
<p>你也可以自己指定子表达式的<span class="name">组名</span>。要指定一个子表达式的组名，请使用这样的语法：<span class="code">(?&lt;Word&gt;\w+)</span>(或者把尖括号换成<span class="code">'</span>也行：<span class="code">(?'Word'\w+)</span>),这样就把<span class="part">\w+</span>的组名指定为<span class="part">Word</span>了。要反向引用这个分组<span class="name">捕获</span>的内容，你可以使用<span class="code">\k&lt;Word&gt;</span>,所以上一个例子也可以写成这样：<span class="regex">\b(?&lt;Word&gt;\w+)\b\s+\k&lt;Word&gt;\b</span>。</p>
<p>使用小括号的时候，还有很多特定用途的语法。下面列出了最常用的一些：</p>
<table cellspacing="0">
    <caption>表4.常用分组语法</caption>
    <tbody>
        <tr>
            <th scope="col">分类</th>
            <th scope="col">代码/语法</th>
            <th scope="col">说明</th>
        </tr>
        <tr>
            <th rowspan="3">捕获</th>
            <td><span class="code">(exp)</span></td>
            <td><span class="desc">匹配exp,并捕获文本到自动命名的组里</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;name&gt;exp)</span></td>
            <td><span class="desc">匹配exp,并捕获文本到名称为name的组里，也可以写成(?'name'exp)</span></td>
        </tr>
        <tr>
            <td><span class="code">(?:exp)</span></td>
            <td><span class="desc">匹配exp,不捕获匹配的文本，也不给此分组分配组号</span></td>
        </tr>
        <tr>
            <th rowspan="4">零宽断言</th>
            <td><span class="code">(?=exp)</span></td>
            <td><span class="desc">匹配exp前面的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;=exp)</span></td>
            <td><span class="desc">匹配exp后面的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?!exp)</span></td>
            <td><span class="desc">匹配后面跟的不是exp的位置</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;!exp)</span></td>
            <td><span class="desc">匹配前面不是exp的位置</span></td>
        </tr>
        <tr>
            <th>注释</th>
            <td><span class="code">(?#comment)</span></td>
            <td><span class="desc">这种类型的分组不对正则表达式的处理产生任何影响，用于提供注释让人阅读</span></td>
        </tr>
    </tbody>
</table>
<p>我们已经讨论了前两种语法。第三个<span class="code">(?:exp)</span>不会改变正则表达式的处理方式，只是这样的组匹配的内容<span class="desc">不会像前两种那样被捕获到某个组里面，也不会拥有组号</span>。</p>
<h2 id="lookaround">零宽断言</h2>
<p class="note">地球人，是不是觉得这些术语名称太复杂，太难记了？我也和你一样。知道有这么一种东西就行了，它叫什么，随它去吧！&#8220;无名，万物之始...&#8221;</p>
<p>接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西，也就是说它们像<span class="code">\b</span>,<span class="code">^</span>,<span class="code">$</span>那样用于指定一个位置，这个位置应该满足一定的条件(即断言)，因此它们也被称为<span class="name">零宽断言</span>。最好还是拿例子来说明吧：</p>
<p class="note">断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。</p>
<p><span class="code">(?=exp)</span>也叫<span class="name">零宽度正预测先行断言</span>，它<span class="desc">断言自身出现的位置的后面能匹配表达式exp</span>。比如<span class="regex">\b\w+(?=ing\b)</span>，匹配<span class="desc">以ing结尾的单词的前面部分(除了ing以外的部分)</span>，如查找<span class="string">I'm singing while you're dancing.</span>时，它会匹配<span class="desc">sing</span>和<span class="desc">danc</span>。</p>
<p><span class="code">(?&lt;=exp)</span>也叫<span class="name">零宽度正回顾后发断言</span>，它<span class="desc">断言自身出现的位置的前面能匹配表达式exp</span>。比如<span class="regex">(?&lt;=\bre)\w+\b</span>会匹配<span class="desc">以re开头的单词的后半部分(除了re以外的部分)</span>，例如在查找<span class="string">reading a book</span>时，它匹配<span class="desc">ading</span>。</p>
<p>假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了)，你可以这样查找需要在前面和里面添加逗号的部分：<span class="regex">((?&lt;=\d)\d{3})*\b</span>，用它对<span class="string">1234567890</span>进行查找时结果是<span class="desc">234567890</span>。</p>
<p>下面这个例子同时使用了这两种断言：<span class="regex">(?&lt;=\s)\d+(?=\s)</span>匹配<span class="desc">以空白符间隔的数字(再次强调，不包括这些空白符)</span>。</p>
<h2 id="negativelookaround">负向零宽断言</h2>
<p>前面我们提到过怎么查找<strong>不是某个字符或不在某个字符类里</strong>的字符的方法(反义)。但是如果我们只是想要<strong>确保某个字符没有出现，但并不想去匹配它</strong>时怎么办？例如，如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样：</p>
<p><span class="regex">\b\w*q[^u]\w*\b</span>匹配<span class="desc">包含<strong>后面不是字母u的字母q</strong>的单词</span>。但是如果多做测试(或者你思维足够敏锐，直接就观察出来了)，你会发现，如果q出现在单词的结尾的话，像<strong>Iraq</strong>,<strong>Benq</strong>，这个表达式就会出错。这是因为<span class="part">[^u]</span>总要匹配一个字符，所以如果q是单词的最后一个字符的话，后面的<span class="part">[^u]</span>将会匹配q后面的单词分隔符(可能是空格，或者是句号或其它的什么)，后面的<span class="part">\w*\b</span>将会匹配下一个单词，于是<span class="regex">\b\w*q[^u]\w*\b</span>就能匹配整个<span class="string">Iraq fighting</span>。<span class="name">负向零宽断言</span>能解决这样的问题，因为它只匹配一个位置，并不<strong>消费</strong>任何字符。现在，我们可以这样来解决这个问题：<span class="regex">\b\w*q(?!u)\w*\b</span>。</p>
<p><span class="name">零宽度负预测先行断言</span><span class="code">(?!exp)</span>，<span class="desc">断言此位置的后面不能匹配表达式exp</span>。例如：<span class="regex">\d{3}(?!\d)</span>匹配<span class="desc">三位数字，而且这三位数字的后面不能是数字</span>；<span class="regex">\b((?!abc)\w)+\b</span>匹配<span class="desc">不包含连续字符串abc的单词</span>。</p>
<p>同理，我们可以用<span class="code">(?&lt;!exp)</span>,<span class="name">零宽度正回顾后发断言</span>来<span class="desc">断言此位置的前面不能匹配表达式exp</span>：<span class="regex">(?&lt;![a-z])\d{7}</span>匹配<span class="desc">前面不是小写字母的七位数字</span>。</p>
<p class="note">请详细分析表达式<span class="regex">(?&lt;=&lt;(\w+)&gt;).*(?=&lt;\/\1&gt;)</span>，这个表达式最能表现零宽断言的真正用途。</p>
<p>一个更复杂的例子：<span class="regex">(?&lt;=&lt;(\w+)&gt;).*(?=&lt;\/\1&gt;)</span>匹配<span class="desc">不包含属性的简单HTML标签内里的内容</span>。<span class="code">(&lt;?(\w+)&gt;)</span>指定了这样的<span class="name">前缀</span>：<span class="desc">被尖括号括起来的单词</span>(比如可能是&lt;b&gt;)，然后是<span class="part">.*</span>(任意的字符串),最后是一个<span class="name">后缀</span><span class="part">(?=&lt;\/\1&gt;)</span>。注意后缀里的<span class="part">\/</span>，它用到了前面提过的字符转义；<span class="part">\1</span>则是一个反向引用，引用的正是<span class="desc">捕获的第一组</span>，前面的<span class="part">(\w+)</span>匹配的内容，这样如果前缀实际上是&lt;b&gt;的话，后缀就是&lt;/b&gt;了。整个表达式匹配的是&lt;b&gt;和&lt;/b&gt;之间的内容(再次提醒，不包括前缀和后缀本身)。</p>
<h2 id="commenting">注释</h2>
<p>小括号的另一种用途是通过语法<span class="code">(?#comment)</span>来包含注释。例如：<span class="regex">2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)</span>。</p>
<p>要包含注释的话，最好是启用&#8220;忽略模式里的空白符&#8221;选项，这样在编写表达式时能任意的添加空格，Tab，换行，而实际使用时这些都将被忽略。启用这个选项后，在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如，我们可以前面的一个表达式写成这样：</p>
<pre class="regex">      (?&lt;=    # 断言要匹配的文本的前缀<br>      &lt;(\w+)&gt; # 查找尖括号括起来的字母或数字(即HTML/XML标签)<br>      )       # 前缀结束<br>      .*      # 匹配任意文本<br>      (?=     # 断言要匹配的文本的后缀<br>      &lt;\/\1&gt;  # 查找尖括号括起来的内容：前面是一个"/"，后面是先前捕获的标签<br>      )       # 后缀结束</pre>
<h2 id="greedyandlazy">贪婪与懒惰</h2>
<p>当正则表达式中包含能接受重复的限定符时，通常的行为是（在使整个表达式能得到匹配的前提下）匹配<strong>尽可能多</strong>的字符。考虑这个表达式：<span class="regex">a.*b</span>，它将会匹配<span class="desc">最长的以a开始，以b结束的字符串</span>。如果用它来搜索<span class="string">aabab</span>的话，它会匹配整个字符串<span class="desc">aabab</span>。这被称为<span class="name">贪婪</span>匹配。</p>
<p>有时，我们更需要<span class="name">懒惰</span>匹配，也就是匹配<strong>尽可能少</strong>的字符。前面给出的限定符都可以被转化为懒惰匹配模式，只要在它后面加上一个问号<span class="code">?</span>。这样<span class="regex">.*?</span>就意味着<span class="desc">匹配任意数量的重复，但是在能使整个匹配成功的前提下使用最少的重复</span>。现在看看懒惰版的例子吧：</p>
<p><span class="regex">a.*?b</span>匹配<span class="desc">最短的，以a开始，以b结束的字符串</span>。如果把它应用于<span class="string">aabab</span>的话，它会匹配<span class="desc">aab（第一到第三个字符）</span>和<span class="desc">ab（第四到第五个字符）</span>。</p>
<p class="note">为什么第一个匹配是aab（第一到第三个字符）而不是ab（第二到第三个字符）？简单地说，因为正则表达式有另一条规则，比懒惰／贪婪规则的优先级更高：最先开始的匹配拥有最高的优先权——The match that begins earliest wins。</p>
<table cellspacing="0">
    <caption>表5.懒惰限定符</caption>
    <thead>
        <tr>
            <th scope="col">代码/语法</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><span class="code">*?</span></td>
            <td><span class="desc">重复任意次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">+?</span></td>
            <td><span class="desc">重复1次或更多次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">??</span></td>
            <td><span class="desc">重复0次或1次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,m}?</span></td>
            <td><span class="desc">重复n到m次，但尽可能少重复</span></td>
        </tr>
        <tr>
            <td><span class="code">{n,}?</span></td>
            <td><span class="desc">重复n次以上，但尽可能少重复</span></td>
        </tr>
    </tbody>
</table>
<h2 id="regexoptions">处理选项</h2>
<p class="note">在C#中，你可以使用<a  href="http://msdn2.microsoft.com/zh-cn/library/h5845fdz.aspx" title="MSDN 相关文档"><u><font color="#0000ff">Regex(String, RegexOptions)构造函数</font></u></a>来设置正则表达式的处理选项。如：Regex regex = new Regex("\ba\w{6}\b", RegexOptions.IgnoreCase);</p>
<p>上面介绍了几个选项如忽略大小写，处理多行等，这些选项能用来改变处理正则表达式的方式。下面是.Net中常用的正则表达式选项：</p>
<table cellspacing="0">
    <caption>表6.常用的处理选项</caption>
    <thead>
        <tr>
            <th scope="col">名称</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>IgnoreCase(忽略大小写)</td>
            <td>匹配时不区分大小写。</td>
        </tr>
        <tr>
            <td>Multiline(多行模式)</td>
            <td>更改<span class="code">^</span>和<span class="code">$</span>的含义，使它们分别在任意一行的行首和行尾匹配，而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,<span class="code">$</span>的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) </td>
        </tr>
        <tr>
            <td>Singleline(单行模式)</td>
            <td>更改<span class="code">.</span>的含义，使它与每一个字符匹配（包括换行符\n）。 </td>
        </tr>
        <tr>
            <td>IgnorePatternWhitespace(忽略空白)</td>
            <td>忽略表达式中的非转义空白并启用由<span class="code">#</span>标记的注释。</td>
        </tr>
        <tr>
            <td>RightToLeft(从右向左查找)</td>
            <td>匹配从右向左而不是从左向右进行。</td>
        </tr>
        <tr>
            <td>ExplicitCapture(显式捕获)</td>
            <td>仅捕获已被显式命名的组。</td>
        </tr>
        <tr>
            <td>ECMAScript(JavaScript兼容模式)</td>
            <td>使表达式的行为与它在JavaScript里的行为一致。</td>
        </tr>
    </tbody>
</table>
<p>一个经常被问到的问题是：是不是只能同时使用多行模式和单行模式中的一种？答案是：不是。这两个选项之间没有任何关系，除了它们的名字比较相似（以至于让人感到疑惑）以外。</p>
<h2 id="balancedgroup">平衡组/递归匹配</h2>
<p class="important note">这里介绍的平衡组语法是由.Net Framework支持的；其它语言／库不一定支持这种功能，或者支持此功能但需要使用不同的语法。</p>
<p>有时我们需要匹配像<span class="desc">( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构</span>，这时简单地使用<span class="code">\(.+\)</span>则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式，懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等，比如<span class="string">( 5 / ( 3 + 2 ) ) )</span>，那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的，配对的括号之间的内容呢？</p>
<p>为了避免<span class="code">(</span>和<span class="code">\(</span>把你的大脑彻底搞糊涂，我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把<span class="string">xx &lt;aa &lt;bbb&gt; &lt;bbb&gt; aa&gt; yy</span>这样的字符串里，最长的配对的尖括号内的内容捕获出来？</p>
<p>这里需要用到以下的语法构造：</p>
<ul>
    <li><span class="code">(?'group')</span> 把捕获的内容命名为group,并压入<span class="name">堆栈(Stack)</span>
    </li>
    <li><span class="code">(?'-group')</span> 从堆栈上弹出最后压入堆栈的名为group的捕获内容，如果堆栈本来为空，则本分组的匹配失败
    </li>
    <li><span class="code">(?(group)yes|no)</span> 如果堆栈上存在以名为group的捕获内容的话，继续匹配yes部分的表达式，否则继续匹配no部分
    </li>
    <li><span class="code">(?!)</span> 零宽负向先行断言，由于没有后缀表达式，试图匹配总是失败</li>
</ul>
<p class="note">如果你不是一个程序员（或者你自称程序员但是不知道堆栈是什么东西），你就这样理解上面的三种语法吧：第一个就是在黑板
上写一个"group"，第二个就是从黑板上擦掉一个"group"，第三个就是看黑板上写的还有没有"group"，如果有就继续匹配yes部分，否则
就匹配no部分。</p>
<p>我们需要做的是每碰到了左括号，就在压入一个"Open",每碰到一个右括号，就弹出一个，到了最后就看看堆栈是否为空－－如果不为空那就证明左括号比右括号多，那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符)，尽量使整个表达式得到匹配。</p>
<pre class="regex">&lt;                         #最外层的左括号<br>    [^&lt;&gt;]*                #最外层的左括号后面的不是括号的内容<br>    (<br>        (<br>            (?'Open'&lt;)    #碰到了左括号，在黑板上写一个"Open"<br>            [^&lt;&gt;]*       #匹配左括号后面的不是括号的内容<br>        )+<br>        (<br>            (?'-Open'&gt;)   #碰到了右括号，擦掉一个"Open"<br>            [^&lt;&gt;]*        #匹配右括号后面不是括号的内容<br>        )+<br>    )*<br>    (?(Open)(?!))         #在遇到最外层的右括号前面，判断黑板上还有没有没擦掉的"Open"；如果还有，则匹配失败<br>&gt;                         #最外层的右括号</pre>
<p>平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配<span class="desc">嵌套的&lt;div&gt;标签</span>：<span class="regex">&lt;div[^&gt;]*&gt;[^&lt;&gt;]*(((?'Open'&lt;div[^&gt;]*&gt;)[^&lt;&gt;]*)+((?'-Open'&lt;/div&gt;)[^&lt;&gt;]*)+)*(?(Open)(?!))&lt;/div&gt;</span>.</p>
<h2 id="more">还有些什么东西没提到</h2>
<p>我已经描述了构造正则表达式的大量元素，还有一些我没有提到的东西。下面是未提到的元素的列表，包含语法和简单的说明。你可以在网上找到更详细的参
考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关于.net下正则表达式详细的文档。</p>
<table cellspacing="0">
    <caption>表7.尚未详细讨论的语法</caption>
    <thead>
        <tr>
            <th scope="col">代码/语法</th>
            <th scope="col">说明</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td><span class="code">\a</span></td>
            <td><span class="desc">报警字符(打印它的效果是电脑嘀一声)</span></td>
        </tr>
        <tr>
            <td><span class="code">\b</span></td>
            <td><span class="desc">通常是单词分界位置，但如果在字符类里使用代表退格</span></td>
        </tr>
        <tr>
            <td><span class="code">\t</span></td>
            <td><span class="desc">制表符，Tab</span></td>
        </tr>
        <tr>
            <td><span class="code">\r</span></td>
            <td><span class="desc">回车</span></td>
        </tr>
        <tr>
            <td><span class="code">\v</span></td>
            <td><span class="desc">竖向制表符</span></td>
        </tr>
        <tr>
            <td><span class="code">\f</span></td>
            <td><span class="desc">换页符</span></td>
        </tr>
        <tr>
            <td><span class="code">\n</span></td>
            <td><span class="desc">换行符</span></td>
        </tr>
        <tr>
            <td><span class="code">\e</span></td>
            <td><span class="desc">Escape</span></td>
        </tr>
        <tr>
            <td><span class="code">\0nn</span></td>
            <td><span class="desc">ASCII代码中八进制代码为nn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\xnn</span></td>
            <td><span class="desc">ASCII代码中十六进制代码为nn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\unnnn</span></td>
            <td><span class="desc">Unicode代码中十六进制代码为nnnn的字符</span></td>
        </tr>
        <tr>
            <td><span class="code">\cN</span></td>
            <td><span class="desc">ASCII控制字符。比如\cC代表Ctrl+C</span></td>
        </tr>
        <tr>
            <td><span class="code">\A</span></td>
            <td><span class="desc">字符串开头(类似^，但不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\Z</span></td>
            <td><span class="desc">字符串结尾或行尾(不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\z</span></td>
            <td><span class="desc">字符串结尾(类似$，但不受处理多行选项的影响)</span></td>
        </tr>
        <tr>
            <td><span class="code">\G</span></td>
            <td><span class="desc">当前搜索的开头</span></td>
        </tr>
        <tr>
            <td><span class="code">\p{name}</span></td>
            <td><span class="desc">Unicode中命名为name的字符类，例如\p{IsGreek}</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&gt;exp)</span></td>
            <td><span class="desc">贪婪子表达式</span></td>
        </tr>
        <tr>
            <td><span class="code">(?&lt;x&gt;-&lt;y&gt;exp)</span></td>
            <td><span class="desc">平衡组</span></td>
        </tr>
        <tr>
            <td><span class="code">(?im-nsx:exp)</span></td>
            <td><span class="desc">在子表达式exp中改变处理选项</span></td>
        </tr>
        <tr>
            <td><span class="code">(?im-nsx)</span></td>
            <td><span class="desc">为表达式后面的部分改变处理选项</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(exp)yes|no)</span></td>
            <td><span class="desc">把exp当作零宽正向先行断言，如果在这个位置能匹配，使用yes作为此组的表达式；否则使用no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(exp)yes)</span></td>
            <td><span class="desc">同上，只是使用空表达式作为no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(name)yes|no)</span></td>
            <td><span class="desc">如果命名为name的组捕获到了内容，使用yes作为表达式；否则使用no</span></td>
        </tr>
        <tr>
            <td><span class="code">(?(name)yes)</span></td>
            <td><span class="desc">同上，只是使用空表达式作为no</span></td>
        </tr>
    </tbody>
</table>
<h2 id="contact">联系作者</h2>
<p>好吧,我承认,我骗了你,读到这里你肯定花了不止30分钟.相信我,这是我的错,而不是因为你太笨.我之所以说"30分钟",是为了让你有信心,有耐心继续下去.既然你看到了这里,那证明我的阴谋成功了.被忽悠的感觉很爽吧？</p>
<p>要投诉我,或者觉得我其实可以做得更好,或者有任何其它问题,欢迎来<a  href="http://www.cnblogs.com/deerchao/archive/2006/08/24/zhengzhe30fengzhongjiaocheng.html"><u><font color="#0000ff">我的博客</font></u></a>让我知道.</p>
</div><img src ="http://www.cppblog.com/eXile/aggbug/72092.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-01-15 14:58 <a href="http://www.cppblog.com/eXile/archive/2009/01/15/72092.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Qt 4.5使用LGPL</title><link>http://www.cppblog.com/eXile/archive/2009/01/14/72031.html</link><dc:creator>eXile</dc:creator><author>eXile</author><pubDate>Wed, 14 Jan 2009 11:01:00 GMT</pubDate><guid>http://www.cppblog.com/eXile/archive/2009/01/14/72031.html</guid><wfw:comment>http://www.cppblog.com/eXile/comments/72031.html</wfw:comment><comments>http://www.cppblog.com/eXile/archive/2009/01/14/72031.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/eXile/comments/commentRss/72031.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eXile/services/trackbacks/72031.html</trackback:ping><description><![CDATA[http://www.qtsoftware.com/about/licensing<br><br>
<p>Nokia today announced that its Qt cross-platform user interface (UI) and application framework for desktop and embedded platforms will be available under the open source LGPL version 2.1 license from the release of Qt 4.5, scheduled for March 2009.&nbsp; In addition:</p>
<ul>
    <li>Qt source code repositories will be made publicly available and will encourage contributions from desktop and embedded developer communities.
    <li>Service offerings for Qt will be expanded to ensure that all Qt development projects can have access to the same levels of support, independent of the selected license. <br></li>
</ul>
<a title="Permanent Link to Nokia to license Qt under LGPL" href="http://labs.trolltech.com/blogs/2009/01/14/nokia-to-license-qt-under-lgpl/" rel=bookmark>Nokia to license Qt under LGPL</a><br><br>
<p>We have some exciting news we&#8217;d like to share with the free software community: Qt will be licensed under the terms of the LGPL version 2.1 with the upcoming Qt 4.5 release, in addition to our standard GPL and commercial licenses. We are also pleased to announce that we are going to open up the Qt source code repository and also make it easier for the community to contribute to Qt.</p>
<p>Adding LGPL version 2.1 will greatly increase adoption of Qt across Windows, Linux, embedded Linux, Mac, S60, Windows CE, maemo, and Ovi web services. Having a larger number of users, including Nokia developers, providing feedback and contributions will help Qt remain a cutting edge, robust UI and application framework.</p>
<p>Going forward we will speed up the development of Qt using additional resources and work in even closer cooperation with the free software community. We will do this in a number of ways, including:</p>
<ul>
    <li>Employing more Qt developers
    <li>Opening our source code repository
    <li>Reducing the overhead needed to make a submission, including no longer requiring copyright assignments.
    <li>Launching a new web infrastructure to support contributions later this year. </li>
</ul>
<p>As we know that license versions are important for many in the free software community, we wanted to also take the opportunity to provide some highlights of our upcoming changes:</p>
<ul>
    <li>Versions of Qt prior to 4.5 are not impacted by this announcement
    <li>We will continue to support the GPL version 2 through the newly added LGPL version 2.1 license, as it allows for the automatic conversion to the GPL.
    <li>We will continue to release Qt under GPL 3 </li>
</ul>
<p>As a first step we have selected LGPL version 2.1 as this is the version of the LGPL that best fits our purposes and we are most comfortable with at this point in time. We will continue to evaluate the adoption, use and legal interpretation of LGPL version 3 by the community and may use this version of the LGPL for future releases. </p>
<p>Finally, we will open the Qt repositories and provide more information regarding how interested parties can contribute to Qt with the release of Qt 4.5, which is scheduled for March. Until then, if you have any questions, please feel free to post your questions below.</p>
<p>Sincerely,</p>
<p>Sebastian Nystr&#246;m<br>Vice President, Qt Software</p>
<br>
<img src ="http://www.cppblog.com/eXile/aggbug/72031.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eXile/" target="_blank">eXile</a> 2009-01-14 19:01 <a href="http://www.cppblog.com/eXile/archive/2009/01/14/72031.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>