﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-面对现实，超越自己-随笔分类-其他</title><link>http://www.cppblog.com/wanghaiguang/category/19460.html</link><description>逆水行舟，不进则退</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2014 16:44:51 GMT</lastBuildDate><pubDate>Tue, 20 May 2014 16:44:51 GMT</pubDate><ttl>60</ttl><item><title>程序员需要具备的基本技能（转载）</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/11/28/204491.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Thu, 28 Nov 2013 03:55:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/11/28/204491.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/204491.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/11/28/204491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/204491.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/204491.html</trackback:ping><description><![CDATA[<p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;">软件开发是一个跨度很大的技术工作，在语言方面，有C，C++，Java，Ruby等等等等，在环境方面，又分嵌入式，桌面系统，企业级，WEB，基础系统，或是科学研究。但是，不管是什么的情况，总是有一些通用的基本职业技能。</p><p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;">这些最基本的职业技能通常决定了一个程序员的级别，能否用好这些技能，直接关系到了程序员的职业生涯。很多程序新手也是因为缺少、达不到或是不熟悉在这些基本技能，所以，他们需要有老手带，需要努力补齐这些技能。而高级程序员应该非常熟悉这些基本技能，而且有能力胜任并带领其他经验不足的程序员。</p><p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;">下面这些基本职业技术可以用来做为对一个程序员的评估，很明显，下面的这些技能都可以用来做面试。虽然，还有很多非技术的因素，但对于评估一个程序员的技术能力来说，其应该是足够的了。</p><p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;">下面是程序员所应该具备的基本职业技能：</p><p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;"></p><table border="0" cellspacing="0" style="background-color: #ffffff; border: 2px solid #cccccc; border-collapse: collapse; margin: 5px 0px 10px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; font-size: 14px; line-height: 22px;"><tbody><tr><th width="20%" style="border: 1px solid #cccccc; padding: 3px 10px; vertical-align: top; background-color: #edeff0;">基本技能</th><th width="80%" style="border: 1px solid #cccccc; padding: 3px 10px; vertical-align: top; background-color: #edeff0;">技能描述</th></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">阅读代码</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">这个技能需要程序员能够具备读懂已经存在的代码的能力，这样的能力可以让程序员分析程序的行为，了解程序，这样才能和开发团队一起工作，继承维护或是改进现有的程序。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">编写程序</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">编写程序并不包括程序设计。不要以为编程是一件很简单的事情，很多程序员都认为编程只需要懂得程序语言的语法，并把设计实现就可以了。但是这离编写程序还远远不够，使用什么样的编码风格成为编写程序员最需要具备的基本技能。能否使用非常良好的编程风格直接决写了程序员的级别。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">软件设计</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">这一能力直接决定了需要吏用什么样的代码技术达到怎么样的功能，而系统架构设计直接决定了软件的质量、性能和可维护性。并不是所有的程序在这一方面都非常优秀，但每个程序员都需要或多或少的明白和掌握这一基本技能。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">熟悉软件工程</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">每个程序员都应该明白软件工程是什么东西，都应该知道，需求分析，设计，编码，测试，Release和维护这几个阶段。当然，几乎所有的人都知道这些东西，但并不是每个人都很清楚这些东西。现在很多高级程序员都会混淆&#8220;需求规格说明书FS&#8221;和&#8220;概要设计HLD&#8221;。另外，程序员还需要知道一些软件开发的方法论，比如：敏捷开发或瀑布模型。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">使用程序库或框架</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">一个程序员需要学会使用已有的代码，无论是标论的程序库，或是第三方的，还是自己公司内部的，都需要学会做。比如：C++中，需要学会使用STL，MFC，ATL，BOOST，ACE，CPPUNIT等等。使用这些东西，可以让你的工作事半功倍。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">程序调试</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">程序调试是分析BUG和解决问题最直接的能力。没有人能够保证程序写出来不用调试就可以运行正常，也没有人可以保证程序永远不会出BUG。所以，熟练使用调试器是一个程序员需要具备的基本技能。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">使用IDE</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">学会使用IDE工具也会让你的工作事半功倍。比如，VC++，Emacs，Eclipse等等，并要知道这些IDE的长处和短处。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">使用版本控制</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">一定要学会使用版本控制工具，什么叫mainline/trunk，什么叫tag，什么叫branch，怎么做patch，怎么merge代码，怎么reverse，怎么利用版本控制工具维护不同版本的软件。这是程序员需要明的的软件配置管理中最重要的一块。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">单元测试</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">单元测试是每个程序都需要做的。很多单元测试也是需要编码的。一定要学会在xUnit框架下进行单元测试。比如JUnit, NUnit, CppUnit等等。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">重构代码</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">每个程序员都需要有最基本的能力去重构目前已有的代码，使代码达到最优但却不能影响任何的已有的功能。有一本书叫《软件的重构》，每个程序员都应该读一下。</td></tr><tr><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">自动化编译</td><td style="border-style: solid; border-color: #cccccc; padding: 3px 10px; vertical-align: top;">程序员需要使用一个脚本，其能自动化编程所有的工程和代码，这样，整个开发团队可以不停地集成代码，自动化测试，自动化部署，以及使用一些工具进行静态代码分析或是自动化测试。</td></tr></tbody></table><p style="margin: 0px 0px 10px; padding: 0px; color: #555555; font-family: Arial, Verdana, 'BitStream vera Sans', Tahoma, Helvetica, sans-serif; line-height: 22px;">当然，还有很多的基本技术也是非常重要的，比如，与人的沟通能力，语言的表达能力，写作能力，团队协作能力，适应变化的能力，时间管理能力，多任务处理能力，自我学习能力，故障处理能力，等等，等等，这里只是列举了和技术相关的能力，这些是程序最最最基本的能力，只要是程序员就必需要有的能力。<br /><br />本文转自：<a href="http://coolshell.cn/articles/428.html" style="font-family: verdana, 'courier new'; line-height: 21px;">http://coolshell.cn/articles/428.html</a></p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/204491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-11-28 11:55 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/11/28/204491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VS2010调试笔记</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/05/22/200536.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Wed, 22 May 2013 09:57:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/05/22/200536.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/200536.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/05/22/200536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/200536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/200536.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana; font-size: 12pt;"><strong>1、</strong>今天调试一个demo，发现如下错误。</span><br /><br /><div><span style="font-family: Verdana; font-size: 12pt;">1&gt; &nbsp;系统找不到指定的文件。</span></div><div><span style="font-family: Verdana; font-size: 12pt;">1&gt;C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: The command "copy D:\WL_SDK_LAN_C++_20130521\demo\c++\Debug\Sample.exe D:\YuanTuo_1\wulianSDK\wulian_sdk_sample\SDK\Sample.exe</span></div><div><span style="font-family: Verdana; font-size: 12pt;">1&gt;C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: :VCEnd" exited with code 1.</span><br /><br /><span style="font-family: Verdana; font-size: 12pt;">可以修改下面属性：</span><br /><img src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/1.png" width="938" height="672" alt="" /><br /><br />&nbsp;<h3><span style="font-size: 12pt; font-family: Verdana;">2、</span><a href="http://blog.csdn.net/abcjennifer/article/details/7558508" style="color: #000000; text-decoration: none; font-weight: normal;"><span style="font-size: 12pt; font-family: Verdana;">VS2010编译错误：</span><br /><br /><span style="font-size: 12pt; font-family: Verdana;">fatal error C1189: #error : This file requires _WIN32_WINNT to be </span><span style="font-size: 12pt; font-family: Verdana;">#defined at least to 0x</span></a></h3><div><span style="font-size: 12pt; font-family: Verdana;">fatal error C1189: #error : &nbsp;This file requires _WIN32_WINNT to be #defined at least to 0x0403. Value 0x0501 or higher is recommended.<br /><br /><br /></span></div><span style="background-color: #ffffff; font-family: Verdana; line-height: 23px; font-size: 12pt;">下面是彻底解决方法：</span><br style="background-color: #ffffff; font-family: simsun; line-height: 23px;" /><span style="background-color: #ffffff; font-family: Verdana; line-height: 23px; font-size: 12pt;">在工程的stdafx.h中添加（如有类似语句，需注释掉）</span><br style="background-color: #ffffff; font-family: simsun; line-height: 23px;" /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->#ifndef&nbsp;WINVER&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Allow&nbsp;use&nbsp;of&nbsp;features&nbsp;specific&nbsp;to&nbsp;Windows&nbsp;95&nbsp;and&nbsp;Windows&nbsp;NT&nbsp;4&nbsp;or&nbsp;later.</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">#define</span>&nbsp;WINVER&nbsp;0x0501&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Change&nbsp;this&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;value&nbsp;to&nbsp;target&nbsp;Windows&nbsp;98&nbsp;and&nbsp;Windows&nbsp;2000&nbsp;or&nbsp;later.</span><br /><span style="color: #0000FF; ">#endif</span><br /><br />#ifndef&nbsp;_WIN32_WINNT&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Allow&nbsp;use&nbsp;of&nbsp;features&nbsp;specific&nbsp;to&nbsp;Windows&nbsp;NT&nbsp;4&nbsp;or&nbsp;later.</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">#define</span>&nbsp;_WIN32_WINNT&nbsp;0x0501&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Change&nbsp;this&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;value&nbsp;to&nbsp;target&nbsp;Windows&nbsp;98&nbsp;and&nbsp;Windows&nbsp;2000&nbsp;or&nbsp;later.</span><br /><span style="color: #0000FF; ">#endif</span><br /><br />#ifndef&nbsp;_WIN32_WINDOWS&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Allow&nbsp;use&nbsp;of&nbsp;features&nbsp;specific&nbsp;to&nbsp;Windows&nbsp;98&nbsp;or&nbsp;later.</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">#define</span>&nbsp;_WIN32_WINDOWS&nbsp;0x0501&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Change&nbsp;this&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;value&nbsp;to&nbsp;target&nbsp;Windows&nbsp;Me&nbsp;or&nbsp;later.</span><br /><span style="color: #0000FF; ">#endif</span><br /><br />#ifndef&nbsp;_WIN32_IE&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Allow&nbsp;use&nbsp;of&nbsp;features&nbsp;specific&nbsp;to&nbsp;IE&nbsp;4.0&nbsp;or&nbsp;later.</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">#define</span>&nbsp;_WIN32_IE&nbsp;0x0601&nbsp;<span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;Change&nbsp;this&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;value&nbsp;to&nbsp;target&nbsp;IE&nbsp;5.0&nbsp;or&nbsp;later.</span><br /><span style="color: #0000FF; ">#endif</span></div></div><img src ="http://www.cppblog.com/wanghaiguang/aggbug/200536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-05-22 17:57 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/05/22/200536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从一道面试题来阐释一个普遍的认知误区（转载）</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/04/18/199537.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Thu, 18 Apr 2013 03:03:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/04/18/199537.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/199537.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/04/18/199537.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/199537.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/199537.html</trackback:ping><description><![CDATA[<p><span style="font-size: 10pt; font-family: Verdana;">&nbsp; <em>&nbsp;</em></span><span style="font-size: 12pt; font-family: Verdana;">解释以下语句的含义：</span><em><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /></span><span style="font-size: 12pt; font-family: Verdana;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1</span></em><em><span style="font-size: 12pt; font-family: Verdana;">、</span></em><em><span style="font-size: 12pt; font-family: Verdana;">new A;<br /></span><span style="font-size: 12pt; font-family: Verdana;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2</span></em><em><span style="font-size: 12pt; font-family: Verdana;">、</span></em><em><span style="font-size: 12pt; font-family: Verdana;">new A();&nbsp; &nbsp;</span></em>&nbsp;<span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">也许很多人包括我自己，都可以马上给出第一种情况的答案：在堆上为</span><span style="font-size: 12pt; font-family: Verdana;">A</span><span style="font-size: 12pt; font-family: Verdana;">类分配内存，然后调用</span><span style="font-size: 12pt; font-family: Verdana;">A</span><span style="font-size: 12pt; font-family: Verdana;">的构造函数。这种说法被大家所熟知，因为包括《</span><span style="font-size: 12pt; font-family: Verdana;">STL</span><span style="font-size: 12pt; font-family: Verdana;">源码剖析》等大作在内也都是这么写的（但是你认为这种说法完全正确吗？</span></span><span style="font-size: 12pt; font-family: Verdana; color: red;">其实不尽然，答案后面揭晓<span style="font-size: 12pt; font-family: Verdana;">)</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp; <span style="font-size: 12pt; font-family: Verdana;">第二种情况，对象构造的时候初始化列表为空会和第一种有什么不同呢？对于这种在实际工程中很少使用的情况，我一时还真给不出确切的答案。</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">网上搜了一下，看到</span><span style="font-size: 12pt; font-family: Verdana;">CSDN</span><span style="font-size: 12pt; font-family: Verdana;">里面还有专门针对这个问题的一个帖子（原帖链接</span><span style="font-size: 12pt; font-family: Verdana;">&nbsp;http://bbs.csdn.net/topics/320161716</span><span style="font-size: 12pt; font-family: Verdana;">）。</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">好像最终也没有可以信服的答案，认同度比较高的是这样的说法：</span><span style="font-size: 12pt; font-family: Verdana;">&#8220;</span></span><span style="font-size: 12pt; font-family: Verdana; color: #333333;">加括号调用没有参数的构造函数，不加括号调用默认构造函数或唯一的构造函数，看需求</span><span style="font-size: 12pt; font-family: Verdana;">&#8221; <span style="font-size: 12pt; font-family: Verdana;">（</span><span style="font-size: 12pt; font-family: Verdana;">peakflys</span><span style="font-size: 12pt; font-family: Verdana;">注：这种说法是错误的，答案后面揭晓）</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">既然没有特别靠谱的答案，不如自己动手找出答案。</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">构造以下示例：</span></span></p><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 793.796875px; word-break: break-all;"><span style="color: #008000; font-family: Verdana;">/*</span><span style="color: #008000; font-family: Verdana;">*<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\brief example1 difference&nbsp;between&nbsp;new&nbsp;and&nbsp;new()<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\author&nbsp;peakflys<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\data&nbsp;12:10:24&nbsp;Monday,&nbsp;April&nbsp;08,&nbsp;2013<br />&nbsp;</span><span style="color: #008000; font-family: Verdana;">*/</span><br /><br /><span style="color: #0000ff; font-family: Verdana;">class</span><span style="font-family: Verdana;">&nbsp;A</span><br /><span style="font-family: Verdana;">{</span><br /><span style="color: #0000ff; font-family: Verdana;">public</span><span style="font-family: Verdana;">:</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;a;</span><br /><span style="font-family: Verdana;">};</span><br /><br /><span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;main()</span><br /><span style="font-family: Verdana;">{</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A();</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-family: Verdana;">return</span><span style="font-family: Verdana;">&nbsp;0;</span><br /><span style="font-family: Verdana;">}</span></div><p class="MsoNormal"><span style="font-size: 12pt; font-family: Verdana;">查看</span><span lang="EN-US" style="font-size: 12pt; font-family: Verdana;">main</span><span style="font-size: 12pt; font-family: Verdana;">函数的汇编代码</span><span lang="EN-US" style="font-size: 12pt; font-family: Verdana;">(</span><span style="font-size: 12pt; font-family: Verdana;">编译器：</span><span lang="EN-US" style="font-size: 12pt; font-family: Verdana;">gcc (GCC) 4.4.6 20120305 (Red Hat
4.4.6-4)&nbsp;)</span></p><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 793.796875px; word-break: break-all;"><span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;main()</span><br /><span style="font-family: Verdana;">{</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c4:&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rsp,%rbp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c8:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;ec&nbsp;10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;$0x10,%rsp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005cc:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005d1:&nbsp;&nbsp;&nbsp;e8&nbsp;f2&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt; &nbsp; &nbsp; &nbsp; &nbsp; //调用new</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005d6:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;45&nbsp;f0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,-0x10(%rbp) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //rax寄存器内容赋给指针pa(rax寄存器里是new调用产生的A对象堆内存地址)</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A();</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005da:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005df:&nbsp;&nbsp;&nbsp;e8&nbsp;e4&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt; &nbsp; &nbsp; &nbsp; &nbsp; //调用new</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005e4:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//rax的内容放入rdx，执行之后，rdx里存放的即是通过new A()产生的内存地址</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005e7:&nbsp;&nbsp;&nbsp;c7&nbsp;02&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;movl&nbsp;&nbsp;&nbsp;$0x0,(%rdx) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //把rdx内存指向的内容赋为0值，即把A::a赋值为0</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005ed:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;45&nbsp;f8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,-0x8(%rbp) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //rax寄存器内容赋给指针paa(rax寄存器里是new()调用产生的A对象堆内存地址)</span><br />&nbsp; &nbsp; &nbsp;<span style="color: #0000ff; font-family: Verdana;">return</span><span style="font-family: Verdana;">&nbsp;0;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f1:&nbsp;&nbsp;&nbsp;b8&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x0,%eax</span><br /><span style="font-family: Verdana;">}</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f6:&nbsp;&nbsp;&nbsp;c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;leaveq&nbsp;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f7:&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retq</span></div><span style="font-size: 10pt; font-family: Verdana, sans-serif;">&nbsp;</span><span style="font-size: 12pt; font-family: Verdana, sans-serif;"> &nbsp;&nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">通过上面产生的汇编代码</span><span style="font-size: 12pt; font-family: Verdana;">(</span><span style="font-size: 12pt; font-family: Verdana;">对</span><span style="font-size: 12pt; font-family: Verdana;">AT&amp;T</span><span style="font-size: 12pt; font-family: Verdana;">汇编不熟悉的可以看注释</span><span style="font-size: 12pt; font-family: Verdana;">)</span><span style="font-size: 12pt; font-family: Verdana;">可以很容易看出，</span><span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">的执行，在调用完</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存后，马上对新分配内存中的对象使用</span><span style="font-size: 12pt; font-family: Verdana;">0</span><span style="font-size: 12pt; font-family: Verdana;">值初始化，而</span><span style="font-size: 12pt; font-family: Verdana;">new A </span><span style="font-size: 12pt; font-family: Verdana;">仅仅是调用了</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存！</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">是不是这样就可以下结论</span><span style="font-size: 12pt; font-family: Verdana;"> new A()</span><span style="font-size: 12pt; font-family: Verdana;">比</span><span style="font-size: 12pt; font-family: Verdana;">new A</span><span style="font-size: 12pt; font-family: Verdana;">多了一步，即初始化对象的步骤呢？</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">我们再看看下面这种情况：</span></span><br /><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 793.796875px; word-break: break-all;"><span style="color: #008000; font-family: Verdana;">/*</span><span style="color: #008000; font-family: Verdana;">*<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\brief example2 difference&nbsp;between&nbsp;new&nbsp;and&nbsp;new()<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\author&nbsp;peakflys<br /></span><span style="color: #008000; font-family: Verdana;">&nbsp;*\data&nbsp;12:23:20&nbsp;Monday,&nbsp;April&nbsp;08,&nbsp;2013<br />&nbsp;</span><span style="color: #008000; font-family: Verdana;">*/</span><br /><br /><span style="color: #0000ff; font-family: Verdana;">class</span><span style="font-family: Verdana;">&nbsp;A</span><br /><span style="font-family: Verdana;">{</span><br /><span style="color: #0000ff; font-family: Verdana;">public</span><span style="font-family: Verdana;">:</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A(){a&nbsp;=&nbsp;10;}</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;a;</span><br /><span style="font-family: Verdana;">};</span><br /><br /><span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;main()</span><br /><span style="font-family: Verdana;">{</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A();</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-family: Verdana;">return</span><span style="font-family: Verdana;">&nbsp;0;</span><br /><span style="font-family: Verdana;">}</span></div><p><span style="font-size: 12pt; font-family: Verdana, sans-serif;">&nbsp; &nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">这种情况是类显示提供含默认值的构造函数。</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">查看汇编实现如下：</span></span></p><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #eeeeee; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 793.796875px; word-break: break-all;"><span style="color: #0000ff; font-family: Verdana;">int</span><span style="font-family: Verdana;">&nbsp;main()</span><br /><span style="font-family: Verdana;">{</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c4:&nbsp;&nbsp;&nbsp;55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;e5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rsp,%rbp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c8:&nbsp;&nbsp;&nbsp;53&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push&nbsp;&nbsp;&nbsp;%rbx</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005c9:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;ec&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sub&nbsp;&nbsp;&nbsp;&nbsp;$0x18,%rsp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*pa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005cd:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005d2:&nbsp;&nbsp;&nbsp;e8&nbsp;f1&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005d7:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rbx</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005da:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;d8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,%rax</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005dd:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005e0:&nbsp;&nbsp;&nbsp;e8&nbsp;2d&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;400612&nbsp;&lt;_ZN1AC1Ev&gt;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005e5:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;5d&nbsp;e0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,-0x20(%rbp)</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;*paa&nbsp;=&nbsp;</span><span style="color: #0000ff; font-family: Verdana;">new</span><span style="font-family: Verdana;">&nbsp;A();</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005e9:&nbsp;&nbsp;&nbsp;bf&nbsp;04&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x4,%edi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005ee:&nbsp;&nbsp;&nbsp;e8&nbsp;d5&nbsp;fe&nbsp;ff&nbsp;ff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;4004c8&nbsp;&lt;_Znwm@plt&gt;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f3:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rbx</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f6:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;d8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,%rax</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005f9:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;c7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rax,%rdi</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;4005fc:&nbsp;&nbsp;&nbsp;e8&nbsp;11&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;callq&nbsp;&nbsp;400612&nbsp;&lt;_ZN1AC1Ev&gt;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;400601:&nbsp;&nbsp;&nbsp;48&nbsp;89&nbsp;5d&nbsp;e8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;%rbx,-0x18(%rbp)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000ff; font-family: Verdana;">return</span><span style="font-family: Verdana;">&nbsp;0;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;400605:&nbsp;&nbsp;&nbsp;b8&nbsp;00&nbsp;00&nbsp;00&nbsp;00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;$0x0,%eax</span><br /><span style="font-family: Verdana;">}</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;40060a:&nbsp;&nbsp;&nbsp;48&nbsp;83&nbsp;c4&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;add&nbsp;&nbsp;&nbsp;&nbsp;$0x18,%rsp</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;40060e:&nbsp;&nbsp;&nbsp;5b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pop&nbsp;&nbsp;&nbsp;&nbsp;%rbx</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;40060f:&nbsp;&nbsp;&nbsp;c9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;leaveq&nbsp;</span><br /><span style="font-family: Verdana;">&nbsp;&nbsp;400610:&nbsp;&nbsp;&nbsp;c3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;retq&nbsp;</span></div><p><span style="font-size: 10pt; font-family: Verdana, sans-serif;">&nbsp; &nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">上面的汇编代码就不在添加注释了，因为两种操作产生的汇编代码是一样的，都是先调用</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存，然后调用构造函数。</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">上面的情况在</span><span style="font-size: 12pt; font-family: Verdana;">VS2010</span><span style="font-size: 12pt; font-family: Verdana;">下验证是一样的情况，有兴趣的朋友可以自己去看，这里就不再贴出</span><span style="font-size: 12pt; font-family: Verdana;">VS2010</span><span style="font-size: 12pt; font-family: Verdana;">下的汇编代码了。</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">通过上面的分析，对于</span></span><span style="font-size: 12pt; font-family: Verdana; color: red;">new A<span style="font-size: 12pt; font-family: Verdana;">和</span><span style="font-size: 12pt; font-family: Verdana;"> new A() </span><span style="font-size: 12pt; font-family: Verdana;">的区别</span></span><span style="font-size: 12pt; font-family: Verdana;">，我们可以得出下面的结论：</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp; &nbsp;&nbsp;</span><span style="font-size: 12pt; font-family: Verdana; color: red;">1<span style="font-size: 12pt; font-family: Verdana;">、类体含有显示适合地默认构造函数时，</span><span style="font-size: 12pt; font-family: Verdana;">new A</span><span style="font-size: 12pt; font-family: Verdana;">和</span><span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">的作用一致，都是首先调用</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存，然后调用默认构造函数初始化对象。</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> </span><span style="font-size: 12pt; font-family: Verdana; color: red;">&nbsp; &nbsp; &nbsp;2<span style="font-size: 12pt; font-family: Verdana;">、类体无显示构造函数时，</span><span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">首先调用</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">来为对象分配内存，然后使用空值初始化对象成员变量，而</span><span style="font-size: 12pt; font-family: Verdana;">new A</span><span style="font-size: 12pt; font-family: Verdana;">仅仅是调用</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存，对象的成员变量是无意义的随机值！</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;">&nbsp; <span style="font-size: 12pt; font-family: Verdana;">（</span><span style="font-size: 12pt; font-family: Verdana;">peakflys</span><span style="font-size: 12pt; font-family: Verdana;">注：对于基本数据类型，如</span><span style="font-size: 12pt; font-family: Verdana;">int</span><span style="font-size: 12pt; font-family: Verdana;">等</span> <span style="font-size: 12pt; font-family: Verdana;">适用此条）</span></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> &nbsp; &nbsp;<span style="font-size: 12pt; font-family: Verdana;">注意到，现在很多书籍对</span><span style="font-size: 12pt; font-family: Verdana;">new</span><span style="font-size: 12pt; font-family: Verdana;">操作符的说明都存在纰漏，例如《</span><span style="font-size: 12pt; font-family: Verdana;">STL</span><span style="font-size: 12pt; font-family: Verdana;">源码剖析》中</span><span style="font-size: 12pt; font-family: Verdana;">2.2.2</span><span style="font-size: 12pt; font-family: Verdana;">节中有以下的描述：</span></span></p><img src="http://www.cppblog.com/images/cppblog_com/peakflys/STL%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90_error.png" border="0" alt="" width="874" height="275" style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #28557e;" /><br style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #28557e;" /><p align="left"><span style="font-size: 12pt; font-family: Verdana;">事实证明，</span><span style="font-size: 12pt; font-family: Verdana;">new Foo</span><span style="font-size: 12pt; font-family: Verdana;">的操作是否有构造函数的调用是不确定的，具体要看</span><span style="font-size: 12pt; font-family: Verdana;">Foo</span><span style="font-size: 12pt; font-family: Verdana;">类体里是否有显示构造函数的出现。</span><span style="font-size: 10pt; font-family: Verdana;"><br /> <br /></span><span style="font-size: 10pt; font-family: Verdana;"> /*****************************************</span><span style="font-size: 10pt; font-family: Verdana;">华丽分割线</span><span style="font-size: 10pt; font-family: Verdana;">**************************************<br /> </span><span style="font-size: 12pt; font-family: Verdana;">补充：刚才发现，在</span><span style="font-size: 12pt; font-family: Verdana;">C++Primer</span><span style="font-size: 12pt; font-family: Verdana;">第四版</span><span style="font-size: 12pt; font-family: Verdana;">5.11</span><span style="font-size: 12pt; font-family: Verdana;">节中，已经有了对于</span><span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">的说明：</span></p>  <p align="left" style="line-height: 14.25pt; background-color: #28557e; background-position: initial initial; background-repeat: initial initial;"><span style="font-size: 10pt; font-family: Verdana, sans-serif;"></span></p><p style="margin-top:3.75pt;margin-right:0cm;margin-bottom:3.75pt;margin-left: 0cm;line-height:14.25pt"><span style="font-size: 10pt; font-family: Verdana;">&nbsp; &nbsp;We indicate that we want to value-initialize the newly allocated object by following the type nameby a pair of empty parentheses. The empty parentheses signal that we want initialization but arenot supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized orask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant</span><span style="font-size: 10pt; font-family: Verdana;">：</span></p>  <p style="margin: 3.75pt 0cm; line-height: 14.25pt;"><span style="font-size: 10pt; font-family: Verdana;">&nbsp; &nbsp; &nbsp;int *pi = new int; &nbsp; &nbsp; &nbsp; &nbsp; // pi points to an uninitialized int&nbsp;</span></p>  <p style="margin: 3.75pt 0cm; line-height: 14.25pt;"><span style="font-size: 10pt; font-family: Verdana;">&nbsp; &nbsp; &nbsp;int *pi = new int(); &nbsp; &nbsp; &nbsp; // pi points to an int value-initialized to 0&nbsp;</span></p>  <p style="margin: 3.75pt 0cm; line-height: 14.25pt;"><span style="font-size: 10pt; font-family: Verdana;">In the first case, the int is uninitialized; in the second case, the int is initialized to zero.<br /></span><span style="font-size: 14pt; font-family: Verdana;"> </span><span style="font-size: 12pt; font-family: Verdana;">&nbsp; &nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">这里给出的解释和上面自己分析的</span><span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">的行为是一致的。<br /></span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /></span><span style="font-size: 12pt; font-family: Verdana;"> /***************************************</span><span style="font-size: 12pt; font-family: Verdana;">再次华丽分割线</span><span style="font-size: 12pt; font-family: Verdana;">************************************<br /> </span><span style="font-size: 12pt; font-family: Verdana;">鉴于上面的结论是通过</span><span style="font-size: 12pt; font-family: Verdana;">GCC</span><span style="font-size: 12pt; font-family: Verdana;">和</span><span style="font-size: 12pt; font-family: Verdana;">VS2010</span><span style="font-size: 12pt; font-family: Verdana;">得出的，而且有朋友也提出同样的质疑，为了确定这种结果是否是编译器相关的，刚才特意查看了一下</span><span style="font-size: 12pt; font-family: Verdana;">C++</span><span style="font-size: 12pt; font-family: Verdana;">的标准化文档。</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> </span><span style="font-size: 12pt; font-family: Verdana;">摘自：</span><span style="font-size: 12pt; font-family: Verdana;">ISO/IEC 14882:2003(E) 5.3.4 - 15</span><span style="font-size: 10pt; font-family: Verdana;"><br /></span><span style="font-size: 10pt; font-family: Verdana;"> &#8212; If the new-initializer is omitted:<br /></span><span style="font-size: 10pt; font-family: Verdana;"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8212; If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is default-initialized(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared default constructor.<br /></span><span style="font-size: 10pt; font-family: Verdana;"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8212; Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;<br /></span><span style="font-size: 10pt; font-family: Verdana;"> &#8212; If the new-initializer is of the form (), the item is value-initialized (8.5);<br /><br /> </span><span style="font-size: 12pt; font-family: Verdana;">所以可以确定，这种情况完全是编译器无关的</span><span style="font-size: 12pt; font-family: Verdana;">(</span><span style="font-size: 12pt; font-family: Verdana;">当然那些不完全按照标准实现的编译器除外</span><span style="font-size: 12pt; font-family: Verdana;">)</span><span style="font-size: 12pt; font-family: Verdana;">。</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /> </span><span style="font-size: 12pt; font-family: Verdana;">但是通过上面标准化文档的描述，我们可以看出文中对</span><span style="font-size: 12pt; font-family: Verdana;">new A</span><span style="font-size: 12pt; font-family: Verdana;">在无显示构造函数时的总结并不是特别准确，鉴于很多公司都有这道面试题</span><span style="font-size: 12pt; font-family: Verdana;">(</span><span style="font-size: 12pt; font-family: Verdana;">撇去这些题目的实际考察意义不说</span><span style="font-size: 12pt; font-family: Verdana;">)</span><span style="font-size: 12pt; font-family: Verdana;">，我们有必要再补充一下：</span>&nbsp;<span style="font-size: 12pt; font-family: Verdana;">对于</span><span style="font-size: 12pt; font-family: Verdana;">new A:&nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">这样的语句，再调用完</span><span style="font-size: 12pt; font-family: Verdana;">operator new</span><span style="font-size: 12pt; font-family: Verdana;">分配内存之后，如果</span><span style="font-size: 12pt; font-family: Verdana;">A</span><span style="font-size: 12pt; font-family: Verdana;">类体内含有</span><span style="font-size: 12pt; font-family: Verdana;">POD</span><span style="font-size: 12pt; font-family: Verdana;">类型，则</span><span style="font-size: 12pt; font-family: Verdana;">POD</span><span style="font-size: 12pt; font-family: Verdana;">类型的成员变量处于未定义状态，如果含有非</span><span style="font-size: 12pt; font-family: Verdana;">POD</span><span style="font-size: 12pt; font-family: Verdana;">类型则调用该类型的默认构造函数。而</span>&nbsp;<span style="font-size: 12pt; font-family: Verdana;">new A()</span><span style="font-size: 12pt; font-family: Verdana;">在这些情况下都会初始化。</span><span style="font-size: 10pt; font-family: Verdana, sans-serif;"><br /></span><span style="font-size: 12pt; font-family: Verdana;"> &nbsp; &nbsp;PS</span><span style="font-size: 12pt; font-family: Verdana;">：估计很多公司的</span><span style="font-size: 12pt; font-family: Verdana;">&#8220;</span><span style="font-size: 12pt; font-family: Verdana;">正确答案</span><span style="font-size: 12pt; font-family: Verdana;">&#8220;&nbsp;</span><span style="font-size: 12pt; font-family: Verdana;">也不一定正确吧。</span></p>  <p style="margin: 3.75pt 0cm; line-height: 15.75pt;">&nbsp;</p>  <p><span style="font-family: Verdana; font-size: 12pt;">本文转自：</span><a href="http://www.cppblog.com/peakflys/archive/2013/04/08/199208.html"><span style="font-family: Verdana; font-size: 12pt;">http://www.cppblog.com/peakflys/archive/2013/04/08/199208.html</span></a></p><p>&nbsp;</p><div style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 19px; background-color: #28557e;"><div><div></div></div></div><img src ="http://www.cppblog.com/wanghaiguang/aggbug/199537.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-04-18 11:03 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/04/18/199537.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>敏捷软件开发</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/04/17/199514.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Wed, 17 Apr 2013 09:13:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/04/17/199514.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/199514.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/04/17/199514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/199514.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/199514.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana; font-size: 12pt;">来源于《敏捷软件开发&#8212;&#8212;原则、模式与实践》</span><br /><br /><span style="font-size: 12pt; line-height: 26px; text-indent: 28px; background-color: #ffffff; font-family: Verdana;">常见的设计的臭味&#8212;&#8212;腐化软件的气味</span><span style="font-size: 12pt; line-height: 26px; text-indent: 28px; background-color: #ffffff; font-family: Verdana;">。</span><br /><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">僵化性（</span><span style="font-family: Verdana; font-size: 12pt;">Rigidity</span><span style="font-family: Verdana; font-size: 12pt;">）：很难对系统进行改动，因为每个改动都会迫使许多对系统其他部分的其他改动。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">脆弱性（</span><span style="font-family: Verdana; font-size: 12pt;">Fragility</span><span style="font-family: Verdana; font-size: 12pt;">）：对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">牢固性（</span><span style="font-family: Verdana; font-size: 12pt;">Immobility</span><span style="font-family: Verdana; font-size: 12pt;">）：很难解开系统的纠结，使之成为一些可在其他系统中重用的组件。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">粘滞性（</span><span style="font-family: Verdana; font-size: 12pt;">Viscosity</span><span style="font-family: Verdana; font-size: 12pt;">）：做正确的事情比做错误的事情要困难。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">不必要的复杂性（</span><span style="font-family: Verdana; font-size: 12pt;">Needless Complexity</span><span style="font-family: Verdana; font-size: 12pt;">）：设计中包含有不具任何直接好处的基础结构。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-family: Verdana; font-size: 12pt;">不必要的重复（</span><span style="font-family: Verdana; font-size: 12pt;">Needless Repetition</span><span style="font-family: Verdana; font-size: 12pt;">）：设计中包含有重复的结构，而该重复的结构本可以使用单一的抽象进行统一。</span></p><p style="background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="color: #333333; font-family: Verdana; line-height: 26px; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="color: #333333; font-family: Verdana; line-height: 26px; font-size: 12pt;">晦涩性（</span><font color="#333333" face="Arial"><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">Opacity</span></font><font color="#333333" face="宋体"><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">）：很难阅读、理解。没有很好的表现出意图。<br /><br /></span></font><span style="font-size: medium; line-height: 26px; text-indent: 28px; font-family: Verdana;">敏捷设计是一个过程，不是一个事件。它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程。它致力于保持系统设计在任何时间都尽可能得简单、干净以及富有表现力</span><span style="font-size: medium; line-height: 26px; text-indent: 28px; font-family: Verdana;">。</span><br /><br /></p><div><span style="line-height: 26px; font-size: 12pt; font-family: Verdana;"><strong>敏捷软件开发宣言：</strong></span><span style="line-height: 26px;"><strong><br /><br /></strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">我们正在通过亲身实践以及帮助他人实践，揭示更好的软件开发方法。通过这项工作，我们认为：</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>个体和交互 胜过 过程和工具</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>可以工作的软件 胜过 面面俱到的文档</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>客户合作 胜过 合同谈判</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>响应变化 胜过 遵循计划</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">虽然右项也具有价值，但我们认为左项具有更大的价值。</span></div><div><span style="font-family: 宋体; font-size: medium; line-height: 26px; text-indent: 28px; background-color: #ffffff;">敏捷开发强调以人为中心，而不是以过程为中心，强调尽可能的沟通（与客户，与团队成员），尽可能地以最简单的设计解决问题（从而能够拥抱变化）。</span><span style="line-height: 26px;"><br /></span></div><br /><div><span style="line-height: 26px; font-size: 12pt; font-family: Verdana;"><strong>敏捷宣言遵循的原则</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">我们遵循以下原则：</span></div><p>&nbsp;</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="font-family: Verdana; font-size: 12pt;">1。我们最优先要做的是通过尽早的、持续的交付有价值的软件来使客户满意。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　 规划迭代故事时必须按照优先级安排，为客户先提供最有价值的功能。通过频繁迭代能与客户形成早期的良好合作，及时反馈提高产品质量。敏捷小组关注完成和交 付具有用户价值的功能，而不是孤立的任务。以前我们都用需求规格说明书或者用例来编写详细的需求，敏捷使用用户故事来罗列需求。用户故事是一种表示需求的 轻量级技术，它没有</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">固定的形式和强制性的语法。但是有一些固定的形式可以用来参考还是比较有益的。敏捷估算中使用了这个模板：&#8220;作为【用户的类型】，我希 望可以【能力】以便【业务价值】&#8220;。使用基于用户故事的需求分析方法时，仍可能需要原型和编写文档，只是工作重点更多的转移到了口头交流。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="font-family: Verdana; font-size: 12pt;">2。即使到了开发的后期，也欢迎改变需求。敏捷过程利用变化来为客户创造竞争优势。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　敏捷过程参与者不怕变化，他们认为改变需求是好事情，因为这些改变意味着我们更了解市场需求。</span><br />&nbsp;&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">3。经常性的交付可以工作的软件，交付的间隔可以从几周到几个月，交付的时间间隔越短越好。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　 　迭代是受实践框限制的，意味着即使放弃一些功能也必须按时结束迭代。只要我们可以保证交付的软件可以很好的工作，那么交付时间越短，我们和客户协作就越 紧密，对产品质量就更有益。虽然我们多次迭代，但并不是每次迭代的结果都需要交付给用户，敏捷开发的目标是让他们可以交付。这意味着开发小组在每次迭代中 都会增加一些功能，增加的每个功能都是经过编码、测试，达到了可发布的质量标准的。</span><br /><span style="font-family: Verdana; font-size: 12pt;">　　另外敏捷开发项目中对开发阶段没有什么重要的分割，没有先期的需求阶段，然后是分析阶段，架构设计阶段，编码测试阶段等，在项目真正开始后，每次迭代中都会同时进</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">行所有的上述阶段工作。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">4。在整个项目开发期间，业务人员和开发人员必须天天都在一起工作。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　软件项目不会依照之前设定的计划原路执行，中间对业务的理解、软件的解决方案肯定会存在偏差，所以客户、需求人员、开发人员以及涉众之间必须进行有意义的、频繁&nbsp;</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">的交互，这样就可以在早期及时的发现并解决问题。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">5。围绕被激励起来的人个来构建项目。给他们提供所需要的环境和支持，并且信任他们能够完成工作。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　业务和技术是引起不确定的二个主要方面，人是第三个方面。而业务和技术又必须由人来执行，所以能够激励人来解决这些问题是解决不确定性的关键。只要个人的目标和团</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">队的目标一致，我们就需要鼓舞起每个人的积极性，以个人为中心构建项目，提供所需的环境、支持与信任。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="font-family: Verdana; font-size: 12pt;">6。在团队内部，最具有效果并且富有效率的传递信息的方法，就是面对面的交谈。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　在十几或者二十几个人组成的大团队中，文档是一种比较合适的传递知识和交流的途径。而敏捷团队一般不会很多人（大团队实施敏捷时也会分成多个小的敏捷团队），所以</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">大量的文档交流其实并不是很经济的做法。此时面对面的交谈反而更快速有效。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">7、可工作的软件是首要进度度量标准。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　 　一般的工作都比较容易衡量任务进展，比如让你去搬运1吨的石头，我只要去称一下你已经搬运的石头重量就知道你完成多少了。而对于软件来说，在软件没有编 码、测试完</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">成之前，我们都不能因为代码编写了多少行，测试用例跑了多少个就去度量这个功能是否完成了。衡量这个功能是否完成的首要标准就是这个功能可以工 作了，对用户来说已经可</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">以应用了。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><strong style="font-family: Verdana; font-size: 12pt;">8。敏捷过程提可持续的开发速度。责任人、开发者和用户应该能够保持一个长期的、恒定的开发速度。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　 　很多人都认为软件开发中加班是很正常的，不加班反而不正常，我对此有点不理解，这个可能是国情所致吧。敏捷过程希望能够可持续的进行开发，开发速度不会 随着迭代的任务不同而不同，不欣赏所谓的拼一拼也能完成的态度，开发工作不应该是突击行为。我们不能指望说突击这个项目后就可以轻松了，因为完成一个项目 后会接踵而来下一个项目，而只要还是拼拼的态度，下一个项目依旧会让你的组员再次突击。这时不知道有人会不会说，那我们就一直加班，也是&#8220;持续的开发速 度&#8221;啊，这时可要注意了，持续加班智</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">慧导致人疲劳、厌倦，保持长期恒定的速度也只是一种理想而已。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">9。不断地关注优秀的技能和好的设计会增强敏捷能力。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　　敏捷过程有很多好的技术实践可以加强产品敏捷能力，很多原则、模式和实践也可以增强敏捷开发能力。 《敏捷软件开发－原则、模式与实践》一书中介绍了很多设计，感兴趣的可以去仔细看看。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">10。简单----使未完成的工作最大化的艺术----是根本的。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">　 　我们不可能预期后面需求会如何变化，所以不可能一开始就构建一个完美的架构来适应以后的所有变化。敏捷团队不会去构建明天的软件，而把注意力放在如何通 过最简单的方法完成现在需要解决的问题。这时有人会说，我已经预计到了肯定存在哪些需求扩展点，我们在一开始是否需要考虑呢？这时团队需要根据自己的理解 去决定是否考虑，如果深信在明天发生了这个问题也可以轻易处理的话，那么就最好先不考虑。</span><br />&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">11。最好的构架、需求和设计出自与自组织的团队。</strong></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 敏捷中有很多种实践，大家都知道，迭代式开发是主要的实践方法，而自组织团队也是主要的实践之一。在自组织团队中，管理者不再发号施令，而是让团队自身寻找最佳的工作方式来完成工作。要形成一个自组织团队其实比较难。CSDN采访Mishkin Berteig中说到 自组织团队的第一个要素就是必须有一个团队，而不仅仅是一群人。一群人是一帮在一起工作的人，他们彼此之间并没有太多的沟通，他们也并不视彼此为一体。项目一开始，我们就会组建&#8220;团队&#8221;，但很多时候由构架师、需求人员、开发人员和测试人员组成的是一群人而已。他还认为，团队的形成必须经历几个时期。在 经历了初期的磨合后，成员才会开始对团队共同的工作理念与文化形成一个基本的认识和理解。团队内会逐渐形成规矩，而且这些规矩是不言而喻的。比如，每个人 都知道上午九点来上班，都会主动询问别人是否需要帮助，也都会去主动和别人探讨问题。如果团队成员之间能够达成这样的默契，那么这个团队将成为一个真正高 效的工作团队。在这样团队中，成员之间相互理解，工作效率非常高。在自组织团队中，团队成员不需要遵从别人的详细指令。他们需要更高层次的指导，这种指 导更像是一个目标，一个致力于开发出更好的软件的目标。总之，自组织团队是一个自动自发、有着共同目标和工作文化的团队，这样的团队总是在向它的组织做出 承诺。但是，实现这些承诺对于自组织团队来说非常重要。否则，一旦出现问题，团队成员之间就会出现信任危机。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><br /><span style="font-family: Verdana; font-size: 12pt;">　　虽然敏捷开发小组是以小组为整体 来工作的，但是还是有必要指明一些承担一定任务的角色。第一个角色是产品所有者（Product Owner）。产品所有者的主要职责包括：确认小组所有成员都在追求一个共同的项目前景，确定功能的优先级以便总是在处理最具有价值的功能，以及作出决定 使得对项目的投入可以产生良好的回报。可以对应为以前开发中的&#8220;产品经理&#8221;。另一角色是开发团队（developer），这里的开发人员包括了架构师、设计师、程序员、需求人员、测试人员、文档编写者等，有时产品所有者也可以被看作是</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: Verdana; font-size: 12pt;">开发人员。还有一个重要角色就是项目经理（project manager）。敏捷开发的项目经理会更多的关注领导而不是管理。在某些项目中，项目经理可能同时也是开发人员，少数时候也会担任产品所有者。</span><br />　　&nbsp;<br /><strong style="font-family: Verdana; font-size: 12pt;">12。每隔一定时间，团队会在如何才能更有效地工作方面进行反省，然后相应地对自己的行为进行调整。</strong></p><p style="background-color: #ffffff;"><font color="#362e2b" face="Arial"><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">　　由于很多不确定性因素会导致计划失效，比如项目成员增减、技术应用效果、用户需求的改变、竞争者对我们的影响等都会让我们作出不同的反应。　敏捷不是基于预定义的工作方式，而是基于经验性的方式，对以上这些变化，小组通过不断的反省调整来保持团队的敏捷性。</span></font><br /><br /></p><div><span style="line-height: 26px; font-size: 12pt; font-family: Verdana;"><strong>面向对象设计的原则：</strong></span><span style="line-height: 26px;"><strong><br /><br /></strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">SRP 单一职责原则</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">&nbsp;</span><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">就一个类而言，应该仅有一个引起它变化的原因。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"></span></div><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><strong><span style="font-family: Verdana; font-size: 12pt;">单一职责原则</span></strong><span style="font-family: Verdana; font-size: 12pt;">（</span><span style="font-family: Verdana; font-size: 12pt;">The Single Responsibility Principle</span><span style="font-family: Verdana; font-size: 12pt;">，简称</span><span style="font-family: Verdana; font-size: 12pt;">SRP</span><span style="font-family: Verdana; font-size: 12pt;">）：就一个类而言，应该仅有一个引起它变化的原因</span><span style="font-family: 宋体; font-size: 12pt;">。在</span><span style="font-size: 12pt;">SRP</span><span style="font-family: 宋体; font-size: 12pt;">中，我们把职责定义为&#8220;变化的原因（）&#8221;。如果你能够想到多于一个的动机去改变一个类，那么这个类就具有多于一个的职责。软件设计真正要做的许多内容，就是发现职</span><span style="font-family: 宋体; font-size: 12pt;">责并把那些职责相互分离。</span><span style="font-family: 宋体; font-size: 12pt;">事实上，</span><span style="font-family: 宋体; font-size: 12pt;">我们将要论述的其余原则都会以这样或那样的方式回到这个问题上。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><strong><span style="font-family: Verdana; font-size: 12pt;">开放封闭原则</span></strong><span style="font-family: Verdana; font-size: 12pt;">（</span><span style="font-family: Verdana; font-size: 12pt;">The Open-Close Principle</span><span style="font-family: Verdana; font-size: 12pt;">，简称</span><span style="font-family: Verdana; font-size: 12pt;">OCP</span><span style="font-family: Verdana; font-size: 12pt;">）：软件实体（类、模块、函数等等）应该是可以扩展的，但是不可以修改的</span><span style="font-family: Verdana; font-size: 12pt;">。遵循开放封闭原则设计出的模块具有两个主要的特征。它们是：（</span><span style="font-family: Verdana; font-size: 12pt;">1</span><span style="font-family: Verdana; font-size: 12pt;">）、对于扩展是开放的。这意味着模块的行为是可以扩展的。当应用的需求改变时，我们可以对模块进行扩展，使其具有满足那些改变的新行为。换句话说，我们可以改变模块的功能。（</span><span style="font-family: Verdana; font-size: 12pt;">2</span><span style="font-family: Verdana; font-size: 12pt;">）、对模块行为进行扩展时，不必改动模块的源代码或者二进制代码。模块的二进制可执行版本，无论是可链接的库、</span><span style="font-family: Verdana; font-size: 12pt;">DLL</span><span style="font-family: Verdana; font-size: 12pt;">或者</span><span style="font-family: Verdana; font-size: 12pt;">Java</span><span style="font-family: Verdana; font-size: 12pt;">的</span><span style="font-family: Verdana; font-size: 12pt;">.jar</span><span style="font-family: Verdana; font-size: 12pt;">文件，都无需改动。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><strong style="font-family: Verdana; font-size: 12pt;">Liskov</strong><strong style="font-size: 12pt;"><span style="font-family: 宋体;">替换原则</span></strong><span style="font-family: 宋体; font-size: 12pt;">（</span><span style="font-size: 12pt;">The Liskov Substitution Principle</span><span style="font-family: 宋体; font-size: 12pt;">，简称</span><span style="font-size: 12pt;">LSP</span><span style="font-family: 宋体; font-size: 12pt;">）：子类型必须能够替换掉它们的基类型。</span><span style="font-size: 12pt;">OCP</span><span style="font-family: 宋体; font-size: 12pt;">原则是</span><span style="font-size: 12pt;">OOD</span><span style="font-family: 宋体; font-size: 12pt;">中很多说法的核心</span><span style="font-family: 宋体; font-size: 12pt;">。</span><span style="font-size: 12pt;">LSP</span><span style="font-family: 宋体; font-size: 12pt;">是使</span><span style="font-size: 12pt;">O</span><span style="font-size: 12pt;">CP</span><span style="font-family: 宋体; font-size: 12pt;">成为可能的主要原则之一。正式子类型的可替换性才使得使用基类类型的模块在无需修改的情况下就可以扩展。这种可替换性必须是开发人员可以隐式依赖的东西。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><strong><span style="font-family: Verdana; font-size: 12pt;">依赖倒置原则</span></strong><span style="font-family: Verdana; font-size: 12pt;">（</span><span style="font-family: Verdana; font-size: 12pt;">The Dependency Inversion Principle</span><span style="font-family: Verdana; font-size: 12pt;">，简称</span><span style="font-family: Verdana; font-size: 12pt;">DIP</span><span style="font-family: Verdana; font-size: 12pt;">）：（</span><span style="font-family: Verdana; font-size: 12pt;">1</span><span style="font-family: Verdana; font-size: 12pt;">）、高层模块不应该依赖于底层模块。二者都应该依赖于抽象</span><span style="font-family: Verdana; font-size: 12pt;">。（</span><span style="font-family: Verdana; font-size: 12pt;">2</span><span style="font-family: Verdana; font-size: 12pt;">）、抽象不应该依赖于细节。细节应该依赖于抽象。使用传统的过程化设计所创建出来的依赖关系结构，策略是依赖于细节的。面向对象的程序设计倒置了依赖关系结构，使得细节和策略都依赖于抽象，并且常常是客户拥有服务接口。事实上，这种依赖关系正式好的面向对象设计的标志所在。</span></p><p style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff; margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-family: Verdana; font-size: 12pt;">l<span style="font-size: 7pt; line-height: normal; font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span><strong><span style="font-family: Verdana; font-size: 12pt;">接口隔离原则</span></strong><span style="font-family: Verdana; font-size: 12pt;">（</span><span style="font-family: Verdana; font-size: 12pt;">The Interface Segregation Interface</span><span style="font-family: Verdana; font-size: 12pt;">，简称</span><span style="font-family: Verdana; font-size: 12pt;">ISP</span><span style="font-family: Verdana; font-size: 12pt;">）：不应该强迫客户依赖它们不用的方法</span><span style="font-family: Verdana; font-size: 12pt;">。如果强迫客户程序依赖于那些它们不适用</span><span style="font-family: Verdana; font-size: 12pt;">的方法，那么这些客户程序就面临着由于这些未使用方法的改变所带来的变更。这就无意中导致了所有客户程序之间的耦合。我们希望尽可能地避免这种耦合，因此我们希望分离接口。</span></p><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"></span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>REP 重用发布等价原则</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family: Verdana; font-size: 12pt; line-height: 26px;">重用的粒度就是发布的粒度</span></div><div>&nbsp; &nbsp;&nbsp;<strong>&nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">CCP 共用封闭原则</span></strong></div><div>&nbsp; &nbsp; &nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包产生影响，则将对该包中的所有类产生影响，而对于其他的包不造成任何影响。</span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;<strong>&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">CRP 共同重用原则</span></strong></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp;<span style="font-family: Verdana; font-size: 12pt; line-height: 26px;">一个包中的所有类应该是共同重用的。如果重用了包中的一个类，那么就要重用包中所有类。</span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>ADP 无环依赖原则</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp;<span style="font-family: Verdana; font-size: 12pt; line-height: 26px;">在包的依赖关系图中不允许存在环。</span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;"><strong>SDP 稳定依赖原则</strong></span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">朝着稳定的方向进行依赖。</span></div><div>&nbsp; &nbsp;&nbsp;&nbsp;<strong>&nbsp;<span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">SAP 稳定抽象原则</span></strong></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">&nbsp;</span>&nbsp; &nbsp;&nbsp;&nbsp;<span style="font-family: Verdana; font-size: 12pt; line-height: 26px;">包的抽象程度应该和其稳定程度一致。</span></div><div><span style="line-height: 26px;"><br /></span></div><div><span style="line-height: 26px; font-size: 12pt; font-family: Verdana;"><strong>极限编程实践</strong></span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">完整团队</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">XP项目的所有参与者（开发人员、业务分析师、测试人员等等）一起工作在一个开放的场所中，他们是同一个团队的成员。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">计划游戏</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">计划是持续的，循序渐进的。每2周，开发人员就为下2周估算候选特性的成本，而客户则根据成本和商务价值来选择要实现的特性。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">客户测试</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">作为选择每个所期望的特性的一部分，客户定义出自动验收测试来表明该特性可以工作。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">简单设计</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">团队保持设计恰好和当前的系统功能相匹配，它通过了所有的测试，不包含任何重复，表达出了编写者想表达的所有东西，并且包含尽可能少的代码。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">结对编程</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">所有的产品软件都是由两个程序员，并排坐在一起在同一台电脑上构建的。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">测试驱动开发</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">程序员以非常短的循环周期工作，他们先增加一个失败的测试，然后使之通过。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">改进设计</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">随时改进糟糕的代码。保持代码尽可能的干净，具有表达力。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">持续集成</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">团队总是使系统完整地被集成。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">集体代码所有权</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">任何结对的程序员都可以在任何时候改进任何代码。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">编码标准</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">系统中所有的代码看起来就好像是被单独一个--非常值得胜任的--人编写的。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">隐喻</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">团队提出一个程序工作原理的公共景像。</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">可持续的速度</span></div><div><span style="line-height: 26px; font-family: Verdana; font-size: 12pt;">团队只有持久才有获胜的希望，他们以能够长期维持的速度努力工作，他们保存精力，他们把项目看作是马拉松长袍，而不是全速短跑。</span></div><br /><p>&nbsp;</p><h1><span style="font-size: 12pt; font-family: Verdana;">测试驱动开发</span></h1><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">极限编程（</span><span style="font-family: Verdana; font-size: 12pt;">eXtreme Programming</span><span style="font-family: Verdana; font-size: 12pt;">，简称</span><span style="font-family: Verdana; font-size: 12pt;">XP</span><span style="font-family: Verdana; font-size: 12pt;">）是敏捷方法中最著名的一个。它由一系列简单却相互依赖的时间组成。这些实践结合在一起形成了一个胜于部分结合的整体。其中一个非常重要的，当前也受到格外重视的实践就是</span><span style="font-family: Verdana; font-size: 12pt;">TDD</span><span style="font-family: Verdana; font-size: 12pt;">（测试驱动的开发方法）。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">在测试驱动的开发方法中，编写所有的代码的目的都是为了使失败的单元测试能够通过。首先编写一个单元测试，由于它要测试的功能还不在，所以它会运行失败。然后编写代码使测试通过。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">编写测试用例和代码之间的更迭速度是很快的，基本上几分钟左右。测试用例和代码共同演化，其中测试用例循序渐进地对代码的编写进行指导。作为结果，一个非常完整的测试用例集和代码一起发展起来。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">测试粗略的可以分为单元测试和验收测试。单元测试是用来验证系统中个别机制的白盒测试。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">单元测试用来验证系统的小的组成单元应该按照所期望的方式工作，但是它们没有验证系统作为一个整体时工作的正确性。所以，单元测试是必要的，但是不够充分。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">验收测试是用来验证系统满足客户需求的黑盒测试。验收测试由不了解系统内部机制的人编写。验收测试是程序，因此是可运行的。通常通过使用专门为应用程序的客户创建的脚本语言来编写验收测试。正如单元测试作为可编译、运行的有关系统内部结构的文档那样，验收测试是有关系统特性的可编译、执行的文档。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">编写代码前就编写单元测试会带来四个很明显的好处：</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">1</span><span style="font-family: Verdana; font-size: 12pt;">、首先编写测试使得程序中的每一项功能都有测试来验证它的操作的正确性。这就可以给以后的开发提供支援，使我们可以更自由地对程序进行更改，因为测试可以告诉我们程序仍然具有正确的行为。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">2</span><span style="font-family: Verdana; font-size: 12pt;">、首先编写测试迫使我们必须从程序调用者的有利视角去观察我们将要编写的程序。这样，我们就会在关注程序的功能的同时，直接关注它的接口，我们也就可以设计出便于调用的软件。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">3</span><span style="font-family: Verdana; font-size: 12pt;">、首先编写测试迫使我们把程序设计为可测试的。为了把程序设计为易于调用和可测试的，程序必须和它周边环境解耦。这样首先编写测试迫使我们解除软件中的耦合。面向对象设计的原则在进行这种解除耦合方面具有巨大的帮助作用。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">4</span><span style="font-family: Verdana; font-size: 12pt;">、首先编写测试的另一个重要效果是，测试可以作为一种无价的文档形式。测试就像一套范例，它帮助其他程序员了解如何使用代码。这份文档是可编译、可运行的。它保持最新。它不会撒谎。</span></p><p style="color: #333333; text-indent: 21pt;"><span style="font-family: Verdana; font-size: 12pt;">首先编写验收测试的行为对于系统的架构方面具有深远的影响。为了使系统具有可测试性，就必须要在很高的系统架构层面对系统进行解耦合</span><span style="font-family: Verdana; font-size: 12pt;">。正如单元测试可以促使你在小的方面可以做出优良的设计决策一样，验收测试可以促使你在大的方面做出优良的系统架构决策。<br /><br /></span><span lang="EN-US" style="color: #000000; font-family: Arial; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff;">&nbsp;</span><span style="color: #000000; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff; font-family: 宋体;">软件大师、</span><span lang="EN-US" style="color: #000000; font-family: Arial; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff;">C++</span><span style="color: #000000; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff; font-family: 宋体;">之父</span><span lang="EN-US" style="color: #000000; font-family: Arial; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff;">Bjarne Stroustrup</span><span style="color: #000000; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff; font-family: 宋体;">曾经说过：设计和编程都是人的活动。忘记了这一点，将会失去一切</span><span style="color: #000000; font-size: medium; line-height: 26px; text-indent: 0px; background-color: #ffffff; font-family: 宋体;">。敏捷软件开发方法正是认识到软件开发的这一本质特征而提出的革新性开发方法。使用敏捷开发方法会给我们带来巨大的好处。当然要完全做到也是很困难的。这不仅需要对敏捷的深刻理解，更需要敏捷团队成员的共同努力。</span><br /><span style="font-family: Verdana; font-size: 12pt;"><br /></span></p><span style="font-family: Verdana; font-size: 12pt;">本文参考：</span><a href="http://blog.csdn.net/open2job/article/details/6335000" style="font-family: verdana, 'ms song', Arial, Helvetica, sans-serif; line-height: 21px;"><span style="font-family: Verdana; font-size: 12pt;">http://blog.csdn.net/open2job/article/details/6335000</span></a><p>&nbsp;</p><p>&nbsp;</p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/199514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-04-17 17:13 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/04/17/199514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转载）架构师不可不知的十大可扩展架构</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197080.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Mon, 07 Jan 2013 08:49:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197080.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/197080.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/197080.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/197080.html</trackback:ping><description><![CDATA[<p style="box-sizing: border-box; font-size: 13px; margin-right: 0px; margin-left: 0px; padding: 5px 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;">转自：<a href="http://www.itivy.com/ivy/archive/2011/11/24/something-that-architecture-must-be-aware-of.html" style="box-sizing: border-box; color: #3468a4;">http://www.itivy.com/ivy/archive/2011/11/24/something-that-architecture-must-be-aware-of.html<br style="box-sizing: border-box;" /></a><br style="box-sizing: border-box;" />对于大多数架构师而言，&#8220;可扩展性&#8221;在软件架构方面是最虚无缥缈的说法。这毫不奇怪，因为可扩展性正是如今软件设计领域最值得优先考虑的要素。然 而，计算机科学家们还无法了解一套单独的架构如何才能扩展至各类应用环境当中。相反，我们在数量繁多的方案中所设计出的可扩展性架构，往往以业界较为通用 的已知可扩展模式及个人偏好为标准。简单来讲，打造一套具备可扩展性的系统已经变得更像是一门艺术而不单单是技术。</p><p style="box-sizing: border-box; font-size: 13px; margin-right: 0px; margin-left: 0px; padding: 5px 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;">我们常常会通过观摩杰作体会并学习艺术的精髓，而可扩展性也应该遵循同样的路线！</p><p style="box-sizing: border-box; font-size: 13px; margin-right: 0px; margin-left: 0px; padding: 5px 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;">在这篇文章中，我将列出数款为大家所耳熟能详的可扩展性架构。通常情况下，架构师们完全可以借鉴已知的可扩展架构模式，进而创造出新的可扩展架构。</p><ol style="box-sizing: border-box; font-size: 13px; padding: 0px; margin: 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;"><li value="1" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">LB (负载平衡器) +&nbsp;无共享单位&nbsp;-&nbsp;该模型中包含一系列单元，各单元彼此间不共享任何内容，且一致指向一个将输入文讯按一定条件发往单元处的负载平衡器（这构成一个循 环，以负载等情况为基础）。每个单元可以是一个单独的节点或是紧密耦合的节点所构成的集群。用户可以使用DNS循环、硬件负载平衡器或者软件负载平衡器达 成负载平衡效果。创建一套负载均衡的层次结构，并在其中结合前面提到的各种负载平衡器也是可行的。在由Michael Stonebraker撰写的《&nbsp;<a target="_blank" href="http://db.cs.berkeley.edu/papers/hpts85-nothing.pdf" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">无共享体系架构实例&nbsp;</a>》一文中，专门讨论了此类架构。<br style="box-sizing: border-box;" />&nbsp;</li><li value="2" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">LB +&nbsp;无状态节点&nbsp;+&nbsp;可扩展存储&nbsp;-&nbsp;传统的&nbsp;<a target="_blank" href="http://en.wikipedia.org/wiki/Multitier_architecture" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">三层式Web架构&nbsp;</a>使用的就是这种模型。该模型包括数个与可扩展存储交互的无状态节点以及一个分布于节点间负载中的负载平衡器。在这一模型中，存储通常作为限制因素存在，但NoSQL存储则可以利用这套模型创建出具备相当可扩展性的系统。<br style="box-sizing: border-box;" />&nbsp;</li><li value="3" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">点对点架构&nbsp;(分布式Hash列表&nbsp;(简称DHT)以及内容寻址网络(简称CAN))&nbsp;-这套模型提供了一些传统的 可扩展算法，这些算法的各个方面几乎全部按对数进行了等比例增加。举例来说，像Chord、Pastry（特指免费版）以及CAN都属于此类。而以 Cassandra为代表的、基于P2P架构的几款NoSQL系统也是其中的成员。《&nbsp;<a target="_blank" href="http://www.cs.berkeley.edu/~istoica/papers/2003/cacm03.pdf" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">展望P2P系统中的数据&nbsp;</a>》一文就深入探讨了这类模型的各种细节。<br style="box-sizing: border-box;" />&nbsp;</li><li value="4" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">分布式队列&nbsp;&#8211; 这种模型以将队列实施（即先进先出交付机制）作为网络服务处理为基础。该模型通过JMS队列而广泛得到采用。一般会遵循这种做法的有任务队列以及通过保持队列分级体系实现扩展性的任务队列版本，后者在负载无法及时处理时，任务会由低级层面向高级层面传递。<br style="box-sizing: border-box;" />&nbsp;</li><li value="5" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">发布/订阅模式&nbsp;-&nbsp;一般用于通过网络向彼此发布订阅讯息。《&nbsp;<a target="_blank" href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.10.1076&amp;rep=rep1&amp;type=pdf" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">发布与订阅的多面性&nbsp;</a>》这一经典论文中详细的介绍这一模型，该模型方面最典型的例子即&nbsp;<a target="_blank" href="http://www.naradabrokering.org/" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">NaradaBroker</a>与&nbsp;<a target="_blank" href="http://dl.acm.org/citation.cfm?id=1615220" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">EventJava&nbsp;</a>。<br style="box-sizing: border-box;" />&nbsp;</li><li value="6" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">小道消息与自然灵感式模型&nbsp;-&nbsp;这种模型源自日常生活中小道消息的传播途径，也就是每个节点将随机选择后续节点以交 换信息。正如现实生活中的实际反馈，这种八卦型算法在信息传播方面出奇地迅速。该模型的另一大分支则是受到生物学影响的启发式算法。自然世界中存在着大量 协调及扩展方面极为卓越的固有算法。举例来说，蚂蚁、人类以及蜜蜂等等，都能够以最简洁的交流方式协调好扩展性方面的需要。模型中的算法正是借鉴了这些实 际存在的现象。在论文《&nbsp;<a target="_blank" href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.75.2916&amp;rep=rep1&amp;type=pdf" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">从流行病的蔓延到分布式计算&nbsp;</a>》中对这种模型有着详尽的叙述。<br style="box-sizing: border-box;" />&nbsp;</li><li value="7" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">地图缩小/数据流&nbsp;-&nbsp;这一概念首先由谷歌公司提出，地图缩小为工作的描述及执行提供了一套可扩展的模式。虽然内容 简单，但它仍然成为联机分析处理方面的首要处理模式。数据流则是一种更先进的方式，用来表达执行信息；而像Dryad及Pig这样的项目为数据流的执行提 供了可扩展的框架。论文《&nbsp;<a target="_blank" href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.120.7010&amp;rep=rep1&amp;type=pdf" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">地图缩小：大型集群上的简化数据处理&nbsp;</a>》中设置了专门的主题，详细讨论这一内容。Apache的Hadoop就是这种模型的代表性产品。<br style="box-sizing: border-box;" />&nbsp;</li><li value="8" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">责任树形图&nbsp;-&nbsp;这种模型打破了递归问题的束缚，将整个流程以树状形式加以处理；每个父节点将工作下放至子节点。这种模型扩展性强，并已经被应用于数款可扩展性架构当中。<br style="box-sizing: border-box;" />&nbsp;</li><li value="9" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">流处理&nbsp;-&nbsp;这种模型被用于处理源源不断的数据流及数据。这种处理方式通过网络中的处理节点获得支持（例如Aurora、Twitter Strom以及Apache S4等）。<br style="box-sizing: border-box;" />&nbsp;</li><li value="10" style="font-size: 12px; box-sizing: border-box; list-style: url(http://www.itivy.com/Skins/gazetteer/li.gif); margin-left: 18px;">可扩展存储&nbsp;&#8211; 该模型的应用范围从数据库、NoSQL存储、服务注册到文件系统都有体现。&nbsp;<a target="_blank" href="http://www.infoq.com/articles/perera-data-storage-haystack" style="box-sizing: border-box; color: #dc8e38; background-color: transparent; text-decoration: initial;">链接中的这篇文章&nbsp;</a>以可扩展性为切入点对其进行了深入讨论。</li></ol><p style="box-sizing: border-box; font-size: 13px; margin-right: 0px; margin-left: 0px; padding: 5px 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;">综上所述，可扩展性的实现只有三种方式，即：分布、缓存及异步处理。前文所提到的各种架构事实上都是把这三种方式进行不同组合并加以实施。而另一方 面，不利于可扩展性的因素，除了糟糕的编码本身，全局性协调也起到了重要的影响。简单来说，任何一种全局性协调都会限制系统的可扩展性。本文中所提到的各 种架构也只是在做好了本地性协调，而非全局性协调。</p><p style="box-sizing: border-box; font-size: 13px; margin-right: 0px; margin-left: 0px; padding: 5px 0px; color: #585858; font-family: verdana, helvetica, arial, sans-serif; line-height: 23.383333206176758px; text-align: justify; background-color: #ffffff;">然而，将它们有机地结合起来以创建一套极具可扩展性的架构可不像说起来那么容易，除非我们能找到一种全新的扩展模式。不过经验告诉我们，比起搞一套全新的架构，采用为我们所熟知且更易驾驭的可扩展性解决方案永远是更好的选择。</p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/197080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-01-07 16:49 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转载）怎样量化评价搜索引擎的结果质量</title><link>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197078.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Mon, 07 Jan 2013 08:46:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197078.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/197078.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197078.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/197078.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/197078.html</trackback:ping><description><![CDATA[<h2>转自：<a href="http://www.infoq.com/cn/articles/cyw-evaluate-seachengine-result-quality" style="box-sizing: border-box; color: #3468a4;">http://www.infoq.com/cn/articles/cyw-evaluate-seachengine-result-quality</a><br style="box-sizing: border-box;" /><br style="box-sizing: border-box;" />前言</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">搜索质量评估是搜索技术研究的基础性工作，也是核心工作之一。评价（Metrics）在搜索技术研发中扮演着重要角色，以至于任何一种新方法与他们的评价方式是融为一体的。</p><div style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><br style="box-sizing: border-box;" />搜索引擎结果的好坏与否，体现在业界所称的在相关性（Relevance）上。相关性的定义包括狭义和广义两方面，狭义的解释是：检索结果和用户查询的相关程度。而从广义的层面，相关性可以理解为为用户查询的综合满意度。直观的来看，从用户进入搜索框的那一刻起，到需求获得满足为止，这之间经历的过程越顺畅，越便捷，搜索相关性就越好。本文总结业界常用的相关性评价指标和量化评价方法。供对此感兴趣的朋友参考。</div><h2>Cranfield评价体系</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">A Cranfield-like approach这个名称来源于英国Cranfield University，因为在二十世纪五十年代该大学首先提出了这样一套评价系统：由查询样例集、正确答案集、评测指标构成的完整评测方案，并从此确立了&#8220;评价&#8221;在信息检索研究中的核心地位。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">Cranfield评价体系由三个环节组成：</p><ol style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><li style="font-size: 12px; box-sizing: border-box;">抽取代表性的查询词，组成一个规模适当的集合</li><li style="font-size: 12px; box-sizing: border-box;">针对查询样例集合，从检索系统的语料库中寻找对应的结果，进行标注（通常人工进行）</li><li style="font-size: 12px; box-sizing: border-box;">将查询词和带有标注信息的语料库输入检索系统，对系统反馈的检索结果，使用预定义好的评价计算公式，用数值化的方法来评价检索系统结果和标注的理想结果的接近程度</li></ol><h2>查询词集合的选取</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">Cranfield评价系统在各大搜索引擎公司内有广泛的应用。具体应用时，首先需要解决的问题是构造一个测试用查询词集合。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">按照Andrei Broder（曾在AltaVista/IBM/Yahoo任职）的研究，查询词可分为3类：寻址类查询（Navigational）、信息类查询(Informational)、事务类查询(Transactional)。对应的比例分别为</p><pre style="box-sizing: border-box; overflow: auto; width: 964.25px; padding: 0px 0px 5px; font-size: 12px; line-height: 15px; font-family: 'Courier New', Courier; color: #222222; margin-top: 0px; margin-bottom: 0px; background-color: #fafafa; border: 2px solid #efefef;">Navigational ： 12.3%  Informational ： 62.0%  Transactional ： 25.7% </pre><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">为了使得评估符合线上实际情况，通常查询词集合也会按比例进行选取。通常从线上用户的Query Log文件中自动抽取。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">另外查询集合的构造时，除了上述查询类型外，还可以考虑Query的频次，对热门query（高频查询）、长尾query（中低频）分别占特定的比例。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">另外，在抽取Query时，往往Query的长短也是一个待考虑的因素。因为短query（单term的查询）和长Query（多Term的查询）排序算法往往会有一些不同。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">构成查询集合后，使用这些查询词，在不同系统（例如对比百度和Google）或不同技术间（新旧两套Ranking算法的环境）进行搜索，并对结果进行评分，以决定优劣。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">附图：对同一Query：&#8220;社会保险法&#8221;，各大搜索引擎的结果示意图。下面具体谈谈评分的方法。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image1.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image2.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image3.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image4.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image5.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image6.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><h2>Precision-recall（准确率-召回率方法）</h2><h3>计算方法</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">信息检索领域最广为人知的评价指标为Precision-Recall（准确率-召回率）方法。该方法从提出至今已经历半个世纪，至今在很多搜索引擎公司的效果评估中使用。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">顾名思义，这个方法由准确率和召回率这两个相互关联的统计量构成：召回率（Recall）衡量一个查询搜索到所有相关文档的能力，而准确率（Precision）衡量搜索系统排除不相关文档的能力。（通俗的解释一下：准确率就是算一算你查询得到的结果中有多少是靠谱的；而召回率表示所有靠谱的结果中，有多少被你给找回来了）。这两项是评价搜索效果的最基础指标，其具体的计算方法如下。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">Precision-recall方法假定对一个给定的查询，对应一个被检索的文档集合和一个不相关的文档集合。这里相关性被假设为二元的，用数学形式化方法来描述，则是：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">A表示相关文档集合</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><span style="box-sizing: border-box; text-decoration: overline;">A</span>表示不相关集合</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">B表示被检索到的文档集合</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><span style="box-sizing: border-box; text-decoration: overline;">B</span>表示未被检索到的文档集合</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">则单次查询的准确率和召回率可以用下述公式来表达：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img alt="" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image7.jpg" _href="img://image7.jpg" _p="true" style="box-sizing: border-box; border: 0px;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">（运算符&#8745; 表示两个集合的交集。|x|符号表示集合x中的元素数量）</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">从上面的定义不难看出，召回率和准确率的取值范围均在[0,1]之间。那么不难想象，如果这个系统找回的相关越多，那么召回率越高，如果相关结果全部都给召回了，那么recall此时就等于1.0。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"></p><table cellspacing="0" cellpadding="0" border="1" style="box-sizing: border-box; color: #000000; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; font-size: 13px; line-height: 19px; text-align: start; background-color: #ffffff;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;">&nbsp;</td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">相关的</p></td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">不相关</p></td></tr><tr style="box-sizing: border-box;"><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">被检索到</p></td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">A&#8745; B</p></td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;"><span style="box-sizing: border-box; text-decoration: overline;">A</span>&#8745; B</p></td></tr><tr style="box-sizing: border-box;"><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">未被检索到</p></td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">A&#8745;<span style="box-sizing: border-box; text-decoration: overline;">B</span></p></td><td width="156" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;"><span style="box-sizing: border-box; text-decoration: overline;">A</span>&#8745;<span style="box-sizing: border-box; text-decoration: overline;">B</span></p></td></tr></tbody></table><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"></p><h3>Precision-Recall曲线</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">召回率和准确率分别反映了检索系统的两个最重要的侧面，而这两个侧面又相互制约。因为大规模数据集合中，如果期望检索到更多相关的文档，必然需要&#8220;放宽&#8221;检索标准，因此会导致一些不相关结果混进来，从而使准确率受到影响。类似的，期望提高准确率，将不相关文档尽量去除时，务必要执行更&#8220;严格&#8221;的检索策略，这样也会使一些相关的文档被排除在外，使召回率下降。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">所以为了更清晰的描述两者间的关系，通常我们将Precison-Recall用曲线的方式绘制出来，可以简称为P-R diagram。常见的形式如下图所示。（通常曲线是一个逐步向下的走势，即随着Recall的提高，Precision逐步降低）</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image8.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><h3>P-R的其它形态</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">一些特定搜索应用，会更关注搜索结果中错误的结果。例如，搜索引擎的反作弊系统（Anti-Spam System）会更关注检索结果中混入了多少条作弊结果。学术界把这些错误结果称作假阳性（False Positive）结果，对这些应用，通常选择用虚报率（Fallout）来统计：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img alt="" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image9.jpg" _href="img://image9.jpg" _p="true" style="box-sizing: border-box; border: 0px;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">Fallout和Presion本质是完全相同的。只是分别从正反两方面来计算。实际上是P-R的一个变种。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">再回到上图，Presion-Recall是一个曲线，用来比较两个方法的效果往往不够直观，能不能对两者进行综合，直接反映到一个数值上呢？为此IR学术界提出了F值度量（F -Measure）的方法。F-Measure通过Presion和Recall的调和平均数来计算，公式为：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image10.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">其中参数&#955;&#949;(0,1)调节系统对Precision和Recall的平衡程度。（通常取&#955;=0.5，此时&nbsp;<img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image11-1.jpg" alt="" style="box-sizing: border-box; border: 0px;" />）</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">这里使用调和平均数而不是通常的几何平均或算术平均，原因是调和平均数强调较小数值的重要性，能敏感的反映小数字的变化，因此更适合用来反映检索效果。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">使用F Measure的好处是只需要一个单一的数字就可以总结系统的检索效果，便于比较不同搜索系统的整体效果。</p><h2>P@N方法</h2><h3>点击因素</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">传统的Precision-Recall并不完全适用对搜索引擎的评估，原因是搜索引擎用户的点击方式有其特殊性，包括：</p><pre style="box-sizing: border-box; overflow: auto; width: 964.25px; padding: 0px 0px 5px; font-size: 12px; line-height: 15px; font-family: 'Courier New', Courier; color: #222222; margin-top: 0px; margin-bottom: 0px; background-color: #fafafa; border: 2px solid #efefef;">A 60-65%的查询点击了名列搜索结果前10条的网页；  B 20-25%的人会考虑点击名列11到20的网页；  C 仅有3-4%的会点击名列搜索结果中列第21到第30名的网页 </pre><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">也就是说，绝大部分用户是不愿意翻页去看搜索引擎给出的后面的结果。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">而即使在搜索结果的首页（通常列出的是前10条结果），用户的点击行为也很有意思，我们通过下面的Google点击热图（Heat Map）来观察（这个热图在二维搜索结果页上通过光谱来形象的表达不同位置用户的点击热度。颜色约靠近红色表示点击强度越高）：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image12.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">从图中可以看出，搜索结果的前3条吸引了大量的点击，属于热度最高的部分。也就是说，对搜苏引擎来说，最前的几条结果是最关键的，决定了用户的满意程度。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image13.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">康乃尔大学的研究人员通过eye tracking实验获得了更为精确的Google搜索结果的用户行为分析图。从这张图中可以看出，第一条结果获得了56.38%的搜索流量，第二条和第三条结果的排名依次降低，但远低于排名第一的结果。前三条结果的点击比例大约为11:3:2 。而前三条结果的总点击几乎分流了搜索流量的80%。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">另外的一些有趣的结论是，点击量并不是按照顺序依次递减的。排名第七位获得的点击是最少的，原因可能在于用户在浏览过程中下拉页面到底部，这时候就只显示最后三位排名网站，第七名便容易被忽略。而首屏最后一个结果获得的注意力（2.55）是大于倒数第二位的(1.45)，原因是用户在翻页前，对最后一条结果印象相对较深。搜索结果页面第二页排名第一的网页（即总排名11位的结果）所获得的点击只有首页排名第十网站的40%，与首页的第一条结果相比，更是只有其1/60至1/100的点击量。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">因此在量化评估搜索引擎的效果时，往往需要根据以上搜索用户的行为特点，进行针对性的设计。</p><h3>P@N的计算方法</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">P@N本身是Precision@N的简称，指的是对特定的查询，考虑位置因素，检测前N条结果的准确率。例如对单次搜索的结果中前5篇，如果有4篇为相关文档，则P@5 = 4/5 = 0.8 。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">测试通常会使用一个查询集合（按照前文所述方法构造），包含若干条不同的查询词，在实际使用P@N进行评估时，通常使用所有查询的P@N数据，计算算术平均值，用来评判该系统的整体搜索结果质量。</p><h3>N的选取</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">对用户来说，通常只关注搜索结果最前若干条结果，因此通常搜索引擎的效果评估只关注前5、或者前3结果，所以我们常用的N取值为P@3或P@5等。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">对一些特定类型的查询应用，如寻址类的查询（Navigational Search），由于目标结果极为明确，因此在评估时，会选择N=1（即使用P@1）。举个例子来说，搜索&#8220;新浪网&#8221;、或&#8220;新浪首页&#8221;，如果首条结果不是 新浪网（url：<a href="http://www.sina.com.cn/" style="box-sizing: border-box; color: #0b59b2;">www.sina.com.cn</a>），则直接判该次查询精度不满足需求，即P@1=0</p><h2>MRR</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">上述的P@N方法，易于计算和理解。但细心的读者一定会发现问题，就是在前N结果中，排序第1位和第N位的结果，对准确率的影响是一样的。但实际情况是，搜索引擎的评价是和排序位置极为相关的。即排第一的结果错误，和第10位的结果错误，其严重程度有天壤之别。因此在评价系统中，需要引入位置这个因素。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">MRR是平均排序倒数（Mean Reciprocal Rank）的简称，MRR方法主要用于寻址类检索（Navigational Search）或问答类检索（Question Answering），这些检索方法只需要一个相关文档，对召回率不敏感，而是更关注搜索引擎检索到的相关文档是否排在结果列表的前面。MRR方法首先计算每一个查询的第一个相关文档位置的倒数，然后将所有倒数值求平均。例如一个包含三个查询词的测试集，前5结果分别为：</p><pre style="box-sizing: border-box; overflow: auto; width: 964.25px; padding: 0px 0px 5px; font-size: 12px; line-height: 15px; font-family: 'Courier New', Courier; color: #222222; margin-top: 0px; margin-bottom: 0px; background-color: #fafafa; border: 2px solid #efefef;">查询一结果：1.AN 2.AR 3.AN 4.AN 5.AR  查询二结果：1.AN 2.AR 3.AR 4.AR 5.AN  查询三结果：1.AR 2.AN 3.AN 4.AN 5.AR  </pre><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">其中AN表示不相关结果，AR表示相关结果。那么第一个查询的排序倒数（Reciprocal Rank）RR<sub style="box-sizing: border-box;">1</sub>&nbsp;= 1/2=0.5 ；第二个结果RR<sub style="box-sizing: border-box;">2</sub>&nbsp;= 1/2 = 0.5 ； 注意倒数的值不变，即使查询二获得的相关结果更多。同理，RR<sub style="box-sizing: border-box;">3</sub>= 1/1 = 1。 对于这个测试集合，最终MRR=（RR<sub style="box-sizing: border-box;">1</sub>+RR<sub style="box-sizing: border-box;">2</sub>+RR<sub style="box-sizing: border-box;">3</sub>）/ 3 = 0.67</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">然而对大部分检索应用来说，只有一条结果无法满足需求，对这种情况，需要更合适的方法来计算效果，其中最常用的是下述MAP方法。</p><h2>MAP</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">MAP方法是Mean Average Precison，即平均准确率法的简称。其定义是求每个相关文档检索出后的准确率的平均值（即Average Precision）的算术平均值（Mean）。这里对准确率求了两次平均，因此称为Mean Average Precision。（注：没叫Average Average Precision一是因为难听，二是因为无法区分两次平均的意义）</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">MAP 是反映系统在全部相关文档上性能的单值指标。系统检索出来的相关文档越靠前(rank 越高)，MAP就应该越高。如果系统没有返回相关文档，则准确率默认为0。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">例如：假设有两个主题：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">主题1有4个相关网页，主题2有5个相关网页。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">某系统对于主题1检索出4个相关网页，其rank分别为1, 2, 4, 7；</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">对于主题2检索出3个相关网页，其rank分别为1,3,5。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">对于主题1，平均准确率MAP计算公式为：</p><pre style="box-sizing: border-box; overflow: auto; width: 964.25px; padding: 0px 0px 5px; font-size: 12px; line-height: 15px; font-family: 'Courier New', Courier; color: #222222; margin-top: 0px; margin-bottom: 0px; background-color: #fafafa; border: 2px solid #efefef;">(1/1+2/2+3/4+4/7)/4=0.83。 </pre><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">对于主题2，平均准确率MAP计算公式为：</p><pre style="box-sizing: border-box; overflow: auto; width: 964.25px; padding: 0px 0px 5px; font-size: 12px; line-height: 15px; font-family: 'Courier New', Courier; color: #222222; margin-top: 0px; margin-bottom: 0px; background-color: #fafafa; border: 2px solid #efefef;">(1/1+2/3+3/5+0+0)/5=0.45。 </pre><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">则MAP= (0.83+0.45)/2=0.64。&#8221;</p><h2>DCG方法</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">DCG是英文Discounted cumulative gain的简称，中文可翻译为&#8220;折扣增益值&#8221;。DCG方法的基本思想是：</p><ol style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><li style="font-size: 12px; box-sizing: border-box;">每条结果的相关性分等级来衡量</li><li style="font-size: 12px; box-sizing: border-box;">考虑结果所在的位置，位置越靠前的则重要程度越高</li><li style="font-size: 12px; box-sizing: border-box;">等级高（即好结果）的结果位置越靠前则值应该越高，否则给予惩罚</li></ol><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">我们首先来看第一条：相关性分级。这里比计算Precision时简单统计&#8220;准确&#8221;或&#8220;不准确&#8221;要更为精细。我们可以将结果细分为多个等级。比如常用的3级：Good（好）、Fair（一般）、Bad（差）。对应的分值rel为：Good:3 / Fair:2 / Bad:1 。一些更为细致的评估使用5级分类法：Very Good（明显好）、Good（好）、Fair（一般）、Bad（差）、Very Bad（明显差），可以将对应分值rel设置为：Very Good:2 / Good:1 / Fair:0 / Bad:-1 / Very Bad: -2</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">评判结果的标准可以根据具体的应用来确定，Very Good通常是指结果的主题完全相关，并且网页内容丰富、质量很高。而具体到每条</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image14.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">DCG的计算公式并不唯一，理论上只要求对数折扣因子的平滑性。我个人认为下面的DCG公式更合理，强调了相关性，第1、2条结果的折扣系数也更合理：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image15.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">此时DCG前4个位置上结果的折扣因子（Discount factor）数值为：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"></p><table cellspacing="0" cellpadding="0" border="1" style="box-sizing: border-box; color: #000000; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; font-size: 13px; line-height: 19px; text-align: start; background-color: #ffffff;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">i</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">log<sub style="box-sizing: border-box;">2</sub>&nbsp;(i+1)</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">1/log<sub style="box-sizing: border-box;">2</sub>&nbsp;(i+1)</p></td></tr><tr style="box-sizing: border-box;"><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">1</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">1</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">1</p></td></tr><tr style="box-sizing: border-box;"><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">2</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">1.59</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">0.63</p></td></tr><tr style="box-sizing: border-box;"><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">3</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">2</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">0.5</p></td></tr><tr style="box-sizing: border-box;"><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">4</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">2.32</p></td><td width="189" valign="top" style="font-family: Verdana, Helvetica, Arial; word-break: break-all; font-size: small; box-sizing: border-box;"><p align="center" style="box-sizing: border-box;">0.43</p></td></tr></tbody></table><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">取以2为底的log值也来自于经验公式，并不存在理论上的依据。实际上，Log的基数可以根据平滑的需求进行修改，当加大数值时（例如使用log<sub style="box-sizing: border-box;">5</sub>&nbsp;代替log<sub style="box-sizing: border-box;">2</sub>），折扣因子降低更为迅速，此时强调了前面结果的权重。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">为了便于不同类型的query结果之间横向比较，以DCG为基础，一些评价系统还对DCG进行了归一，这些方法统称为nDCG（即 normalize DCG）。最常用的计算方法是通过除以每一个查询的理想值iDCG（ideal DCG）来进行归一，公式为：</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image16.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">求nDCG需要标定出理想情况的iDCG，实际操作的时候是异常困难的，因为每个人对&#8220;最好的结果&#8221;理解往往各不相同，从海量数据里选出最优结果是很困难的任务，但是比较两组结果哪个更好通常更容易，所以实践应用中，通常选择结果对比的方法进行评估。</p><h2>怎样实现自动化的评估？</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">以上所介绍的搜索引擎量化评估指标，在Cranfield评估框架（Cranfield Evaluation Framework）中被广泛使用。业界知名的TREC（文本信息检索会议）就一直基于此类方法组织信息检索评测和技术交流。除了TREC外，一些针对不同应用设计的Cranfield评测论坛也在进行进行（如 NTCIR、IREX等）。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">但Cranfield评估框架存在的问题是查询样例集合的标注上。利用手工标注答案的方式进行网络信息检索的评价是一个既耗费人力、又耗费时间的过程，只有少数大公司能够使用。并且由于搜索引擎算法改进、运营维护的需要，检索效果评价反馈的时间需要尽量缩短，因此自动化的评测方法对提高评估效率十分重要。最常用的自动评估方法是A/B testing系统。</p><h3>A/B Testing</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image17.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p align="center" style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><strong style="box-sizing: border-box;">A/B Testing系统</strong></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">A/B testing系统在用户搜索时，由系统来自动决定用户的分组号（Bucket id），通过自动抽取流量导入不同分支，使得相应分组的用户看到的是不同产品版本（或不同搜索引擎）提供的结果。用户在不同版本产品下的行为将被记录下来，这些行为数据通过数据分析形成一系列指标，而通过这些指标的比较，最后就形成了各版本之间孰优孰劣的结论。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">在指标计算时，又可细分为两种方法，一种是基于专家评分的方法；一种是基于点击统计的方法。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">专家评分的方法通常由搜索核心技术研发和产品人员来进行，根据预先设定的标准对A、B两套环境的结果给予评分，获取每个Query的结果对比，并根据nDCG等方法计算整体质量。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">点击评分有更高的自动化程度，这里使用了一个假设：同样的排序位置，点击数量多的结果质量优于点击数量少的结果。（即A2表示A测试环境第2条结果，如果A2 &gt; B2，则表示A2质量更好）。通俗的说，相信群众（因为群众的眼睛是雪亮的）。在这个假设前提下，我们可以将A/B环境前N条结果的点击率自动映射为评分，通过统计大量的Query点击结果，可以获得可靠的评分对比。</p><h3>Interleaving Testing</h3><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">另外2003年由Thorsten Joachims 等人提出的Interleaving testing方法也被广泛使用。该方法设计了一个元搜索引擎，用户输入查询词后，将查询词在几个著名搜索引擎中的查询结果随机混合反馈给用户，并收集随后用户的结果点击行为信息．根据用户不同的点击倾向性，就可以判断搜索引擎返回结果的优劣，</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">如下图所示，将算法A和B的结果交叉放置，并分流量进行测试，记录用户点击信息。根据点击分布来判断A和B环境的优劣。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; overflow-x: auto; width: 964.25px; overflow-y: hidden; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><img border="0" _href="img://image1.jpg" _p="true" src="http://www.infoq.com/resource/articles/cyw-evaluate-seachengine-result-quality/zh/resources/image18.jpg" alt="" style="box-sizing: border-box; border: 0px; display: block; float: none; margin-left: auto; margin-right: auto;" /></p><p align="center" style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;"><strong style="box-sizing: border-box;">Interleaving Testing评估方法</strong></p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">Joachims同时证明了Interleaving Testing评价方法与传统Cranfield评价方法的结果具有较高的相关性。由于记录用户选择检索结果的行为是一个不耗费人力的过程，因此可以便捷的实现自动化的搜索效果评估。</p><h2>总结</h2><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">没有评估就没有进步&#8212;&#8212;对搜索效果的量化评测，目的是准确的找出现有搜索系统的不足（没有哪个搜索系统是完美的），进而一步一个脚印对算法、系统进行改进。本文为大家总结了常用的评价框架和评价指标。这些技术像一把把尺子，度量着搜索技术每一次前进的距离。</p><hr style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;" /><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">感谢<a href="http://www.infoq.com/cn/bycategory.action?authorName=%E5%BC%A0%E5%87%AF%E5%B3%B0" style="box-sizing: border-box; color: #0b59b2;">张凯峰</a>对 本文的审校。</p><p style="box-sizing: border-box; color: #4b4b4b; font-size: 13px; font-family: Lucida, 'Lucida Grande', Tahoma, sans-serif; line-height: 19px; background-color: #ffffff;">给InfoQ中文站投稿或者参与内容翻译工作，请邮件至<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#100;&#105;&#116;&#111;&#114;&#115;&#64;&#99;&#110;&#46;&#105;&#110;&#102;&#111;&#113;&#46;&#99;&#111;&#109;" style="box-sizing: border-box; color: #0b59b2;">editors@cn.infoq.com</a>。也欢迎大家加入到<a target="_blank" href="http://groups.google.com/group/InfoQChina" style="box-sizing: border-box; color: #0b59b2;">InfoQ中文站用户讨论组</a>中与我们的编辑和其他读者 朋友交流。</p><div></div><img src ="http://www.cppblog.com/wanghaiguang/aggbug/197078.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2013-01-07 16:46 <a href="http://www.cppblog.com/wanghaiguang/archive/2013/01/07/197078.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HOOK介绍</title><link>http://www.cppblog.com/wanghaiguang/archive/2012/11/05/194544.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Mon, 05 Nov 2012 03:27:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/11/05/194544.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/194544.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/11/05/194544.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/194544.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/194544.html</trackback:ping><description><![CDATA[<strong><span style="font-family: Verdana; color: #3366ff; font-size: 12pt">一、基本概念：</span><br /></strong><br /><span style="font-family: Verdana; font-size: 12pt">钩子(Hook)，是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息，而且所监视的窗口可以是其他进程所创建的。当消息到达后，在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">钩子实际上是一个处理消息的程序段，通过系统调用，把它挂入系统。每当特定的消息发出，在没有到达目的窗口前，钩子程序就先捕获该消息，亦即钩子函数先得到控制权。这时钩子函数即可以加工处理（改变）该消息，也可以不作处理而继续传递该消息，还可以强制结束消息的传递。</span><br /><br /><strong><span style="font-family: Verdana; color: #3366ff; font-size: 12pt">二、运行机制：</span><br /></strong><br /><span style="font-family: Verdana; font-size: 12pt">1、钩子链表和钩子子程：</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">每一个Hook都有一个与之相关联的指针列表，称之为钩子链表，由系统来维护。这个列表的指针指向指定的，应用程序定义的，被Hook子程调用的回调函数，也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时，系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息，或者修改消息，或者停止消息的前进，避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始，而最早安装的钩子放在最后，也就是后加入的先获得控制权。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载，Windows 便释放其占用的内存，并更新整个Hook链表。如果程序安装了钩子，但是在尚未卸载钩子之前就结束了，那么系统会自动为它做卸载钩子的操作。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数，只能定义为普通的C函数。用以监视系统或某一特定类型的事件，这些事件可以是与某一特定线程关联的，也可以是系统中所有线程的事件。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">钩子子程必须按照以下的语法：</span><br /><span style="font-family: Verdana; font-size: 12pt">
<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; word-break: break-all; 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: #008080">1</span>&nbsp;<span style="color: #000000">LRESULT&nbsp;CALLBACK&nbsp;HookProc<br /></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">(<br /></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;nCode,&nbsp;<br /></span><span style="color: #008080">4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WPARAM&nbsp;wParam,&nbsp;<br /></span><span style="color: #008080">5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPARAM&nbsp;lParam<br /></span><span style="color: #008080">6</span>&nbsp;<span style="color: #000000">);</span></div></span><span style="font-family: Verdana; font-size: 12pt">HookProc是应用程序定义的名字。</span><br /><span style="font-family: Verdana; font-size: 12pt">nCode参数是Hook代码，Hook子程使用这个参数来确定任务。这个参数的值依赖于Hook类型，每一种Hook都有自己的Hook代码特征字符集。</span><br /><span style="font-family: Verdana; font-size: 12pt">wParam和lParam参数的值依赖于Hook代码，但是它们的典型值是包含了关于发送或者接收消息的信息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">2、钩子的安装与释放：</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">使用API函数SetWindowsHookEx()把一个应用程序定义的钩子子程安装到钩子链表中。SetWindowsHookEx函数总是在Hook链的开头安装Hook子程。当指定类型的Hook监视的事件发生时，系统就调用与这个Hook关联的Hook链的开头的Hook子程。每一个Hook链中的Hook子程都决定是否把这个事件传递到下一个Hook子程。Hook子程传递事件到下一个Hook子程需要调用CallNextHookEx函数。</span>&nbsp; 
<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; word-break: break-all; 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: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">HHOOK&nbsp;SetWindowsHookEx(&nbsp;<br /></span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">　　　　　</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;idHook,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;钩子的类型，即它处理的消息类型</span><span style="color: #008000"><br /></span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">　　　　　HOOKPROC&nbsp;lpfn,&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;钩子子程的地址指针。如果dwThreadId参数为0<br /></span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #008000">&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; </span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;或是一个由别的进程创建的线程的标识，<br /></span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #008000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;lpfn必须指向DLL中的钩子子程。<br /></span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #008000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;除此以外，lpfn可以指向当前进程的一段钩子子程代码。<br /></span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #008000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;钩子函数的入口地址，当钩子钩到任何消息后便调用这个函数。</span><span style="color: #008000"><br /></span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">　　　　　HINSTANCE&nbsp;hMod,&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;应用程序实例的句柄。标识包含lpfn所指的子程的</span><span style="color: #339966">DLL。</span><span style="color: #000000"><br /></span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;如果dwThreadId&nbsp;标识当前进程创建的一个线程，<br /></span><span style="color: #008080">11</span>&nbsp;<span style="color: #008000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;而且子程代码位于当前进程，hMod必须为NULL。<br /></span><span style="color: #008080">12</span>&nbsp;<span style="color: #008000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">&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;//</span><span style="color: #008000">&nbsp;可以很简单的设定其为本应用程序的实例句柄。</span><span style="color: #008000"><br /></span><span style="color: #008080">13</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">　　　　　DWORD&nbsp;dwThreadId&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;与安装的钩子子程相关联的线程的标识符。<br /></span><span style="color: #008080">14</span>&nbsp;<span style="color: #008000">&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;</span><span style="color: #008000">//</span><span style="color: #008000">&nbsp;如果为0，钩子子程与所有的线程关联，即为全局钩子。</span><span style="color: #008000"><br /></span><span style="color: #008080">15</span>&nbsp;<span style="color: #008000"></span><span style="color: #000000">　　　　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);&nbsp;</span></div>&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">函数成功则返回钩子子程的句柄，失败返回NULL。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">以上所说的钩子子程与线程相关联是指在一钩子链表中发给该线程的消息同时发送给钩子子程，且被钩子子程先处理。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">在钩子子程中调用得到控制权的钩子函数在完成对消息的处理后，如果想要该消息继续传递，那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它，以执行钩子链表所指的下一个钩子子程。这个函数成功时返回钩子链中下一个钩子过程的返回值，返回值的类型依赖于钩子的类型。这个函数的原型如下：<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; word-break: break-all; 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: #008080">1</span>&nbsp;<span style="color: #000000">LRESULT&nbsp;CallNextHookEx<br /></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<br /></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HHOOK&nbsp;hhk;<br /></span><span style="color: #008080">4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;nCode;<br /></span><span style="color: #008080">5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WPARAM&nbsp;wParam;<br /></span><span style="color: #008080">6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LPARAM&nbsp;lParam;<br /></span><span style="color: #008080">7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);</span></div></span><span style="font-family: Verdana; font-size: 12pt">hhk为当前钩子的句柄，由SetWindowsHookEx()函数返回。</span><br /><span style="font-family: Verdana; font-size: 12pt">NCode为传给钩子过程的事件代码。</span><br /><span style="font-family: Verdana; font-size: 12pt">wParam和lParam 分别是传给钩子子程的wParam值，其具体含义与钩子类型有关。</span><br />&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">钩子函数也可以通过直接返回TRUE来丢弃该消息，并阻止该消息的传递。否则的话，其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">钩子在使用完之后需要用UnHookWindowsHookEx()卸载，否则会造成麻烦。释放钩子比较简单，UnHookWindowsHookEx()只有一个参数。函数原型如下：<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; word-break: break-all; 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: #008080">1</span>&nbsp;<span style="color: #000000">UnHookWindowsHookEx<br /></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">(<br /></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;HHOOK&nbsp;hhk;<br /></span><span style="color: #008080">4</span>&nbsp;<span style="color: #000000">);</span></div></span><span style="font-family: Verdana; font-size: 12pt">函数成功返回TRUE，否则返回FALSE。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">3、一些运行机制：</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">在Win16环境中，DLL的全局数据对每个载入它的进程来说都是相同的；而在Win32环境中，情况却发生了变化，DLL函数中的代码所创建的任何对象（包括变量）都归调用它的线程或进程所有。当进程在载入DLL时，操作系统自动把DLL地址映射到该进程的私有空间，也就是进程的虚拟地址空间，而且也复制该DLL的全局数据的一份拷贝到该进程空间。也就是说每个进程所拥有的相同的DLL的全局数据，它们的名称相同，但其值却并不一定是相同的，而且是互不干涉的。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">因此，在Win32环境下要想在多个进程中共享数据，就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来，放置在一个独立的数据段里，并把该段的属性设置为共享。必须给这些变量赋初值，否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">#pragma data_seg预处理指令用于设置共享数据段。例如：</span><br /><span style="font-family: Verdana; font-size: 12pt">
<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; word-break: break-all; 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: #008080">1</span>&nbsp;<span style="color: #000000">#pragma&nbsp;data_seg(</span><span style="color: #000000">"</span><span style="color: #000000">SharedDataName</span><span style="color: #000000">"</span><span style="color: #000000">)<br /></span><span style="color: #008080">2</span>&nbsp;<span style="color: #000000">HHOOK&nbsp;hHook</span><span style="color: #000000">=</span><span style="color: #000000">NULL;<br /></span><span style="color: #008080">3</span>&nbsp;<span style="color: #000000">#pragma&nbsp;data_seg()</span></div>在#pragma data_seg("SharedDataName")和#pragma data_seg()之间的所有变量 将被访问该Dll的所有进程看到和共享。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">当进程隐式或显式调用一个动态库里的函数时，系统都要把这个动态库映射到这个进程的虚拟地址空间里(以下简称"地址空间")。这使得DLL成为进程的一部分，以这个进程的身份执行，使用这个进程的堆栈。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">4、系统钩子与线程钩子：</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">线程勾子用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">系统勾子监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序，所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中，即将这个DLL注入了那些进程。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">几点说明：</span><br /><span style="font-family: Verdana; font-size: 12pt">（1）如果对于同一事件（如鼠标消息）既安装了线程勾子又安装了系统勾子，那么系统会自动先调用线程勾子，然后调用系统勾子。 </span><br /><br /><span style="font-family: Verdana; font-size: 12pt">（2）对同一事件消息可安装多个勾子处理过程，这些勾子处理过程形成了勾子链。当前勾子处理结束后应把勾子信息传递给下一个勾子函数。 </span><br /><br /><span style="font-family: Verdana; font-size: 12pt">（3）勾子特别是系统勾子会消耗消息处理时间，降低系统性能。只有在必要的时候才安装勾子，在使用完毕后要及时卸载。</span><br /><br /><strong><span style="font-family: Verdana; color: #3366ff; font-size: 12pt">三、钩子类型</span><br /></strong><br /><span style="font-family: Verdana; font-size: 12pt">每一种类型的Hook可以使应用程序能够监视不同类型的系统消息处理机制。下面描述所有可以利用的Hook类型。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以监视发送到窗口过程的消息。系统在消息发送到接收窗口过程之前WH_CALLWNDPROCHook子程，并且在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子程。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_CALLWNDPROCRET Hook传递指针到CWPRETSTRUCT结构，再传递到Hook子程。CWPRETSTRUCT结构包含了来自处理消息的窗口过程的返回值，同样也包括了与这个消息关联的消息参数。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">2、WH_CBT Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">在以下事件之前，系统都会调用WH_CBT Hook子程，这些事件包括：</span><br /><span style="font-family: Verdana; font-size: 12pt">1. 激活，建立，销毁，最小化，最大化，移动，改变尺寸等窗口事件；</span><br /><span style="font-family: Verdana; font-size: 12pt">2. 完成系统指令；</span><br /><span style="font-family: Verdana; font-size: 12pt">3. 来自系统消息队列中的移动鼠标，键盘事件；</span><br /><span style="font-family: Verdana; font-size: 12pt">4. 设置输入焦点事件；</span><br /><span style="font-family: Verdana; font-size: 12pt">5. 同步系统消息队列事件。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">Hook子程的返回值确定系统是否允许或者防止这些操作中的一个。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">3、WH_DEBUG Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">在系统调用系统中与其他Hook关联的Hook子程之前，系统会调用WH_DEBUG Hook子程。你可以使用这个Hook来决定是否允许系统调用与其他Hook关联的Hook子程。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">4、WH_FOREGROUNDIDLE Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">当应用程序的前台线程处于空闲状态时，可以使用WH_FOREGROUNDIDLE Hook执行低优先级的任务。当应用程序的前台线程大概要变成空闲状态时，系统就会调用WH_FOREGROUNDIDLE Hook子程。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">5、WH_GETMESSAGE Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">应用程序使用WH_GETMESSAGE Hook来监视从GetMessage or PeekMessage函数返回的消息。你可以使用WH_GETMESSAGE Hook去监视鼠标和键盘输入，以及其他发送到消息队列中的消息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">6、WH_JOURNALPLAYBACK Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_JOURNALPLAYBACK Hook使应用程序可以插入消息到系统消息队列。可以使用这个Hook回放通过使用WH_JOURNALRECORD Hook记录下来的连续的鼠标和键盘事件。只要WH_JOURNALPLAYBACK Hook已经安装，正常的鼠标和键盘事件就是无效的。WH_JOURNALPLAYBACK Hook是全局Hook，它不能象线程特定Hook一样使用。WH_JOURNALPLAYBACK Hook返回超时值，这个值告诉系统在处理来自回放Hook当前消息之前需要等待多长时间（毫秒）。这就使Hook可以控制实时事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks，它們不會被注射到任何行程位址空間。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">7、WH_JOURNALRECORD Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_JOURNALRECORD Hook用来监视和记录输入事件。典型的，可以使用这个Hook记录连续的鼠标和键盘事件，然后通过使用WH_JOURNALPLAYBACK Hook来回放。WH_JOURNALRECORD Hook是全局Hook，它不能象线程特定Hook一样使用。WH_JOURNALRECORD是system-wide local hooks，它們不會被注射到任何行程位址空間。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">8、WH_KEYBOARD Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">在应用程序中，WH_KEYBOARD Hook用来监视WM_KEYDOWN and WM_KEYUP消息，这些消息通过GetMessage or PeekMessage function返回。可以使用这个Hook来监视输入到消息队列中的键盘消息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">9、WH_KEYBOARD_LL Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_KEYBOARD_LL Hook监视输入到线程消息队列中的键盘消息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">10、WH_MOUSE Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_MOUSE Hook监视从GetMessage 或者 PeekMessage 函数返回的鼠标消息。使用这个Hook监视输入到消息队列中的鼠标消息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">11、WH_MOUSE_LL Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_MOUSE_LL Hook监视输入到线程消息队列中的鼠标消息。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以监视菜单，滚动条，消息框，对话框消息并且发现用户使用ALT+TAB or ALT+ESC 组合键切换窗口。WH_MSGFILTER Hook只能监视传递到菜单，滚动条，消息框的消息，以及传递到通过安装了Hook子程的应用程序建立的对话框的消息。WH_SYSMSGFILTER Hook监视所有应用程序消息。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我们可以在模式循环期间过滤消息，这等价于在主消息循环中过滤消息。</span><br />&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="font-family: Verdana; font-size: 12pt">通过调用CallMsgFilter function可以直接的调用WH_MSGFILTER Hook。通过使用这个函数，应用程序能够在模式循环期间使用相同的代码去过滤消息，如同在主消息循环里一样。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">13、WH_SHELL Hook</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">外壳应用程序可以使用WH_SHELL Hook去接收重要的通知。当外壳应用程序是激活的并且当顶层窗口建立或者销毁时，系统调用WH_SHELL Hook子程。</span><br /><span style="font-family: Verdana; font-size: 12pt">WH_SHELL 共有５钟情況：</span><br /><span style="font-family: Verdana; font-size: 12pt">1. 只要有个top-level、unowned 窗口被产生、起作用、或是被摧毁；</span><br /><span style="font-family: Verdana; font-size: 12pt">2. 当Taskbar需要重画某个按钮；</span><br /><span style="font-family: Verdana; font-size: 12pt">3. 当系统需要显示关于Taskbar的一个程序的最小化形式；</span><br /><span style="font-family: Verdana; font-size: 12pt">4. 当目前的键盘布局状态改变；</span><br /><span style="font-family: Verdana; font-size: 12pt">5. 当使用者按Ctrl+Esc去执行Task Manager（或相同级别的程序）。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;按照惯例，外壳应用程序都不接收WH_SHELL消息。所以，在应用程序能够接收WH_SHELL消息之前，应用程序必须调用SystemParametersInfo function注册它自己。</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">以上转自：<a href="http://www.microsoft.com/china/community/program/originalarticles/techdoc/hook.mspx">http://www.microsoft.com/china/community/program/originalarticles/techdoc/hook.mspx</a></span><br /><span style="font-family: Verdana; font-size: 12pt">以下转自：</span><a href="http://topic.csdn.net/t/20030513/03/1774836.html"><span style="font-family: Verdana; font-size: 12pt">http://topic.csdn.net/t/20030513/03/1774836.html</span></a><br /><br /><span style="font-family: Verdana; font-size: 12pt">CallNextHookEx 作用</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">Hook &nbsp; 串鏈（Hook &nbsp; Chains） </span><br /><span style="font-family: Verdana; font-size: 12pt">當許多程式都安裝了某種型態的hook &nbsp; 時，就會形成一個filter-function &nbsp; chain。一旦特定 的event &nbsp; 發生，Windows &nbsp; 會呼叫該型態中最新掛上的hook &nbsp; filter &nbsp; function。舉個例，如果 程式A &nbsp; 掛上了一個system-wide &nbsp; WH_KEYBOARD &nbsp; hook，每當有任何執行緒取得鍵盤訊 息，Windows &nbsp; 就會呼叫這個filter &nbsp; function。如果程式B &nbsp; 也掛上了一個system-wide WH_KEYBOARD &nbsp; hook，那麼當event &nbsp; 發生，Windows &nbsp; 不再呼叫程式A &nbsp; 的filter &nbsp; function， 改呼叫程式B的filter &nbsp; function。這也意味每一個filter &nbsp; function &nbsp; 有責任確保先前掛上的filter Windows &nbsp; 95 &nbsp; 程式設計指南（Windows &nbsp; 95 &nbsp; : &nbsp; A &nbsp; Developer&#8217;s &nbsp; Guide） 394 function &nbsp; 被呼叫（也就是維護串鏈的完整性）。 SetWindowsHookEx &nbsp; 函式會將新掛上的hook &nbsp; filter &nbsp; function &nbsp; 的代碼傳回。任何程式只要掛 上一個新的filter &nbsp; function &nbsp; 就必須儲存這個代碼（通常存放在全域變數中）： </span><br /><span style="font-family: Verdana; font-size: 12pt">static &nbsp; HHOOK &nbsp; g_hhook &nbsp; = &nbsp; NULL; </span><br /><span style="font-family: Verdana; font-size: 12pt">. &nbsp; . &nbsp; . </span><br /><span style="font-family: Verdana; font-size: 12pt">g_hhook &nbsp; = &nbsp; SetWindowsHookEx(WH_KEYBOARD, &nbsp; Example_kybdHook, &nbsp; hinst, &nbsp; NULL); </span><br /><span style="font-family: Verdana; font-size: 12pt">. &nbsp; . &nbsp; . </span><br /><span style="font-family: Verdana; font-size: 12pt">如果有錯誤發生，SetWindowsHookEx &nbsp; 函式會傳回NULL。 </span><br /><span style="font-family: Verdana; font-size: 12pt">如果你希望hook &nbsp; chain &nbsp; 中的其它filter &nbsp; functions &nbsp; 也能夠執行，你可以在你的filter &nbsp; function 中呼叫CallNextHookEx &nbsp; 函式（或許你已經在先前的Example_KybdHook &nbsp; 函式片段中注意 到了）&nbsp;</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">LRESULT &nbsp; CallNextHookEx(HHOOK &nbsp; hhook, &nbsp; int &nbsp; nCode, &nbsp; WPARAM &nbsp; wParam, &nbsp; LPARAM &nbsp; lParam); </span><br /><span style="font-family: Verdana; font-size: 12pt">這個函式會呼叫filter-function &nbsp; chain &nbsp; 的下一個filter &nbsp; function，並傳入相同的nCode、 wParam &nbsp; 和lParam。下一個filter &nbsp; function &nbsp; 結束之前，應該也遵循這個規則去呼叫 CallNextHookEx &nbsp; 函式，並再次將hook &nbsp; 代碼（通常那是被放在全域變數中）傳入。 CallNextHookEx &nbsp; 函式利用這個hook &nbsp; 代碼，走訪整個串鏈，決定哪一個filter &nbsp; function &nbsp; 是 </span><br /><span style="font-family: Verdana; font-size: 12pt">下一個呼叫目標。如果CallNextHookEx &nbsp; 函式發現已經沒有下一個filter &nbsp; function &nbsp; 可以呼叫 （走到串鏈盡頭了），它會傳回0；否則它就傳回「下一個filter &nbsp; function &nbsp; 執行後的傳回值」。&nbsp;</span><br /><br /><span style="font-family: Verdana; font-size: 12pt">你可能會在許多文件（包括SDK &nbsp; 文件）中發現一個有關CallNextHookEx &nbsp; 函式的過氣警告：「如 果nCode &nbsp; 小於0，則hook &nbsp; 函式應該不做任何處理，直接將它交給CallNextHookEx &nbsp; 函式，並傳回 CallNextHookEx &nbsp; 函式的回返值」。這並不是真的，而且自Windows &nbsp; 3.0 &nbsp; 以來（那時還在使用舊版的 SetWindowsHook &nbsp; 函式）就已經不是真的了！撰寫程式時，你可以完全不理會這項警告。 </span><br /><span style="font-family: Verdana; font-size: 12pt">第６章訊息攔截（Hooks） 395 </span><br /><br /><span style="font-family: Verdana; font-size: 12pt">有些時候你可能不希望呼叫其他的filter &nbsp; functions，這種情況下你只要不在你的filter function &nbsp; 中呼叫CallNextHookEx &nbsp; 函式即可。只要不將CallNextHookEx &nbsp; 函式放到你的filter </span><br /><span style="font-family: Verdana; font-size: 12pt">function &nbsp; 中，你就不會呼叫其他的filter &nbsp; functions，而你也因此可以指定你自己的傳回值。 不幸的是，這裡埋伏著一個陷阱：另一個執行緒可能也為你安裝了一個hook，新的filter </span><br /><span style="font-family: Verdana; font-size: 12pt">function &nbsp; 於是比你的filter &nbsp; function &nbsp; 更早被喚起，而它可能不呼叫你的filter &nbsp; function，完蛋 了！這個問題沒有一般性的解決方案，如果你先將自己的hook &nbsp; 卸除，然後再重新掛上， 那麼你的filter &nbsp; function &nbsp; 就成為最新的一個，會最先被呼叫。沒錯，但你不能夠保證其他 人不會依樣畫葫蘆。簡言之，hooks &nbsp; 是一個合作機制，沒有任何保障。 </span><br /><br /><img src ="http://www.cppblog.com/wanghaiguang/aggbug/194544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-11-05 11:27 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/11/05/194544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>微软研究院Detour开发包之API拦截技术</title><link>http://www.cppblog.com/wanghaiguang/archive/2012/11/02/194179.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Fri, 02 Nov 2012 06:32:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/11/02/194179.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/194179.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/11/02/194179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/194179.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/194179.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body">
<p><span style="font-family: Verdana; font-size: 12pt">我们截获函数执行最直接的目的就是为函数增添功能，修改返回值，或者为调试以及性能测试加入附加的代码，或者截获函数的输入输出作研究，破解使用。通过访 问源代码，我们可以轻而易举的使用重建（Rebuilding）操作系统或者应用程序的方法在它们中间插入新的功能或者做功能扩展。然而，在今天这个商业 化的开发世界里，以及在只有二进制代码发布的系统中，研究人员几乎没有机会可以得到源代码。本文主要讨论Detour在Windows二进制PE文件基础 上的API截获技术。对于Linux平台，作这件事情将会非常的简单，由于最初的操作系统设计者引入了LD_PRELOAD。如果你设置&nbsp; LD_PRELOAD=mylib.so ，那么应用程序在载入 dll时，会先查看mylib.so的符号表，在relocation 的时候会优先 使用mylib.so 里的 symbol 。假如你在mylib.so里有个printf() ，那么这个printf就会替代libc的 printf。 而在mylib.so里的这个printf可以直接访问 libc.so里的printf函数指针来获得真正的 printf的入口地 址。 这样，所有的dll的API HOOK在loader加载dll的时候就已经完成，非常自然，和平台相关的部分全部交给loader去处理。</span><br /><span style="font-family: Verdana; font-size: 12pt">一、&nbsp; Detour开发库：</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 简介</span><br /><span style="font-family: Verdana; font-size: 12pt">Detours是一个在x86平台上截获任意Win32函数调用的工具库。中断代码可以在运行时动态加载。Detours使用一个无条件转移指令来替换目 标函数的最初几条指令，将控制流转移到一个用户提供的截获函数。而目标函数中的一些指令被保存在一个被称为&#8220;trampoline&#8221; （译注：英文意为蹦 床，杂技）的函数中，在这里我觉得翻译成目标函数的部分克隆/拷贝比较贴切。这些指令包括目标函数中被替换的代码以及一个重新跳转到目标函数的无条件分 支。而截获函数可以替换目标函数，或者通过执行&#8220;trampoline&#8221;函数的时候将目标函数作为子程序来调用的办法来扩展功能。</span><br /><span style="font-family: Verdana; font-size: 12pt">Detours是执行时被插入的。内存中的目标函数的代码不是在硬盘上被修改的，因而可以在一个很好的粒度上使得截获二进制函数的执行变得更容易。例如， 一个应用程序执行时加载的DLL中的函数过程可以被插入一段截获代码（detoured），与此同时，这个DLL还可以被其他应用程序按正常情况执行（译 注：也就是按照不被截获的方式执行，因为DLL二进制文件没有被修改，所以发生截获时不会影响其他进程空间加载这个DLL）。不同于DLL的重新链接或者 静态重定向，Detours库中使用的这种中断技术确保不会影响到应用程序中的方法或者系统代码对目标函数的定位。</span><br /><span style="font-family: Verdana; font-size: 12pt">如果其他人为了调试或者在内部使用其他系统检测手段而试图修改二进制代码，Detours将是一个可以普遍使用的开发包。据我所知，Detours是第一 个可以在任意平台上将未修改的目标代码作为一个可以通过&#8220;trampoline&#8221;调用的子程序来保留的开发包。而以前的系统在逻辑上预先将截获代码放到目 标代码中，而不是将原始的目标代码做为一个普通的子程序来调用。我们独特的&#8220;trampoline&#8221;设计对于扩展现有的软件的二进制代码是至关重要的。</span><br /><span style="font-family: Verdana; font-size: 12pt">出于使用基本的函数截获功能的目的，Detours同样提供了编辑任何DLL导入表的功能，达到向存在的二进制代码中添加任意数据节表的目的，向一个新进 程或者一个已经运行着的进程中注入一个DLL。一旦向一个进程注入了DLL，这个动态库就可以截获任何Win32函数，不论它是在应用程序中或者在系统库 中。</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 基本原理</span><br /><span style="font-family: Verdana; font-size: 12pt">1．&nbsp; WIN32进程的内存管理 </span><br /><span style="font-family: Verdana; font-size: 12pt">众所周知，WINDOWS NT实现了虚拟存储器，每一WIN32进程拥有4GB的虚存空间， 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册， 以下仅指出与Detours相关的几点： </span><br /><span style="font-family: Verdana; font-size: 12pt">(1) 进程要执行的指令也放在虚存空间中 </span><br /><span style="font-family: Verdana; font-size: 12pt">(2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行，再改写其内容，从而修改正在运行的程序 </span><br /><span style="font-family: Verdana; font-size: 12pt">(3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存，再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行，并把要执行的指令以二进制机器码的形式写入，从而为一个正在运行的进程注入任意的代码 。</span><br /><span style="font-family: Verdana; font-size: 12pt">2． 拦截WIN32 API的原理 </span><br /><span style="font-family: Verdana; font-size: 12pt">Detours定义了三个概念：</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; (1) Target函数：要拦截的函数，通常为Windows的API。</span><br /><span style="font-family: Verdana; font-size: 12pt">(2) Trampoline函数：Target函数的部分复制品。因为Detours将会改写Target函数，所以先把Target函数的前5个字节复制保存好，一方面仍然保存Target函数的过程调用语义，另一方面便于以后的恢复。</span><br /><span style="font-family: Verdana; font-size: 12pt">(3) Detour 函数：用来替代Target函数的函数。 </span><br /><span style="font-family: Verdana; font-size: 12pt">Detours在Target函数的开头加入JMP Address_of_ Detour_ Function指令（共5个字节）把对Target函数 的调用引导到自己的Detour函数， 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+ 5共10个字节作为Trampoline函数。请参考下面的图1和图2。</span><br /><span style="font-family: Verdana; font-size: 12pt">(图1：Detour函数的过程)</span><br /><a href="http://images.cnblogs.com/cnblogs_com/flying_bat/WindowsLiveWriter/DetourAPI_D69A/crack_01_2.jpg"></a>&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_01_thumb.jpg" width="620" longdesc="" height="460" /><br /><span style="font-family: Verdana; font-size: 12pt">（图2： Detour函数的调用过程）</span><br /><a href="http://images.cnblogs.com/cnblogs_com/flying_bat/WindowsLiveWriter/DetourAPI_D69A/crack_02_2.jpg"></a>&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_02_thumb.jpg" longdesc="" /><br /><span style="font-family: Verdana; font-size: 12pt">说明：</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 目标函数：</span><br /><span style="font-family: Verdana; font-size: 12pt">目标函数的函数体（二进制）至少有5个字节以上。按照微软的说明文档Trampoline函数的函数体是拷贝前5个字节加一个无条件跳转指令的话（如果没 有特殊处理不可分割指令的话），那么前5个字节必须是完整指令，也就是不能第5个字节和第6个字节是一条不可分割的指令，否则会造成Trampoline 函数执行错误，一条完整的指令被硬性分割开来，造成程序崩溃。对于第5字节和第6个字节是不可分割指令需要调整拷贝到杂技函数(Trampoline)的 字节个数，这个值可以查看目标函数的汇编代码得到。此函数是目标函数的修改版本，不能在Detour函数中直接调用，需要通过对Trampoline函数 的调用来达到间接调用。</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; Trampoline函数：</span><br /><span style="font-family: Verdana; font-size: 12pt">此函数默认分配了32个字节，函数的内容就是拷贝的目标函数的前5个字节，加上一个JMP Address_of_ Target _ Function+5指令,共10个字节。</span><br /><span style="font-family: Verdana; font-size: 12pt">此函数仅供您的Detour函数调用，执行完前5个字节的指令后再绝对跳转到目标函数的第6个字节继续执行原功能函数。</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; Detour函数：</span><br /><span style="font-family: Verdana; font-size: 12pt">此函数是用户需要的截获API的一个模拟版本，调用方式，参数个数必须和目标函数相一致。如目标函数是__stdcall，则Detour函数声明也必须 是__stdcall,参数个数和类型也必须相同，否则会造成程序崩溃。此函数在程序调用目标函数的第一条指令的时候就会被调用（无条件跳转过来的），如 果在此函数中想继续调用目标函数，必须调用Trampoline函数（Trampoline函数在执行完目标函数的前5个字节的指令后会无条件跳转到目标 函数的5个字节后继续执行），不能再直接调用目标函数，否则将进入无穷递归（目标函数跳转到Detour函数，Detour函数又跳转到目标函数的递归， 因为目标函数在内存中的前5个字节已经被修改成绝对跳转）。通过对Trampoline函数的调用后可以获取目标函数的执行结果，此特性对分析目标函数非 常有用，而且可以将目标函数的输出结果进行修改后再传回给应用程序。</span><br /><span style="font-family: Verdana; font-size: 12pt">Detour提供了向运行中的应用程序注入Detour函数和在二进制文件基础上注入Detour函数两种方式。本章主要讨论第二种工作方式。通过 Detours提供的开发包可以在二进制EXE文件中添加一个名称为Detour的节表，如下图3所示，主要目的是实现PE加载器加载应用程序的时候会自 动加载您编写的Detours DLL，在Detours Dll中的DLLMain中完成对目标函数的Detour。</span><br /><span style="font-family: Verdana; font-size: 12pt">（图3）</span><br /><a href="http://images.cnblogs.com/cnblogs_com/flying_bat/WindowsLiveWriter/DetourAPI_D69A/crack_03_2.jpg"></a>&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_03_thumb.jpg" width="506" longdesc="" height="299" /><br /><span style="font-family: Verdana; font-size: 12pt">二、&nbsp; Detours提供的截获API的相关接口</span><br /><span style="font-family: Verdana; font-size: 12pt">Detours的提供的API 接口可以作为一个共享DLL给外部程序调用，也可以作为一个静态Lib链接到您的程序内部。</span><br /><span style="font-family: Verdana; font-size: 12pt">Trampoline函数可以动态或者静态的创建，如果目标函数本身是一个链接符号，使用静态的trampoline函数将非常简单。如果目标函数不能在链接时可见，那么可以使用动态trampoline函数。</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 要使用静态的trampoline函数来截获目标函数，应用程序生成trampoline的时候必须使用</span><br /><span style="font-family: Verdana; font-size: 12pt">DETOUR_TRAMPOLINE宏。DETOUR_TRAMPOLINE有两个输入参数：trampoline的原型和目标函数的名字。</span><br /><span style="font-family: Verdana; font-size: 12pt">注意，对于正确的截获模型，包括目标函数，trampoline函数，以及截获函数都必须是完全一致的调用形式，包括参数格式和调用约定。当通过 trampoline函数调用目标函数的时候拷贝正确参数是截获函数的责任。由于目标函数仅仅是截获函数的一个可调用分支（截获函数可以调用 trampoline函数也可以不调用），这种责任几乎就是一种下意识的行为。</span><br /><span style="font-family: Verdana; font-size: 12pt">使用相同的调用约定可以确保寄存器中的值被正确的保存，并且保证调用堆栈在截获函数调用目标函数的时候能正确的建立和销毁。</span><br /><span style="font-family: Verdana; font-size: 12pt">可以使用DetourFunctionWithTrampoline函数来截获目标函数。这个函数有两个参数：trampoline函数以及截获函数的指针。因为目标函数已经被加到trampoline函数中，所有不需要在参数中特别指定。</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 我们可以使用DetourFunction函数来创建一个动态的trampoline函数，它包括两个参数：一个指向目标函数的指针和一个截获函数的指针。DetourFunction分配一个新的trampoline函数并将适当的截获代码插入到目标函数中去。</span><br /><span style="font-family: Verdana; font-size: 12pt">当目标函数不是很容易使用的时候，DetourFindFunction函数可以找到那个函数，不管它是DLL中导出的函数，或者是可以通过二进制目标函数的调试符号找到。</span><br /><span style="font-family: Verdana; font-size: 12pt">DetourFindFunction接受两个参数：库的名字和函数的名字。如果DetourFindFunction函数找到了指定的函数，返回该函数 的指针，否则将返回一个NULL指针。DetourFindFunction会首先使用Win32函数LoadLibrary 和 GetProcAddress来定位函数，如果函数没有在DLL的导出表中找到，DetourFindFunction将使用ImageHlp库来搜索有 效的调试符号（译注：这里的调试符号是指Windows本身提供的调试符号，需要单独安装，具体信息请参考Windows的用户诊断支持信息）。 DetourFindFunction返回的函数指针可以用来传递给DetourFunction以生成一个动态的trampoline函数。</span><br /><span style="font-family: Verdana; font-size: 12pt">我们可以调用DetourRemoveTrampoline来去掉对一个目标函数的截获。</span><br /><span style="font-family: Verdana; font-size: 12pt">注意，因为Detours中的函数会修改应用程序的地址空间，请确保当加入截获函数或者去掉截获函数的时候没有其他线程在进程空间中执行，这是程序员的责任。一个简单的方法保证这个时候是单线程执行就是在加载Detours库的时候在DllMain中呼叫函数。</span><br /><span style="font-family: Verdana; font-size: 12pt">三、&nbsp; 使用Detours实现对API的截获的两种方法</span><br /><span style="font-family: Verdana; font-size: 12pt">建立一个MFC对话框工程，在对话框的OK按钮的单击事件中加入对MessageBoxA函数的调用，编译后的程序名称MessageBoxApp,效果如图。</span><br /><a href="http://images.cnblogs.com/cnblogs_com/flying_bat/WindowsLiveWriter/DetourAPI_D69A/crack_04_2.jpg"></a>&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_04_thumb.jpg" longdesc="" /><br /><span style="font-family: Verdana; font-size: 12pt">(图4)</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 静态方法</span><br /><span style="font-family: Verdana; font-size: 12pt">建立一个Dll工程，名称为ApiHook，这里以Visual C++6.0开发环境，以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">DETOUR_TRAMPOLINE(int WINAPI Real_Messagebox(HWND hWnd ,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpText,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpCaption,</span><br /><span style="font-family: Verdana; font-size: 12pt">UINT uType), ::MessageBoxA);</span><br /><span style="font-family: Verdana; font-size: 12pt">生成一个静态的MessageBoxA的Trampoline函数，在Dll工程中加入目标函数的Detour函数：</span><br /><span style="font-family: Verdana; font-size: 12pt">int WINAPI MessageBox_Mine( HWND hWnd ,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpText,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpCaption,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; UINT uType)</span><br /><span style="font-family: Verdana; font-size: 12pt">{</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; CString tmp= lpText;</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; tmp+=&#8221; 被Detour截获&#8221;;</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; return Real_Messagebox(hWnd,tmp,lpCaption,uType);</span><br /><span style="font-family: Verdana; font-size: 12pt">//&nbsp; return ::MessageBoxA(hWnd,tmp,lpCaption,uType);&nbsp; //Error </span><br /><span style="font-family: Verdana; font-size: 12pt">}</span><br /><span style="font-family: Verdana; font-size: 12pt">在Dll入口函数中的加载Dll事件中加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">DetourFunctionWithTrampoline((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);</span><br /><span style="font-family: Verdana; font-size: 12pt">在Dll入口函数中的卸载Dll事件中加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 动态方法</span><br /><span style="font-family: Verdana; font-size: 12pt">建立一个Dll工程，名称为ApiHook，这里以Visual C++6.0开发环境，以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">//声明MessageBoxA一样的函数原型</span><br /><span style="font-family: Verdana; font-size: 12pt">typedef int&nbsp; (WINAPI * MessageBoxSys)( HWND hWnd ,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpText,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpCaption,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; UINT uType);</span><br /><span style="font-family: Verdana; font-size: 12pt">//目标函数指针</span><br /><span style="font-family: Verdana; font-size: 12pt">MessageBoxSys SystemMessageBox=NULL;</span><br /><span style="font-family: Verdana; font-size: 12pt">//Trampoline函数指针</span><br /><span style="font-family: Verdana; font-size: 12pt">MessageBoxSys Real_MessageBox=NULL;</span><br /><span style="font-family: Verdana; font-size: 12pt">在Dll工程中加入目标函数的Detour函数：</span><br /><span style="font-family: Verdana; font-size: 12pt">int WINAPI MessageBox_Mine( HWND hWnd ,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpText,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; LPCSTR lpCaption,</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; UINT uType)</span><br /><span style="font-family: Verdana; font-size: 12pt">{</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; CString tmp= lpText;</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; tmp+=&#8221; 被Detour截获&#8221;;</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; return Real_Messagebox(hWnd,tmp,lpCaption,uType);</span><br /><span style="font-family: Verdana; font-size: 12pt">//&nbsp; return ::MessageBoxA(hWnd,tmp,lpCaption,uType);&nbsp; //Error </span><br /><span style="font-family: Verdana; font-size: 12pt">}</span><br /><span style="font-family: Verdana; font-size: 12pt">在Dll入口函数中的加载Dll事件中加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; SystemMessageBox=(MessageBoxSys)DetourFindFunction("user32.dll","MessageBoxA");</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; if(SystemMessageBox==NULL)</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; {</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp; return FASLE;</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; }</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; Real_MessageBox=(MessageBoxSys)DetourFunction((PBYTE)SystemMessageBox, (PBYTE)MessageBox_Mine);</span><br /><span style="font-family: Verdana; font-size: 12pt">在Dll入口函数中的卸载Dll事件中加入：</span><br /><span style="font-family: Verdana; font-size: 12pt">DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);</span><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp; 重写二进制可执行文件</span><br /><span style="font-family: Verdana; font-size: 12pt">使用Detours自带的SetDll.exe重写二进制可执行文件，可以在需要截获的程序中加入一个新的Detours的PE节表。对于本文就是新建一个批处理文件调用SetDll.exe。</span><br /><span style="font-family: Verdana; font-size: 12pt">@echo off</span><br /><span style="font-family: Verdana; font-size: 12pt">if not exist MessageBoxApp.exe (</span><br /><span style="font-family: Verdana; font-size: 12pt">echo 请将文件解压到MessageBoxApp.exe的安装目录, 然后执行补丁程序</span><br /><span style="font-family: Verdana; font-size: 12pt">) else (</span><br /><span style="font-family: Verdana; font-size: 12pt">setdll /d:ApiHook.dll MessageBoxApp.exe</span><br /><span style="font-family: Verdana; font-size: 12pt">)</span><br /><span style="font-family: Verdana; font-size: 12pt">Pause</span><br /><span style="font-family: Verdana; font-size: 12pt">调用后使用depends.exe（微软VC6.0开发包的工具之一）观察MessageBoxApp.exe前后变化， 可以看到Setdll已经重写MessageBoxApp.exe</span><br /><span style="font-family: Verdana; font-size: 12pt">成功，加入了对ApiHook.dll的依赖关系。</span><br /><a href="http://images.cnblogs.com/cnblogs_com/flying_bat/WindowsLiveWriter/DetourAPI_D69A/crack_05_2.jpg"></a>&nbsp;<img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_05_thumb.jpg" width="872" longdesc="" height="220" /><br /><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; （执行SetDll.exe前）&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (执行SetDll.exe后)</span><br /><span style="font-family: Verdana; font-size: 12pt">执行SetDll.exe重写后的MessageBoxApp.exe，点击确定后可以看到结果如下：</span><br /><span style="font-family: Verdana; font-size: 12pt">至此，MessageBoxApp.exe对MessageBoxA函数的调用已经被截获，弹出的对话框内容已经明显说明这一点。</span><br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/wanghaiguang/crack_06_thumb.jpg" width="407" longdesc="" height="263" /><br /><br /><span style="font-family: Verdana; font-size: 12pt">本文转自：</span><a href="http://www.cnblogs.com/flying_bat/archive/2008/04/18/1159996.html"><span style="font-family: Verdana; font-size: 12pt">http://www.cnblogs.com/flying_bat/archive/2008/04/18/1159996.html</span></a></p></div><img src ="http://www.cppblog.com/wanghaiguang/aggbug/194179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-11-02 14:32 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/11/02/194179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转: 数学之美笔记</title><link>http://www.cppblog.com/wanghaiguang/archive/2012/09/21/191509.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Fri, 21 Sep 2012 09:50:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/09/21/191509.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/191509.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/09/21/191509.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/191509.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/191509.html</trackback:ping><description><![CDATA[<span style="font-family: Verdana; font-size: 12pt">&nbsp;半个月前在豆瓣上看到了一本新书《数学之美》，评价很高。而因为在半年前看了《什么是数学》就对数学产生浓厚兴趣，但苦于水平不足的我便立马买了一本，希望能对数学多一些了解，并认真阅读起来。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 令我意外并欣喜的是，这本书里边的数学内容并不晦涩难懂，而且作者为了讲述数学之美而搭配的一些工程实例都是和我学习并感兴趣的模式识别，目标分类相关算法相关联的。这让我觉得捡到了意外的宝藏。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 书中每一个章节都或多或少是作者亲身经历过的，比如世界级教授的小故事，或者Google的搜索引擎原理，又或者是Google的云计算等。作者用其行云流水般的语言将各个知识点像讲故事一样有趣的叙述出来。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这本书着实让我印象深刻，所以我把笔记分享出来，希望更多和我学习研究领域一样的人会喜欢并亲自阅读这本书，并能支持作者。毕竟国内这种书实在是太少了，也希望能有更多领域内的大牛能再写出一些这种书籍来让我们共同提高。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">1.&nbsp;&nbsp;&nbsp; 因为需要传播信息量的增加，不同的声音并不能完全表达信息，语言便产生了。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">2.&nbsp;&nbsp;&nbsp; 当文字增加到没有人能完全记住所有文字时，聚类和归类就开始了。例如日代表太阳或者代表一天。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">3.&nbsp;&nbsp;&nbsp; 聚类会带来歧义性，但上下文可以消除歧义。信息冗余是信息安全的保障。例如罗塞塔石碑上同一信息重复三次。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">4.&nbsp;&nbsp;&nbsp; 最短编码原理即常用信息短编码，生僻信息长编码。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">5.&nbsp;&nbsp;&nbsp; 因为文字只是信息的载体而非信息本身，所以翻译是可以实现的。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">6.&nbsp;&nbsp;&nbsp; 2012，其实是玛雅文明采用二十进制，即四百年是一个太阳纪，而2012年恰巧是当前太阳纪的最后一年，2013年是新的太阳纪的开始，故被误传为世界末日。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">7.&nbsp;&nbsp;&nbsp; 字母可以看为是一维编码，而汉字可以看为二维编码。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">8.&nbsp;&nbsp;&nbsp; 基于统计的自然语言处理方法，在数学模型上和通信是相通的，甚至是相同的。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">9.&nbsp;&nbsp;&nbsp; 让计算机处理自然语言的基本问题就是为自然语言这种上下文相关的特性建立数学模型，即统计语言模型（Statistical Language Modal）。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">10.&nbsp;&nbsp;&nbsp; 根据大数定理（Law of Large Numbers），只要统计量足够，相对频度就等于概率。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">11.&nbsp;&nbsp;&nbsp; 二元模型。对于p(w1,w2,&#8230;,wn)=p(w1)p(w2|w1)p(w3|w1,w2)&#8230;p(wn|w1,w2,&#8230;,wn-1)的展开问题，因为p(w3|w1,w2)难计算，p(wn|w1,w2,&#8230;,wn-1)更难计算，马尔科夫给出了一个偷懒但是颇为有效的方法，也就是每当遇到这种情况时，就假设任意wi出现的概率只与它前面的wi-1有关，即p(s)=p(w1)p(w2|w1)p(w3|w2)&#8230;p(wi|wi-1)&#8230;p(wn|wn-1)。现在这个概率就变的简单了。对应的语言模型为2元模型（Bigram Model）。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">12.&nbsp;&nbsp;&nbsp; *N元模型。wi只与前一个wi-1有关近似的过头了，所以N-1阶马尔科夫假设为p(wi|w1,w2,&#8230;,wi-1)=p(wi|wi-N+1,wi-N+2,&#8230;,wi-1)，对应的语言模型成为N元模型（N-Gram Model）。一元模型就是上下文无关模型，实际应用中更多实用的是三元模型。Google的罗塞塔翻译系统和语言搜索系统实用的是四元模型，存储于500台以上的Google服务器中。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">13.&nbsp;&nbsp;&nbsp; *卡兹退避法（Katz backoff），对于频率超过一定阈值的词，它们的概率估计就是它们在语料库中的相对频度，对于频率小于这个阈值的词，它们的概率估计就小于他们的相对频度，出现次数越少，频率下调越多。对于未看见的词，也给予一个比较小的概率（即下调得到的频率总和），这样所有词的概率估计都平滑了。这就是卡兹退避法（Katz backoff）。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">14.&nbsp;&nbsp;&nbsp; 训练数据通常是越多越好，通过平滑过渡的方法可以解决零概率和很小概率的问题，毕竟在数据量多的时候概率模型的参数可以估计的比较准确。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">15.&nbsp;&nbsp;&nbsp; 利用统计语言模型进行分词，即最好的分词方法应该保证分完词后这个句子出现的概率最大。根据不同应用，汉语分词的颗粒度大小应该不同。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">16.&nbsp;&nbsp;&nbsp; 符合马尔科夫假设（各个状态st的概率分布只与它前一个状态st-1有关）的随即过程即成为马尔科夫过程，也称为马尔科夫链。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">17.&nbsp;&nbsp;&nbsp; 隐含马尔科夫模型是马尔科夫链的扩展，任意时刻t的状态st是不可见的，所以观察者没法通过观察到一个状态序列s1,s2,s3,&#8230;,sT来推测转移概率等参数。但是隐马尔科夫模型在每个时刻t会输出一个符号ot，而且ot和st相关且仅和ot相关。这个被称为独立输出假设。其中隐含的状态s1,s2,s3,&#8230;是一个典型的马尔科夫链。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">18.&nbsp;&nbsp;&nbsp; 隐含马尔科夫模型是机器学习主要工具之一，和几乎所有机器学习的模型工具一样，它需要一个训练算法（鲍姆-韦尔奇算法）和使用时的解码算法（维特比算法）。掌握了这两类算法，就基本上可以使用隐含马尔科夫模型这个工具了。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">19.&nbsp;&nbsp;&nbsp; 鲍姆-韦尔奇算法（Baum-Welch Algorithm），首先找到一组能够产生输出序列O的模型参数，这个初始模型成为Mtheta0，需要在此基础上找到一个更好的模型，假定不但可以算出这个模型产生O的概率P(O|Mtheta0)，而且能够找到这个模型产生O的所有可能的路径以及这些路径的概率。并算出一组新的模型参数theta1，从Mtheta0到Mtheta1的过程称为一次迭代。接下来从Mtheta1出发寻找更好的模型Mtheta2，并一直找下去，直到模型的质量没有明显提高为止。这样一直估计（Expectation）新的模型参数，使得输出的概率达到最大化（Maximization）的过程被称为期望值最大化（Expectation-Maximization）简称EM过程。EM过程能保证一定能收敛到一个局部最优点，但不能保证找到全局最优点。因此，在一些自然语言处理的应用中，这种无监督的鲍姆-韦尔奇算法训练处的模型比有监督的训练得到的模型效果略差。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">20.&nbsp;&nbsp;&nbsp; 熵，信息熵的定义为H(X)=-SumP(x)logP(x)，变量的不确定性越大，熵也越大。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">21.&nbsp;&nbsp;&nbsp; 一个事物内部会存在随机性，也就是不确定性，假定为U，而从外部消除这个不确定性唯一的办法是引入信息I，而需要引入的信息量取决于这个不确定性的大小，即I&gt;U才行。当I&lt;U时，这些信息可以消除一部分不确定性，U'=U-I。反之，如果没有信息，任何公示或者数字的游戏都无法排除不确定性。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">22.&nbsp;&nbsp;&nbsp; 信息的作用在于消除不确定性。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">23.&nbsp;&nbsp;&nbsp; 互信息，对两个随机事件相关性的量化度量，即随机事件X的不确定性或者说熵H(X)，在知道随机事件Y条件下的不确定性，或者说条件熵H(X|Y)之间的差异，即I(X;Y)=H(X)-H(X|Y)。所谓两个事件相关性的量化度量，即在了解了其中一个Y的前提下，对消除另一个X不确定性所提供的信息量。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">24.&nbsp;&nbsp;&nbsp; 相对熵（Kullback-Leibler Divergence）也叫交叉熵，对两个完全相同的函数，他们的相对熵为零；相对熵越大，两个函数差异越大，反之，相对熵越小，两个函数差异越小；对于概率分布或者概率密度函数，如果取值均大于零，相对熵可以度量两个随机分布的差异性。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">25.&nbsp;&nbsp;&nbsp; 弗里德里克&#183;贾里尼克（Frederek Jelinek）是自然语言处理真谛的先驱者。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">26.&nbsp;&nbsp;&nbsp; 技术分为术和道两种，具体的做事方法是术，做事的原理和原则是道。术会从独门绝技到普及再到落伍，追求术的人会很辛苦，只有掌握了道的本质和精髓才能永远游刃有余。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">27.&nbsp;&nbsp;&nbsp; 真理在形式上从来是简单的，而不是复杂和含混的。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">28.&nbsp;&nbsp;&nbsp; 搜索引擎不过是一张大表，表的每一行对应一个关键字，而每一个关键字后面跟着一组数字，是包含该关键词的文献序号。但当索引变的非常大的时候，这些索引需要通过分布式的方式存储到不同的服务器上。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">29.&nbsp;&nbsp;&nbsp; 网络爬虫（Web Crawlers），图论的遍历算法和搜索引擎的关系。互联网虽然复杂，但是说穿了其实就是一张大图&#8230;&#8230;可以把每一个网页当做一个节点，把那些超链接当做连接网页的弧。有了超链接，可以从任何一个网页出发，用图的遍历算法，自动访问到每一个网页并且把他们存储起来。完成这个功能的程序叫网络爬虫。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">30.&nbsp;&nbsp;&nbsp; 哥尼斯堡七桥，如果一个图能从一个顶点出发，每条边不重复的遍历一遍回到这个顶点，那么每一个顶点的度必须为偶数。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">31.&nbsp;&nbsp;&nbsp; 构建网络爬虫的工程要点：1.用BFS（广度优先搜索）还是DFS（深度优先搜索），一般是先下载完一个网站，再进入下一个网站，即BFS的成分多一些。2.页面的分析和URL的提取，如果有些网页明明存在，但搜索引擎并没有收录，可能的原因之一是网络爬虫中的解析程序没能成功解析网页中不规范的脚本程序。3.记录哪些网页已经下载过的URL表，可以用哈希表。最终，好的方法一般都采用了这样两个技术：首先明确每台下载服务器的分工，也就是在调度时，一看到某个URL就知道要交给哪台服务器去下载，这样就避免了很多服务器对同一个URL做出是否需要下载的判断。然后，在明确分工的基础上，判断URL是否下载就可以批处理了，比如每次向哈希表（一组独立的服务器）发送一大批询问，或者每次更新一大批哈希表的内容，这样通信的次数就大大减少了。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">32.&nbsp;&nbsp;&nbsp; PageRank衡量网页质量的核心思想，在互联网上，如果一个网页被很多其他网页所链接，说明它受到普遍的承认和信赖，那么它的排名就高。同时，对于来自不同网页的链接区别对待，因为网页排名高的那些网页的链接更可靠，于是要给这些链接比较大的权重。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">33.&nbsp;&nbsp;&nbsp; TF-IDF(Term Frequency / Inverse Document Frequency) ，关键词频率-逆文本频率值，其中，TF为某个网页上出现关键词的频率，IDF为假定一个关键词w在Dw个网页中出现过，那么Dw越大，w的权重越小，反之亦然，公式为log(D/Dw)。1.一个词预测主题的能力越强，权重越大，反之，权重越小。2.停止词的权重为零。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">34.&nbsp;&nbsp;&nbsp; 动态规划（Dynamic Programming）的原理，将一个寻找全程最优的问题分解成一个个寻找局部最优的小问题。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">35.&nbsp;&nbsp;&nbsp; 一个好的算法应该像轻武器中最有名的AK-47冲锋枪那样：简单、有效、可靠性好而且容易读懂（易操作）而不应该故弄玄虚。选择简单方案可以容易解释每个步骤和方法背后的道理，这样不仅便于出问题时的查错，也容易找到今后改进的目标。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">36.&nbsp;&nbsp;&nbsp; 在实际的分类中，可以先进行奇异值分解（得到分类结果略显粗糙但能较快得到结果），在粗分类结果的基础上，利用计算向量余弦的方法（对范围内的分类做两两计算），在粗分类结果的基础上，进行几次迭代，得到比较精确的结果。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">37.&nbsp;&nbsp;&nbsp; 奇异值分解（Singular Value Decomposition），在需要用一个大矩阵A来描述成千上万文章和几十上百万词的关联性时，计算量非常大，可以将A奇异值分解为X、B和Y三个矩阵，Amn=Xmm*Bmn*Ynn，X表示词和词类的相关性，Y表示文本和主题的相关性，B表示词类和主题的相关性，其中B对角线上的元素很多值相对其他的非常小，或者为零，可以省略。对关联矩阵A进行一次奇异值分解，就可以同时完成近义词分类和文章的分类，同时能得到每个主题和每个词义类之间的相关性，这个结果非常漂亮。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">38.&nbsp;&nbsp;&nbsp; 信息指纹。如果能够找到一种函数，将5000亿网址随即地映射到128位二进制，也就是16字节的整数空间，就称这16字节的随机数做该网址的信息指纹。信息指纹可以理解为将一段信息映射到一个多维二进制空间中的一个点，只要这个随即函数做的好，那么不同信息对应的点不会重合，因此这个二进制的数字就变成了原来信息所具有的独一无二的指纹。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">39.&nbsp;&nbsp;&nbsp; 判断两个集合是否相同，最笨的方法是这个集合中的元素一一比较，复杂度O(squareN)，稍好的是将元素排序后顺序比较，复杂度O(NlogN)，最完美的方法是计算这两个集合的指纹，然后直接进行比较，计算复杂度O(N)。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">40.&nbsp;&nbsp;&nbsp; 伪随机数产生器算法（Pseudo-Random Number Generator，PRNG），这是产生信息指纹的关键算法，通过他可以将任意长的整数转换成特定长度的伪随机数。最早的PRNG是将一个数的平方掐头去尾取中间，当然这种方法不是很随即，现在常用的是梅森旋转算法（Mersenne Twister）。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">41.&nbsp;&nbsp;&nbsp; 在互联网上加密要使用基于加密的伪随机数产生器（Cryptography Secure Pseudo-Random Number Generator，CSPRNG），常用的算法有MD5或者SHA-1等标准，可以将不定长的信息变成定长的128位或者160位二进制随机数。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">42.&nbsp;&nbsp;&nbsp; 最大熵模型（Maximum Entropy）的原理就是保留全部的不确定性，将风险降到最小。最大熵原理指出，需要对一个随机事件的概率分布进行预测时，我们的预测应当满足全部已知的条件，而对未知的情况不要做任何主观假设。在这种情况下，概率分布最均匀，预测的风险最小。I.Csiszar证明，对任何一组不自相矛盾的信息，这个最大熵模型不仅存在，而且是唯一的，此外，他们都有同一个非常简单的形式-指数函数。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">43.&nbsp;&nbsp;&nbsp; 通用迭代算法（Generalized Iterative Scaling，GIS）是最原始的最大熵模型的训练方法。1.假定第零次迭代的初始模型为等概率的均匀分布。2.用第N次迭代的模型来估算每种信息特征在训练数据中的分布。如果超过了实际的，就把相应的模型参数变小，反之变大。3.重复步骤2直至收敛。这是一种典型的期望值最大化（Expectation Maximization，EM）算法。IIS(Improved Iterative Scaling)比GIS缩短了一到两个数量级。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">44.&nbsp;&nbsp;&nbsp; 布隆过滤器实际上是一个很长的二进制向量和一系列随机映射的函数。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">45.&nbsp;&nbsp;&nbsp; 贝叶斯网络从数学的层面讲是一个加权的有向图，是马尔科夫链的扩展，而从知识论的层面看，贝叶斯网络克服了马尔科夫那种机械的线性的约束，它可以把任何有关联的事件统一到它的框架下面。在网络中，假定马尔科夫假设成立，即每一个状态只与和它直接相连的状态有关，而和他间接相连的状态没有直接关系，那么它就是贝叶斯网络。在网络中每个节点概率的计算，都可以用贝叶斯公式来进行，贝叶斯网络也因此得名。由于网络的每个弧都有一个可信度，贝叶斯网络也被称作信念网络（Belief Networks）。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">46.&nbsp;&nbsp;&nbsp; 条件随机场是计算联合概率分布的有效模型。在一个隐含马尔科夫模型中，以x1,x2,...,xn表示观测值序列，以y1,y2,...,yn表示隐含的状态序列，那么xi只取决于产生它们的状态yi,和前后的状态yi-1和yi+1都无关。显然很多应用里观察值xi可能和前后的状态都有关，如果把xi和yi-1,yi,yi+1都考虑进来，这样的模型就是条件随机场。它是一种特殊的概率图模型（Probablistic Graph Model），它的特殊性在于，变量之间要遵守马尔科夫假设，即每个状态的转移概率只取决于相邻的状态，这一点和另一种概率图模型贝叶斯网络相同，它们的不同之处在于条件随机场是无向图，而贝叶斯网络是有向图。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">47.&nbsp;&nbsp;&nbsp; 维特比算法（Viterbi Algoritm）是一个特殊但应用最广的动态规划算法，利用动态规划，可以解决任何一个图中的最短路径问题。它之所以重要，是因为凡是使用隐含马尔科夫模型描述的问题都可以用它来解码。1.从点S出发，对于第一个状态x1的各个节点，不妨假定有n1个，计算出S到他们的距离d(S,x1i)，其中x1i代表任意状态1的节点。因为只有一步，所以这些距离都是S到他们各自的最短距离。2.对于第二个状态x2的所有节点，要计算出从S到他们的最短距离。d(S,x2i)=min_I=1,n1_d(S,x1j)+d(x1j,x2i)，由于j有n1种可能性，需要一一计算，然后找到最小值。这样对于第二个状态的每个节点，需要n1次乘法计算。假定这个状态有n2个节点，把S这些节点的距离都算一遍，就有O(n1*n2)次运算。3.按照上述方法从第二个状态走到第三个状态一直走到最后一个状态，这样就得到整个网络从头到尾的最短路径。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">48.&nbsp;&nbsp;&nbsp; 扩频传输（Spread-Spectrum Transmission）和固定频率的传输相比，有三点明显的好处：1.抗干扰能力强。2.信号能量非常低，很难获取。3.扩频传输利用带宽更充分。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">49.&nbsp;&nbsp;&nbsp; Google针对云计算给出的解决工具是MapReduce，其根本原理就是计算机算法上常见的分治算法（Divide-and-Conquer）。将一个大任务拆分成小的子任务，并完成子任务的计算，这个过程叫Map，将中间结果合并成最终结果，这个过程叫Reduce。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">50.&nbsp;&nbsp;&nbsp; 逻辑回归模型（Logistic Regression）是将一个事件出现的概率适应到一条逻辑曲线（Logistic Curve）上。典型的逻辑回归函数：f(z)=e`z/e`z+1=1/1+e`-z。逻辑曲线是一条S型曲线，其特点是开始变化快，逐渐减慢，最后饱和。逻辑自回归的好处是它的变量范围从负无穷到正无穷，而值域范围限制在0-1之间。因为值域的范围在0-1之间，这样逻辑回归函数就可以和一个概率分别联系起来了。因为自变量范围在负无穷到正无穷之间，它就可以把信号组合起来，不论组合成多大或者多小的值，最后依然能得到一个概率分布。 </span>
<div style="padding-bottom: 1em"></div><span style="font-family: Verdana; font-size: 12pt">51.&nbsp;&nbsp;&nbsp; 期望最大化算法（Expectation Maximization Algorithm），根据现有的模型，计算各个观测数据输入到模型中的计算结果，这个过程称为期望值计算过程（Expectation），或E过程；接下来，重新计算模型参数，以最大化期望值，这个过程称为最大化的过程（Maximization），或M过程。这一类算法都称为EM算法，比如隐含马尔科夫模型的训练方法Baum-Welch算法，以及最大熵模型的训练方法GIS算法。<br /><br />本文转自：<a href="http://www.cppblog.com/humanchao">http://www.cppblog.com/humanchao</a></span><img src ="http://www.cppblog.com/wanghaiguang/aggbug/191509.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-09-21 17:50 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/09/21/191509.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何写好UI程序 </title><link>http://www.cppblog.com/wanghaiguang/archive/2012/09/05/189522.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Wed, 05 Sep 2012 02:45:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/09/05/189522.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/189522.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/09/05/189522.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/189522.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/189522.html</trackback:ping><description><![CDATA[<p><span style="font-family: Verdana; font-size: 14pt">在Windows平台做开发肯定会接触到UI程序的编写，以MFC的UI开发为例，可以开发单文档，多文档，对话框等形式的应用。写一个UI程序容易，写好却不是一件简单的事情。在整个代码结构的清晰性与可维护性方面需要多加注意。写好UI程序需求注意以下几点：</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">1、围绕数据编程与不是围绕UI编程</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">当我们拿到需求最先接触到的就是UI的设计，也许是美工画的，也许是设计草图。工程师在具体设计的时候容易受UI的影响，或者干脆从UI开始编程。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">这是一个错误的编程习惯，无论UI如何展现与交互，最终都应该围绕数据编程。拿到需求后，应该先思考和推敲数据的设计与流转，UI不过就是数据的一种展现形式而已。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">2、做好UI与逻辑的解耦</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">UI的编程会涉及到许多控件的操作，消息的处理，不知不觉，一个UI类的代码会越写越大，以至于一段时间以后，浏览和梳理都会变得不太方便。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">在UI类里，除了与UI本身的操作有关的代码以外，任何逻辑代码都应该与此解耦，并根据具体情况进行封装调用。如果一个控件关联了太多数据操作，应该把这些操作封装到控件的继承类中，把一类代码进行集中管理和维护。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">上述问题，在程序写作的初期还不太明显，随着代码逐渐膨胀，会越会越让人难以忍受。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">3、数据单向依赖，单向更新</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">UI围绕的数据进行展现与更新，在这个过程中，所以对数据的操作应该进行封装，而不是散落在UI程序在各个角落，数据的更新、获取和UI传递消息时，应该单向操作，如果出现循环处理的情况，在以后维护调试的BUG的过程中会变得比较困难，导致维护效率下降。</span></p>
<p>&nbsp;</p>
<p><span style="font-family: Verdana; font-size: 14pt">本文来自CSDN博客，转载请标明出处：</span><a href="http://www.cppblog.com/humanchao"><span style="font-family: Verdana; font-size: 14pt">http://www.cppblog.com/humanchao</span></a></p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/189522.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-09-05 10:45 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/09/05/189522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>优秀程序员的特征 </title><link>http://www.cppblog.com/wanghaiguang/archive/2012/07/17/183832.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Tue, 17 Jul 2012 00:13:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/07/17/183832.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/183832.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/07/17/183832.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/183832.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/183832.html</trackback:ping><description><![CDATA[<span style="font-size: 12pt">什么样的程序员称得上优秀，根据我所看到，有如下体会： </span>
<p><span style="font-size: 12pt"><strong>1、不愿意将就的人</strong></span></p>
<p><span style="font-size: 12pt">程序设计工作是一项地地道道的脑力劳动，把工作做得很好和做的很差往往只在工作中的一个小小的细节，我发现我身边优秀的程序员都不太喜欢将就，始终把自己的计算机和自己的开发环境调整到最佳状态，原来带我的老员工甚至会自己写一些小工具，来提高工作效率。</span></p>
<p><span style="font-size: 12pt"><strong>2、不喜欢蛮干</strong></span></p>
<p><span style="font-size: 12pt">脑力劳动与体力劳动不同，很多时候很难通过简单的量的积累达到目的，尤其是处理一些难题的时候。一味的强调蛮干，加班几乎天生与高手无缘。没有思路的时候，换个环境，也许答案就在明天上班的路上想起。</span></p>
<p><span style="font-size: 12pt"><strong>3、愿意思考、专注改进</strong></span></p>
<p><span style="font-size: 12pt">程序员与其他劳动者相似，熟练了以后都会形成惯性思维，会不自觉的用自己习惯的方式解决问题，但问题的形式与本质总会变化，只有不断的改进才能使工作效率不断提高。而把脑力劳动变成体力劳动的现象在实际工作中比比皆是。</span></p>
<p><span style="font-size: 12pt"><strong>4、良好的基础和不断的学习</strong></span></p>
<p><span style="font-size: 12pt">良好的基础与不断的学习是天生的一对孪生兄弟，因为基础好所以学的快，因为学得快，所以基本功好。良好学习习惯不是不停的简单追踪新技术，一方面是了解新技术，另一方面需要不断的弥补思维盲区，学习可以有很多种状态，有一种是闻一而知一，技也，有一种是闻一而知三，术也，有一种是闻一而知十，道也。</span></p>
<p><span style="font-size: 12pt"><strong>5、直接切入问题的能力</strong></span></p>
<p><span style="font-size: 12pt">在解决一个问题的时候，有些人总是能够直接切入问题核心，而有些人总是喜欢关注边缘问题。直入主题是一种核心能力，需要思考，实践，改进，积累，提高，周而复使，螺旋上升。另外我觉得这与思维方式与知识面关系很大，多涉猎一些领域没有坏处。</span></p>
<p><span style="font-size: 12pt">***英语***：呵呵，对，还是英语，流利的听说读写。</span></p>
<p><br /><span style="font-size: 12pt">本文来自CSDN博客，转载请标明出处：</span><a href="http://www.cppblog.com/humanchao"><span style="font-size: 12pt">http://www.cppblog.com/humanchao</span></a></p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/183832.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-07-17 08:13 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/07/17/183832.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>断言式编程</title><link>http://www.cppblog.com/wanghaiguang/archive/2012/04/12/171069.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Thu, 12 Apr 2012 04:58:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/04/12/171069.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/171069.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/04/12/171069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/171069.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/171069.html</trackback:ping><description><![CDATA[<span style="font-size: 14pt">本文转自：<a href="http://www.cppblog.com/humanchao/archive/2008/08/19/59309.html">http://www.cppblog.com/humanchao/archive/2008/08/19/59309.html</a><br /><br />我们先看一个函数：函数的功能完成1~10的加法。<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">int</span><span style="color: #000000">&nbsp;Add1to10(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;a,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;b)<br />{<br />&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;a&nbsp;</span><span style="color: #000000">+</span><span style="color: #000000">b;<br />}<br /></span></div><br />但是一般我们还需要加上几条代码：<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">int</span><span style="color: #000000">&nbsp;Add1to10(</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;a,&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;b)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;assert(a&nbsp;</span><span style="color: #000000">&gt;=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&amp;&amp;</span><span style="color: #000000">&nbsp;a&nbsp;</span><span style="color: #000000">&lt;=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">10</span><span style="color: #000000">);<br />&nbsp;&nbsp;&nbsp;&nbsp;assert(b&nbsp;</span><span style="color: #000000">&gt;=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">&amp;&amp;</span><span style="color: #000000">&nbsp;b&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">10</span><span style="color: #000000">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">&nbsp;(&nbsp;a&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">||</span><span style="color: #000000">&nbsp;a&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">10</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">||</span><span style="color: #000000">&nbsp;b&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">1</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">||</span><span style="color: #000000">&nbsp;b&nbsp;</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">10</span><span style="color: #000000">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">-</span><span style="color: #000000">1</span><span style="color: #000000">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;a&nbsp;</span><span style="color: #000000">+</span><span style="color: #000000">b;<br />}</span></div><br />加上上面几条代码的作用是检查函数的输入参数，当参数不正确的时候不光要在返回值上得到体现，而且会触发assert断言，提醒我们参数有误。<br /><br />断言式编程体现一个编程的思想，在我们的程序执行偏离预想的路线时给出提醒。当程序执行偏离预想的路线时一般会出现两种可能：即断言以上的程序没有理解下面程序的调用条件、或断言以下的程序需要接受更为宽泛输入条件。以下分别讨论修改方法：<br /><br />1、如果函数的输入参数是我们编程的一个疏漏，我们认为根本就不应该出现或产生这样的值，那我们应该修改调用函数处的代码，避免非预想的值出现。<br /><br />2、如果无法避免出现或者产生一个非法输入值，那我们要么在函数调用处加入判断，产生符合条件的值时调用函数，不符合参数条件else处理；要么修改函数，使函数可以接受更为宽泛输入条件，并调整断言内容和参数判断逻辑。<br /><br />断言不仅可以出现在函数的参数检查的场合，也可以出现在其他的上下文调用的场合。而且它还会随着程序的开发进程逐渐的增加、删除和调整。它可以验证程序是按照我们预想的思路在执行，当出现意外时及时的给出提醒，提醒我们修正程序或者自己的思路。</span>  <img src ="http://www.cppblog.com/wanghaiguang/aggbug/171069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-04-12 12:58 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/04/12/171069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]写好代码的10个秘密 </title><link>http://www.cppblog.com/wanghaiguang/archive/2012/04/09/170576.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Mon, 09 Apr 2012 05:36:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/04/09/170576.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/170576.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/04/09/170576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/170576.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/170576.html</trackback:ping><description><![CDATA[<p>[转]写好代码的10个秘密 收藏 <br />作者：飞哥 （百度）</p>
<p>先给大家看一段据说是史上最强的程序：<br />e100 33 f6 bf 0 20 b5 10 f3 a5 8c c8 5 0 2 50 68 13 1 cb e 1f be a1 1 bf 0 1 <br />e11b 6 57 b8 11 1 bb 21 13 89 7 4b 4b 48 79 f9 ad 86 e0 8b c8 bd ff ff e8 20 <br />e134 0 3d 0 1 74 1a 7f 3 aa eb f3 2d ff 0 50 e8 f 0 5a f7 d8 8b d8 26 8a 1 aa <br />e14f 4a 75 f9 eb de cb 57 bb 21 13 8b c1 40 f7 27 f7 f5 8b fb ba 11 1 4f 4f 4a <br />e168 39 5 7f f9 52 8b c5 f7 25 f7 37 2b c8 95 f7 65 2 f7 37 95 2b e8 fe e fe <br />e181 10 79 6 c6 6 fe 10 7 46 d0 14 d1 d1 d1 e5 79 ec 5a b8 11 1 ff 7 4b 4b 48 <br />e19b 3b d0 75 f7 5f c3 83 f7 83 a6 5d 59 82 cd b2 8 42 46 9 57 a9 c5 ca aa 1b <br />.............................................................................</p>
<p><br />这段程序是1997年世界程序设计大赛的一等奖作品的部分代码（完整的代码下载，把代码复制粘贴到cmd的debug命令中，回车看到效果）。这个程序运行后将是一个3D的且伴随着音乐的动画。震撼吧！<br />是不是从事软件开发的人员都希望成为这样的武林高手呢？然而真要是用这样的高手来设计、编写我们的产品代码，恐怕某一天，我们什么都不用干了，只能人手一本机器代码，一句一句进行翻译了；那么对于软件产品开发而言，如何写好代码呢？一流的软件产品的代码具备哪些特征呢？</p>
<p><br />&nbsp;</p>
<p><span style="font-size: medium"><strong><span style="color: #800000"><span style="font-size: large">一流代码的特征<br /></span></span></strong></span></p>
<p><br /><strong><span>1、稳定可靠（Robustness） </span><br /></strong>代码写出来以后，一定要能够运行得非常好，非常稳定可靠。在现今的IT行业，软件产品都是是24*7，即要保证系统一天24小时，一星期7天中都可以无间断的正常运行。比如我们百度的搜索引擎系统，比如我们的通信系统，等等。到了产品开发后期，大部分的成本都将投入到产品稳定性的提高。 </p>
<p><strong>2、可维护且简洁（Maintainable and Simple Code）<br /></strong>在写代码时，首先要考虑的是：写出来的代码不但要自己可以读懂，而且我们的同事、测试工程师都可能要修改这些代码，对其进行增减。如果代码很复杂，不容易读懂，如程序中的递归一大堆、程序不知何时或从何地跳出，则会使程序的可维护性和简洁性降低。所以必要的注释、统一的编程规范等都是非常重要的。 </p>
<p><strong>3、高效（Fast）<br /></strong>在软件行业中效率是非常重要的，比如搜索引擎。有些软件的搜索效率就不高，搜索过程特别缓慢，让人难以接受。当然这里面有一个带宽的问题，但是程序效率不高也是一个重要的原因。而实际上程序的效率提高，有时候很简单，并没有什么神秘之处，如使用数组索引时候，可以用指针方式而不使用数组下标；数组的空间定义应该定义为2的N次幂等等。 </p>
<p><strong>4、简短（Small）<br /></strong>这方面大家的感受可能不是很深，但是我的感受是很深的。配置过PSTN程控交换机、路由器、VoIP网关设备的人都知道，这些设备的软件都是从PC机通过网口或串口下载到这些设备的Flash上（类似PC机的BIOS）再通过设备上的CPU启动。如果程序写的很罗嗦，随着特性不断增加，程序规模将变大的巨大，Flash空间告急、内存告急、下载升级变的不可忍受，等等，带来的就是成本不断增加，利润不断下降。 </p>
<p><strong>5、共享性（Reusable）<br /></strong>如果做大型产品开发，程序的共享性也是非常重要的。我们产品有那么多开发人员，如果每一个人都自己定义字符串、链表等数据结构，那么开发效率就会降低，我们的产品恐怕到今天也不能出台。我所说的&#8220;共享&#8221;不是指将别人的代码复制到自己的代码中，而是指直接调用别人的代码，拿来即可用。这一方面可以减少代码的冗余性，另一方面可以增强代码的可维护性。如果别人的代码里有Bug，只需修改他的代码，而调用此代码的程序不用进行任何修改就可以达到同步。这同时要求我们在设计的时候，如何考虑系统的内聚和耦合的问题。 </p>
<p><strong>6、可测试性（Testable）<br /></strong>我们的产品开发里，除了软件开发人员，还有一部分工程师负责软件测试。软件测试人员会将开发代码拿来，一行一行地运行，看程序运行是否有错。如果软件开发人员的代码不可测试，那测试工程师就没有办法进行工作。因此可测试性在大型软件开发里是很重要的一点。可测试性有时候与可维护性是遥相呼应的，一个具有好的可测试性和可维护性的代码，测试人员可以根据开发提供的维护手册、debug信息手册等就可以判断出程序出错在哪个模块。 </p>
<p><strong>7、可移植性（Portable）<br /></strong>可移植性是指程序写出来以后，不仅在windows 2000里可以运行，在NT/9X下可以运行，而且在Linux甚至Macintosh等系统下都可以运行。所有这些特性都是一流代码所具备的特性。但是其中有些特性是会有冲突的。比如高效性，程序写的效率很高，就可能变得很复杂，牺牲的就是简洁。好的代码要在这些特性中取得平衡。 </p>
<p><strong>&nbsp;</p>
<p><strong><span style="font-size: large"><span style="color: #800000">写好代码的10个秘密<br /><br /></span></span></strong></p>
<p>1、百家之长归我所有（Follow Basic Coding Style）<br /></strong>其实写代码的方式有很多，每个人都有自己的风格，但是众多的风格中总有一些共性的、基本的写代码的风格，如为程序写注释、代码对齐，等等。是不是编程规范？对就是编程规范。 </p>
<p><strong>2、取个好名字（Use Naming Conventions）<br /></strong>取个好的函数名、变量名，最好按照一定的规则起名。还是编程规范。 </p>
<p><strong>3、凌波微步，未必摔跤（Evil goto's?Maybe Not...）<br /></strong>这里我用&#8220;凌波微步&#8221;来形容goto语句。通常，goto语句使程序跳来跳去，不容易读，而且不能优化，但是在某种情况下，goto语句反而可以增强程序的可读性。Just go ahead，not go back。 </p>
<p><strong>4、先发制人，后发制于人（Practic Defensive Coding）<br /></strong>Defensive Coding指一些可能会出错的情况，如变量的初始化等，要考虑到出现错误情况下的处理策略。测试时要多运行几个线程。有些程序在一个线城下运行是正常的，但是在多个线程并行运行时就会出现问题；而有些程序在一个CPU下运行几个线程是正常的，但是在多个CPU下运行时就会出现问题，因为单CPU运行线程只是狭义的并行，多CPU一起运行程序，才是真正的并行运算。 </p>
<p><strong>5、见招拆招，滴水不漏（Handle The Error Cases：They Will Occur！）<br /></strong>这里的Error Case（错误情况），是指那些不易重视的错误。如果不对Error Case进行处理，程序在多数情况下不会出错，但是一旦出现异常，程序就会崩溃。 6、熟习剑法刀术，所向无敌（Learn Win32 API Seriously）<br />用&#8220;剑法刀术&#8221;来形容一些API是因为它们都是经过了很多优秀开发人员的不断开发、测试，其效率很高，而且简洁易懂，希望大家能掌握它，熟悉它，使用它。是不是象我们的ULIB。 </p>
<p><strong>7、双手互搏，无坚不摧（Test,but don't stop there）<br /></strong>这里的测试不是指别人来测试你的代码，而是指自己去测试。因为你是写代码的原作者，对代码的了解最深，别人不可能比你更了解，所以你自己在测试时，可以很好地去测试哪些边界条件，以及一些意向不到的情况。 </p>
<p><strong>8、活用断言（Use,don't abuse,assertions）<br /></strong>断言（assertion）是个很好的调试工具和方法，希望大家能多用断言，但是并不是所有的情况下都可以用到断言。有些情况使用断言反而不合适。 </p>
<p><strong>9、草木皆兵，不可大意（Avoid Assumptions）<br /></strong>是指在写代码时，要小心一些输入的情况，比如输入文件、TCP的sockets、函数的参数等等，不要认为使用我们的API的用户都知道什么是正确的、什么是错的，也就是说一定要考虑到对外接口的出错处理问题。 </p>
<p><strong>10、最高境界、无招胜有招（Stop writing so much code）<br /></strong>意思就是说尽量避免写太多的代码，写的越多，出错的机会也越多。最好能重用别人开放的接口函数或直接调用别人的api。 </p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://www.cppblog.com/humanchao/archive/2010/08/05/122334.html">http://www.cppblog.com/humanchao/archive/2010/08/05/122334.html</a></p> <img src ="http://www.cppblog.com/wanghaiguang/aggbug/170576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-04-09 13:36 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/04/09/170576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载：如何查询端口被占用的程序</title><link>http://www.cppblog.com/wanghaiguang/archive/2012/02/22/166251.html</link><dc:creator>王海光</dc:creator><author>王海光</author><pubDate>Wed, 22 Feb 2012 09:06:00 GMT</pubDate><guid>http://www.cppblog.com/wanghaiguang/archive/2012/02/22/166251.html</guid><wfw:comment>http://www.cppblog.com/wanghaiguang/comments/166251.html</wfw:comment><comments>http://www.cppblog.com/wanghaiguang/archive/2012/02/22/166251.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/wanghaiguang/comments/commentRss/166251.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/wanghaiguang/services/trackbacks/166251.html</trackback:ping><description><![CDATA[<p>大家在启动服务器时，有时正常启动有时又启动不了是怎么回事呢？？那为什么关掉迅雷等软件就又好了呢？？现在就来给大家讲解一下，</p>
<p><a href="http://img.blog.163.com/photo/Tl2tguIIMavHqSmPIqmjUg==/1422293057320130173.jpg" target="_blank"><img alt="如何查询端口被占用的程序 - kakis - Kakis" src="http://img.blog.163.com/photo/Tl2tguIIMavHqSmPIqmjUg==/1422293057320130173.jpg" __1329901352878__="ev_7614859533" /></a></p>
<p>转自：<a href="http://yinkai210.blog.163.com/blog/static/287483452009050256466/"><font color="#3468a4">http://yinkai210.blog.163.com/blog/static/287483452009050256466/</font></a><br /><br />这些端口如果被其他程序占用就不能正常启动，比如有时启动时会提示WEB启动失败，其实就是80端口被占用了，而迅雷等下载软件恰恰就是占用了80端口，关掉就行了。但有时迅雷等都没有开也启动不了，那就是别的东西占用了，那怎么办呢？我来叫你查看端口并关掉的方法。<br />1.在开始--运行&nbsp;&nbsp; 里面输入cmd点回车，会出现运行窗口。<br />2.在提示符后输入netstat -ano回车，找到tcp 80端口对应的pid，比如1484.<br />3.ctrl+alt+del打开任务管理器，选进程，这里有很多正在运行的程序怎么找？别急点上面的&nbsp;&nbsp; 查看--选择列--在PID（进程标示符）前面打钩。好了，下面的进程前面都有了PID号码。这时上一步找到的PID就有用了，找到1484，比如PEER.EXE什么的，结束进程吧。这时再开服务器，看WEB可以启动了！</p>
<p><font color="#ff0000"><strong>如上面的不清楚还有简明的：</strong></font></p>
<p><font color="#ff0000"><strong>假如我们需要确定谁占用了我们的80端口</strong></font></p>
<p><font color="#ff0000"><strong>1、Windows平台<br />在windows命令行窗口下执行：<br />C:\&gt;netstat -aon|findstr "80" <br />TCP&nbsp;&nbsp;&nbsp;&nbsp; 127.0.0.1:80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0.0.0.0:0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LISTENING&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2448<br />看到了吗，端口被进程号为2448的进程占用，继续执行下面命令：<br />C:\&gt;tasklist|findstr "2448" <br />thread.exe&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2016 Console&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp; 16,064 K<br />很清楚吧，thread占用了你的端口,Kill it<br />如果第二步查不到，那就开任务管理器，看哪个进程是2448，然后杀之即可。</strong></font></p>
<p><font color="#ff0000"><strong>如果需要查看其他端口。把 80 改掉即可<br /><br /></strong></font><a href="http://blog.csdn.net/wudiyi815/article/details/7473097" style="color: #333333; text-decoration: none; font-family: 'Microsoft YaHei'; font-size: 20px; line-height: 30px; background-color: #ffffff;">Linux查看端口使用状态、关闭端口方法<br /></a>转自：<a href="http://blog.csdn.net/wudiyi815/article/details/7473097">http://blog.csdn.net/wudiyi815/article/details/7473097</a><br /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">前提：首先你必须知道，端口不是独立存在的，它是依附于进程的。某个进程开启，那么它对应的端口就开启了，进程关闭，则该端口也就关闭了。下次若某个进程再次开启，则相应的端口也再次开启。而不要纯粹的理解为关闭掉某个端口，不过可以禁用某个端口。</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">1. 可以通过"netstat -anp" 来查看哪些端口被打开。</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">（注：加参数'-n'会将应用程序转为端口显示，即数字格式的地址，如：nfs-&gt;2049, ftp-&gt;21，因此可以开启两个终端，一一对应一下程序所对应的端口号）</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">2. 然后可以通过"lsof -i:$PORT"查看应用该端口的程序（$PORT指对应的端口号）。或者你也可以查看文件/etc/services，从里面可以找出端口所对应的服务。</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">（注：有些端口通过netstat查不出来，更可靠的方法是"sudo nmap -sT -O localhost"）</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">3. 若要关闭某个端口，则可以：</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">1)通过iptables工具将该端口禁掉，如：</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp;"sudo iptables -A INPUT -p tcp --dport $PORT -j DROP"</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp;"sudo iptables -A OUTPUT -p tcp --dport $PORT -j DROP"&nbsp;&nbsp;&nbsp;&nbsp;</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">2)或者关掉对应的应用程序，则端口就自然关闭了，如：</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp;"kill -9 PID" (PID：进程号)</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp; 如：&nbsp;&nbsp;&nbsp; 通过"netstat -anp | grep ssh"</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp; 有显示：&nbsp;&nbsp;&nbsp; tcp 0 127.0.0.1:2121 0.0.0.0:* LISTEN 7546/ssh</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">&nbsp; 则：&nbsp;&nbsp;&nbsp; "kill -9 7546"</span><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><br style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;" /><span style="font-family: Arial; font-size: 16px; line-height: 26px; background-color: #ffffff;">（可通过"chkconfig"查看系统服务的开启状态）</span><br /><font color="#ff0000"><strong><br /></strong></font></p><img src ="http://www.cppblog.com/wanghaiguang/aggbug/166251.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/wanghaiguang/" target="_blank">王海光</a> 2012-02-22 17:06 <a href="http://www.cppblog.com/wanghaiguang/archive/2012/02/22/166251.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>