<?xml version="1.0" encoding="gb2312"?>

<!-- RSS generated by oioj.net on 4/16/2004 ; 感谢LeXRus提供 RSS 2.0 文档; 此文件可自由使用，但请保留此行信息 --> 
<!-- Source download URL: http://blogger.org.cn/blog/rss2.asp       -->
<rss version="2.0">

<channel>
<title>eaglebetter的博客</title>
<link>http://blogger.org.cn/blog/blog.asp?name=eaglebetter</link>
<description>eaglebetter的博客</description>
<copyright>blogger.org.cn</copyright>
<generator>W3CHINA Blog</generator>
<webMaster>webmaster@blogger.org.cn</webMaster>
<item>
<title><![CDATA[10年IT老兵跳槽到银行1年后的体会]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=26390</link>
<author>eaglebetter</author>
<pubDate>2007/7/17 9:12:08</pubDate>
<description><![CDATA[<a>来源：csai论坛<br>我目前供职于一家全国性股份制商业银行总行的电脑部门，在这里工作刚满一年。此前在国内一家著名的系统集成公司里连续工作了将近十年的时间，而且大部份的时间是在银行的客户现场做应用软件的项目实施工作，时间之久让很多圈内人听来难以置信吧？！在乙方工作了十年再转到甲方工作的这一年间，亲历了由于两种企业文化的不同而带来的许多感受，憋在心里很久了，在此写出来与大家分享。 <br><br>（一） 为什么要换工作 <br><br>九十年代中期，我国银行业的信息化建设如火如涂的进行着，由著名的“两天两联”统领，我也正是在那个时期进入这一领域的。那时做项目的特点是：客户水平较低，集成商在项目中处于统领地位，项目组的成员工作虽然十分辛苦，但住的是宾馆，吃的也不错，每年的薪水也会上涨。在这个时期做项目，自我感觉良好，而且那时自己也年轻，想得不多。 <br><br>情况发身变化，我觉得是从2002年开始的，国内系统集成行业竞争日益激烈，外部严峻的大环境的造成公司的赢利能力下降。公司开始成本压缩，说的好一些是公司的项目管理能力提高了，出差补助开始降低，我们在外地做项目也不允许住宾馆，而是租民居，公司对项目组的各种考核也多了起来，几年不涨一次工资也很普遍。 <br><br>到了近几年，随着银行客户水平的不断提高，与此相比国内金融IT厂商水平却没有明显进步，在客户面前也没有了早年的风光，不少当年著名的企业在今天沦落到了只能给银行服务的境况了。最苦的要算项目组的技术人员了，面对长年出差的压力，面对几年内不涨工资出差补助随时下调的压力，面对每晚加班很晚的辛苦，真不知什么时候能见到曙光？做项目到真的像“IT民工”了。 <br><br>对我个人来说，最最恐怖的是总被公司领导惦念着派出外地做项目了，谁让我在这个行业干了那么多年呢，算是有些经验的了。但我有小孩子了呀，我不要出差，我是需要照管家庭与小孩成长的。 <br><br>到了这个时候，只有换工作离开这个行业这条路了。 <br><br>（二）35岁时换了工作，进入银行业 <br><br>换工作，说起来容易做起来难，因为我的年龄也到了很多企业招聘时要求的最上限了。从开始找工作到找到目前这份工作，整整花了16个月的时间，期间我共去面试了4个单位，前三个不成功各有原因，到不是我自身的条件不好，也许是我比较挑剔：长期出差的不能去，不用出差但工资比原先低的也不行，这和大龄男女青年找对象一个道理，真是高不成、低不就，找到适合自己的真的比较难呀。 <br><br>其实那时自己也明白，IT公司是不想去了，IBM又能怎样呢？目前最适合我的当然是银行了，稳定，特别是不用出差，干的又是我十分熟悉的工作；但我对自己进银行工作一点也不报希望，一方面是我没有任何可以帮助我进入银行界的朋友，虽然一直给银行做项目，但认识的全是和我一样的普通IT技术人员。另一方面就是年龄了，谁愿意要一个高龄的普通IT人员呢？何况银行这种单位了。 <br><br>直到有一天，在网上看到了这家银行的招聘启事，年岭要求在28岁以下，条件特别好的可以放宽到32岁。可我马上就35岁了！死马当活马医吧，不投简历可是一点希望都没有，投了简历或许还有些希望。 <br><br>投了简历的第3天，就收到了这家银行的面试通知。我真不相信这是真的。当天晚上就去面试了。面试我的是电脑部门负责开发的两名领导，具体细节就不讲了，总之感觉他们很需要我这样的人，对我也没问什么希奇古怪的问题，只简单的问了一些工作经历。到最后他们才想起来问我的年龄，（我在简历上没有写明，想尽力回避这点），在面试过程中我一直在实话实说，年龄我也照实说了，他们也没什么反应，可能对我这么多年一直在这个领域工作的经历比较满意吧，于是让我回去等HR的面试。 <br><br>面试后的第二天，就等来了HR部门的通知，让我去二次面试。HR看门见山的说，“业务部门的人面试完你后，对你十分满意，…….”。就这样，在我的35岁生日刚过完没多少天后，我被这家银行录用为正式员工了。 <br><br>现在回想起来，应该是我凭借多年的银行软件实施经验、良好的沟通表达能力及比较好的客户服务意识帮助我成功的换了工作，进入了梦寐以求的银行。追求一种比较全面的综合素质是我一直努力的方向。 <br><br>那些日子真的很兴奋，一直都不敢相信这一切是真的。我从书店买来很多银行方面的书籍准备好好充实一下自己，希望将在此行业一直工作下去，直到能工作到退休。 <br><br>（三） IT公司与银行工作之七点异同 <br><br>到目前为止，我在新单位工作已经一年了，刚开始进入银行时的兴奋劲儿已经褪去，剩下的只有平静的工作与生活了。我这一年的感受若用一句话来概括的话，那就是：银行提供了丰富稳定的物质生活，但我的精神却感到非常的空虚。也许有人会说我这是身在福中不知福，但这是我的真实感受。 <br><br>（1）福利待遇 <br><br>我所在的这家银行的现任行长是位非常有能力、有个人魅力及领导素质的银行家，他精通现代银行的整体运作规则，知道该做什么、该抓什么，也知道不该做什么。在他的领导下，我们银行这几年发生了显著的变化，各项业务增量均列在的中小银行的前列，竞争能力也日显突出，全行上下员工也干劲十足。别的不说，自他上任以来，员工的总体收入每年都在上涨，而且幅度还不小。什么是好领导？这就是好领导，员工在付出后真正得到了实惠。 <br><br>受惠于中国金融业目前整体蓬勃向上的发展形式和一个好的领导，我个人的物质收获也远远超过原来在IT公司的收入。我总的感受是银行对自己的员工在福利待遇上很是舍得，把员工真的当成自家人对待，这和我以前的公司有很大的不同。我不知道这是缘于两者在体制上的不同还是企业文化的不同？ <br><br>我原来的IT公司的老总也是一位在业界非常闻名的企业家，但系统集成整体行业的不景气造成了在IT公司工作员工的整体收入的滞涨，这不是任何个人可以改变的。总是埋怨公司领导的黑心肠也不是聪明之举，只能期盼这个行业赶快走出低谷，重新迎来新的经济增长点。 <br><br>（2）周围的同事 <br><br>在到银行工作前，听别人说，银行的环境很复杂，里面有复杂的人事关系，要小心。但我目前所工作的电脑部却是比较简单，周围的同事都很能干，大部分时间都在干活，也赶上目前有很多很多的项目要实施，也没有人有精力扯闲篇。人员工作的高饱和度是我在其他银行没有见过的。这里聚集着一群能干活的同事。 <br><br>但要讲到人才，还是在我原来的IT公司里遇到的更多。在那里我遇到过一批可以称作人才的人，他们的整体素质很高，不光有聪明的头脑、高超的技术、很好的与人沟通的能力及语言表达能力、通晓业界动态，还有管理一个大项目的组织协调能力，他们是一群符合现代企业用人标准即能干、能说、能写的人才。他们的能力不是天生的，是随着IT企业的不断发展壮大过程中磨练出来的，被逼出来的，可以说IT企业是很锻炼人的地方。我从他们身上也学到了很多很多。 <br><br>但长叹，在目前这家银行里我周围的同事里还没有一个这样的综合性人才！也许银行的电脑部门要的只是能干活的人，我所说的那种人才并不需要，来了也是浪费资源，耽误前程。 <br><br>（3）工作的情况 <br><br>银行对员工的要求真的很低，可以说没什么要求，至少我到目前还没有听到或看到领导对我们的要求，或许我身处在银行的后系统，领导的精力就根本不在我们部门身上？或许我身处总行机关，没有任务压力，从而感受不到分行和支行员工身背的各项考核指标。总之，在这里只要能干活不出大错误就行。这和我原来的IT公司可有着天壤之别。 <br><br>我原来的IT公司对人的要求很高，它的人才理念是要求人要不断的追求卓越，你现在比较好，那还不够，还要追求更好，只有更好，没有最好。这也是可以理解的，我们那时是身处公司的一线部门，是利润中心，部门有经营压力，当然对赚钱的工具——人也要严格管理了。那时我们的精神状态很紧张，努力做好每一件事，生怕落后了，因为有一大堆的考核在等着你，进而影响到你的薪水。而现在银行我所在的部门不是利润中心，是成本中心，是花钱的部门，因而没有那么多的考核等着你，人的精神状态也放松多了。这也是为什么那么多的在IT公司工作的人想进入银行，想从乙方变成甲方的原因。 <br><br>话说回来，正因为电脑部门在银行是成本中心，不是银行的赚钱部门，更不是它的主营业务，因此造成了我们部门的地位在总行很低。比如说有些业务部门有很多人，虽说个人技能上不怎么样，是那种从社会上随便找个人培训一下就能代替他的，但年终奖金比我们这些天天加班做项目的掌握着高科技的人拿的还多，真让人不平衡。比如说任何业务部门随时可以向我们提需求，我们没有理由说不做，在有的项目的实施过程中，他们有些人对待我们的态度真让我感觉不到是在甲方。 <br><br>还有一个与在IT公司工作的不同之处，是在IT公司给客户做完项目，可以马上走人，后续的维护工作都留给了客户（除非客户不断的购买服务）。而在银行不行，这个模块是你负责开发的，那今后发生的任何维护工作都会来找你做，随着你实施的项目越来越多，你负责维护的范围也会越多，就象一个越滚越大的雪球，估计最终会有一天撑不住了。 <br><br>（4）激励考核制度 <br><br>我原来的IT公司对员工有着一整套严格有效执行的激励考核制度。它对部门领导、销售人员、项目经理和普通技术人员分别制定了一系列完整的考核制度。就拿技术人员来说，一年共有两次考核，成绩排名在后5%的人会立刻被辞退，而排在前5%的人就可以加薪或得到升职，这里的升职不是让你去当官，而是在你目前的岗位上向上提升一级：比如你目前是工程师，在连续两次的考核若在前5%以内，会被提升为高级工程师。如果你对管理感兴趣，能带领一个团队做事，你就可以申请转为项目经理。在这里，它为每个人都提供了合适的岗位和发展方向。这样的考核制度，是IT公司领导管理人员的一种重要手段，优胜劣汰在任何一个组织里都是必要的。我认为是公平的，合理的，至少它给每个人都指明了前进的方向。 <br><br>但这种考核在我目前的银行里是看不到的。在这里吃的是大锅饭。做的最好的人和最差的人每月的收入也就相差三千元左右。（我原来的IT公司凭个人能力，每月收入相差万元是非常常见的）。比如在银行每次发福利，大家人均一份，给人的感觉是你也高兴，我也高兴，大家都高兴。就算平时干活不怎么样的人，在福利待遇上一分都不少拿。在这里，对人是没有激励机制的。你工作五六年了，原来是什么级别现在还是什么级别，抬头看不到前进的方向。在这里，平时没有人对你的工作做什么评价，你做的好与不好全凭个人的素质。做的好，听不到表扬，做的差，也没有什么批评，领导对员工都很客气。没有人站出来号召大家应该怎样做，应该弘扬什么而抵制什么。 <br><br>（5）项目管理情况 <br><br>我原来的IT公司在项目管理上可以说是一流的。它是最早在软件行业上通过ISO9000体系认证的那一批公司之一，目前在公司里有几个部门还通过了CMM的认证。IT公司在实施项目时也是严格按照质量体系的要求去做的，并不是拿到认证就完成任务了。它从项目的立项开始，到需求分析阶段、设计阶段直致整个项目周期结束的维护阶段的每一步都有计划、每一步都有规矩、每一步也是必须严格执行的。这也是大公司和小公司在实施项目时的重要区别，它给客户的感觉也是不一样的。至于按照ISO9000体系实施项目，这个项目就一定能按期完成，就一定能盈利吗？事实当然不是这样的，一个项目是否能赚钱、是否能按期交付是受很多因素制约的。说起来就又会引出一大堆的话题来，就此止住。 <br><br>在我目前的这家银行的电脑部门，对ISO9000还没有什么认识，在项目管理上也处于比较原始的状态，是目前急于要解决的问题之一。以项目组为单元做事情的概念较弱，人员之间的沟通能力以及和业务部门之间做事情的效率也有待提高。 <br><br>（6） 人员流失情况 <br><br>在这里，有必要对两个单位的人员流失率单独提一下。我原来的IT公司的整体流失率不很清楚，单就我所在的金融事业部来说，人员的离职率一般保持在20%左右，如果遇到集体跳槽等事件发生，比率还会更高。在这里，基本上不到3年人员就会大换血一次，也就是说，你在这里工作三年后，那你就是老人了。 <br><br>而在这家银行里呢，在电脑部门工作八年、十年左右的大有人在。在我来的这一年里，只有2人离开，其中一个去向不明，另外一个通过行内调动去了业务部门。算下来离职率不到2%。现在银行里的人才流动趋势是只进不出。从两者的离职率上，各位也能分析出些什么来吧。 <br><br>（7）不同的精神状态 <br><br>我在IT公司的这十年里，看到或亲身经历了太多的象部门重组、业务撤并，裁员、减薪、集体跳槽、高层领导频繁变动等事件，面对系统集成行业目前的惨淡经营和自身发展前途的不确定，精神上一直处于紧张忧虑的状态。时刻在思考着未来该是怎样的，出路到底在哪儿里。思考成了我日常生活的一部分。在IT公司里同事之间的关系很密切，有时我们会集体在一起探讨未来的发展、同业动态等话题，也很有意思。 <br><br>但在银行里，周围同事的成长背景、从业经历和我有很大的不同，他们大部分一毕业就进银行工作了，他们的生活长年处于一种十分稳定的状态，这与我经历多年的动荡职场生涯形成了鲜明的对比。在这里我找不到与我能说到一起的朋友，我关心的他们不感兴趣，他们喜欢谈论的我又不屑加入，因此精神上很苦闷。我曾随意问过几个同事，目前是否有危机感，他们均摇头否认。但多年IT公司的工作经历让我养成了不光要低头干活，更要抬头看路的习惯。学会居安思危是我从IT公司得到的最大财富，不管目前的生活状态多么稳定，我都要时刻保持清醒，保持积极向上的生活态度，珍惜目前在银行工作的经历，努力学习金融知识。别看我以前一直给银行做项目，但真正要掌握银行业务还非得是要亲自在银行工作几年才行，在金融IT公司里学不到银行业务的真谛，只是些大量皮毛而已。 <br><br>（四）我的职业发展目标 <br><br>再过几年自己就四十岁了，想想好可怕。好在自己对未来的追求却一直没有放弃。到底追求什么呢，其实就是不想在目前的安稳中度过剩下的职业生涯，总想给自己找个奋斗目标。当然了，银行是高风险的行业，其实也不安稳，一切都是暂时的，未来充满了不确定性。 <br><br>目前给自己定的目标之一是过几年转向业务部门，IT这一行我也不想在做了，做个通晓业务的金融人士应该挺好的吧。在银行的IT部门有个最大的好处是可以接触到所有部门的业务，眼界比某个具体的业务部门的人还要广些，这是需要好好把握的机会。 <br><br>（五）写给想从银行辞职的IT人员 <br><br>如果你具备高超的技术水平，想不断的接受新知识，想创新，想挑战自我，不满足现状，那就去IT公司好了。银行IT一向奉行的是拿来主义，自己是不会投入力量去研发新产品的。但如果你的年龄已偏大，有家有室，那还是呆在银行里别动了吧。外面的世界很精彩但更无奈。 <br><br>（六）写给想做甲方的IT人员 <br><br>如果你只会编码，没其他过人的本事，喜欢安稳的生活，那就去甲方吧，但要等机会。 <br><br>如果你的头脑灵活，喜欢挑战自我，各方面能力极强，就不要来甲方了，这里没有你的用武之地。 <br><br>但我还是认为刚毕业的人应该在IT公司工作几年，在那里十分锻练人，它提供了各种机会供发展，干的好，升职加薪也很快，也能快速实现个人的价值。</a>]]></description>
</item><item>
<title><![CDATA[劳动纠纷2]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=23956</link>
<author>eaglebetter</author>
<pubDate>2007/4/12 22:09:36</pubDate>
<description><![CDATA[
<P><STRONG>劳动部门：不签合同就“试用”违法</STRONG></P>
<P>记者在各大高校论坛发现，许多大学生对于试用期相当陌生。记者昨从市劳动监察和仲裁部门获悉，试用期并非“无权期”。 </P>
<P>　　上海劳动监察总队副总队长庄雅彪表示，无论试用期长短，单位首先要和求职者签订劳动合同，仅仅口头定个试用期不签合同的行为是违法的。此外，只要劳动者在法定工作时间内提供了正常劳动，用人单位就应该支付工资。 </P>
<P>　　此外，试用期长短与劳动合同期限有关：劳动合同期限在6个月以下的，试用期不得超过15日；劳动合同期限在6个月以上1年以下的，试用期不得超过30日；劳动合同期限在1年以上2年以下的，试用期不得超过60日；劳动合同期限在2年以上的，试用期不得超过6个月。 </P>
<P>　　劳动专家表示，用人单位支付的工资标准是除了保险、福利等待遇之外，不得低于本市最低工资标准，也就是750元，低于最低工资标准属违法行为。 </P>
<P>　　记者在采访中发现，有的大学生认为试用期间可能会被无故裁员，或是觉得试用期间随意辞职也没有关系。 </P>
<P>　　市劳动仲裁部门专家隋伟表示，按劳动法相关规定，试用期间，在合同没有特殊条款的前提下，毕业生辞职须通知单位。此外，用人单位如果想在试用期间辞退毕业生，则必须证明劳动者在试用期间不符合哪些录用条件和详细的情况，否则不能随意解除劳动合同<BR></P>
<P><STRONG>聘任算不算建立了劳动关系？</STRONG></P>
<P>去年，张立应聘来到一家网络公司，进公司后，公司给他发了一纸聘期为3年的聘任书，他被聘任为部门经理，在聘任书中公司明确了他的职责、待遇、期限等。今年5月，公司的领导层调整，新任领导以聘任书不是劳动合同，公司与张立没有签订劳动合同为由，要跟他解除劳动关系。那么聘任书是否算劳动合同？公司有权跟他解除劳动关系吗？ </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;张立跟公司之间虽然没有签订劳动合同，但公司发给他的那份聘任书可以视为劳动合同。因此，双方属于劳动关系，公司无权与他解除劳动关系。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;根据《中华人民共和国劳动法》规定，劳动合同应当以书面形式订立，并具备以下条款：劳动合同期限；工作内容；劳动保护和劳动条件；劳动报酬；劳动纪律；劳动合同终止的条件；违反劳动合同的责任。张立的聘任书中已基本具备了以上必备条款，张立一直按照聘任书中的规定进行工作，领取工资，享受待遇。这说明双方当事人就聘任书中明确注明的聘用期限、工作内容、待遇等达成一致，并已实际履行。所以，该聘任书应视为劳动合同。既然聘任书被视为劳动合同，公司在聘任书中注明的聘用期限届满之前，随意解除张立的劳动关系违反了合同的约定，张立有权予以拒绝。<BR></P>
<P><STRONG>签约第一分工作，学会用法律维权</STRONG></P>
<P>尽管许多毕业的大学生已做了充分的精神准备和心理准备，然而，当他们带着憧憬和懵懂真正踏入职场，仍有茫然不知所措之感。在求职受骗后，不知如何投诉，不会用法律武器维权。没有充分了解自己和用人单位之间的权利和义务，不懂劳动合同怎么签订，招工手续如何办理，试用期的期限又是多久？ </P>
<P>　　职场案例：小萌大学毕业来到一家私企公司，觉得工资还不错，干的也有劲。起早贪晚，加班不断。谁知试用期没完没了，只字不提转正的事。忍耐了一个月之后，小萌终于忍耐不住，跑去问老板，老板只是口头答应马上办理转正手续，但一个月过去了，老板好象忘了这事，小萌就再次提起这事，谁知等来的结果却是以心态不稳，被炒了鱿鱼。因为没有合同在先，只能哑巴吃黄连，有苦说不出。 </P>
<P>　　宏威职业顾问指导：大学生离开学校，进入职场，首先要签定一个三方协定，进入公司以后，要与用工单位签定劳动合同，受到劳动法的保护，遇到问题，学会拿起法律武器，保护自己。 </P>
<P>　　一、 三方协议是什么？ </P>
<P>　　三方是指学校，企业，毕业生三方。三方协议是《全国普通高等学校毕业生就业协议书》的简称，它是明确三方在毕业生就业工作中的权利和义务的书面表现形式，能解决应届毕业生户籍、档案、保险、公积金等一系列相关问题。协议在毕业生到单位报到、用人单位正式接收后自行终止。 </P>
<P>　　三方协议的意义何在，什么情况下应该签？应届毕业生，三方协议一旦签署，就意味着大学生第一份工作就基本确定，因此，应届毕业生为了保障自身权益，签三方协议要注意细节和签约事项。在签定三方协议前，要与企业充分地洽谈，认真查看用人单位的隶属关系，国家机关、事业单位、国有企业一般都有人事接收权；民营企业、外资企业则需要经过人事局或人才交流中心的审批才能招收职工，协议书上要签署他们的意见才能有效。应届毕业生还要对不同地方人事主管部门的特殊规定有所了解。详细了解用人单位的情况，包括规模、效益、管理制度等，还要了解自身与用人单位的权益、职责关系，了解协议期限。在协议中，岗位、待遇、工作环境等都没有体现，因此，毕业生要先对用人单位做好充分了解，认真考虑自己的权利与义务关系，权衡利弊。 </P>
<P>　　一旦签了三方协议，就要受法约限制。不要出现签三方协议后违约问题，以免给人留下不诚信印象，给自身带来负面影响，频频违约会形成一种恶性循环。毕业生签订协议书时要慎重，毕业生在协议书上签了个人意见，用人单位在协议上签字盖章后，该协议即开始生效，毕业生不得单方面终止协议。在签订合同或解除协议之前，该协议都具有效力。如果在签订之后，又有其他就业选择，必须与原单位办理书面解约手续，经用人单位上级人事主管部门备案后，办理改派或其他手续。但毕业生可能要承担相关违约责任。 </P>
<P>　　毕业生毁约无外乎两个原因：一是新的签约单位薪水高，二是新的签约单位有更大的发展空间。从2005年开始，毕业生与用人单位签订就业协议书后，如出现毕业生违约情况，违约金被限定不超过毕业生一个月的工资。虽然违约要受到经济损失，但是数量上不是随意的，如遇到企业漫天要价的时候，毕业生要懂得用法律法规保护自己。 </P>
<P>　　二、 就业协议不能取代劳动合同三方协议签定后不是完事大吉，还要与企业签定劳动合同。 </P>
<P>　　劳动保障部门指出：就业协议和劳动合同是两个不同的概念，单位一旦和求职者确立了劳动关系，就应依法签订正式的劳动合同；应当订立劳动合同而未订立的，劳动者可以随时终止劳动关系。 </P>
<P>　　有关专家形象地比喻说，就业协议类似于“出嫁协议”，而劳动合同类似于“夫妻协定”。前者发生在学生毕业之前，由学生、学校、用人单位三方共同签订“出嫁协议”，以确定就业意向和相关权益，包括擅自解除约定方应支付的违约金；但是，“出嫁协议”只约束“婚前”，“婚后”的生活如何安排，应由“夫妻协定”明确。一旦学生毕业离校后，学校将脱离三方关系，毕业生和用人单位双方应确立劳动关系，签订劳动（聘用）合同，而就业协议则同时终止。 </P>
<P>　　一份合法的合同包括：合同双方的权利和义务应当在合同中明确约定。对于工作的内容应当尽量细化。如，岗位工种外延大或比较广，说明在履行劳动合同期间，当事人从事的岗位工种变化范围大。求职者可以要求用人单位对岗位工种适度细化。对于试用期、培训、保守商业秘密、补充保险和福利待遇等求职者希望在劳动合同中体现的内容，当事人可提出在劳动合同中写明。求职者一旦发现条款表述不清、概念模糊的，及时要求用人单位进行说明修订。 </P>
<P>　　《劳动法》第16条明确规定： 劳动合同是劳动者与用人单位确立劳动关系、明确双方权利和义务的协议。建立劳动关系应当订立劳动合同。建立劳动关系就应当订立《劳动合同》。而《关于贯彻执行〈劳动法〉若干问题的意见》第18条又补充规定：试用期应包括在劳动合同期限内。第21条还指出：劳动合同可以约定试用期。试用期最长不得超过六个月。 </P>
<P>　　劳动保障部门还提醒劳动者：“在就业协议中约定了服务期和违约金，就无需再签订劳动合同。”这是一种误解，在明确劳动关系之后，求职者应注意及时与单位签订劳动合同，以防止社会保险费等权益受到侵害；单位不与毕业生签订劳动合同的做法明显是违法的，毕业生不要以为不签合同就可以更自由，不签合同会损害毕业生作为劳动者的权益，特别是在双方产生劳动纠纷、劳动者出现工伤等情况时，会带来更大麻烦。双方未订立劳动合同的，“跳槽”行为将不受用人单位约束。 </P>
<P>　　宏威职业顾问友情提示：职场维权，是大学生进入职场前的最后一课，《劳动法》中相关条文，作为毕业生应该牢记在心，学法才能知法，懂法才能用法。学会职场维权，避免今后的职业之路跌跌撞撞、深一脚、浅一脚，甚至摔跟斗。学会用法律武器保护自己，该出手时就出手，才能顺利进入职业发展的健康之路</P>]]></description>
</item><item>
<title><![CDATA[劳动纠纷1]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=23955</link>
<author>eaglebetter</author>
<pubDate>2007/4/12 22:07:09</pubDate>
<description><![CDATA[<STRONG>辞职大学生不必向企业支付违约金<BR></STRONG><SPAN id=tpid287086><SPAN id=spid287086>小殷是一名外地大学生，毕业后与一家公司签订了服务三年的协议。此后，公司为他办理了上海户口。一年后，小殷辞职。公司以落实户口是公司的特殊待遇为由，要求小殷支付违约金2.1万元。小殷不服，将“老东家”告上法庭。日前，上海浦东新区法院作出判决，支持了小殷的诉请，判定小殷不必支付违约金。 <BR>
<P>&nbsp;&nbsp;&nbsp;&nbsp;小殷是上海市某高校2004届研究生，毕业后进入一家软件公司就业，与公司签订了服务三年的毕业生服务期协议。协议约定，公司负责为小殷办理上海户口落户手续，小殷若在服务期内擅自离开公司将赔偿违约金3万元。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;2005年，小殷辞职离开了公司。软件公司以小殷违约为由向劳动仲裁部门申请仲裁，要求小殷支付违约金2.1万元并获得支持。小殷不服，向浦东新区法院提出上诉，要求不支付违约金。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;近日，法院开庭审理了此案。为外地高校应届毕业生办理上海户籍是否属用人单位提供的一种特殊待遇成为双方争议的焦点。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;小殷告诉法官，他在离开前一个月就按规定向公司提出了辞呈，公司也批准了，因此不存在违约情况。同时他认为，落实户口并不是公司给的特殊待遇。应届毕业生能否得到上海市户口，主要取决于上海市的有关政策规定和毕业生的自身条件，公司仅仅是办理手续而已，并且公司在办理户口时也未支付与协议书约定的违约金3万元相应的对价。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;被告公司认为，公司凭借自己的实力和声誉，才具有为外地高校应届毕业生办理上海户籍的能力，并在接收小殷后通过努力为他办理了上海户口落户手续，因此公司实际上是付出了努力，相对于其他未解决上海户口的毕业生而言，这是一种特殊待遇。为此，双方签订的劳动合同和毕业生服务期协议书都已明确约定，原告提前辞职应承担违约责任。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;法院经审理后认为，根据《上海市劳动合同条例》规定，劳动合同当事人可以对由用人单位出资招用、培训或者提供其他特殊待遇的劳动者的服务期作出约定。而本案中，被告公司以办理上海户口落户手续作为提供的特殊待遇没有法律依据。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;法院认为，非上海生源高校毕业生进沪就业能否办理上海市户籍，需符合《2004年进沪就业非上海生源高校毕业生办理上海市户籍工作的规定》中所规定的基本条件和具体要求，而这些基本条件和具体要求中，除与符合条件的在沪企业、事业等单位签订就业协议一项外，其余条件和要求均与非上海生源高校毕业生本人的知识结构、学历、毕业院校等相关情况有关。因此，对于与符合条件的在沪企业、事业单位签订就业协议的非上海生源高校毕业生来说，其是否能取得上海市户籍，更主要的是取决于其本人的素质和相关条件，并经上海市高等学校毕业生就业管理部门审核批准。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;据此，法院认为原被告双方签订的毕业生服务期协议书约定三年服务期及相应违约金的相关内容与相关劳动法律、法规相悖，于是作出了上述判决。<BR><STRONG>大学生“跳槽”合同没到期怎么办<BR></STRONG><SPAN id=tpid287089><SPAN id=spid287089>尽管合同尚未到期，一些大学生却急于跳槽，因此引发违约赔偿等一系列问题。沈阳市劳动和社会保障局建议准备“跳槽”的大学生，如果需要离职，最好提前一个月打报告。这样可以让原单位有充足的时间寻找合适的接替人选。在跳槽过程中，市民遭遇不公，可以向劳动仲裁部门投诉。 <BR></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;记者从劳动部门获悉，有的大学生在毕业以后，匆忙选择了就业岗位。由于对工作环境并不适应，他们在合同期未满的时候，便毅然选择了跳槽。然而，这种行为可能造成大学生与单位在违约金赔偿方面出现纠纷问题。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;如果合同中说明违约后的赔偿，那么双方按照合同进行赔偿就可以。一般来说，大学生可能赔偿原公司一两个月的工资数额，或者遵照合同约定的百分比来赔偿。如果合同中没约定违约金赔偿问题，那么按照《劳动法》规定，跳槽者要对公司对其进行的前期培训费用进行一定的赔偿。这个数额一般是不确定的，有的公司可能会要求跳槽者赔偿全部培训费用，有的则要求赔偿其中的一部分。 </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;没到期的合同，对跳槽者会存在不同的约束。如果曾经任职的单位是一家IT、通讯等技术领域公司，跳槽者又将到同业的公司工作，那么就涉及到了一个对原单位技术保密的问题。如果要比较顺利地解除与原单位的合同，通常要接受原公司的一些约束，比如原公司会要求跳槽者辞职后不可以到同业工作，或者与原单位签订另一份合同，说明在跳槽后几年内不准应用原有单位的技术等等，这样才完成顺利解除合同的关键一步</P></SPAN></SPAN></SPAN></SPAN>]]></description>
</item><item>
<title><![CDATA[中国人力资源总监谈如何折腾毕业生（转）]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=23952</link>
<author>eaglebetter</author>
<pubDate>2007/4/12 20:26:47</pubDate>
<description><![CDATA[看到下面着篇文章也许对于即将毕业同学们来说是不公平的,甚至有些残忍,但是我觉得作为一个过来人,说出这些,对我的学弟学妹是一种负责的行为. <BR>去年我们同一期签到深圳的这家民营企业的长安大学同学有20人左右,这家公司在全国招聘了交通建设相关专业的毕业生约100人左右,这100人如今留在这家公司的约20多人,分布在全国各地从事销售工作. <BR>这一年中我和同期来到这家公司的校友们交流,听到最多的话就是想辞职,这些想法也在后面一一变成了现实. <BR>对于刚毕业走向社会的大学生来说,肯定要经历很多的挫折,遇到很多自己不想遇到的事,但是谁都希望能有一个高的起点,一个好一点的公司.学校为了提高就业率而不顾及毕业生利益的做法,作为学生实在无法避开,而且社会上也充满了这种存在投机心理的公司,让人避无可避.看到以下这篇文章的时候,我觉得很符合这家公司的情况,希望能对即将去这种公司工作的同学起一些警示作用. <BR>“就业市场的水到底有多深，也许比你想象的还要深”。这是记者在与胡军（化名）交谈之后的感触，用“触目惊心，难以置信”来形容，一点儿也不为过。 <BR>4月初，记者接到一个来自成都的电话，一位不愿透露姓名的中年男子在电话中称，自己在担任单位人力资源总监的4年多时间里通过各种“手段”剥削了手下初入公司的毕业生，为公司“节省”支出上百万，如今，他觉得非常“不安”而又难以自拔。 <BR>在答应严格保密对方的姓名、公司名称的条件下，本报记者二天后如约来到成都一家茶馆，对这位神秘人士进行了采访。 <BR>核心提示：“就业市场的水到底有多深，也许比你想象的还要深”。这是记者在与胡军（化名）交谈之后的感触，用“触目惊心，难以置信”来形容，一点儿也不为过。 <BR>“搞得我连觉都睡不着。”胡军靠在椅子上对着记者一脸苦笑，“近段时间，我一直在痛苦中折磨自己，是继续维护老板的利益，充当老板的忠实‘走狗’。还是替员工说话，不再担当损害员工利益的角色。维护前者我将得到更多的好处，钱、及其他；维护后者我的后果将是降职、降薪甚至被辞退。”胡军告诉记者，他从外省来到成都已经七八年了，先后在几个大中型私人企业打过工，凭他的资历、经验和处世能力，很快都得到了老板的赏识和重用，从担任办公室主任、行政人事部长干到总经理助理、人力资源总监。 <BR>“戴上人力资源总监帽子的那一天，老板就找了我谈话。”胡军开始回忆起4年前的经历，显得很痛苦。 <BR>“当时老板很直接，对我说：‘既然我提拔你做总监，也不把你当外人了。现在公司600多员工一个月的工资开销在120万以上，下个月要还银行贷款，资金很紧张，你动动脑筋、想办法节省点工资开支……”胡军回忆，老板还给了他两个前任人资总监留下的绝招：一是多招毕业生，用完就“开”，二是克扣和少发员工报酬。在得到老板授意后，胡军开始一步步实施他的计划。 <BR>“首先，每年我都会通过招聘会招来大批应届毕业生。因为给他们承诺的待遇不错，这几年招聘都很成功。”胡军说，他深刻理解了老板的意图。 <BR>“然后我重新制订了劳动合同。招聘员工本应签订国家统一的劳动合同，但我要的是倾向于企业自身利益的格式合同。我冥思苦想，制定出九大章五十多条，这些条款内容里要求员工（乙方）履行的职责就占80%—90%，而企业（甲方）应履行的职责只是象征性的占10%—20%。主要是一些对员工的强制性条款。” <BR>拿着公司“聘用合同”，胡军向记者逐一解释。“你看第2条，因工作需要员工工作时间灵活延长，但我们根本没提加班费；第5条，我们规定员工当月工资在次月中旬发放，如果员工离开公司那么就有半个月甚至更多的工资被扣发。而第7条，明显非常吸引大学生的应聘，非常有诱惑力。实际上在合同条款的最后有一句话必不可少，那就是：若员工（乙方）工作中出现失误或差错，公司（甲方）有权无理由辞退。怎么才是失误和差错？无界定的标准，完全由老板说了算，因为‘欲加之罪、何患无辞’。” <BR>“当然，我还配合了其他手段，像员工应聘时尽量不提签订劳动合同，能不签则不签；不执行国家周五工作制，每周工作六天等等。关于社会保险能不办则不办，能拖就拖，政府有关部门如果来查问，实在拖不过也只是按企业员工总数的10%—30%办理。其余的可以用签临时合同或兼职合同来应付。”胡军说，即使是这样，来应聘的人员还是源源不绝，而且来了以后无不拼命工作，表现突出，很多人不提、也不敢提加班费和补休，一天工作8个小时以上的非常普遍。” <BR>胡军还告诉记者，曾经有一个毕业生不满规定提出辞职，胡军按老板的意思不但扣发了他半个月工资，而且近千元的预留金也被扣下。因为，人无完人、工作中哪能一点不出差错，老板的借口和理由太多了。这些员工离开公司后，也有少部分到有关部门去投诉，有关部门只是打个电话问问情况，他们自有一套理由应付。 <BR>在老板授意下，胡军也不是随意辞退员工，有时也给一些小恩小惠进行“奖励”或“鼓励”。如在员工大会上进行表杨（但无奖金），偶尔也会拿出百来元钱对个别有“突出贡献”的员工奖励一下，而且会给员工口头承诺年底重奖等等。当然，不能忘记对特殊岗位的员工（技术和管理人才）给一些小恩小惠。但真正到了年底，他就会以种种借口和理由扣罚、少发奖金。 <BR>“对部分管理干部来说有些方面就更惨了。当他们工作快到四年时，老板和我商量，无论采取何种方法都要赶紧让他们一个一个离开。就这样很多员工稀里糊涂地离开了公司。辛苦几年，到头来竹篮打水一场空。” <BR>“既然面对这种合同，员工可以提出意见或不干。你是怎么处理这种情况的？”记者对胡军抛出了这样的疑问。 <BR>“如今这个社会大家都知道，社会劳动力过剩，老板比我们更清楚‘三条腿的哈蟆难找，两条腿的中国人到处都是’的道理。人才招聘会上，往往都是应聘者人山人海，招聘单位名额却廖廖无几。大学生找不到工作的比比皆是，而且现在大都不再刻意要求工资、待遇，只求有一份工作就行。”胡军说，“这样的情况下，随便你乱整。” <BR>“当然。也有不少敢出头的毕业生，我在员工大会上还要给他们进一步进行‘培训教育’。比如我会叫大家好好想一想，现在的社会竞争非常激烈，我们面临的现实是残酷的，今天能有一份工作就已经不错了，所以，我们要感谢我们的老板，很好地珍惜这份工作。今天工作不努力，明天就得努力找工作。” <BR>胡军笑道：“这只是我对付员工的一个小小的手段而已。只要是企业自己制定劳动合同，小小百姓能翻得起大浪吗？” <BR>据胡军统计，他所经历过的这几家企业，以各种原因离开企业的员工大约有上千人次，但投诉反映的不占10%。主要原因是：一是没有时间，因为这些人还要赶紧抓紧时间找工作；二是拖不起，有关部门三天两头要这个材料、那个证据，至少要跑10次以上；三是没有钱，有的员工工资都没有要回，生活宭迫，哪还有钱交这费那费的；四是无可奈何，有关部门接受了申诉久拖不决，却口口声声找借口，不是说人少，就是说忙不过来等等。一个普通老百姓怎么能经得起这样折腾呢？]]></description>
</item><item>
<title><![CDATA[辞职报告(转)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=23771</link>
<author>eaglebetter</author>
<pubDate>2007/4/7 11:58:29</pubDate>
<description><![CDATA[尊敬的公司领导：<BR>&nbsp;&nbsp;&nbsp; 您们好！<BR>&nbsp;&nbsp;&nbsp; 首先祝公司在新的一年中蓬勃发展，取得更加优异的成绩；祝愿公司上下所有领导和员工身体健康，万事如意！我为能在****工作过感到无比的荣幸和自豪，我会怀念在这里的每时每刻，感谢公司给了我这么一个机会。在公司这一年多里，公司领导和周围同事给了我无比的照顾和关怀，让我深切的感受到了公司倡导的人情化管理给我们带来的亲切和温馨。我深信，公司将沿着通往胜利彼岸的高速轨道飞速前进，将一如既往的在*******领域领航！<BR>&nbsp;&nbsp;&nbsp; 我是06年初带着满身激情来到北京的，原打算在我们伟大的首都能有一番作为，现在看来这些远大抱负都将离我远去了。也许我是一个弱者，不能去面对困难与挫折，没能很好的预料到即将到来的暴风雨，以至于现在让我措手不及，不敢面对，面对重重压力，我只能选择逃避。毕竟我不单单是我，我还有我的家庭，有我的父母和妻子，我不能太过于自私，不能为了自己的事业而忽略了他们的感受。一切的理想和抱负都是过眼云烟，在这繁忙的首都我终将迷失方向。回家过着一份安静平和与世无争的生活也许是我最好的选择。在家里我能照顾我年迈的双亲，能和妻子相濡以沫恩恩爱爱，能很好的养育我的孩子们，但是在北京这一切都无法做到，面对疯涨的房价，高额的房租，攀升的物价，想在北京安个家永远只是那些被上帝宠坏了的人所能实现的，而我只能痴心妄想。我不想眼睁睁的看着自己的双亲老去，而自己不能很好的尽到做儿子的职责，不想忍受等我有所成时子欲养而父不在的那种痛苦；我不想看着妻子和我在北京居无定所，瓢泼不定的一天天老去，这会让我一辈子永远愧对我的妻子；我不想让我的孩子在刚出生时就不知道他的爸爸在哪，不想让他成为时下最为关注的留守儿童，如果不能尽到一个父亲的责任，我会永远对不住我的孩子……所有这一切让我不得不重新考虑自己的人生，我想我最好的选择就是回家，虽然那会让我留下终生的遗憾，但是我想做一个好儿子，好丈夫，好爸爸比什么都更重要，况且回家我照样可以有自己的一番事业，是金子到哪都会闪光。<BR>&nbsp;&nbsp;&nbsp; 这段时间，我会把自己负责的****项目那块很好的完成，会把自己的活交接完毕。如果我的离去给公司带来了好多不便，我表示万分的歉意，希望公司领导考虑考虑我的实际情况给予理解。我真诚的恳请公司领导批准我的辞职申请，在这里我表示衷心的感谢，同时祝愿公司不断壮大发展！<BR>&nbsp;&nbsp;&nbsp; 此致<BR>敬礼！<BR>]]></description>
</item><item>
<title><![CDATA[匈牙利命名法]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=23305</link>
<author>eaglebetter</author>
<pubDate>2007/3/18 21:47:19</pubDate>
<description><![CDATA[
<P>匈牙利命名法是一种编程时的命名规范。基本原则是：变量名＝属性＋类型＋对象描述，其中每一对象的名称都要求有明确含义，可以取对象名字全称或名字的一部分。命名要基于容易记忆容易理解的原则。保证名字的连贯性是非常重要的。　　 </P>
<P>举例来说，表单的名称为form，那么在匈牙利命名法中可以简写为frm，则当表单变量名称为Switchboard时，变量全称应该为frmSwitchboard。这样可以很容易从变量名看出Switchboard是一个表单，同样，如果此变量类型为标签，那么就应命名成lblSwitchboard。可以看出，匈牙利命名法非常便于记忆，而且使变量名非常清晰易懂，这样，增强了代码的可读性，方便各程序员之间相互交流代码。　　 </P>
<P>这种命名技术是由一位能干的Microsoft程序员查尔斯·西蒙尼(Charles Simonyi) 提出的，他出生在匈牙利。在 Microsoft 公司中和他一起工作的人被教会使用这种约定。这对他们来说一切都很正常。但对那些 Simonyi 领导的项目组之外的人来说却感到很奇特，他们认为这是死板的表达方式，甚至说带有这样奇怪的外观是因为它是用匈牙利文写的。从此这种命名方式就被叫做匈牙利命名法。 <BR><BR>据说这种命名法是一位叫 Charles Simonyi 的匈牙利程序员发明的，后来他在微软呆了几年，于是 <BR>这种命名法就通过微软的各种产品和文档资料向世界传播开了。现在，大部分程序员不管自己使用 <BR>什么软件进行开发，或多或少都使用了这种命名法。这种命名法的出发点是把量名变按：属性+类型 <BR>+对象 描述的顺序组合起来，以使程序员作变量时对变量的类型和其它属性有直观的了解，下面 <BR>是HN变量命名规范，其中也有一些是我个人的偏向： <BR><BR>属性部分 <BR>全局变量 <BR>g_ <BR>常量 <BR>c_ <BR>c++类成员变量 <BR>m_ <BR>静态变量 <BR>s_ <BR><BR>类型部分 <BR>指针 <BR>p <BR>函数 <BR>fn <BR>无效 <BR>v <BR>句柄 <BR>h <BR>长整型 <BR>l <BR>布尔 <BR>b <BR>浮点型（有时也指文件） <BR>f <BR>双字 <BR>dw <BR>字符串 <BR>sz <BR>短整型 <BR>n <BR>双精度浮点 <BR>d <BR>计数 <BR>c（通常用cnt） <BR>字符 <BR>ch（通常用c） <BR>整型 <BR>i（通常用n） <BR>字节 <BR>by <BR>字 <BR>w <BR>实型 <BR>r <BR>无符号 <BR>u <BR><BR>描述部分 <BR>最大 <BR>Max <BR>最小 <BR>Min <BR>初始化 <BR>Init <BR>临时变量 <BR>T（或Temp） <BR>源对象 <BR>Src <BR>目的对象 <BR>Dest <BR><BR><BR><BR>这里顺便写几个例子： <BR>hwnd ： h 是类型描述，表示句柄， wnd 是变量对象描述，表示窗口，所以 hwnd 表示窗口句柄； <BR>pfnEatApple ： pfn 是类型描述，表示指向函数的指针， EatApple 是变量对象描述，所以它表示 <BR>指向 EatApple 函数的函数指针变量。 <BR>g_cch ： g_ 是属性描述，表示全局变量，c 和 ch 分别是计数类型和字符类型，一起表示变量类 <BR>型，这里忽略了对象描述，所以它表示一个对字符进行计数的全局变量。 <BR>上面就是HN命名法的一般规则。 <BR><BR><BR>小结:匈牙利命名法 </P>
<P align=center><FONT face=宋体 size=7><STRONG>匈牙利命名法</STRONG></FONT></P>
<P align=center><BR><FONT face=宋体><STRONG>MFC、句柄、控件及结构的命名规范</STRONG></FONT> 
<TABLE style="557px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>Windows类型</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>样本变量</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>MFC类</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>样本变量</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hWnd；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CWnd*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pWnd；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HDLG</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hDlg；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CDialog*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pDlg；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HDC</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hDC；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CDC*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pDC；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HGDIOBJ</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hGdiObj；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CGdiObject*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pGdiObj；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HPEN</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hPen；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CPen*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pPen；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HBRUSH</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hBrush；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CBrush*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pBrush；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HFONT </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hFont； </FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CFont*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pFont；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HBITMAP </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hBitmap；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CBitmap*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pBitmap；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HPALETTE </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hPaltte；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CPalette*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pPalette；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HRGN </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hRgn；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CRgn*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pRgn；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HMENU </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hMenu；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CMenu*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pMenu；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CState* </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pState；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CButton*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pButton；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CEdit*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pEdit；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CListBox*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pListBox；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CComboBox*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pComboBox；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HWND </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hCtl；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CScrollBar*</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pScrollBar；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HSZ </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>hszStr；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CString </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pStr；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>POINT </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pt；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CPoint </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>pt；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>SIZE </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>size；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CSize </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>size；</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>RECT </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>rect；</FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>CRect </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>rect；</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><STRONG><FONT face=宋体>一般前缀命名规范</FONT></STRONG> 
<TABLE style="78px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>前缀</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>类型</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>实例</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>C</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>类或结构</FONT></TD>
<TD><FONT face=宋体 size=2>CDocument，CPrintInfo</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>m_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>成员变量</FONT></TD>
<TD><FONT face=宋体 size=2>m_pDoc，m_nCustomers</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><STRONG><FONT face=宋体>变量命名规范</FONT></STRONG><STRONG> 
<TABLE border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>前缀</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>类型</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>描述</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>实例</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>ch</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>char</FONT></TD>
<TD><FONT face=宋体 size=2>8位字符</FONT></TD>
<TD><FONT face=宋体 size=2>chGrade</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>ch </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>TCHAR</FONT></TD>
<TD><FONT face=宋体 size=2>如果<STRONG>_UNICODE</STRONG>定义，则为16位字符</FONT></TD>
<TD><FONT face=宋体 size=2>chName</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>b</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>BOOL</FONT></TD>
<TD><FONT face=宋体 size=2>布尔值</FONT></TD>
<TD><FONT face=宋体 size=2>bEnable</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>n </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>int</FONT></TD>
<TD><FONT face=宋体 size=2>整型（其大小依赖于操作系统）</FONT></TD>
<TD><FONT face=宋体 size=2>nLength</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>n </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>UINT </FONT></TD>
<TD><FONT face=宋体 size=2>无符号值（其大小依赖于操作系统）</FONT></TD>
<TD><FONT face=宋体 size=2>nHeight</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>w </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>WORD </FONT></TD>
<TD><FONT face=宋体 size=2>16位无符号值</FONT></TD>
<TD><FONT face=宋体 size=2>wPos</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>l </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>LONG </FONT></TD>
<TD><FONT face=宋体 size=2>32位有符号整型</FONT></TD>
<TD><FONT face=宋体 size=2>lOffset</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>dw </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>DWORD </FONT></TD>
<TD><FONT face=宋体 size=2>32位无符号整型 </FONT></TD>
<TD><FONT face=宋体 size=2>dwRange</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>p </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>* </FONT></TD>
<TD><FONT face=宋体 size=2>指针</FONT></TD>
<TD><FONT face=宋体 size=2>pDoc</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>lp </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>FAR* </FONT></TD>
<TD><FONT face=宋体 size=2>远指针 </FONT></TD>
<TD><FONT face=宋体 size=2>lpszName</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>lpsz </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>LPSTR </FONT></TD>
<TD><FONT face=宋体 size=2>32位字符串指针</FONT></TD>
<TD><FONT face=宋体 size=2>lpszName</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>lpsz </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>LPCSTR </FONT></TD>
<TD><FONT face=宋体 size=2>32位常量字符串指针</FONT></TD>
<TD><FONT face=宋体 size=2>lpszName</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>lpsz </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>LPCTSTR </FONT></TD>
<TD><FONT face=宋体 size=2>如果<STRONG>_UNICODE</STRONG>定义，则为32位常量字符串指针</FONT></TD>
<TD><FONT face=宋体 size=2>lpszName</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>h </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>handle </FONT></TD>
<TD><FONT face=宋体 size=2>Windows对象句柄</FONT></TD>
<TD><FONT face=宋体 size=2>hWnd</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>lpfn </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>callback</FONT></TD>
<TD><FONT face=宋体 size=2>指向<STRONG>CALLBACK</STRONG>函数的远指针 </FONT></TD>
<TD></TD></TR></TBODY></TABLE></STRONG></P>
<P align=center><FONT size=2></FONT>
<TABLE border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>前缀</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>符号类型</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>实例</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>范围</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDR_ </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>不同类型的多个资源共享标识</FONT></TD>
<TD><FONT face=宋体 size=2>IDR_MAIINFRAME</FONT></TD>
<TD><FONT face=宋体 size=2>1～0x6FFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDD_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>对话框资源</FONT></TD>
<TD><FONT face=宋体 size=2>IDD_SPELL_CHECK </FONT></TD>
<TD><FONT face=宋体 size=2>1～0x6FFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HIDD_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>对话框资源的Help上下文</FONT></TD>
<TD><FONT face=宋体 size=2>HIDD_SPELL_CHECK </FONT></TD>
<TD><FONT face=宋体 size=2>0x20001～0x26FF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDB_ </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>位图资源</FONT></TD>
<TD><FONT face=宋体 size=2>IDB_COMPANY_LOGO </FONT></TD>
<TD><FONT face=宋体 size=2>1～0x6FFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDC_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>光标资源</FONT></TD>
<TD><FONT face=宋体 size=2>IDC_PENCIL </FONT></TD>
<TD><FONT face=宋体 size=2>1～0x6FFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDI_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>图标资源</FONT></TD>
<TD><FONT face=宋体 size=2>IDI_NOTEPAD </FONT></TD>
<TD><FONT face=宋体 size=2>1～0x6FFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>ID_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>来自菜单项或工具栏的命令</FONT></TD>
<TD><FONT face=宋体 size=2>ID_TOOLS_SPELLING </FONT></TD>
<TD><FONT face=宋体 size=2>0x8000～0xDFFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HID_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>命令Help上下文</FONT></TD>
<TD><FONT face=宋体 size=2>HID_TOOLS_SPELLING </FONT></TD>
<TD><FONT face=宋体 size=2>0x18000～0x1DFFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDP_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>消息框提示</FONT></TD>
<TD><FONT face=宋体 size=2>IDP_INVALID_PARTNO </FONT></TD>
<TD><FONT face=宋体 size=2>8～0xDEEF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>HIDP_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>消息框Help上下文</FONT></TD>
<TD><FONT face=宋体 size=2>HIDP_INVALID_PARTNO </FONT></TD>
<TD><FONT face=宋体 size=2>0x30008～0x3DEFF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDS_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>串资源</FONT></TD>
<TD><FONT face=宋体 size=2>IDS_COPYRIGHT </FONT></TD>
<TD><FONT face=宋体 size=2>1～0x7EEF</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>IDC_</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>对话框内的控件</FONT></TD>
<TD><FONT face=宋体 size=2>IDC_RECALC </FONT></TD>
<TD><FONT face=宋体 size=2>8～0xDEEF</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><FONT face=宋体><STRONG>Microsoft MFC宏命名规范</STRONG></FONT> 
<TABLE style="203px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>名称</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>类型</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_AFXDLL</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>唯一的动态连接库（Dynamic Link Library，DLL）版本</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_ALPHA</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>仅编译DEC Alpha处理器</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_DEBUG</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>包括诊断的调试版本</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_MBCS</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>编译多字节字符集</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_UNICODE</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>在一个应用程序中打开Unicode</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>AFXAPI </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>MFC提供的函数</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>CALLBACK</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>通过指针回调的函数 </FONT></TD></TR></TBODY></TABLE></P>
<P align=center><FONT face=宋体><STRONG>库标识符命名法</STRONG></FONT> 
<TABLE style="78px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>标识符</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>值和含义</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>u </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>ANSI（N）或Unicode（U）</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>d </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>调试或发行：D = 调试；忽略标识符为发行。</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><STRONG><FONT face=宋体>静态库版本命名规范</FONT></STRONG> 
<TABLE style="147px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>库</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>描述</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>NAFXCWD.LIB</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>调试版本：MFC静态连接库</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>NAFXCW.LIB</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>发行版本：MFC静态连接库</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>UAFXCWD.LIB</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>调试版本：具有Unicode支持的MFC静态连接库</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>UAFXCW.LIB</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>发行版本：具有Unicode支持的MFC静态连接库</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><FONT face=宋体><STRONG>动态连接库命名规范</STRONG></FONT> 
<TABLE style="97px: " border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>名称</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>类型</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>_AFXDLL</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>唯一的动态连接库（DLL）版本</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>WINAPI </STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>Windows所提供的函数</FONT></TD></TR></TBODY></TABLE></P>
<P align=center><FONT face=宋体><STRONG>Windows.h中新的命名规范</STRONG></FONT> 
<TABLE border=1>
<TBODY>
<TR>
<TD><FONT face=宋体 size=2><STRONG>类型</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2><STRONG>定义描述</STRONG></FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>WINAPI</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>使用在API声明中的FAR PASCAL位置，如果正在编写一个具有导出API人口点的DLL，则可以在自己的API中使用该类型</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>CALLBACK</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>使用在应用程序回叫例程，如窗口和对话框过程中的FAR PASCAL的位置</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>LPCSTR</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>与LPSTR相同，只是LPCSTR用于只读串指针，其定义类似（const char FAR*）</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>UINT</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>可移植的无符号整型类型，其大小由主机环境决定（对于Windows NT和Windows 9x为32位）；它是unsigned int的同义词</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>LRESULT</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>窗口程序返回值的类型</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>LPARAM</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>声明lParam所使用的类型，lParam是窗口程序的第四个参数</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>WPARAM</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>声明wParam所使用的类型，wParam是窗口程序的第三个参数</FONT></TD></TR>
<TR>
<TD><FONT face=宋体 size=2><STRONG>LPVOID</STRONG></FONT></TD>
<TD><FONT face=宋体 size=2>一般指针类型，与（void *）相同，可以用来代替LPSTR </FONT></TD></TR></TBODY></TABLE></P>]]></description>
</item><item>
<title><![CDATA[数据库设计三大范式应用实例剖析]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=22174</link>
<author>eaglebetter</author>
<pubDate>2007/1/27 19:54:08</pubDate>
<description><![CDATA[数据库的设计范式是数据库设计所需要满足的规范，满足这些规范的数据库是简洁的、结构明晰的，同时，不会发生插入（insert）、删除（delete）和更新（update）操作异常。反之则是乱七八糟，不仅给数据库的编程人员制造麻烦，而且面目可憎，可能存储了大量不需要的冗余信息。<BR><BR>　　设计范式是不是很难懂呢？非也，大学教材上给我们一堆数学公式我们当然看不懂，也记不住。所以我们很多人就根本不按照范式来设计数据库。<BR><BR>　　实质上，设计范式用很形象、很简洁的话语就能说清楚，道明白。本文将对范式进行通俗地说明，并以笔者曾经设计的一个简单论坛的数据库为例来讲解怎样将这些范式应用于实际工程。<BR><BR>　　范式说明<BR><BR>　　第一范式（1NF）：数据库表中的字段都是单一属性的，不可再分。这个单一属性由基本类型构成，包括整型、实数、字符型、逻辑型、日期型等。<BR><BR>　　例如，如下的数据库表是符合第一范式的：<BR><BR>
<TABLE cellSpacing=0 cellPadding=2 width="90%" align=center border=1>
<TBODY>
<TR>
<TD>字段1 </TD>
<TD>字段2 </TD>
<TD>字段3 </TD>
<TD>字段4</TD></TR>
<TR>
<TD>&nbsp;</TD>
<TD>&nbsp;</TD>
<TD>&nbsp;</TD>
<TD>&nbsp;</TD></TR></TBODY></TABLE><BR>　　而这样的数据库表是不符合第一范式的：<BR><BR>
<TABLE cellSpacing=0 cellPadding=2 width="90%" align=center border=1>
<TBODY>
<TR>
<TD>字段1 </TD>
<TD>字段2 </TD>
<TD colSpan=2>字段3 </TD>
<TD>字段4</TD></TR>
<TR>
<TD>&nbsp;</TD>
<TD>&nbsp;</TD>
<TD>字段3.1</TD>
<TD>字段3.2 </TD>
<TD>&nbsp;</TD></TR></TBODY></TABLE>
<P><BR>　　很显然，在当前的任何关系数据库管理系统（DBMS）中，傻瓜也不可能做出不符合第一范式的数据库，因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此，你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。<BR><BR>　　第二范式（2NF）：数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖（部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况），也即所有非关键字段都完全依赖于任意一组候选关键字。 
<TABLE cellSpacing=0 cellPadding=0 align=left border=0>
<TBODY>
<TR>
<TD vAlign=top>&nbsp;</TD></TR>
<TR>
<TD>&nbsp;</TD></TR></TBODY></TABLE><BR><BR>　　假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分)，关键字为组合关键字(学号, 课程名称)，因为存在如下决定关系：<BR><BR>　　(学号, 课程名称) → (姓名, 年龄, 成绩, 学分)<BR><BR>　　这个数据库表不满足第二范式，因为存在如下决定关系：<BR><BR>　　(课程名称) → (学分)<BR><BR>　　(学号) → (姓名, 年龄)<BR><BR>　　即存在组合关键字中的字段决定非关键字的情况。<BR><BR>　　由于不符合2NF，这个选课关系表会存在如下问题：<BR><BR>　　(1) 数据冗余：<BR><BR>　　同一门课程由n个学生选修，"学分"就重复n-1次；同一个学生选修了m门课程，姓名和年龄就重复了m-1次。<BR><BR>　　(2) 更新异常：<BR><BR>　　若调整了某门课程的学分，数据表中所有行的"学分"值都要更新，否则会出现同一门课程学分不同的情况。<BR><BR>　　(3) 插入异常：<BR><BR>　　假设要开设一门新的课程，暂时还没有人选修。这样，由于还没有"学号"关键字，课程名称和学分也无法记录入数据库。<BR><BR>　　(4) 删除异常：<BR><BR>　　假设一批学生已经完成课程的选修，这些选修记录就应该从数据库表中删除。但是，与此同时，课程名称和学分信息也被删除了。很显然，这也会导致插入异常。 <BR><BR>　　把选课关系表SelectCourse改为如下三个表：<BR><BR>　　学生：Student(学号, 姓名, 年龄)；<BR><BR>　　课程：Course(课程名称, 学分)；<BR><BR>　　选课关系：SelectCourse(学号, 课程名称, 成绩)。<BR><BR>　　这样的数据库表是符合第二范式的，消除了数据冗余、更新异常、插入异常和删除异常。<BR><BR>　　另外，所有单关键字的数据库表都符合第二范式，因为不可能存在组合关键字。<BR><BR>　　第三范式（3NF）：在第二范式的基础上，数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖，指的是如果存在"A → B → C"的决定关系，则C传递函数依赖于A。因此，满足第三范式的数据库表应该不存在如下依赖关系：<BR><BR>　　关键字段 → 非关键字段x → 非关键字段y<BR><BR>　　假定学生关系表为Student(学号, 姓名, 年龄, 所在<A href="http://edu.itbulo.com/"><FONT color=#000000>学院</FONT></A>, <A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>地点, <A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>电话)，关键字为单一关键字"学号"，因为存在如下决定关系：<BR><BR>　　(学号) → (姓名, 年龄, 所在<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>, <A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>地点, <A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>电话)<BR><BR>　　这个数据库是符合2NF的，但是不符合3NF，因为存在如下决定关系：<BR><BR>　　(学号) → (所在<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>) → (<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>地点, <A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>电话)<BR><BR>　　即存在非关键字段"<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>地点"、"<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>电话"对关键字段"学号"的传递函数依赖。<BR><BR>　　它也会存在数据冗余、更新异常、插入异常和删除异常的情况，读者可自行分析得知。<BR><BR>　　把学生关系表分为如下两个表：<BR><BR>　　学生：(学号, 姓名, 年龄, 所在<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>)；<BR><BR>　　<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>：(<A href="http://edu.itbulo.com/"><U><FONT color=#004a9c>学院</FONT></U></A>, 地点, 电话)。<BR><BR>　　这样的数据库表是符合第三范式的，消除了数据冗余、更新异常、插入异常和删除异常。<BR><BR>　　鲍依斯-科得范式（BCNF）：在第三范式的基础上，数据库表中如果不存在任何字段对任一候选关键字段的传递函数依赖则符合第三范式。</P>
<P>　假设仓库管理关系表为StorehouseManage(仓库ID, 存储物品ID, 管理员ID, 数量)，且有一个管理员只在一个仓库工作；一个仓库可以存储多种物品。这个数据库表中存在如下决定关系：<BR><BR>　　(仓库ID, 存储物品ID) →(管理员ID, 数量)<BR><BR>　　(管理员ID, 存储物品ID) → (仓库ID, 数量)<BR><BR>　　所以，(仓库ID, 存储物品ID)和(管理员ID, 存储物品ID)都是StorehouseManage的候选关键字，表中的唯一非关键字段为数量，它是符合第三范式的。但是，由于存在如下决定关系：<BR><BR>　　(仓库ID) → (管理员ID)<BR><BR>　　(管理员ID) → (仓库ID)<BR><BR>　　即存在关键字段决定关键字段的情况，所以其不符合BCNF范式。它会出现如下异常情况：<BR><BR>　　(1) 删除异常：<BR><BR>　　当仓库被清空后，所有"存储物品ID"和"数量"信息被删除的同时，"仓库ID"和"管理员ID"信息也被删除了。<BR><BR>　　(2) 插入异常：<BR><BR>　　当仓库没有存储任何物品时，无法给仓库分配管理员。<BR><BR>　　(3) 更新异常：<BR><BR>　　如果仓库换了管理员，则表中所有行的管理员ID都要修改。<BR><BR>　　把仓库管理关系表分解为二个关系表：<BR><BR>　　仓库管理：StorehouseManage(仓库ID, 管理员ID)；<BR><BR>　　仓库：Storehouse(仓库ID, 存储物品ID, 数量)。<BR><BR>　　这样的数据库表是符合BCNF范式的，消除了删除异常、插入异常和更新异常。 </P>
<P>&nbsp;</P>
<P>范式应用<BR><BR>　　我们来逐步搞定一个论坛的数据库，有如下信息：<BR><BR>　　（1） 用户：用户名，email，主页，电话，联系地址<BR><BR>　　（2） 帖子：发帖标题，发帖内容，回复标题，回复内容 <BR><BR>　　第一次我们将数据库设计为仅仅存在表：<BR>　　 
<TABLE cellSpacing=0 cellPadding=2 width="90%" align=center border=1>
<TBODY>
<TR>
<TD>用户名 </TD>
<TD>email </TD>
<TD>主页</TD>
<TD>电话</TD>
<TD>联系地址</TD>
<TD>发帖标题</TD>
<TD>发帖内容</TD>
<TD>回复标题</TD>
<TD>回复内容</TD></TR></TBODY></TABLE><BR>　　这个数据库表符合第一范式，但是没有任何一组候选关键字能决定数据库表的整行，唯一的关键字段用户名也不能完全决定整个元组。我们需要增加"发帖ID"、"回复ID"字段，即将表修改为：<BR><BR>
<TABLE cellSpacing=0 cellPadding=2 width="90%" align=center border=1>
<TBODY>
<TR>
<TD>用户名</TD>
<TD>email</TD>
<TD>主页</TD>
<TD>电话</TD>
<TD>联系地址</TD>
<TD>发帖ID</TD>
<TD>发帖标题</TD>
<TD>发帖内容</TD>
<TD>回复ID</TD>
<TD>回复标题</TD>
<TD>回复内容</TD></TR></TBODY></TABLE><BR>　　这样数据表中的关键字(用户名，发帖ID，回复ID)能决定整行：<BR><BR>　　(用户名,发帖ID,回复ID) → (email,主页,电话,联系地址,发帖标题,发帖内容,回复标题,回复内容)<BR><BR>　　但是，这样的设计不符合第二范式，因为存在如下决定关系：<BR><BR>　　(用户名) → (email,主页,电话,联系地址)<BR><BR>　　(发帖ID) → (发帖标题,发帖内容)<BR><BR>　　(回复ID) → (回复标题,回复内容)<BR><BR>　　即非关键字段部分函数依赖于候选关键字段，很明显，这个设计会导致大量的数据冗余和操作异常。 
<TABLE cellSpacing=0 cellPadding=0 align=left border=0>
<TBODY>
<TR>
<TD vAlign=top>&nbsp;</TD></TR>
<TR>
<TD>&nbsp;</TD></TR></TBODY></TABLE><BR><BR>　　我们将数据库表分解为（带下划线的为关键字）：<BR><BR>　　（1） 用户信息：用户名，email，主页，电话，联系地址<BR><BR>　　（2） 帖子信息：发帖ID，标题，内容<BR><BR>　　（3） 回复信息：回复ID，标题，内容<BR><BR>　　（4） 发贴：用户名，发帖ID<BR><BR>　　（5） 回复：发帖ID，回复ID<BR><BR>　　这样的设计是满足第1、2、3范式和BCNF范式要求的，但是这样的设计是不是最好的呢？<BR><BR>　　不一定。<BR><BR>　　观察可知，第4项"发帖"中的"用户名"和"发帖ID"之间是1：N的关系，因此我们可以把"发帖"合并到第2项的"帖子信息"中；第5项"回复"中的"发帖ID"和"回复ID"之间也是1：N的关系，因此我们可以把"回复"合并到第3项的"回复信息"中。这样可以一定量地减少数据冗余，新的设计为：<BR><BR>　　（1） 用户信息：用户名，email，主页，电话，联系地址<BR><BR>　　（2） 帖子信息：用户名，发帖ID，标题，内容<BR><BR>　　（3） 回复信息：发帖ID，回复ID，标题，内容<BR><BR>　　数据库表1显然满足所有范式的要求；<BR><BR>　　数据库表2中存在非关键字段"标题"、"内容"对关键字段"发帖ID"的部分函数依赖，即不满足第二范式的要求，但是这一设计并不会导致数据冗余和操作异常；<BR><BR>　　数据库表3中也存在非关键字段"标题"、"内容"对关键字段"回复ID"的部分函数依赖，也不满足第二范式的要求，但是与数据库表2相似，这一设计也不会导致数据冗余和操作异常。<BR><BR>　　由此可以看出，并不一定要强行满足范式的要求，对于1：N关系，当1的一边合并到N的那边后，N的那边就不再满足第二范式了，但是这种设计反而比较好！<BR><BR>　　对于M：N的关系，不能将M一边或N一边合并到另一边去，这样会导致不符合范式要求，同时导致操作异常和数据冗余。 <BR>对于1：1的关系，我们可以将左边的1或者右边的1合并到另一边去，设计导致不符合范式要求，但是并不会导致操作异常和数据冗余。<BR><BR>　　结论<BR><BR>　　满足范式要求的数据库设计是结构清晰的，同时可避免数据冗余和操作异常。这并意味着不符合范式要求的设计一定是错误的，在数据库表中存在1：1或1：N关系这种较特殊的情况下，合并导致的不符合范式要求反而是合理的。<BR><BR>　　在我们设计数据库的时候，一定要时刻考虑范式的要求。</P>]]></description>
</item><item>
<title><![CDATA[C语言指针的奥秘]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=21788</link>
<author>eaglebetter</author>
<pubDate>2007/1/15 22:50:09</pubDate>
<description><![CDATA[<A href="http://embedfans.com/C/2007181016375897.htm">http://embedfans.com/C/2007181016375897.htm</A><BR><SPAN class=14>指针是一个特殊的变量，它里面存储的数值被解释成为内存里的一个地址。 要搞清一个指针需要搞清指针的四方面的内容：指针的类型，指针所指向的 类型，指针的值或者叫指针所指向的内存区，还有指针本身所占据的内存区。让我们分别说明。 <BR>　　先声明几个指针放着做例子： <BR>　　例一： <BR>　　(1)int*ptr; <BR>　　(2)char*ptr; <BR>　　(3)int**ptr; <BR>　　(4)int(*ptr)[3]; <BR>　　(5)int*(*ptr)[4]; <BR>　　<BR>　　<STRONG>指针的类型</STRONG><BR>　　从语法的角度看，你只要把指针声明语句里的指针名字去掉，剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型： </SPAN>
<P class=14>　　(1)int*ptr;//指针的类型是int* <BR>　　(2)char*ptr;//指针的类型是char* <BR>　　(3)int**ptr;//指针的类型是int** <BR>　　(4)int(*ptr)[3];//指针的类型是int(*)[3] <BR>　　(5)int*(*ptr)[4];//指针的类型是int*(*)[4] <BR>　　怎么样？找出指针的类型的方法是不是很简单？ <BR>　　指针所指向的类型<BR>　　当你通过指针来访问指针所指向的内存区时，指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。 <BR>　　从语法上看，你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉，剩下的就是指针所指向的类型。例如： <BR>　　(1)int*ptr;//指针所指向的类型是int <BR>　　(2)char*ptr;//指针所指向的的类型是char <BR>　　(3)int**ptr;//指针所指向的的类型是int* <BR>　　(4)int(*ptr)[3];//指针所指向的的类型是int()[3] <BR>　　(5)int*(*ptr)[4];//指针所指向的的类型是int*()[4] <BR>　　在指针的算术运算中，指针所指向的类型有很大的作用。 <BR>　　指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时，你会发现，把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念，是精通指针的关键点之一。我看了不少书，发现有些写得差的书中，就把指针的这两个概念搅在一起了，所以看起书来前后矛盾，越看越糊涂。<BR>指针的值，或者叫指针所指向的内存区或地址<BR>　　指针的值是指针本身存储的数值，这个值将被编译器当作一个地址，而不是一个一般的数值。在32位程序里，所有类型的指针的值都是一个32位整数，因为32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始，长度为si zeof(指针所指向的类型)的一片内存区。以后，我们说一个指针的值是XX，就相当于说该指针指向了以XX为首地址的一片内存区域；我们说一个指针指向了某块内存区域，就相当于说该指针的值是这块内存区域的首地址。 <BR>　　指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中，指针所指向的类型已经有了，但由于指针还未初始化，所以它所指向的内存区是不存在的，或者说是无意义的。 <BR>　　以后，每遇到一个指针，都应该问问：这个指针的类型是什么？指针指的类型是什么？该指针指向了哪里？ <BR>　　指针本身所占据的内存区<BR>　　指针本身占了多大的内存？你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里，指针本身占据了4个字节的长度。 <BR>　　指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。 <STRONG><BR>　　指针的算术运算 </STRONG><BR>指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如： <BR>　　例二： <BR>　　1、chara[20]; <BR>　　2、int*ptr=a; <BR>　　... <BR>　... <BR>　　3、ptr++; <BR>　　在上例中，指针ptr的类型是int*,它指向的类型是int，它被初始化为指向整形变量a。接下来的第3句中，指针ptr被加了1，编译器是这样处理的：它把指针ptr的值加上了sizeof(int)，在32位程序中，是被加上了4。由于地址是用字节做单位的，故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。 <BR>由于char类型的长度是一个字节，所以，原来ptr是指向数组a的第0号单元开始的四个字节，此时指向了数组a中从第4号单元开始的四个字节。 <BR>　　我们可以用一个指针和一个循环来遍历一个数组，看例子：<BR>　　例三： <BR>intarray[20]; <BR>int*ptr=array; <BR>... <BR>//此处略去为整型数组赋值的代码。 <BR>... <BR>for(i=0;i&lt;20;i++) <BR>{ <BR>　(*ptr)++; <BR>　ptr++； <BR>}<BR>　　这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1，所以每次循环都能访问数组的下一个单元。 </P>
<P class=14>　　再看例子： </P>
<P class=14>　　例四： </P>
<P class=14>　　1、chara[20]; <BR>　　2、int*ptr=a; <BR>　　... <BR>　　... <BR>　　3、ptr+=5;<BR>　　在这个例子中，ptr被加上了5，编译器是这样处理的：将指针ptr的值加上5乘sizeof(int)，在32位程序中就是加上了5乘4=20。由于地址的单位是字节，故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说，向高地址方向移动了20个字节。在这个例子中，没加5前的ptr指向数组a的第0号单元开始的四个字节，加5后，ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题，但在语法上却是可以的。这也体现出了指针的灵活性。 </P>
<P class=14>　　如果上例中，ptr是被减去5，那么处理过程大同小异，只不过ptr的值是被减去5乘sizeof(int)，新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。 <BR>　　总结一下，一个指针ptrold加上一个整数n后，结果是一个新的指针ptrnew，ptrnew的类型和ptrold的类型相同，ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说，ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。 <BR>　　一个指针ptrold减去一个整数n后，结果是一个新的指针ptrnew，ptrnew的类型和ptrold的类型相同，ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节，就是说，ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。 <BR>运算符&amp;和* <BR>这里&amp;是取地址运算符，*是...书上叫做"间接运算符"。 <BR>　　&amp;a的运算结果是一个指针，指针的类型是a的类型加个*，指针所指向的类型是a的类型，指针所指向的地址嘛，那就是a的地址。 <BR>　　*p的运算结果就五花八门了。总之*p的结果是p所指向的东西，这个东西有这些特点：它的类型是p指向的类型，它所占用的地址是p所指向的地址。 <BR>　　例五： <BR>inta=12; <BR>intb; <BR>int*p;<BR>int**ptr; <BR>p=&amp;a;<BR>//&amp;a的结果是一个指针，类型是int*，指向的类型是int，指向的地址是a的地址。 <BR>*p=24;<BR>//*p的结果，在这里它的类型是int，它所占用的地址是p所指向的地址，显然，*p就是变量a。 <BR>ptr=&amp;p;<BR>//&amp;p的结果是个指针，该指针的类型是p的类型加个*，在这里是int **。该指针所指向的类型是p的类型，这里是int*。该指针所指向的地址就是指针p自己的地址。 <BR>*ptr=&amp;b;<BR>//*ptr是个指针，&amp;b的结果也是个指针，且这两个指针的类型和所指向的类型是一样的，所以用&amp;b来给*ptr赋值就是毫无问题的了。 <BR>**ptr=34;<BR>//*ptr的结果是ptr所指向的东西，在这里是一个指针，对这个指针再做一次*运算，结果就是一个int类型的变量。<BR>　　指针表达式 <BR>一个表达式的最后结果如果是一个指针，那么这个表达式就叫指针表式。 <BR>　　下面是一些指针表达式的例子： <BR>　　例六： <BR>inta,b; <BR>intarray[10]; <BR>int*pa; <BR>pa=&amp;a;//&amp;a是一个指针表达式。 <BR>int**ptr=&amp;pa;//&amp;pa也是一个指针表达式。 <BR>*ptr=&amp;b;//*ptr和&amp;b都是指针表达式。 <BR>pa=array; <BR>pa++;//这也是指针表达式。<BR>例七： <BR>char*arr[20]; <BR>char**parr=arr;//如果把arr看作指针的话，arr也是指针表达式 <BR>char*str; <BR>str=*parr;//*parr是指针表达式 <BR>str=*(parr+1);//*(parr+1)是指针表达式 <BR>str=*(parr+2);//*(parr+2)是指针表达式<BR>　　由于指针表达式的结果是一个指针，所以指针表达式也具有指针所具有的四个要素：指针的类型，指针所指向的类型，指针指向的内存区，指针自身占据的内存。</P>
<P class=14>　　好了，当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话，这个指针表达式就是一个左值，否则就不是一个左值。 <BR>　　在例七中，&amp;a不是一个左值，因为它还没有占据明确的内存。*ptr是一个左值，因为*ptr这个指针已经占据了内存，其实*ptr就是指针pa，既然pa已经在内存中有了自己的位置，那么*ptr当然也有了自己的位置。 <BR>　　数组和指针的关系 <BR>　　数组的数组名其实可以看作一个指针。看下例： <BR>　　例八： <BR>intarray[10]={0,1,2,3,4,5,6,7,8,9},value; <BR>... <BR>... <BR>value=array[0];//也可写成：value=*array; <BR>value=array[3];//也可写成：value=*(array+3); <BR>value=array[4];//也可写成：value=*(array+4);<BR>上例中，一般而言数组名array代表数组本身，类型是int[10]，但如果把array看做指针的话，它指向数组的第0个单元，类型是int*，所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理，array+3是一个指向数组第3个单元的指针，所以*(array+3)等于3。其它依此类推。 </P>
<P class=14>　　例九： <BR>char*str[3]={ <BR>　"Hello,thisisasample!", <BR>　"Hi,goodmorning.", <BR>　"Helloworld" <BR>}; <BR>chars[80]； <BR>strcpy(s,str[0]);//也可写成strcpy(s,*str); <BR>strcpy(s,str[1]);//也可写成strcpy(s,*(str+1)); <BR>strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));<BR>上例中，str是一个三单元的数组，该数组的每个单元都是一个指针，这些指针各指向一个字符串。把指针数组名str当作一个指针的话，它指向数组的第0号单元，它的类型是char**，它指向的类型是char*。 <BR>*str也是一个指针，它的类型是char*，它所指向的类型是char，它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址，即'H'的地址。 str+1也是一个指针，它指向数组的第1号单元，它的类型是char**，它指向的类型是char*。 </P>
<P class=14>　　*(str+1)也是一个指针，它的类型是char*，它所指向的类型是char，它指向 "Hi,goodmorning."的第一个字符'H'，等等。 </P>
<P class=14>　　下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n]，则数组名称array就有了两重含义：第一，它代表整个数组，它的类型是TYPE[n]；第二 ，它是一个指针，该指针的类型是TYPE*，该指针指向的类型是TYPE，也就是数组单元的类型，该指针指向的内存区就是数组第0号单元，该指针自己占有单独的内存区，注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的，即类似array++的表达式是错误的。 <BR>　　在不同的表达式中数组名array可以扮演不同的角色。 <BR>　　在表达式sizeof(array)中，数组名array代表数组本身，故这时sizeof函数测出的是整个数组的大小。 <BR>在表达式*array中，array扮演的是指针，因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。 <BR>　　表达式array+n（其中n=0，1，2，....。）中，array扮演的是指针，故array+n的结果是一个指针，它的类型是TYPE*，它指向的类型是TYPE，它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。 <BR>例十<BR>intarray[10]; <BR>int(*ptr)[10]; <BR>ptr=&amp;array;： <BR>上例中ptr是一个指针，它的类型是int(*)[10]，他指向的类型是int[10] ，我们用整个数组的首地址来初始化它。在语句ptr=&amp;array中，array代表数组本身。 </P>
<P class=14>　　本节中提到了函数sizeof()，那么我来问一问，sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小？答案是前者。例如： <BR>int(*ptr)[10]; <BR>　　则在32位程序中，有： <BR>sizeof(int(*)[10])==4 <BR>sizeof(int[10])==40 <BR>sizeof(ptr)==4<BR>实际上，sizeof(对象)测出的都是对象自身的类型的大小，而不是别的什么类型的大小。<BR>指针和结构类型的关系 <BR>可以声明一个指向结构类型对象的指针。 <BR>　　例十一： <BR>structMyStruct <BR>{ <BR>　inta; <BR>　intb; <BR>　intc; <BR>} <BR>MyStructss={20,30,40};<BR>//声明了结构对象ss，并把ss的三个成员初始化为20，30和40。 <BR>MyStruct*ptr=&amp;ss;<BR>//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。 <BR>int*pstr=(int*)&amp;ss;<BR>//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。<BR>　　请问怎样通过指针ptr来访问ss的三个成员变量？ <BR>　　答案： <BR>ptr-&gt;a; <BR>ptr-&gt;b; <BR>ptr-&gt;c; <BR>　　又请问怎样通过指针pstr来访问ss的三个成员变量？ <BR>　　答案： <BR>*pstr；//访问了ss的成员a。 <BR>*(pstr+1);//访问了ss的成员b。 <BR>*(pstr+2)//访问了ss的成员c。 <BR>　　虽然我在我的MSVC++6.0上调式过上述代码，但是要知道，这样使用pstr来访问结构成员是不正规的，为了说明为什么不正规，让我们看看怎样通过指针来访问数组的各个单元： <BR>　　例十二： <BR>intarray[3]={35,56,37}; <BR>int*pa=array; <BR>　　通过指针pa访问数组array的三个单元的方法是： <BR>*pa;//访问了第0号单元 <BR>*(pa+1);//访问了第1号单元 <BR>*(pa+2);//访问了第2号单元 <BR>　　从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。 <BR>　　所有的C/C++编译器在排列数组的单元时，总是把各个数组单元存放在连续的存储区里，单元和单元之间没有空隙。但在存放结构对象的各个成员时，在某种编译环境下，可能会需要字对齐或双字对齐或者是别的什么对齐，需要在相邻两个成员之间加若干个"填充字节"，这就导致各个成员之间可能会有若干个字节的空隙。 <BR>　　所以，在例十二中，即使*pstr访问到了结构对象ss的第一个成员变量a，也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节，说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节，嘿，这倒是个不错的方法。 <BR>过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。 <BR>　　指针和函数的关系 <BR>　　可以把一个指针声明成为一个指向函数的指针。intfun1(char*,int); <BR>int(*pfun1)(char*,int); <BR>pfun1=fun1; <BR>.... <BR>.... <BR>inta=(*pfun1)("abcdefg",7);//通过函数指针调用函数。 <BR>可以把指针作为函数的形参。在函数调用语句中，可以用指针表达式来作为实参。 <BR>　　例十三： <BR>intfun(char*); <BR>inta; <BR>charstr[]="abcdefghijklmn"; <BR>a=fun(str); <BR>... <BR>... <BR>intfun(char*s) <BR>{ <BR>intnum=0; <BR>for(inti=0;i{ <BR>num+=*s;s++; <BR>} <BR>returnnum;<BR>}<BR>　　这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了，数组的名字也是一个指针。在函数调用中，当把str作为实参传递给形参s后，实际是把str的值传递给了s，s所指向的地址就和str所指向的地址一致，但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算，并不意味着同时对str进行了自加1运算。<BR>指针类型转换 <BR>当我们初始化一个指针或给一个指针赋值时，赋值号的左边是一个指针，赋值号的右边是一个指针表达式。在我们前面所举的例子中，绝大多数情况下，指针的类型和指针表达式的类型是一样的，指针所指向的类型和指针表达式所指向的类型是一样的。 <BR>　　例十四： <BR>　　1、floatf=12.3; <BR>　　2、float*fptr=&amp;f; <BR>　　3、int*p; <BR>　　　在上面的例子中，假如我们想让指针p指向实数f，应该怎么搞？是用下面的语句吗？ </P>
<P class=14>　　p=&amp;f; </P>
<P class=14>　　不对。因为指针p的类型是int*，它指向的类型是int。表达式&amp;f的结果是一个指针，指针的类型是float*,它指向的类型是float。两者不一致，直接赋值的方法是不行的。至少在我的MSVC++6.0上，对指针的赋值语句要求赋值号两边的类型一致，所指向的类型也一致，其它的编译器上我没试过，大家可以试试。为了实现我们的目的，需要进行"强制类型转换"： <BR>p=(int*)&amp;f;<BR>如果有一个指针p，我们需要把它的类型和所指向的类型改为TYEP*TYPE， 那么语法格式是： <BR>　　(TYPE*)p； <BR>　　这样强制类型转换的结果是一个新指针，该新指针的类型是TYPE*，它指向的类型是TYPE，它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。 <BR>　　一个函数如果使用了指针作为形参，那么在函数调用语句的实参和形参的结合过程中，也会发生指针类型的转换。 <BR>　　例十五：<BR>voidfun(char*); <BR>inta=125,b; <BR>fun((char*)&amp;a); <BR>... <BR>... <BR>voidfun(char*s) <BR>{ <BR>charc; <BR>c=*(s+3);*(s+3)=*(s+0);*(s+0)=c; <BR>c=*(s+2);*(s+2)=*(s+1);*(s+1)=c; <BR>} <BR>} <BR>注意这是一个32位程序，故int类型占了四个字节，char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗？在函数调用语句中，实参&amp;a的结果是一个指针，它的类型是int*，它指向的类型是int。形参这个指针的类型是char*，它指向的类型是char。这样，在实参和形参的结合过程中，我们必须进行一次从int*类型到char*类型的转换。结合这个例子，我们可以这样来想象编译器进行转换的过程：编译器先构造一个临时指针char*temp， 然后执行temp=(char*)&amp;a，最后再把temp的值传递给s。所以最后的结果是：s的类型是char*,它指向的类型是char，它指向的地址就是a的首地址。 </P>
<P class=14>　　我们已经知道，指针的值就是指针指向的地址，在32位程序中，指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢？就象下面的语句：<BR>unsignedinta; <BR>TYPE*ptr;//TYPE是int，char或结构类型等等类型。 <BR>... <BR>... <BR>a=20345686; <BR>ptr=20345686;//我们的目的是要使指针ptr指向地址20345686（十进制 <BR>） <BR>ptr=a;//我们的目的是要使指针ptr指向地址20345686（十进制）<BR>编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗？不，还有办法： <BR>unsignedinta; <BR>TYPE*ptr;//TYPE是int，char或结构类型等等类型。 <BR>... <BR>... <BR>a=某个数，这个数必须代表一个合法的地址； <BR>ptr=(TYPE*)a；//呵呵，这就可以了。<BR>严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址，否则的话，在你使用ptr的时候，就会出现非法操作错误。 </P>
<P><SPAN class=14>　　想想能不能反过来，把指针指向的地址即指针的值当作一个整数取出来。完 全可以。下面的例子演示了把一个指针的值当作一个整数取出来，然后再把这个整数当作一个地址赋给一个指针： <BR>　　例十六： <BR>inta=123,b; <BR>int*ptr=&amp;a; <BR>char*str; <BR>b=(int)ptr;//把指针ptr的值当作一个整数取出来。 <BR>str=(char*)b;//把这个整数的值当作一个地址赋给指针str。<BR>　　现在我们已经知道了，可以把指针的值当作一个整数取出来，也可以把一个整数值当作地址赋给一个指针。 <BR>　　<STRONG>指针的安全问题 </STRONG><BR>看下面的例子： <BR>　　例十七：<BR>chars='a'; <BR>int*ptr; <BR>ptr=(int*)&amp;s; <BR>*ptr=1298； <BR>　　指针ptr是一个int*类型的指针，它指向的类型是int。它指向的地址就是s的首地址。在32位程序中，s占一个字节，int类型占四个字节。最后一条语句不但改变了s所占的一个字节，还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的？只有编译程序知道，而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据，也许这三个字节里正好是程序的一条代码，而由于你对指针的马虎应用，这三个字节的值被改变了！这会造成崩溃性的错误。 <BR>　　让我们再来看一例： <BR>　　例十八： <BR>　　1、chara; <BR>　　2、int*ptr=&amp;a; <BR>　　... <BR>　　... <BR>　　3、ptr++; <BR>　　4、*ptr=115; <BR>　　该例子完全可以通过编译，并能执行。但是看到没有？第3句对指针ptr进行自加1运算后，ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么？我们不知道。有可能它是一个非常重要的数据，甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据！这是严重的错误。所以在使用指针时，程序员心里必须非常清楚：我的指针究竟指向了哪里。在用指针访问数组的时候，也要注意不要超出数组的低端和高端界限，否则也会造成类似的错误。 <BR>　　在指针的强制类型转换：ptr1=(TYPE*)ptr2中，如果sizeof(ptr2的类型)大于sizeof(ptr1的类型)，那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型)，那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么，读者结合例十七来想一想，应该会明白的。<BR></SPAN></P>]]></description>
</item><item>
<title><![CDATA[C++类库介绍]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=21700</link>
<author>eaglebetter</author>
<pubDate>2007/1/13 0:06:16</pubDate>
<description><![CDATA[再次体现了C++保持核心语言的效率同时大力发展应用库的发展趋势!!在C++中，库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中，C++的库门类繁多，解决的问题也是极其广泛，库从轻量级到重量级的都有。不少都是让人眼界大开，亦或是望而生叹的思维杰作。由于库的数量非常庞大，而且限于笔者水平，其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。 
<P>标准库 </P>
<P>标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年，直到标准的出台才正式定型，但是在标准库的实现上却很令人欣慰得看到多种实现，并且已被实践证明为有工业级别强度的佳作。 </P>
<P>1、 Dinkumware C++ Library </P>
<P>参考站点：<A href="http://www.dinkumware.com/">http://www.dinkumware.com</A></P>
<P>P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb's程序设计杰出奖的获得者。其编写的库长期被Microsoft采用，并且最近Borland也取得了其OEM的license，在其C/C+ +的产品中采用Dinkumware的库。 </P>
<P>2、 RogueWave Standard C++ Library </P>
<P>参考站点：<A href="http://www.roguewave.com/">http://www.roguewave.com/</A> </P>
<P>这个库在Borland C++ Builder的早期版本中曾经被采用，后来被其他的库给替换了。笔者不推荐使用。 </P>
<P>3、SGI STL </P>
<P>参考站点：<A href="http://www.roguewave.com/">http://www.roguewave.com/</A><BR>　SGI公司的C++标准模版库。 </P>
<P>4、STLport </P>
<P>参考站点：<A href="http://www.stlport.org/">http://www.stlport.org/</A><BR>　SGI STL库的跨平台可移植版本。 </P>
<P>准标准库——Boost </P>
<P>Boost 库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后备，是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起，在C++社区中影响甚大，其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术，是不折不扣的"准"标准库。 </P>
<P>Boost中比较有名气的有这么几个库： </P>
<P>　Regex <BR>　正则表达式库 </P>
<P>Spirit <BR>　LL parser framework，用C++代码直接表达EBNF </P>
<P>Graph <BR>　图组件和算法 </P>
<P>Lambda <BR>　在调用的地方定义短小匿名的函数对象，很实用的functional功能 </P>
<P>concept check <BR>　检查泛型编程中的concept </P>
<P>Mpl <BR>　用模板实现的元编程框架 </P>
<P>Thread <BR>　可移植的C++多线程库 </P>
<P>Python <BR>　把C++类和函数映射到Python之中 </P>
<P>Pool <BR>　内存池管理 </P>
<P>smart_ptr <BR>　5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章： </P>
<P>Smart Pointers in Boost,哦，这篇文章可以查到，CUJ是提供在线浏览的。中文版见笔者在《Dr. Dobb's Journal软件研发杂志》第7辑上的译文。 </P>
<P>Boost 总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，对标准C++的强调，是编写平台无关，现代C++的开发者必备的工具。但是Boost 中也有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展，其构造用尽精巧的手法，不要贸然的花费时间研读。Boost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的精品代码，并且也可以放心的在产品代码中多多利用。 </P>
<P>参考站点：<A href="http://www.boost.org/">http://www.boost.org</A>（国内镜像：<A href="http://www.c-view.org/tech/lib/boost/index.htm">http://www.c-view.org/tech/lib/boost/index.htm</A>） </P>
<P>GUI </P>
<P>在众多C++的库中，GUI部分的库算是比较繁荣，也比较引人注目的。在实际开发中，GUI库的选择也是非常重要的一件事情，下面我们综述一下可选择的GUI库，各自的特点以及相关工具的支持。 </P>
<P>1、 MFC </P>
<P>大名鼎鼎的微软基础类库（Microsoft Foundation Class）。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲，MFC是不大漂亮的，但是它构建于Windows API 之上，能够使程序员的工作更容易,编程效率高，减少了大量在建立 Windows 程序时必须编写的代码，同时它还提供了所有一般 C++ 编程的优点，例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的，例如，在 Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近发展以及官方支持上日渐势微。 </P>
<P>2、 QT </P>
<P>参考网站：<A href="http://www.trolltech.com/">http://www.trolltech.com</A><BR>Qt 是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。 </P>
<P>3、WxWindows </P>
<P>参考网站：<A href="http://www.wxwindows.org/">http://www.wxwindows.org/</A></P>
<P>跨平台的GUI库。因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的 GUI库，支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。 </P>
<P>4、Fox </P>
<P>开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。 </P>
<P>参考网站：<A href="http://www.fox-toolkit.org/">http://www.fox-toolkit.org/</A> </P>
<P>5、 WTL </P>
<P>基于ATL的一个库。因为使用了大量ATL的轻量级手法，模板等技术，在代码尺寸，以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。 </P>
<P>6、 GTK </P>
<P>参考网站：<A href="http://gtkmm.sourceforge.net/">http://gtkmm.sourceforge.net/</A> </P>
<P>GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而GTK就是这个库的C++封装版本。 </P>
<P>网络通信库</P>
<P>ACE </P>
<P>参考网站：<A href="http://www.cs.wustl.edu/~schmidt/ACE.html">http://www.cs.wustl.edu/~schmidt/ACE.html</A> <BR>　ACE网络编程开发论坛：<A href="http://www.acejoy.com/">http://www.acejoy.com</A></P>
<P>C+ +库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive Communication Environment）是可以自由使用、开放源代码的面向对象框架，在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++ 包装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等等。 </P>
<P>StreamModule </P>
<P>参考网站：<A href="http://www.omnifarious.org/StrMod/">http://www.omnifarious.org/StrMod/</A> </P>
<P>设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易，而不是用同步的外壳包起异步的本质。 </P>
<P>SimpleSocket </P>
<P>参考网站：<A href="http://home.hetnet.nl/~lcbokkers/simsock.htm">http://home.hetnet.nl/~lcbokkers/simsock.htm</A> </P>
<P>这个类库让编写基于socket的客户/服务器程序更加容易。 </P>
<P>A Stream Socket API for C++ </P>
<P>　参考网站：<A href="http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html">http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html</A> </P>
<P>又一个对Socket的封装库。 </P>
<P>XML </P>
<P>Xerces </P>
<P>参考网站：<A href="http://xml.apache.org/xerces-c/">http://xml.apache.org/xerces-c/</A> </P>
<P>Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition，DTD)方面有很好的支持，并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。 </P>
<P>XMLBooster </P>
<P>参考网站：<A href="http://www.xmlbooster.com/">http://www.xmlbooster.com/</A> </P>
<P>这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主XML解析办法之外提供了另外一个可行的解决方案。 </P>
<P>Pull Parser </P>
<P>参考网站：<A href="http://www.extreme.indiana.edu/xgws/xsoap/xpp/">http://www.extreme.indiana.edu/xgws/xsoap/xpp/</A> </P>
<P>这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser，这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。 </P>
<P>Xalan </P>
<P>参考网站：<A href="http://xml.apache.org/xalan-c/">http://xml.apache.org/xalan-c/</A> </P>
<P>Xalan是一个用于把XML文档转换为HTML，纯文本或者其他XML类型文档的XSLT处理器。 </P>
<P>CMarkup </P>
<P>参考网站：<A href="http://www.firstobject.com/xml.htm">http://www.firstobject.com/xml.htm</A> </P>
<P>　这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。 </P>
<P>libxml++ </P>
<P><A href="http://libxmlplusplus.sourceforge.net/">http://libxmlplusplus.sourceforge.net/</A> </P>
<P>libxml++是对著名的libxml XML解析器的C++封装版本 </P>
<P><BR>科学计算 </P>
<P>Blitz++ </P>
<P>参考网站：<A href="http://www.oonumerics.org/blitz/">http://www.oonumerics.org/blitz/</A> </P>
<P>Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具像C++ 一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出的数值程序，比 Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术，程序执行甚至可以比Fortran更快。 Blitz++目前仍在发展中，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以很容易地利用Blitz++所提供的函数来构建。 </P>
<P>POOMA </P>
<P>参考网站：<A href="http://www.codesourcery.com/pooma/pooma">http://www.codesourcery.com/pooma/pooma</A> </P>
<P>POOMA是一个免费的高性能的C++库，用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发，对并行机器进行了优化以达到最高的效率，方便在工业和研究环境中使用。 </P>
<P>MTL </P>
<P>参考网站：<A href="http://www.osl.iu.edu/research/mtl/">http://www.osl.iu.edu/research/mtl/</A> </P>
<P>Matrix Template Library(MTL)是一个高性能的泛型组件库，提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下，比如Intel的编译器，从产生的汇编代码可以看出其与手写几乎没有两样的效能。 </P>
<P>CGAL </P>
<P>　参考网站：<A href="http://www.cgal.org/">www.cgal.org</A> </P>
<P>Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。 </P>
<P>游戏开发 </P>
<P>Audio/Video 3D C++ Programming Library </P>
<P>参考网站：<A href="http://www.galacticasoftware.com/products/av/">http://www.galacticasoftware.com/products/av/</A> </P>
<P>AV3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（SB,以及S3M），控制接口（键盘，鼠标和遥感），XMS。 </P>
<P>KlayGE </P>
<P>参考网站：<A href="http://home.g365.net/enginedev/">http://home.g365.net/enginedev/</A> </P>
<P>国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。 </P>
<P>OGRE </P>
<P>参考网站：<A href="http://www.ogre3d.org/">http://www.ogre3d.org</A> </P>
<P>OGRE （面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了抽象，并提供了基于现实世界对象的接口和其它类。 </P>
<P>线程 </P>
<P>C++ Threads </P>
<P>参考网站：<A href="http://threads.sourceforge.net/">http://threads.sourceforge.net/</A> </P>
<P>这个库的目标是给程序员提供易于使用的类，这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。 </P>
<P>ZThreads </P>
<P>参考网站：<A href="http://zthread.sourceforge.net/">http://zthread.sourceforge.net/</A> </P>
<P>一个先进的面向对象，跨平台的C++线程和同步库。 </P>
<P>序列化 <BR>　s11n <BR>　参考网站：<A href="http://s11n.net/">http://s11n.net/</A> <BR>　一个基于STL的C++库，用于序列化POD，STL容器以及用户定义的类型。 <BR>　Simple XML Persistence Library <BR>　参考网站：<A href="http://sxp.sourceforge.net/">http://sxp.sourceforge.net/</A> <BR>　这是个把对象序列化为XML的轻量级的C++库。 </P>
<P>字符串 </P>
<P>C++ Str Library </P>
<P>参考网站：<A href="http://www.utilitycode.com/str/">http://www.utilitycode.com/str/</A> </P>
<P>操作字符串和字符的库，支持Windows和支持gcc的多种平台。提供高度优化的代码，并且支持多线程环境和Unicode，同时还有正则表达式的支持。 </P>
<P>Common Text Transformation Library </P>
<P>参考网站：<A href="http://cttl.sourceforge.net/">http://cttl.sourceforge.net/</A> </P>
<P>这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较，插入，替换以及用EBNF的语法进行解析。 </P>
<P>GRETA </P>
<P>参考网站：<A href="http://research.microsoft.com/projects/greta/">http://research.microsoft.com/projects/greta/</A> </P>
<P>这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。 </P>
<P>综合 </P>
<P>P::Classes </P>
<P>参考网站：<A href="http://pclasses.com/">http://pclasses.com/</A> </P>
<P>一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制，i/o系统包括基于插件的网络协议透明的i/o架构，基于插件的应用程序消息日志框架，访问sql数据库的类等等。 </P>
<P>ACDK - Artefaktur Component Development Kit </P>
<P>参考网站：<A href="http://acdk.sourceforge.net/">http://acdk.sourceforge.net/</A> </P>
<P>这是一个平台无关的C++组件框架，类似于Java或者.NET中的框架（反射机制，线程，Unicode，废料收集，I/O，网络，实用工具，XML，等等），以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。 </P>
<P>dlib C++ library </P>
<P>参考网站：<A href="http://www.cis.ohio-state.edu/~kingd/dlib/">http://www.cis.ohio-state.edu/~kingd/dlib/</A> </P>
<P>各种各样的类的一个综合。大整数，Socket，线程，GUI，容器类,以及浏览目录的API等等。 </P>
<P>Chilkat C++ Libraries </P>
<P>参考网站：<A href="http://www.chilkatsoft.com/cpp_libraries.asp">http://www.chilkatsoft.com/cpp_libraries.asp</A> </P>
<P>这是提供zip，e-mail，编码，S/MIME，XML等方面的库。 </P>
<P>C++ Portable Types Library (PTypes) </P>
<P>参考网站：<A href="http://www.melikyan.com/ptypes/">http://www.melikyan.com/ptypes/</A> </P>
<P>这是STL的比较简单的替代品，以及可移植的多线程和网络库。 </P>
<P>LFC </P>
<P>参考网站：<A href="http://lfc.sourceforge.net/">http://lfc.sourceforge.net/</A> </P>
<P>哦，这又是一个尝试提供一切的C++库 </P>
<P><BR>其他库 </P>
<P>Loki </P>
<P>参考网站：<A href="http://www.moderncppdesign.com/">http://www.moderncppdesign.com/</A> </P>
<P>哦，你可能抱怨我早该和Boost一起介绍它，一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。 </P>
<P>ATL </P>
<P>ATL(Active Template Library)是一组小巧、高效、灵活的类，这些类为创建可互操作的COM组件提供了基本的设施。 </P>
<P>FC++: The Functional C++ Library </P>
<P>这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大师 Peter Norvig在 "Teach Yourself Programming in Ten Years"一文中就将函数式语言列为至少应当学习的6类编程语言之一。 </P>
<P>FACT! </P>
<P>参考网站：<A href="http://www.kfa-juelich.de/zam/FACT/start/index.html">http://www.kfa-juelich.de/zam/FACT/start/index.html</A> </P>
<P>另外一个实现函数式语言特性的库 </P>
<P>Crypto++ </P>
<P>提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。 </P>
<P>还有很多非常激动人心或者是极其实用的C++库，限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中，由于并不是每一个我们都使用过，所以难免有偏颇之处，请读者见谅。 </P>
<P>资源网站 </P>
<P>正如我们可以通过计算机历史上的重要人物了解计算机史的发展，C++相关人物的网站也可以使我们得到最有价值的参考与借鉴，下面的人物我们认为没有介绍的必要，只因下面的人物在C++领域的地位众所周知，我们只将相关的资源进行罗列以供读者学习，他们有的工作于贝尔实验室，有的工作于知名编译器厂商，有的在不断推进语言的标准化，有的为读者撰写了多部千古奇作...... </P>
<P>Bjarne Stroustrup <A href="http://www.research.att.com/~bs/">http://www.research.att.com/~bs/</A> </P>
<P>Stanley B. Lippman </P>
<P>http: //blogs.msdn.com/slippman/ 中文版 http: //www.zengyihome.net/slippman/index.htm<BR>　Scott Meyers <A href="http://www.aristeia.com/">http://www.aristeia.com/</A> </P>
<P>David Musser <A href="http://www.cs.rpi.edu/~musser/">http://www.cs.rpi.edu/~musser/</A> </P>
<P>Bruce Eckel <A href="http://www.bruceeckel.com/">http://www.bruceeckel.com</A> </P>
<P>Nicolai M. Josuttis <A href="http://www.josuttis.com/">http://www.josuttis.com/</A> </P>
<P>Herb Sutter <A href="http://www.gotw.ca/">http://www.gotw.ca/</A> </P>
<P>Andrei Alexandrescu <A href="http://www.moderncppdesign.com/">http://www.moderncppdesign.com/</A></P>
<P><BR>转<A href="http://www.uml.org.cn/c%2B%2B/200612015.htm">http://www.uml.org.cn/c%2B%2B/200612015.htm</A><BR></P>]]></description>
</item><item>
<title><![CDATA[VC 界面库 皮肤库]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=21453</link>
<author>eaglebetter</author>
<pubDate>2007/1/4 19:48:41</pubDate>
<description><![CDATA[
<P>CJLib(mfc扩展开发包,是xtreme toolkit的前生，但xtreme toolkit收费了)<BR><A href="http://www.codejock.com/">http://www.codejock.com/</A></P>
<P>BCGControlBar(收费，mfc扩展开发包，功能很强大)<BR><A href="http://www.bcgsoft.com/">http://www.bcgsoft.com</A></P>
<P>SkinMagic(收费，看起来像Office)<BR><A href="http://appspeed.com/html/download.html">http://appspeed.com/html/download.html</A></P>
<P>AppFace(收费，支持 MFC ,VCL,ATL , WTL 框架 )<BR><A href="http://www.appface.com/chs/index.htm">http://www.appface.com/chs/index.htm</A></P>
<P>SKin++(收费，界面很好看)<BR><A href="http://www.uipower.com/">http://www.uipower.com/</A></P>
<P>USkin(收费，界面很好看)<BR><A href="http://www.neemedia.com/">http://www.neemedia.com/</A></P>
<P>SYGUI(收费,mfc扩展框架)<BR><A href="http://www.sygui.com/">http://www.sygui.com/</A></P>
<P>LibUIDK(部分免费，不开源，效果好,适合贴图)<BR><A href="http://www.iuishop.com/download.htm">http://www.iuishop.com/download.htm</A></P>
<P>GuiToolkit(开源，mfc扩展框架)<BR><A href="http://www.beyondata.com/default.htm">http://www.beyondata.com/default.htm</A></P>
<P>GardenUI(免费，界面效果挺好的,XML，代码 界面 分离)<BR><A href="http://www.gardenui.com/">http://www.gardenui.com/</A><A href="http://www.iuishop.com/download.htm"></A></P>]]></description>
</item><item>
<title><![CDATA[详细解说STL string]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=20491</link>
<author>eaglebetter</author>
<pubDate>2006/12/3 23:14:50</pubDate>
<description><![CDATA[<EM>0 前言: string 的角色 <BR>1 string 使用 <BR>1.1 充分使用string 操作符 <BR>1.2 眼花缭乱的string find 函数 <BR>1.3 string insert, replace, erase <BR>2 string 和 C风格字符串 <BR>3 string 和 Charactor Traits <BR>4 string 建议 <BR>5 小结 <BR>6 附录 <BR>7 参考文章 <BR><BR></EM><SPAN style="COLOR: green"><STRONG>0 前言: string 的角色 </STRONG></SPAN><BR>C++ 语言是个十分优秀的语言，但优秀并不表示完美。还是有许多人不愿意使用C或者C++，为什么？原因众多，其中之一就是C/C++的文本处理功能太麻烦，用起来很不方便。以前没有接触过其他语言时，每当别人这么说，我总是不屑一顾，认为他们根本就没有领会C++的精华，或者不太懂C++，现在我接触 perl, php, 和Shell脚本以后，开始理解了以前为什么有人说C++文本处理不方便了。 <BR>举例来说，如果文本格式是：用户名 电话号码，文件名name.txt <BR><BR>Tom 23245332<BR>Jenny 22231231<BR>Heny 22183942<BR>Tom 23245332<BR>...现在我们需要对用户名排序，且只输出不同的姓名。 <BR>那么在shell 编程中，可以这样用： <BR><BR>awk '{print $1}' name.txt | sort | uniq 简单吧？ <BR>如果使用C/C++ 就麻烦了，他需要做以下工作： <BR><BR>先打开文件，检测文件是否打开，如果失败，则退出。 <BR>声明一个足够大得二维字符数组或者一个字符指针数组 <BR>读入一行到字符空间 <BR>然后分析一行的结构，找到空格，存入字符数组中。 <BR>关闭文件 <BR>写一个排序函数，或者使用写一个比较函数，使用qsort排序 <BR>遍历数组，比较是否有相同的，如果有，则要删除，copy... <BR>输出信息 <BR>你可以用C++或者C语言去实现这个流程。如果一个人的主要工作就是处理这种类似的文本(例如做apache的日志统计和分析),你说他会喜欢C/C++么？ <BR><BR>当然，有了STL，这些处理会得到很大的简化。我们可以使用 fstream来代替麻烦的fopen fread fclose, 用vector 来代替数组。最重要的是用 string来代替char * 数组，使用sort排序算法来排序，用unique 函数来去重。听起来好像很不错。看看下面代码(例程1）： <BR>
<DIV class=code>#include &lt;string&gt;<BR>#include &lt;iostream&gt;<BR>#include &lt;algorithm&gt;<BR>#include &lt;vector&gt;<BR>#include &lt;fstream&gt;<BR>using namespace std;<BR>int main(){<BR>&nbsp;ifstream in("name.txt");<BR>&nbsp;string strtmp;<BR>&nbsp;vector&lt;string&gt; vect;<BR>&nbsp;while(getline(in, strtmp, '\n'))<BR>&nbsp;vect.push_back(strtmp.substr(0, strtmp.find(' ')));<BR>&nbsp;sort(vect.begin(), vect.end());<BR>&nbsp;vector&lt;string&gt;::iterator it=unique(vect.begin(), vect.end());<BR>&nbsp;copy(vect.begin(), it, ostream_iterator&lt;string&gt;(cout, "\n"));<BR>&nbsp;return 0;<BR>}</DIV>也还不错吧，至少会比想象得要简单得多！（代码里面没有对错误进行处理，只是为了说明问题，不要效仿). <BR>当然，在这个文本格式中，不用vector而使用map会更有扩充性，例如，还可通过人名找电话号码等等，但是使用了map就不那么好用sort了。你可以用map试一试。 <BR><BR>这里string的作用不只是可以存储字符串，还可以提供字符串的比较，查找等。在sort和unique函数中就默认使用了less 和equal_to函数, 上面的一段代码，其实使用了string的以下功能： <BR><BR>存储功能，在getline() 函数中 <BR>查找功能，在find() 函数中 <BR>子串功能，在substr() 函数中 <BR>string operator &lt; , 默认在sort() 函数中调用 <BR>string operator == , 默认在unique() 函数中调用 <BR>总之，有了string 后，C++的字符文本处理功能总算得到了一定补充，加上配合STL其他容器使用，其在文本处理上的功能已经与perl, shell, php的距离缩小很多了。 因此掌握string 会让你的工作事半功倍。 <BR><BR><SPAN style="COLOR: green"><STRONG>1 string 使用</STRONG></SPAN> <BR>其实，string并不是一个单独的容器，只是basic_string 模板类的一个typedef 而已，相对应的还有wstring, 你在string 头文件中你会发现下面的代码: 
<DIV class=code>extern "C++" {<BR>&nbsp;typedef basic_string &lt;char&gt; string;<BR>&nbsp;typedef basic_string &lt;wchar_t&gt; wstring;<BR>} // extern "C++"由于只是解释string的用法，如果没有特殊的说明，本文并不区分string 和 basic_string的区别。</DIV><BR>string 其实相当于一个保存字符的序列容器，因此除了有字符串的一些常用操作以外，还有包含了所有的序列容器的操作。字符串的常用操作包括：增加、删除、修改、查找比较、链接、输入、输出等。详细函数列表参看附录。不要害怕这么多函数，其实有许多是序列容器带有的，平时不一定用的上。 <BR><BR>如果你要想了解所有函数的详细用法，你需要查看basic_string，或者下载STL编程手册。这里通过实例介绍一些常用函数。 <BR><BR><SPAN style="COLOR: green"><STRONG>1.1 充分使用string 操作符</STRONG></SPAN> <BR>string 重载了许多操作符，包括 +, +=, &lt;, =, , [], &lt;&lt;, &gt;&gt;等，正式这些操作符，对字符串操作非常方便。先看看下面这个例子：tt.cpp（例程2） 
<DIV class=code>#include &lt;string&gt;<BR>#include &lt;iostream&gt;<BR>using namespace std;<BR>int main(){<BR>&nbsp;string strinfo="Please input your name:";<BR>&nbsp;cout &lt;&lt; strinfo ;<BR>&nbsp;cin &gt;&gt; strinfo;<BR>&nbsp;if( strinfo == "winter" )<BR>&nbsp;cout &lt;&lt; "you are winter!"&lt;&lt;endl;<BR>&nbsp;else if( strinfo != "wende" )<BR>&nbsp;cout &lt;&lt; "you are not wende!"&lt;&lt;endl;<BR>&nbsp;else if( strinfo &lt; "winter")<BR>&nbsp;cout &lt;&lt; "your name should be ahead of winter"&lt;&lt;endl;<BR>&nbsp;else <BR>&nbsp;cout &lt;&lt; "your name should be after of winter"&lt;&lt;endl;<BR>&nbsp;strinfo += " , Welcome to China!";<BR>&nbsp;cout &lt;&lt; strinfo&lt;&lt;endl;<BR>&nbsp;cout &lt;&lt;"Your name is :"&lt;&lt;endl;<BR>&nbsp;string strtmp = "How are you? " + strinfo;<BR>&nbsp;for(int i = 0 ; i &lt; strtmp.size(); i ++)<BR>&nbsp;cout&lt;&lt;strtmp[i];<BR>&nbsp;return 0;<BR>} </DIV>下面是程序的输出 <BR><BR>-bash-2.05b$ make tt<BR>c++ -O -pipe -march=pentiumpro tt.cpp -o tt<BR>-bash-2.05b$ ./tt<BR>Please input your name:Hero<BR>you are not wende!<BR>Hero , Welcome to China!<BR>How are you? Hero , Welcome to China!有了这些操作符，在STL中仿函数都可以直接使用string作为参数，例如 less, great, equal_to 等，因此在把string作为参数传递的时候，它的使用和int 或者float等已经没有什么区别了。例如，你可以使用： <BR><BR>map&lt;string, int&gt; mymap;<BR>//以上默认使用了 less&lt;string&gt;有了 operator + 以后，你可以直接连加，例如： <BR>string strinfo="Winter";<BR>string strlast="Hello " + strinfo + "!";<BR>//你还可以这样：<BR>string strtest="Hello " + strinfo + " Welcome" + " to China" + " !";看见其中的特点了吗？只要你的等式里面有一个 string 对象，你就可以一直连续"+"，但有一点需要保证的是，在开始的两项中，必须有一项是 string 对象。其原理很简单： <BR>系统遇到"+"号，发现有一项是string 对象。 <BR>系统把另一项转化为一个临时 string 对象。 <BR>执行 operator + 操作，返回新的临时string 对象。 <BR>如果又发现"+"号，继续第一步操作。 <BR>由于这个等式是由左到右开始检测执行，如果开始两项都是const char* ，程序自己并没有定义两个const char* 的加法，编译的时候肯定就有问题了。 <BR>有了操作符以后，assign(), append(), compare(), at()等函数，除非有一些特殊的需求时，一般是用不上。当然at()函数还有一个功能，那就是检查下标是否合法，如果是使用： <BR><BR>string str="winter";<BR>//下面一行有可能会引起程序中断错误<BR>str[100]='!';<BR>//下面会抛出异常:throws: out_of_range<BR>cout&lt;&lt;str.at(100)&lt;&lt;endl;了解了吗？如果你希望效率高，还是使用[]来访问，如果你希望稳定性好，最好使用at()来访问。 <BR>1.2 眼花缭乱的string find 函数 <BR>由于查找是使用最为频繁的功能之一，string 提供了非常丰富的查找函数。其列表如下： 函数名 描述 <BR>find 查找 <BR>rfind 反向查找 <BR>find_first_of 查找包含子串中的任何字符，返回第一个位置 <BR>find_first_not_of 查找不包含子串中的任何字符，返回第一个位置 <BR>find_last_of 查找包含子串中的任何字符，返回最后一个位置 <BR>find_last_not_of 查找不包含子串中的任何字符，返回最后一个位置 <BR>以上函数都是被重载了4次，以下是以find_first_of 函数为例说明他们的参数，其他函数和其参数一样，也就是说总共有24个函数 ： <BR>size_type find_first_of(const basic_string&amp; s, size_type pos = 0)<BR>size_type find_first_of(const charT* s, size_type pos, size_type n)<BR>size_type find_first_of(const charT* s, size_type pos = 0)<BR>size_type find_first_of(charT c, size_type pos = 0)所有的查找函数都返回一个size_type类型，这个返回值一般都是所找到字符串的位置，如果没有找到，则返回<A name=baidusnap0></A><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>。有一点需要特别注意，所有和<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>的比较一定要用string::size_type来使用，不要直接使用int 或者unsigned int等类型。其实<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>表示的是-1, 看看头文件： <BR>template &lt;class _CharT, class _Traits, class _Alloc&gt; <BR>const basic_string&lt;_CharT,_Traits,_Alloc&gt;::size_type <BR>basic_string&lt;_CharT,_Traits,_Alloc&gt;::npos <BR>= basic_string&lt;_CharT,_Traits,_Alloc&gt;::size_type) -1;find 和 rfind 都还比较容易理解，一个是正向匹配，一个是逆向匹配，后面的参数pos都是用来指定起始查找位置。对于find_first_of 和find_last_of 就不是那么好理解。 <BR><BR>find_first_of 是给定一个要查找的字符集，找到这个字符集中任何一个字符所在字符串中第一个位置。或许看一个例子更容易明白。 <BR><BR>有这样一个需求：过滤一行开头和结尾的所有非英文字符。看看用string 如何实现： <BR>
<DIV class=code>#include &lt;string&gt;<BR>#include &lt;iostream&gt;<BR>using namespace std;<BR>int main(){<BR>&nbsp;string strinfo=" //*---Hello Word!......------";<BR>&nbsp;string strset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";<BR>&nbsp;int first = strinfo.find_first_of(strset);<BR>&nbsp;if(first == <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>) { <BR>&nbsp;cout&lt;&lt;"not find any characters"&lt;&lt;endl;<BR>&nbsp;return -1;<BR>&nbsp;} <BR>&nbsp;int last = strinfo.find_last_of(strset);<BR>&nbsp;if(last == <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>) { <BR>&nbsp;cout&lt;&lt;"not find any characters"&lt;&lt;endl;<BR>&nbsp;return -1;<BR>&nbsp;} <BR>&nbsp;cout &lt;&lt; strinfo.substr(first, last - first + 1)&lt;&lt;endl;<BR>&nbsp;return 0;<BR>}</DIV>这里把所有的英文字母大小写作为了需要查找的字符集，先查找第一个英文字母的位置，然后查找最后一个英文字母的位置，然后用substr 来的到中间的一部分，用于输出结果。下面就是其结果： <BR>Hello Word前面的符号和后面的符号都没有了。像这种用法可以用来查找分隔符，从而把一个连续的字符串分割成为几部分，达到 shell 命令中的 awk 的用法。特别是当分隔符有多个的时候，可以一次指定。例如有这样的需求： <BR>张三|3456123, 湖南<BR>李四,4564234| 湖北<BR>王小二, 4433253|北京<BR>...我们需要以 "|" ","为分隔符，同时又要过滤空格，把每行分成相应的字段。可以作为你的一个家庭作业来试试，要求代码简洁。 <BR><BR><SPAN style="COLOR: purple">1.3 string insert, replace, erase </SPAN><BR>了解了string 的操作符，查找函数和substr，其实就已经了解了string的80%的操作了。insert函数, replace函数和erase函数在使用起来相对简单。下面以一个例子来说明其应用。 <BR>string只是提供了按照位置和区间的replace函数，而不能用一个string字串来替换指定string中的另一个字串。这里写一个函数来实现这个功能： <BR><BR>void string_replace(string &amp; strBig, const string &amp; strsrc, const string &amp;strdst) {<BR>string::size_type pos=0;<BR>string::size_type srclen=strsrc.size();<BR>string::size_type dstlen=strdst.size();<BR>while( (pos=strBig.find(strsrc, pos)) != <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>){<BR>strBig.replace(pos, srclen, strdst);<BR>pos += dstlen;<BR>}<BR>}看看如何调用： 
<DIV class=code>#include &lt;string&gt;<BR>#include &lt;iostream&gt;<BR>using namespace std;<BR>int main() {<BR>&nbsp;string strinfo="This is Winter, Winter is a programmer. Do you know Winter?";<BR>&nbsp;cout&lt;&lt;"Orign string is :\n"&lt;&lt;strinfo&lt;&lt;endl;<BR>&nbsp;string_replace(strinfo, "Winter", "wende");<BR>&nbsp;cout&lt;&lt;"After replace Winter with wende, the string is :\n"&lt;&lt;strinfo&lt;&lt;endl;<BR>&nbsp;return 0;<BR>}</DIV>其输出结果： <BR>Orign string is :<BR>This is Winter, Winter is a programmer. Do you know Winter?<BR>After replace Winter with wende, the string is :<BR>This is wende, wende is a programmer. Do you know wende?如果不用replace函数，则可以使用erase和insert来替换，也能实现string_replace函数的功能： <BR>void string_replace(string &amp; strBig, const string &amp; strsrc, const string &amp;strdst) {<BR>string::size_type pos=0;<BR>string::size_type srclen=strsrc.size();<BR>string::size_type dstlen=strdst.size();<BR>while( (pos=strBig.find(strsrc, pos)) != <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">string::npos</STRONG>){<BR>strBig.erase(pos, srclen);<BR>strBig.insert(pos, strdst);<BR>pos += dstlen;<BR>}<BR>}当然，这种方法没有使用replace来得直接。 <BR>2 string 和 C风格字符串 <BR>现在看了这么多例子，发现const char* 可以和string 直接转换，例如我们在上面的例子中，使用 <BR>string_replace(strinfo, "Winter", "wende");来代用 <BR>void string_replace(string &amp; strBig, const string &amp; strsrc, const string &amp;strdst) 在C语言中只有char* 和 const char*，为了使用起来方便，string提供了三个函数满足其要求： <BR>const charT* c_str() const <BR>const charT* data() const <BR>size_type copy(charT* buf, size_type n, size_type pos = 0) const 其中： <BR>c_str 直接返回一个以\0结尾的字符串。 <BR>data 直接以数组方式返回string的内容，其大小为size()的返回值，结尾并没有\0字符。 <BR>copy 把string的内容拷贝到buf空间中。 <BR>你或许会问，c_str()的功能包含data()，那还需要data()函数干什么？看看源码： <BR>const charT* c_str () const<BR>{ if (length () == 0) return ""; terminate (); return data (); }原来c_str()的流程是：先调用terminate()，然后在返回data()。因此如果你对效率要求比较高，而且你的处理又不一定需要以\0的方式结束，你最好选择data()。但是对于一般的C函数中，需要以const char*为输入参数，你就要使用c_str()函数。 <BR>对于 c_str() data()函数，返回的数组都是由string本身拥有，千万不可修改其内容。其原因是许多string实现的时候采用了引用机制，也就是说，有可能几个string使用同一个字符存储空间。而且你不能使用sizeof(string)来查看其大小。详细的解释和实现查看Effective STL的条款15：小心string实现的多样性。 <BR><BR>另外在你的程序中，只在需要时才使用c_str()或者data()得到字符串，每调用一次，下次再使用就会失效，如： <BR><BR>string strinfo("this is Winter");<BR>...<BR>//最好的方式是:<BR>foo(strinfo.c_str());<BR>//也可以这么用:<BR>const char* pstr=strinfo.c_str();<BR>foo(pstr);<BR>//不要再使用了pstr了, 下面的操作已经使pstr无效了。<BR>strinfo += " Hello!";<BR>foo(pstr);//错误！会遇到什么错误？当你幸运的时候pstr可能只是指向"this is Winter Hello!"的字符串，如果不幸运，就会导致程序出现其他问题，总会有一些不可遇见的错误。总之不会是你预期的那个结果。 <BR><BR>3 string 和 Charactor Traits <BR>了解了string的用法，该详细看看string的真相了。前面提到string 只是basic_string的一个typedef。看看basic_string 的参数： <BR>template &lt;class charT, class traits = char_traits&lt;charT&gt;,<BR>class Allocator = allocator&lt;charT&gt; &gt;<BR>class basic_string<BR>{<BR>//...<BR>}char_traits不仅是在basic_string 中有用，在basic_istream 和 basic_ostream中也需要用到。 <BR>就像Steve Donovan在过度使用C++模板中提到的，这些确实有些过头了，要不是系统自己定义了相关的一些属性，而且用了个typedef，否则还真不知道如何使用。 <BR><BR>但复杂总有复杂道理。有了char_traits，你可以定义自己的字符串类型。当然，有了char_traits &lt; char &gt; 和char_traits &lt; wchar_t &gt; 你的需求使用已经足够了，为了更好的理解string ，咱们来看看char_traits都有哪些要求。 <BR><BR>如果你希望使用你自己定义的字符，你必须定义包含下列成员的结构： 表达式 描述 <BR>char_type 字符类型 <BR>int_type int 类型 <BR>pos_type 位置类型 <BR>off_type 表示位置之间距离的类型 <BR>state_type 表示状态的类型 <BR>assign(c1,c2) 把字符c2赋值给c1 <BR>eq(c1,c2) 判断c1,c2 是否相等 <BR>lt(c1,c2) 判断c1是否小于c2 <BR>length(str) 判断str的长度 <BR>compare(s1,s2,n) 比较s1和s2的前n个字符 <BR>copy(s1,s2, n) 把s2的前n个字符拷贝到s1中 <BR>move(s1,s2, n) 把s2中的前n个字符移动到s1中 <BR>assign(s,n,c) 把s中的前n个字符赋值为c <BR>find(s,n,c) 在s的前n个字符内查找c <BR>eof() 返回end-of-file <BR>to_int_type(c) 将c转换成int_type <BR>to_char_type(i) 将i转换成char_type <BR>not_eof(i) 判断i是否为EOF <BR>eq_int_type(i1,i2) 判断i1和i2是否相等 <BR>想看看实际的例子，你可以看看sgi STL的char_traits结构源码. <BR><BR>现在默认的string版本中，并不支持忽略大小写的比较函数和查找函数，如果你想练练手，你可以试试改写一个char_traits , 然后生成一个case_string类, 也可以在string 上做继承，然后派生一个新的类，例如：ext_string，提供一些常用的功能，例如： <BR><BR>定义分隔符。给定分隔符，把string分为几个字段。 <BR>提供替换功能。例如，用winter, 替换字符串中的wende <BR>大小写处理。例如，忽略大小写比较，转换等 <BR>整形转换。例如把"123"字符串转换为123数字。 <BR>这些都是常用的功能，如果你有兴趣可以试试。其实有人已经实现了，看看Extended STL string。如果你想偷懒，下载一个头文件就可以用，有了它确实方便了很多。要是有人能提供一个支持正则表达式的string，我会非常乐意用。 <BR><BR>4 string 建议 <BR>使用string 的方便性就不用再说了，这里要重点强调的是string的安全性。 <BR>string并不是万能的，如果你在一个大工程中需要频繁处理字符串，而且有可能是多线程，那么你一定要慎重(当然，在多线程下你使用任何STL容器都要慎重)。 <BR>string的实现和效率并不一定是你想象的那样，如果你对大量的字符串操作，而且特别关心其效率，那么你有两个选择，首先，你可以看看你使用的STL版本中string实现的源码；另一选择是你自己写一个只提供你需要的功能的类。 <BR>string的c_str()函数是用来得到C语言风格的字符串，其返回的指针不能修改其空间。而且在下一次使用时重新调用获得新的指针。 <BR>string的data()函数返回的字符串指针不会以'\0'结束，千万不可忽视。 <BR>尽量去使用操作符，这样可以让程序更加易懂（特别是那些脚本程序员也可以看懂） <BR>5 小结 <BR>难怪有人说：<BR>string 使用方便功能强，我们一直用它！ <BR><BR>6 附录 <BR>string 函数列表 函数名 描述 <BR>begin 得到指向字符串开头的Iterator <BR>end 得到指向字符串结尾的Iterator <BR>rbegin 得到指向反向字符串开头的Iterator <BR>rend 得到指向反向字符串结尾的Iterator <BR>size 得到字符串的大小 <BR>length 和size函数功能相同 <BR>max_size 字符串可能的最大大小 <BR>capacity 在不重新分配内存的情况下，字符串可能的大小 <BR>empty 判断是否为空 <BR>operator[] 取第几个元素，相当于数组 <BR>c_str 取得C风格的const char* 字符串 <BR>data 取得字符串内容地址 <BR>operator= 赋值操作符 <BR>reserve 预留空间 <BR>swap 交换函数 <BR>insert 插入字符 <BR>append 追加字符 <BR>push_back 追加字符 <BR>operator+= += 操作符 <BR>erase 删除字符串 <BR>clear 清空字符容器中所有内容 <BR>resize 重新分配空间 <BR>assign 和赋值操作符一样 <BR>replace 替代 <BR>copy 字符串到空间 <BR>find 查找 <BR>rfind 反向查找 <BR>find_first_of 查找包含子串中的任何字符，返回第一个位置 <BR>find_first_not_of 查找不包含子串中的任何字符，返回第一个位置 <BR>find_last_of 查找包含子串中的任何字符，返回最后一个位置 <BR>find_last_not_of 查找不包含子串中的任何字符，返回最后一个位置 <BR>substr 得到字串 <BR>compare 比较字符串 <BR>operator+ 字符串链接 <BR>operator== 判断是否相等 <BR>operator!= 判断是否不等于 <BR>operator&lt; 判断是否小于 <BR>operator&gt;&gt; 从输入流中读入字符串 <BR>operator&lt;&lt; 字符串写入输出流 <BR>getline 从输入流中读入一行 <BR><BR>7 参考文章 <BR>SGI STL: char_traits 源码 <BR>STL 编程手册: basic_string <BR>详细解说 STL 排序(Sort) <BR>详细解说 STL hash_map系列 <BR>Effective STL 中文版]]></description>
</item><item>
<title><![CDATA[标准C++的类型转换符：static_cast、dynamic_cast、reinterpret_cast和const_cast]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=20490</link>
<author>eaglebetter</author>
<pubDate>2006/12/3 23:10:30</pubDate>
<description><![CDATA[
<P style="FONT-SIZE: 10pt">C 风格（C-style）强制转型如下： </P>
<P style="FONT-SIZE: 10pt">(T) expression // cast expression to be of type T </P>
<P style="FONT-SIZE: 10pt">函数风格（Function-style）强制转型使用这样的语法： </P>
<P style="FONT-SIZE: 10pt">T(expression) // cast expression to be of type T </P>
<P style="FONT-SIZE: 10pt">&nbsp;</P>
<P style="FONT-SIZE: 10pt">这两种形式之间没有本质上的不同，它纯粹就是一个把括号放在哪的问题。我把这两种形式称为旧风格（old-style）的强制转型。</P>
<P style="FONT-SIZE: 10pt">使用标准C++的类型转换符：<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>、<STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>、reinterpret_cast、和const_cast。</P>
<P style="FONT-SIZE: 10pt">3.1 <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG><BR>用法：<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG> &lt; type-id &gt; ( expression ) <BR>该运算符把expression转换为type-id类型，但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法：<BR>①用于类层次结构中基类和子类之间指针或引用的转换。<BR>　　进行上行转换（把子类的指针或引用转换成基类表示）是安全的；<BR>　　进行下行转换（把基类指针或引用转换成子类表示）时，由于没有动态类型检查，所以是不安全的。<BR>②用于基本数据类型之间的转换，如把int转换成char，把int转换成enum。这种转换的安全性也要开发人员来保证。<BR>③把空指针转换成目标类型的空指针。<BR>④把任何类型的表达式转换成void类型。</P>
<P style="FONT-SIZE: 10pt">注意：<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>不能转换掉expression的const、volitale、或者__unaligned属性。</P>
<P style="FONT-SIZE: 10pt"><BR>3.2 <STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG><BR>用法：<STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG> &lt; type-id &gt; ( expression )<BR>该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *；<BR>如果type-id是类指针类型，那么expression也必须是一个指针，如果type-id是一个引用，那么expression也必须是一个引用。</P>
<P style="FONT-SIZE: 10pt"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>主要用于类层次间的上行转换和下行转换，还可以用于类之间的交叉转换。<BR>在类层次间进行上行转换时，<STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>和<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>的效果是一样的；<BR>在进行下行转换时，<STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>具有类型检查的功能，比<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>更安全。<BR>class B{<BR>public:<BR>int m_iNum;<BR>virtual void foo();<BR>};</P>
<P style="FONT-SIZE: 10pt">class D:public B{<BR>public:<BR>char *m_szName[100];<BR>};</P>
<P style="FONT-SIZE: 10pt">void func(B *pb){<BR>D *pd1 = <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG><D></D>(pb);<BR>D *pd2 = <STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG><D></D>(pb);<BR>}</P>
<P style="FONT-SIZE: 10pt">在上面的代码段中，如果pb指向一个D类型的对象，pd1和pd2是一样的，并且对这两个指针执行D类型的任何操作都是安全的；<BR>但是，如果pb指向的是一个B类型的对象，那么pd1将是一个指向该对象的指针，对它进行D类型的操作将是不安全的（如访问m_szName），<BR>而pd2将是一个空指针。</P>
<P style="FONT-SIZE: 10pt">另外要注意：B要有虚函数，否则会编译出错；<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>则没有这个限制。<BR>这是由于运行时类型检查需要运行时类型信息，而这个信息存储在类的虚函数表（<BR>关于虚函数表的概念，详细可见<INSIDE></INSIDE>）中，只有定义了虚函数的类才有虚函数表，<BR>没有定义虚函数的类是没有虚函数表的。</P>
<P style="FONT-SIZE: 10pt">另外，<STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>还支持交叉转换（cross cast）。如下代码所示。<BR>class A{<BR>public:<BR>int m_iNum;<BR>virtual void f(){}<BR>};</P>
<P style="FONT-SIZE: 10pt">class B:public A{<BR>};</P>
<P style="FONT-SIZE: 10pt">class D:public A{<BR>};</P>
<P style="FONT-SIZE: 10pt">void foo(){<BR>B *pb = new B;<BR>pb-&gt;m_iNum = 100;</P>
<P style="FONT-SIZE: 10pt">D *pd1 = <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG><D></D>(pb); //compile error<BR>D *pd2 = <STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG><D></D>(pb); //pd2 is NULL<BR>delete pb;<BR>}</P>
<P style="FONT-SIZE: 10pt">在函数foo中，使用<STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG>进行转换是不被允许的，将在编译时出错；而使用 <STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG>的转换则是允许的，结果是空指针。</P>
<P style="FONT-SIZE: 10pt"><BR>3.3 reinpreter_cast<BR>用法：reinpreter_cast<TYPE-ID></TYPE-ID> (expression)<BR>type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。<BR>它可以把一个指针转换成一个整数，也可以把一个整数转换成一个指针（先把一个指针转换成一个整数，<BR>在把该整数转换成原类型的指针，还可以得到原先的指针值）。</P>
<P style="FONT-SIZE: 10pt">该运算符的用法比较多。</P>
<P style="FONT-SIZE: 10pt">3.4 const_cast <BR>用法：const_cast<TYPE_ID></TYPE_ID> (expression)<BR>该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外， type_id和expression的类型是一样的。<BR>常量指针被转化成非常量指针，并且仍然指向原来的对象；<BR>常量引用被转换成非常量引用，并且仍然指向原来的对象；常量对象被转换成非常量对象。</P>
<P style="FONT-SIZE: 10pt">Voiatile和const类试。举如下一例：<BR>class B{<BR>public:<BR>int m_iNum;<BR>}<BR>void foo(){<BR>const B b1;<BR>b1.m_iNum = 100; //comile error<BR>B b2 = const_cast<STRONG>(b1);<BR>b2. m_iNum = 200; //fine<BR>}<BR>上面的代码编译时会报错，因为b1是一个常量对象，不能对它进行改变；<BR>使用const_cast把它转换成一个常量对象，就可以对它的数据成员任意改变。注意：b1和b2是两个不同的对象。<BR></STRONG>&nbsp;</P>
<P style="FONT-SIZE: 10pt"><FONT size=2><FONT color=#333333><SPAN>== </SPAN><FONT face=Arial>===========================================</FONT></FONT></FONT></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">== <STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG></STRONG> .vs. <STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></STRONG> </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><FONT size=2><SPAN>== ===========================================</SPAN><BR><BR><SPAN>class B { ... }; </SPAN><BR><SPAN>class D : public B { ... }; </SPAN><BR><BR><SPAN>void f(B* pb) </SPAN><BR><SPAN>{ </SPAN></FONT></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">D* pd1 = <STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG></STRONG>(pb); </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">D* pd2 = <STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></STRONG>(pb); </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><FONT face=Arial><FONT size=2><SPAN>} </SPAN><BR><BR></FONT></FONT></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">If pb really points to an object of type D, then pd1 and pd2 will get the same value. They will also get the same value if pb == 0. </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">If pb points to an object of type B and not to the complete D class, then <STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG> will know enough to return zero. However, <STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG> relies on the programmer’s assertion that pb points to an object of type D and simply returns a pointer to that supposed D object. </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR></SPAN></P>
<P><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">即</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">可用于继承体系中的向下转型，即将基类指针转换为派生类指针，比</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">更严格更安全。</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ff9999">dynamic_cast</STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">在执行效率上比</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">要差一些，但</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">在更宽上范围内可以完成映射，这种不加限制的映射伴随着不安全性。</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">覆盖的变换类型除类层次的静态导航以外，还包括无映射变换、窄化变换</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">(</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">这种变换会导致对象切片</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">,</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">丢失信息</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">)</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">、用</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">VOID*</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">的强制变换、隐式类型变换等</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">... </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR><BR><FONT face=Arial><FONT size=2><SPAN>== ===========================================</SPAN><BR><SPAN>== <STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></STRONG> .vs. <STRONG>reinterpret_cast</STRONG> </SPAN><BR><SPAN>== ================================================</SPAN><BR><BR></FONT></FONT></SPAN></P>
<P><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">reinterpret_cast</SPAN></STRONG></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">是为了映射到一个完全不同类型的意思，这个关键词在我们需要把类型映射回原有类型时用到它。我们映射到的类型仅仅是为了故弄玄虚和其他目的，这是所有映射中最危险的。</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">(</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">这句话是</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">C++</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">编程思想中的原话</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">) </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<P><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">和</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> <FONT face=Arial><FONT size=2><STRONG><SPAN lang=EN-US>reinterpret_cast</SPAN></STRONG><SPAN lang=EN-US> </SPAN></FONT></FONT></SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">操作符修改了操作数类型。它们不是互逆的；</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> <FONT face=Arial><FONT size=2><STRONG><SPAN lang=EN-US><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG><SPAN lang=EN-US> </SPAN></FONT></FONT></SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">在编译时使用类型信息执行转换，在转换执行必要的检测</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">(</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">诸如指针越界计算</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">, </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">类型检查</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">). </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">其操作数相对是安全的。另一方面；</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">reinterpret_cast</SPAN></STRONG></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换，</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">例子如下：</SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">int n=9; double d=<STRONG><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG> </STRONG>&lt; double &gt; (n); </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR></SPAN></P>
<P><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">上面的例子中</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">, </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">我们将一个变量从</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> int </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">转换到</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> double</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">。</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">这些类型的二进制表达式是不同的。</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">要将整数</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> 9 </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">转换到</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">双精度整数</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> 9</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">，</SPAN></SPAN><SPAN><STRONG><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66"><STRONG style="COLOR: black; BACKGROUND-COLOR: #ffff66">static_cast</STRONG></STRONG></SPAN></STRONG></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">需要正确地为双精度整数</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> d </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">补足比特位。其结果为</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> 9.0</SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">。而</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">reinterpret_cast </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">的行为却不同</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">: </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"><BR><BR></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">int n=9; </SPAN></SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">double d=<STRONG>reinterpret_cast</STRONG><DOUBLE></DOUBLE> (n);</SPAN></SPAN></P>
<P><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"></SPAN></SPAN></P>
<P align=left><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">这次</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">, </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">结果有所不同</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">. </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">在进行计算以后</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">, d </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">包含无用值</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">. </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">这是因为</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> <STRONG>reinterpret_cast </STRONG></SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">仅仅是复制</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> n </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">的比特位到</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> d, </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">没有进行必要的分析</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">. <BR><BR></SPAN></SPAN></P>
<P align=left><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">因此</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial">, </SPAN></SPAN><SPAN><SPAN style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: 宋体">你需要谨慎使用</SPAN></SPAN><SPAN><SPAN lang=EN-US style="FONT-SIZE: 10pt; COLOR: #333333; FONT-FAMILY: Arial"> <STRONG>reinterpret_cast</STRONG>.</SPAN></SPAN></P>]]></description>
</item><item>
<title><![CDATA[Linux下各类TCP网络服务器的实现源代码]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=20294</link>
<author>eaglebetter</author>
<pubDate>2006/11/25 10:48:30</pubDate>
<description><![CDATA[
<P>Linux下各类TCP网络服务器的实现源代码</P>
<P>大家都知道各类网络服务器程序的编写步骤，并且都知道网络服务器就两大类：循环服务和并发服务。这里附上源代码来个小结吧。</P>
<P>首先，循环网络服务器编程实现的步骤是这样的：</P>
<P><IMG src="http://zhoulifa.bokee.com/inc/directsocket.png"></P>
<P>这种服务器模型是典型循环服务，如果不加上多进程/线程技术，此种服务吞吐量有限，大家都可以看到，如果前一个连接服务数据没有收发完毕后面的连接没办法处理。所以一般有多进程技术，对一个新连接启用一个新进程去处理，而监听socket继续监听。</P>
<P>/************关于本文档********************************************<BR>*filename: Linux下各类TCP网络服务器的实现源代码<BR>*purpose: 记录Linux下各类tcp服务程序源代码<BR>*wrote by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-04 22:00:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*********************************************************************/</P>
<P>一个循环TCP服务源代码（因为用fork进行多进程服务了，所以这种服务现实中也有用）如下：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码开始--------------------------------------------*/<BR>#include &lt;stdio.h&gt;<BR>#include &lt;stdlib.h&gt;<BR>#include &lt;errno.h&gt;<BR>#include &lt;string.h&gt;<BR>#include &lt;sys/types.h&gt;<BR>#include &lt;netinet/in.h&gt;<BR>#include &lt;sys/socket.h&gt;<BR>#include &lt;sys/wait.h&gt;<BR>/*********************************************************************<BR>*filename: cycletcpserver.c<BR>*purpose: 循环tcp服务端程序<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-04 22:00:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Google.com<BR>*********************************************************************/<BR>int main(int argc, char ** argv)<BR>{<BR>&nbsp;&nbsp;&nbsp; int sockfd,new_fd; /* 监听socket: sock_fd,数据传输socket: new_fd */<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in my_addr; /* 本机地址信息 */<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in their_addr; /* 客户地址信息 */<BR>&nbsp;&nbsp;&nbsp; unsigned int sin_size, myport, lisnum;</P>
<P>&nbsp;&nbsp;&nbsp; if(argv[1])&nbsp; myport = atoi(argv[1]);<BR>&nbsp;&nbsp;&nbsp; else myport = 7838;</P>
<P>&nbsp;&nbsp;&nbsp; if(argv[2])&nbsp; lisnum = atoi(argv[2]);<BR>&nbsp;&nbsp;&nbsp; else lisnum = 2;</P>
<P>&nbsp;&nbsp;&nbsp; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("socket"); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1); <BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; my_addr.sin_family=PF_INET;<BR>&nbsp;&nbsp;&nbsp; my_addr.sin_port=htons(myport);<BR>&nbsp;&nbsp;&nbsp; my_addr.sin_addr.s_addr = INADDR_ANY;<BR>&nbsp;&nbsp;&nbsp; bzero(&amp;(my_addr.sin_zero), 0);<BR>&nbsp;&nbsp;&nbsp; if (bind(sockfd, (struct sockaddr *)&amp;my_addr, sizeof(struct sockaddr)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("bind");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; if (listen(sockfd, lisnum) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("listen");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; while(1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sin_size = sizeof(struct sockaddr_in);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((new_fd = accept(sockfd, (struct sockaddr *)&amp;their_addr, &amp;sin_size)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("accept");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!fork()) { /* 子进程代码段 */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (send(new_fd, "Hello, world!\n", 14, 0) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("send");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(new_fd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(new_fd); /*父进程不再需要该socket*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; waitpid(-1,NULL,WNOHANG);/*等待子进程结束，清除子进程所占用资源*/<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR>/*----------------------源代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE></P>一个测试客户端代码如下：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码开始--------------------------------------------*/<BR>#include &lt;stdio.h&gt;<BR>#include &lt;stdlib.h&gt;<BR>#include &lt;errno.h&gt;<BR>#include &lt;string.h&gt;<BR>#include &lt;netdb.h&gt;<BR>#include &lt;sys/types.h&gt;<BR>#include &lt;netinet/in.h&gt;<BR>#include &lt;sys/socket.h&gt;<BR>#define MAXDATASIZE 100 /*每次最大数据传输量 */<BR>/*********************************************************************<BR>*filename: cycletcpclient.c<BR>*purpose: 循环tcp客户端程序<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-04 22:20:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Google.com<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*********************************************************************/</P>
<P>int main(int argc, char *argv[])<BR>{<BR>&nbsp;&nbsp;&nbsp; int sockfd, numbytes;<BR>&nbsp;&nbsp;&nbsp; char buf[MAXDATASIZE];<BR>&nbsp;&nbsp;&nbsp; struct hostent *he;<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in their_addr;<BR>&nbsp;&nbsp;&nbsp; unsigned int myport;</P>
<P>&nbsp;&nbsp;&nbsp; if(argv[2]) myport = atoi(argv[2]);<BR>&nbsp;&nbsp;&nbsp; else myport = 7838;</P>
<P>&nbsp;&nbsp;&nbsp; if (argc != 3) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr,"usage: %s hostname port\n", argv[0]); <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if((he=gethostbyname(argv[1]))==NULL) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; herror("gethostbyname");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("socket");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; their_addr.sin_family=PF_INET;<BR>&nbsp;&nbsp;&nbsp; their_addr.sin_port=htons(myport);<BR>&nbsp;&nbsp;&nbsp; their_addr.sin_addr = *((struct in_addr *)he-&gt;h_addr);<BR>&nbsp;&nbsp;&nbsp; bzero(&amp;(their_addr.sin_zero),0);<BR>&nbsp;&nbsp;&nbsp; if (connect(sockfd, (struct sockaddr *)&amp;their_addr, sizeof(struct sockaddr)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("connect");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror("recv");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; buf[numbytes] = 0;<BR>&nbsp;&nbsp;&nbsp; printf("Received: %s\n",buf);<BR>&nbsp;&nbsp;&nbsp; close(sockfd);<BR>&nbsp;&nbsp;&nbsp; return 0;<BR>}<BR>/*----------------------源代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE>用gcc cycletcpserver.c -o tcpserver和gcc cycletcpclient.c -o tcpclient分别编译上述代码后运行情况如下：<BR>服务端运行显示：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD><A href="mailto:administrator@ubuzlf:/data/example/c$">administrator@ubuzlf:/data/example/c$</A> ./tcpserver<BR>server: got connection from 127.0.0.1<BR>server: got connection from 127.0.0.1<BR>server: got connection from 127.0.0.1<BR></TD></TR></TBODY></TABLE>客户端运行显示：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P><A href="mailto:administrator@ubuzlf:/data/example/c$">administrator@ubuzlf:/data/example/c$</A> ./tcpclient 127.0.0.1 7838<BR>Received: Hello, world!</P>
<P><A href="mailto:administrator@ubuzlf:/data/example/c$">administrator@ubuzlf:/data/example/c$</A> ./tcpclient 127.0.0.1 7838<BR>Received: Hello, world!</P>
<P><A href="mailto:administrator@ubuzlf:/data/example/c$">administrator@ubuzlf:/data/example/c$</A> ./tcpclient 127.0.0.1 7838<BR>Received: Hello, world!<BR></P></TD></TR></TBODY></TABLE>
<P>不得不说的一个概念性问题：阻塞与非阻塞<BR>在阻塞服务中，当服务器运行到accept语句而没有客户连接服务请求到来，那么会发生什么情况?这时服务器就会停止在accept语句上等待连接服务请求的到来；同样，当程序运行到接收数据语句recv时，如果没有数据可以读取，则程序同样会停止在接收语句上。这种情况称为阻塞(blocking)。<BR>但如果你希望服务器仅仅注意检查是否有客户在等待连接，有就接受连接;否则就继续做其他事情，则可以通过将 socket设置为非阻塞方式来实现:非阻塞socket在没有客户在等待时就使accept调用立即返回 。<BR>通过设置socket为非阻塞方式，可以实现“轮询”若干socket。当企图从一个没有数据等待处理的非阻塞socket读入数据时，函数将立即返回，并且返回值置为-1，并且errno置为EWOULDBLOCK。但是这种“轮询”会使CPU处于忙等待方式，从而降低性能。考虑到这种情况，假设你希望服务器监听连接服务请求的同时从已经建立的连接读取数据，你也许会想到用一个accept语句和多个recv()语句，但是由于accept及recv都是会阻塞的，所以这个想法显然不会成功。<BR>调用非阻塞的socket会大大地浪费系统资源。而调用select()会有效地解决这个问题，它允许你把进程本身挂起来，而同时使系统内核监听所要求的一组文件描述符的任何活动，只要确认在任何被监控的文件描述符上出现活动，select()调用将返回指示该文件描述符已准备好的信息，从而实现了为进程选出随机的变化，而不必由进程本身对输入进行测试而浪费CPU开销。</P>
<P>其次，并发服务器，在上述cycletcpserver.c中，由于使用了fork技术也可以称之为并发服务器，但这种服务器并不是真正意义上的IO多路复用的并发服务器，并且由于没有处理阻塞问题，实际应用有各种各样的问题。</P>
<P>一个典型IO多路复用的单进程并发服务器流程如下：<BR>/*IO多路复用并发服务流程图*/<BR><IMG alt=简单IO复用流程图 src="http://zhoulifa.bokee.com/inc/simpleselect.png" onload="function anonymous()&#13;&#10;{&#13;&#10;function anonymous()&#13;&#10;{&#13;&#10;function anonymous()&#13;&#10;{&#13;&#10; img_auto_size(this,450,true);&#13;&#10;}&#13;&#10;}&#13;&#10;}" align=baseline></P>
<P>下面是一个演示IO多路复用的源程序，是一个端口转发程序，但它的用处相当大，实际应用中的各类代理软件或端口映射软件都是基于这样的代码的，比如Windows下的WinGate、WinProxy等都是在此基础上实现的。源代码如下：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码开始--------------------------------------------*/<BR>#include &lt;stdlib.h&gt;<BR>#include &lt;stdio.h&gt;<BR>#include &lt;unistd.h&gt;<BR>#include &lt;sys/time.h&gt;<BR>#include &lt;sys/types.h&gt;<BR>#include &lt;string.h&gt;<BR>#include &lt;signal.h&gt;<BR>#include &lt;sys/socket.h&gt;<BR>#include &lt;netinet/in.h&gt;<BR>#include &lt;arpa/inet.h&gt;<BR>#include &lt;errno.h&gt;<BR></P>
<P>static int forward_port;</P>
<P>#undef max<BR>#define max(x,y) ((x) &gt; (y) ? (x) : (y))</P>
<P>/*************************关于本文档************************************<BR>*filename: tcpforwardport.c<BR>*purpose: 演示了select的用法，这是一个极好的代理软件核心，专门作端口映射用<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-05 19:00:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*********************************************************************/</P>
<P>static int listen_socket (int listen_port) {<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in a;<BR>&nbsp;&nbsp;&nbsp; int s;<BR>&nbsp;&nbsp;&nbsp; int yes;<BR>&nbsp;&nbsp;&nbsp; if ((s = socket (AF_INET, SOCK_STREAM, 0)) &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("socket");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; yes = 1;<BR>&nbsp;&nbsp;&nbsp; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &amp;yes, sizeof (yes)) &lt;<BR>0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("setsockopt");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; memset (&amp;a, 0, sizeof (a));<BR>&nbsp;&nbsp;&nbsp; a.sin_port = htons (listen_port);<BR>&nbsp;&nbsp;&nbsp; a.sin_family = AF_INET;<BR>&nbsp;&nbsp;&nbsp; if (bind(s, (struct sockaddr *) &amp;a, sizeof (a)) &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("bind");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; printf ("accepting connections on port %d\n", (int) listen_port);<BR>&nbsp;&nbsp;&nbsp; listen (s, 10);<BR>&nbsp;&nbsp;&nbsp; return s;<BR>}</P>
<P>static int connect_socket (int connect_port, char *address) {<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in a;<BR>&nbsp;&nbsp;&nbsp; int s;<BR>&nbsp;&nbsp;&nbsp; if ((s = socket (AF_INET, SOCK_STREAM, 0)) &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("socket");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; memset (&amp;a, 0, sizeof (a));<BR>&nbsp;&nbsp;&nbsp; a.sin_port = htons (connect_port);<BR>&nbsp;&nbsp;&nbsp; a.sin_family = AF_INET;</P>
<P>&nbsp;&nbsp;&nbsp; if (!inet_aton(address, (struct in_addr *) &amp;a.sin_addr.s_addr)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("bad IP address format");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; if (connect(s, (struct sockaddr *) &amp;a, sizeof (a)) &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("connect()");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shutdown (s, SHUT_RDWR);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (s);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; return s;<BR>}</P>
<P>#define SHUT_FD1 { \<BR>&nbsp;&nbsp;&nbsp; if (fd1 &gt;= 0) {&nbsp;&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shutdown (fd1, SHUT_RDWR);&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (fd1);&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd1 = -1;&nbsp;&nbsp;&nbsp;&nbsp; \<BR>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; \<BR>}</P>
<P>#define SHUT_FD2 { \<BR>&nbsp;&nbsp;&nbsp; if (fd2 &gt;= 0) {&nbsp;&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shutdown (fd2, SHUT_RDWR);&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close (fd2);&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd2 = -1;&nbsp;&nbsp;&nbsp;&nbsp; \<BR>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; \<BR>}</P>
<P>#define BUF_SIZE 1024</P>
<P>int main (int argc, char **argv) {<BR>&nbsp;&nbsp;&nbsp; int h;<BR>&nbsp;&nbsp;&nbsp; int fd1 = -1, fd2 = -1;<BR>&nbsp;&nbsp;&nbsp; char buf1[BUF_SIZE], buf2[BUF_SIZE];<BR>&nbsp;&nbsp;&nbsp; int buf1_avail, buf1_written;<BR>&nbsp;&nbsp;&nbsp; int buf2_avail, buf2_written;</P>
<P>&nbsp;&nbsp;&nbsp; if (argc != 4) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf (stderr, "Usage\n\tfwd <LISTEN-PORT />&nbsp; <FORWARD-TO-PORT /><FORWARD-TO-IP-ADDRESS />\n");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (1);<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; signal (SIGPIPE, SIG_IGN);</P>
<P>&nbsp;&nbsp;&nbsp; forward_port = atoi (argv[2]);</P>
<P>&nbsp;&nbsp;&nbsp; /*建立监听socket*/<BR>&nbsp;&nbsp;&nbsp; h = listen_socket (atoi (argv[1]));<BR>&nbsp;&nbsp;&nbsp; if (h &lt; 0)&nbsp;exit (1);</P>
<P>&nbsp;&nbsp;&nbsp; for (;;) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int r, nfds = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd_set rd, wr, er;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;rd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;wr);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;er);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (h, &amp;rd);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*把监听socket和可读socket三个一起放入select的可读句柄列表里*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, h);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0 &amp;&amp; buf1_avail &lt; BUF_SIZE) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd1, &amp;rd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0 &amp;&amp; buf2_avail &lt; BUF_SIZE) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd2, &amp;rd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd2);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*把可写socket两个一起放入select的可写句柄列表里*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0 &amp;&amp; buf2_avail - buf2_written &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd1, &amp;wr);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0 &amp;&amp; buf1_avail - buf1_written &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd2, &amp;wr);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd2);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*把有异常数据的socket两个一起放入select的异常句柄列表里*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd1, &amp;er);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd2, &amp;er);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nfds = max (nfds, fd2);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*开始select*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = select (nfds + 1, &amp;rd, &amp;wr, &amp;er, NULL);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r == -1 &amp;&amp; errno == EINTR) continue;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("select()");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit (1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*处理新连接*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (h, &amp;rd)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int l;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct sockaddr_in client_address;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset (&amp;client_address, 0, l = sizeof (client_address));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = accept (h, (struct sockaddr *)&amp;client_address, &amp;l);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; perror ("accept()");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*关闭原有连接，把新连接作为fd1，同时连接新的目标fd2*/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf1_avail = buf1_written = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf2_avail = buf2_written = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd1 = r;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd2 = connect_socket (forward_port, argv[3]);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &lt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf ("connect from %s\n", inet_ntoa(client_address.sin_addr));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* NB: read oob data before normal reads */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd1, &amp;er)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char c;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errno = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = recv (fd1, &amp;c, 1, MSG_OOB);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send (fd2, &amp;c, 1, MSG_OOB);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd2, &amp;er)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char c;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errno = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = recv (fd2, &amp;c, 1, MSG_OOB);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; send (fd1, &amp;c, 1, MSG_OOB);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* NB: read data from fd1 */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd1, &amp;rd)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = read (fd1, buf1 + buf1_avail, BUF_SIZE - buf1_avail);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf1_avail += r;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* NB: read data from fd2 */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd2, &amp;rd)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = read (fd2, buf2 + buf2_avail, BUF_SIZE - buf2_avail);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf2_avail += r;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* NB: write data to fd1 */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd1, &amp;wr)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = write (fd1, buf2 + buf2_written, buf2_avail - buf2_written);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf2_written += r;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* NB: write data to fd1 */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &gt; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (FD_ISSET (fd2, &amp;wr)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r = write (fd2, buf1 + buf1_written, buf1_avail - buf1_written);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (r &lt; 1) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf1_written += r;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* check if write data has caught read data */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (buf1_written == buf1_avail) buf1_written = buf1_avail = 0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (buf2_written == buf2_avail) buf2_written = buf2_avail = 0;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* one side has closed the connection, keep writing to the other side until empty */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd1 &lt; 0 &amp;&amp; buf1_avail - buf1_written == 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD2;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fd2 &lt; 0 &amp;&amp; buf2_avail - buf2_written == 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SHUT_FD1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; return 0;<BR>}<BR>/*----------------------源代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE></P>用gcc tcpforwardport.c -o MyProxy编译此程序后运行效果如下：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>./MyProxy 8000 80 172.16.100.218<BR>accepting connections on port 8000<BR>connect from 127.0.0.1<BR></TD></TR></TBODY></TABLE>
<P>当有用户访问本机的8000端口时，MyProxy程序将把此请求转发到172.16.100.218主机的80端口，即实现了一个http代理。</P>
<P>关于select函数：<BR>其函数原型为：<BR>int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);<BR>此函数的功能是由内核检测在timeout时间内，是否有readfds，writefds，exceptfds三个句柄集(file descriptors)里的某个句柄（file descriptor)的状态符合寻求，即readfds句柄集里有句柄可读或writefds句柄集里有可写或exceptfds句柄集里有例外发生，任何一个有变化函数就立即返回，返回值为timeout发生状态变化的句柄个数。<BR>n是所有readfds，writefds，exceptfds三个句柄集(file descriptors)里编号最大值加1。比如：要检测两个socket句柄fd1和fd2在timeout时间内是否分别可读和可写就可以这样：<BR>先把两个句柄集(file descriptors)清零：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;readfds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;writefds);<BR>然后把fd1加入读检测集：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd1, &amp;readfds);<BR>然后把fd2加入写检测集：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd2, &amp;writefds);<BR>再给timeout设置值，timeout是这样的一个结构：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct timeval {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long&nbsp;&nbsp;&nbsp; tv_sec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* seconds */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long&nbsp;&nbsp;&nbsp; tv_usec;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* microseconds */<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<BR>你可以这样赋值：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout.tv_sec=1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout.tv_uec=0;<BR>表示检测在1秒钟内是否有句柄状态发生变化。<BR>如果有句柄发生变化，就可以用FD_ISSET检测各个句柄，比如：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ISSET (fd1, &amp;readfds);//检测是否fd1变成可读的了<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ISSET (fd2, &amp;writefds);//检测是否fd2变成可写的了<BR>示意程序代码如下：<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>/*----------------------示意代码开始--------------------------------------------*/<BR>&nbsp;&nbsp;&nbsp; fd1 = socket();//创建一个socket<BR>&nbsp;&nbsp;&nbsp; fd2 = socket();//创建一个socket<BR>&nbsp;&nbsp;&nbsp; while(1)&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;readfds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_ZERO (&amp;writefds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd1, &amp;readfds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET (fd2, &amp;writefds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout.tv_sec=1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout.tv_uec=0;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = select(fd1&gt;fd2?(fd1+1):(fd2+1), &amp;readfds, &amp;writefds, NULL, &amp;timeout);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;{printf("系统错误，select出错，错误代码：%d, 错误信息：%s", errno, strerror(errno));}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(ret == 0)&nbsp;{printf("select超时返回，没有任何句柄状态发生变化！");}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //有句柄状态发生了变化<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(FD_ISSET(fd1, &amp;readfds))&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd1有数据可读;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd1里的数据被读出来;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(FD_ISSET(fd2, &amp;writefds))&nbsp;{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd2可写;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fd2里发送数据给对方;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>/*----------------------示意代码结束--------------------------------------------*/<BR></TD></TR></TBODY></TABLE></P>经常用到的几个自定义函数：<BR>1、开启监听的函数<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码代码开始--------------------------------------------*/<BR>int<BR>OpenSCPServer(int port, int total, int sendbuflen, int recvbuflen, int blockORnot, int reuseORnot)&nbsp;&nbsp;&nbsp; {<BR>/*************************关于本函数************************************<BR>*function_name: OpenSCPServer<BR>*参数说明：port整数型监听端口号，total整数型监听个数，sendbuflen整数型发送缓冲区大小<BR>*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recvbuflen整数型接收缓冲区大小，blockORnot整数型是否阻塞，reuseORnot整数型是否端口重用<BR>*purpose: 用来建立一个tcp服务端socket<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-05 20:00:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含GetCurrentTime.h头文件<BR>*********************************************************************/<BR>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; sockfd = 0, ret = 0, opt = 0, flags=1;<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in&nbsp;&nbsp;&nbsp; laddr;</P>
<P>&nbsp;&nbsp;&nbsp; ret = sockfd = socket(PF_INET, SOCK_STREAM, 0);<BR>&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer socket() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &amp;reuseORnot, sizeof(int));<BR>&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer setsockopt() reuse error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -2;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &amp;recvbuflen, sizeof(int));<BR>&nbsp;&nbsp;&nbsp; if ( ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer setsockopt() recvbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -3;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &amp;sendbuflen, sizeof(int));<BR>&nbsp;&nbsp;&nbsp; if (ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer setsockopt() sendbuf error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -4;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ioctl(sockfd,FIONBIO,&amp;blockORnot);/*block or not*/</P>
<P>&nbsp;&nbsp;&nbsp; laddr.sin_family = PF_INET;<BR>&nbsp;&nbsp;&nbsp; laddr.sin_port = htons(port);<BR>&nbsp;&nbsp;&nbsp; laddr.sin_addr.s_addr = INADDR_ANY;<BR>&nbsp;&nbsp;&nbsp; bzero(&amp;(laddr.sin_zero), 8);</P>
<P>&nbsp;&nbsp;&nbsp; ret = bind(sockfd, (struct sockaddr *)&amp;laddr, sizeof(struct sockaddr));<BR>&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer bind() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sockfd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -5;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; ret = listen(sockfd, total);<BR>&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer listen() error! return:%d, errno=%d, errortext:'%s' %s", ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sockfd);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -6;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "OpenTCPServer opened on port.%d(%d) OK, socket(%d), buf(%d:%d)! %s", port, total, sockfd, sendbuflen, recvbuflen, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp; return sockfd;<BR>}<BR>/*----------------------源代码代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE>2、连接服务器的函数<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码代码开始--------------------------------------------*/<BR>int<BR>ConnectSCPServer(char * serverip, int serverport, int blockORnot)&nbsp;&nbsp;&nbsp; {<BR>/*************************关于本函数************************************<BR>*function_name: ConnectSCPServer<BR>*参数说明：serverip服务器IP地址或主机名，serverport服务器端口，blockORnot整数型是否阻塞<BR>*purpose: 用来建立一个tcp客户端socket<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-05 20:40:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件<BR>*********************************************************************/<BR>&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp; serversock = 0, ret = 0;<BR>&nbsp;&nbsp;&nbsp; unsigned long&nbsp;&nbsp;&nbsp; addr;<BR>&nbsp;&nbsp;&nbsp; struct sockaddr_in&nbsp;&nbsp;&nbsp; sin;<BR>&nbsp;&nbsp;&nbsp; struct hostent *he;</P>
<P>&nbsp;&nbsp;&nbsp; if((he=gethostbyname(serverip))== 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "ConnectSCPServer IP address '%s' error! return:-1 %s", serverip, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; serversock = socket(PF_INET, SOCK_STREAM, 0);<BR>&nbsp;&nbsp;&nbsp; if(serversock == -1)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "ConnectSCPServer socket() error! return:-2, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -2;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ioctl(serversock, FIONBIO, &amp;blockORnot);&nbsp; //block or not</P>
<P>&nbsp;&nbsp;&nbsp; memset((char*)&amp;sin, 0, sizeof(struct sockaddr_in));<BR>&nbsp;&nbsp;&nbsp; sin.sin_family = PF_INET;<BR>&nbsp;&nbsp;&nbsp; sin.sin_port = htons(serverport);<BR>&nbsp;&nbsp;&nbsp; sin.sin_addr = *((struct in_addr *)he-&gt;h_addr);</P>
<P>&nbsp;&nbsp;&nbsp; ret = connect(serversock, (struct sockaddr *)&amp;sin, sizeof(sin));</P>
<P>&nbsp;&nbsp;&nbsp; if(ret == -1)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "ConnectSCPServer connect() error! return:-3, errno=%d, errortext:'%s' %s", errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(serversock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -3;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; return serversock;<BR>}<BR>/*----------------------源代码代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE>3、发送数据函数Send<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------源代码代码开始--------------------------------------------*/<BR>int<BR>Send(int sock, char * buf, size_t size, int flag, int timeout)&nbsp;&nbsp;&nbsp; {<BR>/*************************关于本函数************************************<BR>*function_name: Send<BR>*参数说明：sock整数型socket，buf待发送的内容，size要发送的大小，flag发送选项，timeout超时时间值<BR>*purpose: 用来通过一个socket在指定时间内发送数据<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) 周立发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言<BR>*date time:2006-07-05 20:58:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件<BR>*********************************************************************/<BR>&nbsp;&nbsp;&nbsp; int i = 0, ret = 0, intretry = 0;</P>
<P>&nbsp;&nbsp;&nbsp; struct timeval tival;<BR>&nbsp;&nbsp;&nbsp; fd_set writefds;<BR>&nbsp;&nbsp;&nbsp; int maxfds = 0;</P>
<P>&nbsp;&nbsp;&nbsp; tival.tv_sec = timeout;<BR>&nbsp;&nbsp;&nbsp; tival.tv_usec = 0;</P>
<P>&nbsp;&nbsp;&nbsp; FD_ZERO(&amp;writefds);</P>
<P>&nbsp;&nbsp;&nbsp; if(sock &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET(sock, &amp;writefds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxfds=((sock &gt; maxfds)?sock:maxfds);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Send socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -2;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ret = select(maxfds + 1, NULL, &amp;writefds, NULL, &amp;tival);<BR>&nbsp;&nbsp;&nbsp; if(ret &lt;= 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Send socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else sprintf(errorMessage, "Send socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -3;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if(!(FD_ISSET(sock, &amp;writefds)))&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Send socket:%d not in writefds! %s", sock, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -4;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; while(i &lt; size)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = send(sock, buf + i, size - i, flag);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret &lt;= 0)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Send socket:%d send() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (EINTR == errno)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(intretry &lt; 10)&nbsp; {intretry++;continue;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else sprintf(errorMessage, "Send socket:%d send() error!EINTR 10 times! %s", sock, GetCurrentTime(0, 0));</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else i += ret;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Send socket:%d send() OK! %d/%d bytes sent! %s", sock, i, size, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp; return i;<BR>}<BR>/*----------------------源代码代码结束--------------------------------------------*/<BR></P></TD></TR></TBODY></TABLE>4、接收数据函数Recv<BR>
<TABLE style="WIDTH: 100%" cellSpacing=1 cellPadding=1 align=baseline border=1>
<TBODY>
<TR>
<TD>
<P>/*----------------------<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(0); onmouseover=kwE(event,0); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=源代码','_blank');" onmouseout=kwL(event); target=_blank>源代码</A></NOBR>代码开始--------------------------------------------*/<BR>int<BR>Recv(int sock, char * buf, size_t size, int flag, int timeout)&nbsp;&nbsp;&nbsp; {<BR>/*************************关于本<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(2); onmouseover=kwE(event,2); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=函数','_blank');" onmouseout=kwL(event); target=_blank>函数</A></NOBR>************************************<BR>*function_name: Recv<BR>*参数说明：sock<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(1); onmouseover=kwE(event,1); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=整数型','_blank');" onmouseout=kwL(event); target=_blank>整数型</A></NOBR>socket，buf接收数据的缓冲区，size要接收数据的大小，flag接收选项，timeout超时时间值<BR>*purpose: 用来从一个socket在指定时间内读取数据<BR>*tidied by: zhoulifa(<A href="mailto:zhoulifa@163.com">zhoulifa@163.com</A>) <NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(3); onmouseover=kwE(event,3); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=周立','_blank');" onmouseout=kwL(event); target=_blank>周立</A></NOBR>发(<A href="http://zhoulifa.bokee.com/">http://zhoulifa.bokee.com</A>)<BR>Linux爱好者 Linux知识<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(4); onmouseover=kwE(event,4); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=传播者','_blank');" onmouseout=kwL(event); target=_blank>传播者</A></NOBR> SOHO族 <NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(5); onmouseover=kwE(event,5); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=开发者','_blank');" onmouseout=kwL(event); target=_blank>开发者</A></NOBR> 最擅长C语言<BR>*date time:2006-07-05 21:10:00<BR>*Note: 任何人可以任意复制代码并运用这些文档，当然包括你的商业用途<BR>* 但请遵循GPL<BR>*Thanks to: Paul Sheer 感谢Paul Sheer在select_tut的man手册里提供了这份源代码<BR>*Hope:希望越来越多的人贡献自己的力量，为科学技术发展出力<BR>*Note:要使用此函数需要自定义一个全局变量char errorMessage[1024];并包含自己编写的GetCurrentTime.h头文件<BR>*********************************************************************/<BR>&nbsp;&nbsp;&nbsp; int i = 0, ret = 0, intretry = 0;</P>
<P>&nbsp;&nbsp;&nbsp; struct timeval tival;<BR>&nbsp;&nbsp;&nbsp; fd_set readfds;<BR>&nbsp;&nbsp;&nbsp; int maxfds = 0;</P>
<P>&nbsp;&nbsp;&nbsp; tival.tv_sec = timeout;<BR>&nbsp;&nbsp;&nbsp; tival.tv_usec = 0;</P>
<P>&nbsp;&nbsp;&nbsp; FD_ZERO(&amp;readfds);</P>
<P>&nbsp;&nbsp;&nbsp; if(sock &gt; 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FD_SET(sock, &amp;readfds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxfds=((sock &gt; maxfds)?sock:maxfds);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Recv socket:%d error! return:-2 %s", sock, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -2;<BR>&nbsp;&nbsp;&nbsp; }</P>
<P>&nbsp;&nbsp;&nbsp; ret = select(maxfds + 1, &amp;readfds, NULL, NULL, &amp;tival);<BR>&nbsp;&nbsp;&nbsp; if(ret &lt;= 0) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret &lt; 0)&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Recv socket:%d select() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else sprintf(errorMessage, "Recv socket:%d select timeout(%d)! %s", sock, timeout, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -3;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; if(!(FD_ISSET(sock, &amp;readfds)))&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Recv socket:%d not in readfds! %s", sock, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -4;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; while(i &lt; size)&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = recv(sock, buf + i, size - i, flag);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ret &lt;= 0){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Recv socket:%d recv() error! return:%d, errno=%d, errortext:'%s' %s", sock, ret, errno, strerror(errno), GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(errno == EINTR)&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(intretry &lt; 10)&nbsp; {intretry++;continue;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else sprintf(errorMessage, "Recv socket:%d recv() error! EINTR 10 times! %s", sock, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(sock);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return -1;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else i += ret;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; sprintf(errorMessage, "Recv socket:%d recv() OK! %d/%d bytes received! %s", sock, i, size, GetCurrentTime(0, 0));<BR>&nbsp;&nbsp;&nbsp; return i;<BR>}</P></TD></TR></TBODY></TABLE>最后需要说明的是：我这里讲到的源程序并不能实际地作为一个产品程序来用，实际情况下可能会有其它许多工作要做，比如可能要建立共享队列来存放socket里读到的消息，也可能把发送消息先进行排队然后再调用Send函数。还有，如果不是全数字，在发送前一定要htonl转换为网络字节序，同理接收到后一定要先ntohl由网络字节序转换为主机字节序，否则对方发送过来的0x00000001在你这里可能是0x00010000，因为高低位顺序不同。<BR>]]></description>
</item><item>
<title><![CDATA[Linux入门]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=20233</link>
<author>eaglebetter</author>
<pubDate>2006/11/22 20:49:41</pubDate>
<description><![CDATA[
<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" width="98%" align=center border=0>
<TBODY>
<TR>
<TD vAlign=center align=top>以下内容来自中国Linux公社! <BR>扫盲行动之一：Linux常用命令简介 <BR><BR>su <BR>su命令是最基本的命令之一，常用于不同用户间切换。例如，如果登录为 user1，要切换为user2，只要用如下命令： <BR>$su user2 <BR>然后系统提示输入user2口令，输入正确的口令之后就可以切换到user2。完成之后就可以用exit命令返回到user1。 <BR>su命令的常见用法是变成根用户或超级用户。如果发出不带用户名的su命令 ，则系统提示输入根口令，输入之后则可切换为根用户。 <BR>如果登录为根用户，则可以用su命令成为系统上任何用户而不需要口令。 <BR><BR>pwd <BR>pwd命令也是最常用最基本的命令之一，用于显示用户当前所在的目录。 <BR><BR>cd <BR>cd命令不仅显示当前状态，还改变当前状态，它的用发跟dos下的cd命令基本一致。 <BR>cd ..可进入上一层目录 <BR>cd -可进入上一个进入的目录 <BR>cd ~可进入用户的home目录 <BR><BR>ls <BR>ls命令跟dos下的dir命令一样，用于显示当前目录的内容。 <BR>如果想取得详细的信息，可用ls -l命令， 这样就可以显示目录内容的详细信息。 <BR>如果目录下的文件太多，用一屏显示不了，可以用ls -l |more分屏显示 。 <BR><BR>find <BR>find命令用于查找文件。这个命令可以按文件名、建立或修改日期、所有者(通常是建立文件的用户)、文件长度或文件类型进行搜索。 <BR>find命令的基本结构如下： <BR>$find <BR>其中指定从哪个目录开始搜索。指定搜索条件。表示找到文件怎么处理。一般来说，要用-print动作，显示 整个文件路径和名称。如果没有这个动作，则find命令进行所要搜索而不显示结果，等于白费劲。 <BR>例如，要搜索系统上所有名称为ye的文件，可用如下命令： <BR>$find / -name ye -print <BR>这样就可以显示出系统上所有名称为ye的文件。 <BR><BR>tar <BR>tar最初用于建立磁带备份系统，目前广泛用于建立文件发布档案。可用如下方法建立tar档案： <BR>$tar cvf <BR>例如，如果要将当前目录中所有文件存档到ye.tar中，可用如下命令： <BR>$tar cvf ye.tar *.* <BR>要浏览档案内容，将c选项变成t。如果要浏览ye.tar档案中的内容，可用如下命令： <BR>$tar tvf ye.tar <BR>要取出档案内的内容，将c选项变成x。如果要将ye.tar档案中的内容取到当前目录中，可用如下命令： <BR>$tar xvf ye.tar <BR><BR>gzip <BR>gzip命令用于压缩文件。 例如，如果要将ye.txt文件压缩，可用如下命令： <BR>$gzip ye.txt <BR>这样就可以压缩文件并在文件名后面加上gz扩展名，变成文件ye.txt.gz。 <BR>解压缩文件可用gzip -d命令实现： <BR>$gzip -d ye.txt.gz <BR>这样就可以解压缩文件并删除gz扩展名。除此之外还可以用gunzip命令来解 压缩文件，效果跟用gzip -d命令一样。 <BR>旧版的tar命令不压缩档案，可用gzip压缩。例如： <BR>$tar cvf ye.tar *.txt <BR>$gzip ye.tar <BR>则可建立压缩档案ye.tar.gz。 <BR>新版的tar可以直接访问和建立gzip压缩的tar档案，只要在tar命令中加上z 选项就可以了。例如： <BR>$tar czvf ye.tar *.txt <BR>生成压缩档案ye.tar.gz， <BR>$tar tzvf ye.tar *.txt <BR>显示压缩档案ye.tar.gz的内容，而 <BR>$tar xzvf ye.tar *.txt <BR>取出压缩档案ye.tar.gz的内容。 <BR><BR>mkdir <BR>这个命令很简单，跟dos的md命令用法几乎一样，用于建立目录。 <BR><BR>cp <BR>cp命令用于复制文件或目录。 <BR>cp命令可以一次复制多个文件，例如： <BR>$cp *.txt *.doc *.bak /home <BR>将当前目录中扩展名为txt、doc和bak的文件全部复制到/home目录中。 <BR>如果要复制整个目录及其所有子目录，可以用cp -R命令。 <BR><BR>rm <BR>rm命令用于删除文件或目录。 <BR>rm命令会强制删除文件，如果想要在删除时提示确认，可用rm -i命令。 <BR>如果要删除目录，可用rm -r命令。rm -r命令在删除目录时，每删除一个文件或目录都会显示提示，如果目录太大，响应每个提示是不现实的。这时可以用 rm -rf命令来强制删除目录，这样即使用了-i标志也当无效处理。 <BR><BR>mv <BR>mv命令用于移动文件和更名文件。例如： <BR>$mv ye.txt /home <BR>将当前目录下的ye.txt文件移动到/home目录下， <BR>$mv ye.txt ye1.txt <BR>将ye.txt文件改名为ye1.txt。 <BR>类似于跟cp命令，mv命令也可以一次移动多个文件，在此不再赘叙。 <BR><BR>reboot <BR>重启命令，不必多说。 <BR><BR>halt <BR>关机命令，不必多说。 <BR>
<HR>
</TD></TR><!--printpage.asp##{$bbslist}循环部分-->
<TR>
<TD vAlign=center align=top>--&nbsp;&nbsp;作者：DSICO<BR>--&nbsp;&nbsp;发布时间：2003-8-6 22:59:16<BR><BR>--&nbsp;&nbsp;<BR>扫盲行动之二：轻松安装、卸载Linux软件 <BR><BR>第一篇： <BR><BR>Linux下软件的安装与卸载 <BR><BR>在Windows下安装软件时，只需运行软件的安装程序（setup、install等）或者用zip等解压缩软件解开即可安装，运行反安装程序（uninstall、unware、“卸载”等）就能将软件清除干净，完全图形化的操作界面，简单到只要用鼠标一直点击“下一步”就可以了。而Linux好象就不一样了，很多的初学者都抱怨在Linux下安装和卸载软件非常地困难，没有像使用Windows时那么直观。其实在Linux下安装和卸载软件也非常简单，同样也有安装向导或解压安装的方式，不相同的只不过是除了二进制形式的软件分发外，还有许许多多以源代码形式分发的软件包，下面就来详细地讲一讲这些软件的安装与卸载： <BR><BR>一、二进制分发软件包的安装与卸载 <BR>Linux软件的二进制分发是指事先已经编译好二进制形式的软件包的发布形式，其优点是安装使用容易，缺点则是缺乏灵活性，如果该软件包是为特定的硬件/操作系统平台编译的，那它就不能在另外的平台或环境下正确执行。 <BR>1、*.rpm形式的二进制软件包 <BR>安装：rpm -ivh *.rpm <BR>卸载：rpm -e packgename <BR>说明：RPM（RedHat Packge Manager）是RedHat公司出的软件包管理器，使用它可以很容易地对rpm形式的软件包进行安装、升级、卸载、验证、查询等操作，安装简单，而卸载时也可以将软件安装在多处目录中的文件删除干净，因此推荐初学者尽可能使用rpm形式的软件包。rpm的参数中-i是安装，-v是校验，-h是用散列符显示安装进度，*.rpm是软件包的文件名（这里的*.rpm特指*.src.rpm以外的以rpm为后缀的文件）；参数-e是删除软件包，packgename是软件包名，与软件包的文件名有所区别，它往往是文件名中位于版本号前面的字符串，例如apache-3.1.12-i386.rpm和apache-devel-3.1.12-i386.rpm是软件包文件名，它们的软件包名称分别是apache和apache-devel。更多的rpm参数请自行参看手册页：man rpm。 <BR>如果你不喜欢在字符界面下安装或卸载这些软件包，完全可以在X-Window下使用图形界面的软件包管理程序，如glint、xrpm这样的图形接口，或者是KDE的kpackge等，这样对软件包的安装、升级、卸载、验证和查询就可以通过点击鼠标来轻松完成。 <BR>2、*.tar.gz/*.tgz、*.bz2形式的二进制软件包 <BR>安装：tar zxvf *.tar.gz 或 tar yxvf *.bz2 <BR>卸载：手动删除 <BR>说明：*.tar.gz/*.bz2形式的二进制软件包是用tar工具来打包、用gzip/bzip2压缩的，安装时直接解包即可。对于解压后只有单一目录的软件，卸载时用命令“rm -rf 软件目录名”；如果解压后文件分散在多处目录中，则必须一一手动删除（稍麻烦），想知道解压时向系统中安装了哪些文件，可以用命令“tar ztvf *.tar.gz”/“tar ytvf *.bz2”获取清单。tar的参数z是调用gzip解压，x是解包，v是校验，f是显示结果，y是调用bzip2解压，t是列出包的文件清单。更多的参数请参看手册页：man tar。 <BR>如果你更喜欢图形界面的操作，可以在X-Window下使用KDE的ArK压缩档案管理工具。 <BR>3、提供安装程序的软件包 <BR>这类软件包已经提供了安装脚本或二进制的安装向导程序（setup、install、install.sh等），只需运行它就可以完成软件的安装；而卸载时也相应地提供了反安装的脚本或程序。例如SUN公司的StarOffice办公软件套件就使用名为setup的安装程序，而且在软件安装后提供反安装的功能，目前这种类型的软件包还比较少，因其安装与卸载的方式与Windows软件一样，所以就无需多讲了。 <BR><BR>二、源代码分发软件包的安装与卸载 <BR>Linux软件的源代码分发是指提供了该软件所有程序源代码的发布形式，需要用户自己编译成可执行的二进制代码并进行安装，其优点是配置灵活，可以随意去掉或保留某些功能/模块，适应多种硬件/操作系统平台及编译环境，缺点是难度较大，一般不适合初学者使用。 <BR>1、*.src.rpm形式的源代码软件包 <BR>安装：rpm -rebuild *.src.rpm <BR>cd /usr/src/dist/RPMS <BR>rpm -ivh *.rpm <BR>卸载：rpm -e packgename <BR>说明：rpm --rebuild *.src.rpm命令将源代码编译并在/usr/src/dist/RPMS下生成二进制的rpm包，然后再安装该二进制包即可。packgename如前所述。 <BR>2、*.tar.gz/*.tgz、*.bz2形式的源代码软件包 <BR>安装：tar zxvf *.tar.gz 或 tar yxvf *.bz2 先解压 <BR>然后进入解压后的目录： <BR>./configure 配置 <BR>make 编译 <BR>make install 安装 <BR>卸载：make uninstall 或 手动删除 <BR>说明：建议解压后先阅读说明文件，可以了解安装有哪些需求，有必要时还需改动编译配置。有些软件包的源代码在编译安装后可以用make install命令来进行卸载，如果不提供此功能，则软件的卸载必须手动删除。由于软件可能将文件分散地安装在系统的多个目录中，往往很难把它删除干净，那你应该在编译前进行配置，指定软件将要安装到目标路径：./configure --prefix=目录名，这样可以使用“rm -rf 软件目录名”命令来进行干净彻底的卸载。与其它安装方式相比，需要用户自己编译安装是最难的，它适合于使用Linux已有一定经验的人，一般不推荐初学者使用。 <BR><BR>关于Linux下软件的安装与卸载lanche已经讲了这么多，但可能还会有人问怎么知道一个tar.gz/bz2包是二进制文件包呢还是源代码包？如果你用过压缩工具就会明白，压缩包未必就是软件，它也可能是备份的许多图片，也可能是打包在一起的普通资料，要分辨它到底是什么最好的办法就是查看包里的文件清单，使用命令tar ztvf *.tar.gz / tar ytvf *.bz2或者在X-Window下使用图形化的ArK压缩档案管理工具都可以，源代码包里的文件往往会含有种种源代码文件，头文件*.h、c代码源文件*.c、C++代码源文件*.cc/*.cpp等；而二进制包里的文件则会有可执行文件（与软件同名的往往是主执行文件），标志是其所在路径含有名为bin的目录（仅有少数例外）。原来这么简单呀，还不快点自己试试！ <BR><BR>第二篇： <BR><BR>如何安装Linux的软件 <BR><BR>在windows下安装软件大家都觉得很容易，只要双击setup或是install的图标，然后跟着向导一步一步的按提示做就可以了，但是在linux下安装软件就不像windows下那样容易了，有时你找到的是没有编译过的软件源码，那就更加的麻烦了，这里就介绍一下如何安装linux的软件啦！ <BR>linux下的软件一般都是经过压缩的，主要的格式有这几种：rpm、tar、tar.gz、tgz等。所以首先拿到软件后第一件事就是解压缩。 <BR>一、在xwindow下以rpm格式的软件安装比较容易，只要在把鼠标移到文件上单击右键，在弹出的菜单里会有专门的三项（只有在右键单击rpm文件才会出现）show info，upgrade和install，这三项的意思大家都很清楚了，我就不多说了。rpm格式说了，接着就是tar，tar.gz，tgz等，在xwindow下双击这些格式的文件就会自动解压缩的，是解压缩而不是像rpm那样有install选项的，install文件会在你解压缩后才出现，不过这里我要先说一下，在你双击install前建议先好好看一下readme，因为你所要安装这些软件可能是没有编译的，所以看一下会比较好。 <BR>二、说完了xwindow下的安装和解压缩，接着说在文本模式下如何解压缩和安装，还有编译源码了。首先说rpm的，以root身份登陆后（用其他的身份登陆也可以但有些软件就不能被安装）键入rpm －i你所要解压缩的文件＋扩展名（当然是rpm）也不是很难吧，这里我要说的是“－i”意思是install，如果你想要卸载或是要执行其他的指令就打rpm －－help，看电脑给你的提示吧，为了照顾那些E文不太好的人我这里就写几个常用的参数：－e：卸载相关的应用程序，－U（注意是大写）：升级软件包，－pql：列出rpm软件包的相关信息，－qf：查找指定文件属于哪个软件包。至于那些其他格式的软件则可以用gunzip，gzip，tar和unzip等指令来解压缩，然后再运行install。通过解包后会得到一些文件，然后进入刚才解压缩后文件目录用“ls －F －color”指令看一下所得到的文件，一般有两种情况：第一种是文件为已经编译好的程序（无须再编译了）用上面的察看文件命令会以带“＊”标记的亮绿色显示；第二种则是需要由你自己编译的源代码。得到的是源代码的话，就需要我们自己编译来得到可运行的程序。编译源代码听到编译是不是就吓到你了呢，其实说穿了也就加上几句话而已，对初学者是有些困难想用好的操作系统就需要学习嘛，等你学会了在MM前露一手的时候想想吧……，在编译之前先要认真的阅读一下readme文档，老鸟们就不需要了吧，等你阅读完了以后就执行 <BR>./configure <BR>make <BR>make install（只有Root身份才能执行此命令），编译完成之后便会在当前目录或src子目录下得到软件的可执行程序。 <BR>介绍完了，大家是不是觉的RPM的软件安装比较容易一点呢，但我个人意见初学者还是多选择RPM格式以外的软件，因为通过编译软件的源码可以为你以后编译内核打下一定的基础。（上海 张国荣） <BR><BR>第三篇： <BR><BR>轻松安装、卸载Linux软件 <BR><BR>Linux软件的安装和卸载一直是困扰许多新用户的难题。在Windows中，我们可以使用软件自带的安装卸载程序或在控制面板中的“添加/删除程序”来实现。与其相类似，在Linux下有一个功能强大的软件安装卸载工具，名为RPM。它可以用来建立、安装、查询、更新、卸载软件。该工具是在命令行下使用的。在Shell的提示符后输入rpm，就可获得该命令的帮助信息。 <BR><BR>软件的安装 <BR>Linux下软件的安装主要有两种不同的形式。第一种安装文件名为xxx.tar.gz；另一种安装文件名为xxx.i386.rpm。以第一种方式发行的软件多为以源码形式发送的；第二种方式则是直接以二进制形式发送的。 <BR><BR>对于第一种，安装方法如下： <BR><BR>1.首先，将安装文件拷贝至你的目录中。例如，如果你是以root身份登录上的，就将软件拷贝至/root中。 <BR><BR>#cp xxx.tar.gz /root <BR><BR>2.由于该文件是被压缩并打包的,应对其解压缩。命令为： <BR><BR>#tar xvzf filename.tar.gz <BR><BR>3.执行该命令后，安装文件按路径，解压缩在当前目录下。用ls命令可以看到解压缩后的文件。通常在解压缩后产生的文件中，有“Install”的文件。该文件为纯文本文件，详细讲述了该软件包的安装方法。 <BR><BR>4.执行解压缩后产生的一个名为configure的可执行脚本程序。它是用于检查系统是否有编译时所需的库，以及库的版本是否满足编译的需要等安装所需要的系统信息。为随后的编译工作做准备。命令为：#./configure。 <BR><BR>5.检查通过后，将生成用于编译的MakeFile文件。此时，可以开始进行编译了。编译的过程视软件的规模和计算机性能的不同，所耗费的时间也不同。命令为：#make。 <BR><BR>6.成功编译后，键入如下的命令开始安装： <BR><BR>#make install <BR><BR>7.安装完毕，应清除编译过程中产生的临时文件和配置过程中产生的文件。键入如下命令： <BR><BR>#make clean <BR>#make distclean <BR><BR>至此，软件的安装结束。 <BR><BR>对于第二种，其安装方法要简单得多。 <BR><BR>同第一种方式一样，将安装文件拷贝至你的目录中。然后使用rpm来安装该文件。命令如下： <BR><BR>#rpm -i filename.i386.rpm <BR><BR>rpm将自动将安装文件解包，并将软件安装到缺省的目录下。并将软件的安装信息注册到rpm的数据库中。参数i的作用是使rpm进入安装模式。 <BR><BR>软件的卸载 <BR>1.软件的卸载主要是使用rpm来进行的。卸载软件首先要知道软件包在系统中注册的名称。键入命令： <BR><BR>#rpm -q -a <BR><BR>即可查询到当前系统中安装的所有的软件包。 <BR><BR>2.确定了要卸载的软件的名称，就可以开始实际卸载该软件了。键入如下命令即可卸载软件： <BR><BR>#rpm -e [package name] <BR><BR>参数e的作用是使rpm进入卸载模式。对名为[package name]的软件包进行卸载。由于系统中各个软件包之间相互有依赖关系。如果因存在依赖关系而不能卸载，rpm将给予提示并停止卸载。你可以使用如下的命令来忽略依赖关系，直接开始卸载： <BR><BR>#rpm -e [package name] -nodeps <BR><BR>忽略依赖关系的卸载可能会导致系统中其它的一些软件无法使用。 <BR><BR>补充： <BR><BR>如果是以.bin结尾的二进制软件，可以用以下方法安装（以so-6_0-beta-bin-linux-zh-Tw1.bin为例）： <BR><BR>在so-6_0-beta-bin-linux-zh-Tw1.bin所在文件夹下运行模拟终端 <BR>输入： <BR>./so-6_0-beta-bin-linux-zh-Tw1.bin <BR>并按回车！ <BR><BR>当然也可以输入： <BR>./so 再用[Tab]键补全 <BR><BR>（./表示当前目录，如果终端不在该软件所在目录下打开，则在软件名前尚需输入相应的路径。） <BR><BR>如果在图形界面，也可直接单击（或双击，视具体的鼠标设置而定）进行安装！ <BR>
<HR>
</TD></TR><!--printpage.asp##{$bbslist}循环部分-->
<TR>
<TD vAlign=center align=top>--&nbsp;&nbsp;作者：DSICO<BR>--&nbsp;&nbsp;发布时间：2003-8-6 22:59:56<BR><BR>--&nbsp;&nbsp;<BR>扫盲行动之三：LINUX的目录树 <BR>/bin bin是binary的缩写。这个目录是对UNIX系统习惯的沿袭，存放着使用者最经常使用的命令。例如：cp,ls,cat。 <BR>/boot 这里存放的是启动LINUX时使用的一些核心文件。 <BR>/dev dev是device(设备)的缩写。这个目录下是所有LINUX的外部设备，其功能类似DOS下的.sys和Win下的.vxd。在LINUX中设备和文件是用同种方法访问的。例如:/dev/hda代表第一个物理IDE硬盘。 <BR>/etc 这个目录用来存放所有的系统管理所需要的配置文件和子目录。 <BR>/home 用户的主目录，比如说有个用户叫wang，那他的主目录就是/home/wang也可以用~wang表示，说到这里，打个岔，你现应该明白在我们访问一些个人网页(如http://www.netease.net/~zhangjia)的时候，~zhangjia就是表示访问www.netease.net站点中的用户zhangjia的用户主目录。 <BR>如果这个网站的操作系统是LINUX，那就是表示/home/zhangjia。 <BR>/lib 这个目录里存放着系统最基本的动态链接共享库，其作用类似于Windows里的.dll文件。几乎所有的应用程序都需要用到这些共享库。 <BR>/lost+found 这个目录平时是空的，当系统不正常关机后，这里就成了一些无家可归的文件的避难所。对了，有点类似于DOS下的.chk文件。 <BR>/mnt 这个目录是空的，系统提供这个目录是让用户临时挂载别的文件系统。 <BR>/proc 这个目录是一个虚拟的目录，它是系统内存的映射，我们可以通过直接访问这个目录来获取系统信息。也就是说，这个目录的内容不在硬盘上而是在内存里啊！？ <BR>/root 系统管理员，也叫作超级权限者的用户主目录。当然系统的拥有者，总要有些特权啊！ <BR>/sbin s就是Super User的意思，也就是说这里存放的是一些系统管理员使用的系统管理程序。 <BR>/tmp 这个目录不用说，一定是用来存放一些临时文件的地方了。 <BR>/usr 这是个最庞大的目录，我们要用到的很多应用程序和文件几乎都存放在这个目录下。具体来说： <BR>/usr/X11R6 存放X-Windows的目录； <BR>/usr/bin 存放着许多应用程序； <BR>/usr/sbin 给超级用户使用的一些管理程序就放在这； <BR>/usr/doc 这就是LINUX文档的大本营； <BR>/usr/include LINUX下开发和编译应用程序需要的头文件，在这找； <BR>/usr/lib 存放一些常用的动态链接共享库和静态档案库； <BR>/usr/local 这是提供给一般用户的/usr目录，在这安装软件最适合； <BR>/usr/man man是什么，对了是帮助。这里就是帮助文档目录啊！ <BR>/usr/src LINUX开放的源代码，就存在这个目录，爱好者们别放过哦！ <BR>/var 这个目录中存放着那些不断在扩充着的东西，为了保持/usr的相对稳定，那些经常被修改的目录可以放在这个目录下，实际上许多系统管理员都是这样干的。顺带说一下系统的日志文件就在/var/log目录中。 <BR>这里列出了最常见的目录，根据LINUX发行套件的不同，目录结构也是有一定差别的。还有你自己建立的目录，我当然不知是存什么的。希望上面的文字能使你在大脑里留下了LINUX的全景图，以便大家深入掌握LINUX的其它方方面面。 <BR>
<HR>
</TD></TR><!--printpage.asp##{$bbslist}循环部分-->
<TR>
<TD vAlign=center align=top>--&nbsp;&nbsp;作者：DSICO<BR>--&nbsp;&nbsp;发布时间：2003-8-6 23:00:30<BR><BR>--&nbsp;&nbsp;<BR>扫盲行动之四：GRUB三步通 <BR><BR>朋友，你装好linux后是不是每次启动后系统就默认进入到linux，想让它变成windows吗？请修改你的引导装入器吧（当然它不止这个功能）！由于现在的linux都喜欢用GRUB来引导，LILO已越来越少用了，所以我在这里主要给大家讲讲GRUB这个东东！！ <BR><BR>################## <BR># GRUB的优点 # <BR>################## <BR>GRUB 是引导装入器(boot loader) -- 它负责装入内核并引导 Linux 系统。GRUB 还可以引导其它操作系统，如 FreeBSD、NetBSD、OpenBSD、GNU HURD 和 DOS，以及 Windows 95、98、NT 和 2000。尽管引导操作系统看上去是件平凡且琐碎的任务，但它实际上很重要。如果引导装入器不能很好地完成工作或者不具有弹性，那么就可能锁住系统，而无法引导计算机。另外，好的引导装入器可以给您灵活性，让您可以在计算机上安装多个操作系统，而不必处理不必要的麻烦。 <BR>GRUB 是一个很棒的boot loader。它有许多功能，可以使引导过程变得非常可靠。例如，它可以直接从 FAT、minix、FFS、ext2 或 ReiserFS 分区读取 Linux 内核。这就意味着无论怎样它总能找到内核。另外，GRUB 有一个特殊的交互式控制台方式，可以让您手工装入内核并选择引导分区。这个功能是无价的：假设 GRUB 菜单配置不正确，但仍可以引导系统。哦，对了 -- GRUB 还有一个彩色引导菜单。 <BR><BR>更令人惊讶的是，这是一个自由软件！！！ <BR><BR>################## <BR># GRUB菜单 # <BR>################## <BR>先来看一个例子，这是位于/boot/grub/目录下的menu.lst文件。 <BR>此文件将在开机是产生一个菜单，包含有Debian linux,Windows2000,RedHat linux和 Mandrake linux,共四个选择项。我一共分了8个区，一个fat16（0x6），一个ntfs（0x7），三个ext2fs分区（0x83），一个swap分区（0x82）。ntfs用来装win2000，三个ext2fs装了三个linux，c盘fat16分区没有装任何东西。 <BR>＃例子由此开始 <BR><BR>＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃ <BR>＃ ＃ <BR>＃ 一个GRUB configure 的例子 ＃ <BR>＃ ＃ <BR>＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃＃ <BR><BR>timeout 10 <BR>default 2 <BR><BR># --&gt; Debian linux &lt;-- <BR><BR>title Debian linux <BR>root (hd0,2) <BR>kernel /boot/vmlinuz-2.2.18 root=/dev/hda3 ro <BR>initrd /boot/initrd-2.2.18.gz <BR><BR># --&gt; Debian END &lt;-- <BR><BR># --&gt; Windows 菜单选项 &lt;-- <BR><BR>title Windows2000 <BR>root (hd0,0) <BR>chainloader +1 <BR><BR># --&gt; Winddows 结束 &lt;-- <BR><BR># --&gt; RedHat linux 菜单选项 &lt;-- <BR><BR>title RedHat linux <BR>root (hd0,8) <BR>chainloader +1 # 在硬盘主引导分区装了lilo，所以也用了chainloader。 <BR><BR># --&gt; RedHat linux 结束 &lt;-- <BR><BR># --&gt; Mandrake linux 菜单选项 &lt;-- <BR><BR>title Mandrake linux <BR>root (hd0,5) <BR>kernel /boot/vmlinuz-2.4.3-20mdk root=/dev/hda6 ro <BR>initrd /boot/initrd-2.4.3-20mdk.img <BR><BR># --&gt; Mandrake linux 结束 &lt;-- <BR><BR>＃例子到此结束 <BR><BR>以符号井＂＃＂开头的行表示被注释掉，没有任何意义。 <BR><BR>timeout表示默认等待的时间，这儿是10秒钟。超过10秒，用户还没有作出选择的话，系统将自动选择默认的操作系统。 <BR><BR>默认的操作系统就是由default控制的。default后加一个数字n，表明是第n＋1个。需要注意的是，GRUB中，计数是从0开始的，第一个硬盘是hd0，第一个软驱是fd0，等等。所以，default 2 表示默认的操作系统在这儿是 Redhat linux。 <BR><BR>接下来，正如你所想象的，title表示的是“Debian linux”菜单项。root (hd0,2)表示第一个硬盘,第三个分区。这儿的root 于linux的root分区及其不同，此root非彼root也！ <BR><BR>在 Linux 中，当谈到 "root" 文件系统时，通常是指主 Linux 分区。但是，GRUB 有它自己的 root 分区定义。GRUB 的 root 分区是保存 Linux 内核的分区。这可能是您的正式 root 文件系统，也可能不是。我们讨论的是 GRUB，需要指定 GRUB 的 root 分区。进入 root 分区时，GRUB 将把这个分区安装成只读型，这样就可以从该分区中装入 Linux 内核。GRUB 的一个很“酷”的功能是它可以读取本机的 FAT、FFS、minix、ext2 和 ReiserFS 分区。 <BR><BR>到目前为止，您可能会感到一点疑惑，因为 GRUB 所使用的硬盘／分区命名约定与 Linux 使用的命名约定不同。在 Linux 中，第一个硬盘的第五个分区称作 "hda5"。而 GRUB 把这个分区称作 "(hd0,4)"。GRUB 对硬盘和分区的编号都是从 0 开始计算。另外，硬盘和分区都用逗号分隔，整个表达式用括号括起。现在，可以发现如果要引导 Linux 硬盘 hda5，应输入 "root (hd0,4)"。 <BR><BR>知道了内核在哪儿，还要具体指出哪个文件是内核文件，这就是kernel的工作。 <BR>kernel /boot/vmlinuz-2.2.18 root=/dev/hda3 ro说明/boot/vmlinuz-2.2.18 就是要载入的内核。后面的都是传递给内核的参数。root=/dev/hda3就是linux的硬盘分区表示法，ro是以readonly的意思。 <BR>initrd用来初始的linux image，并设置相应的参数。 <BR><BR>是不是感觉很简单啊！再来看一看windows的定义段吧。 <BR>这里，我添加了一项来引导 Windows2000。要完成此操作，GRUB 使用了“链式装入器”(chainloader)。链式装入器从分区 (hd0,0) 的引导记录中装入 win2000 自己的引导装入器，然后引导它。这就是这种技术叫做链式装入的原因 -- 它创建了一个从引导装入器到另一个的链。这种链式装入技术可以用于引导任何版本的 DOS 或 Windows。 <BR><BR>我的RedHat linux在硬盘主引导分区装了lilo，所以也用了chainloader。 <BR><BR>GRUB的配置文件要简单就这么简单，如果你要更个性化一点，试一试把“color light-gray/blue ”加在default语句的下面，下一次启动GRUB时，看看有什么变化，再试一试“color light-blue/red",惊喜吗？ 有趣吧! <BR><BR>###################### <BR># GRUB的交互性 # <BR>###################### <BR><BR>GRUB 最好的优点之一就是其强健的设计 -- 在不断使用它时请别忘了这点。如果更新内核或更改它在磁盘上的位置，不必重新安装 GRUB。事实上，如有必要，只要更新 menu.lst 文件即可，一切将保持正常。 <BR><BR>只有少数情况下，才需要将 GRUB 引导装入器重新安装到引导记录。首先，如果更改 GRUB root 分区的分区类型（例如，从 ext2 改成 ReiserFS），则需要重新安装。或者，如果更新 /boot/grub 中的 stage1 和 stage2 文件，由于它们来自更新版本的 GRUB，很有可能要重新安装引导装入器。其它情况下，可以不必理睬！ <BR><BR>GRUB的最大的特点就是交互性特别强。在开机时，按一下“c”，将进入GRUB 控制台。显示如下： <BR><BR>GRUB version 0.5.96.1 (640K lower / 3072K upper memory) <BR><BR>[ Minimal BASH-like line editing is supported. For the first word, TAB <BR>lists possible command completions. Anywhere else TAB lists the possible <BR>completions of a device/filename. ] <BR><BR>grub&gt; <BR><BR>欢迎使用 GRUB 控制台。现在，再研究命令： <BR>我将通过GRUB 控制台绕过lilo来启动RedHat linux， <BR><BR>grub&gt; root (h <BR><BR>现在，按一次 Tab 键。如果系统中有多个硬盘，GRUB 将显示可能完成的列表，从 "hd0" 开始。如果只有一个硬盘，GRUB 将插入 "hd0,"。如果有多个硬盘，继续进行，在 ("hd2") 中输入名称并在名称后紧跟着输入逗号，但不要按 Enter 键。部分完成的 root 命令看起来如下： <BR><BR>grub&gt; root (hd0, <BR><BR>现在，继续操作，再按一次 Tab 键。GRUB 将显示特定硬盘上所有分区的列表，以及它们的文件系统类型。在我的系统中，按 Tab 键时得到以下列表： <BR><BR>grub&gt; root (hd0, (tab，按tab一下键) <BR>Possible partitions are: <BR>Partition num: 0, Filesystem type is fat, partition type 0x6 <BR>Partition num: 2, Filesystem type is ext2fs, partition type 0x83 <BR>Partition num: 4, Filesystem type unknown, partition type 0x7 <BR>Partition num: 5, Filesystem type is ext2fs, partition type 0x83 <BR>Partition num: 6, Filesystem type is fat, partition type 0xb <BR>Partition num: 7, Filesystem type is fat, partition type 0xb <BR>Partition num: 8, Filesystem type is ext2fs, partition type 0x83 <BR>Partition num: 9, Filesystem type unknown, partition type 0x82 <BR><BR>如您所见，GRUB 的交互式硬盘和分区名称实现功能非常有条理。这些，只需要好好理解 GRUB 新奇的硬盘和分区命名语法，然后就可以继续操作了 <BR>grub&gt; root (hd0,8) <BR>现在已安装了 root 文件系统，到装入内核的时候了 <BR><BR>grub&gt; kernel /boot/vmlinuz-2.4.2 root=/dev/hda5 ro <BR>[Linux-bzImage, setup=0x1200, size=0xe1a30] <BR><BR>您已经安装了 root 文件系统并装入了内核。现在，可以引导了。只要输入 "boot"，Linux 引导过程就将开始。是不是很cool啊，GRUB的menu.lst更像一个linux下的脚本程序。 <BR><BR>##################### <BR># GRUB启动盘 # <BR>##################### <BR>要制作引导盘，需执行一些简单的步骤。首先，在新的软盘上创建 ext2 文件系统。然后，将其安装，并将一些 GRUB 文件复制到该文件系统，最后运行 "grub" 程序，它将负责设置软盘的引导扇区。准备好了吗？ <BR><BR>将一张空盘插入 1.44MB 软驱，输入： <BR><BR># mke2fs /dev/fd0 <BR>创建了 ext2 文件系统后，需要安装该文件系统： <BR><BR># mount /dev/fd0 /mnt/floppy <BR>现在，需要创建一些目录，并将一些关键文件（原先安装 GRUB 时已安装了这些文件）复制到软盘： <BR><BR># mkdir /mnt/floppy/boot <BR># mkdir /mnt/floppy/boot/grub <BR># cp /boot/grub/stage1 /mnt/floppy/boot/grub <BR># cp /boot/grub/stage2 /mnt/floppy/boot/grub <BR>再有一个步骤，就能得到可用的引导盘。 <BR><BR>在linux bash中，从 root 用户运行“grub”，该程序非常有趣并值得注意，因为它实际上是 GRUB 引导装入器的半功能性版本。尽管 Linux 已经启动并正在运行，您仍可以运行 GRUB 并执行某些任务，而且其界面与使用 GRUB 引导盘或将 GRUB 安装到硬盘 MBR 时看到的界面(即GRUB控制台)完全相同。 <BR>在 grub&gt; 提示符处，输入： <BR><BR>grub&gt; root (fd0) <BR>grub&gt; setup (fd0) <BR>grub&gt; quit <BR><BR>现在，引导盘完成了。 <BR>如果要把GRUB装到硬盘上，也很容易。这个过程几乎与引导盘安装过程一样。首先，需要决定哪个硬盘分区将成为 root GRUB 分区。在这个分区上，创建 /boot/grub 目录，并将 stage1 和 stage2 文件复制到该目录中，可以通过重新引导系统并使用引导盘，或者使用驻留版本的 GRUB 来执行后一步操作。在这两种情况下，启动 GRUB，并用 root 命令指定 root 分区。例如，如果将 stage1 和 stage2 文件复制到 hda5 的 /boot/grub 目录中，应输入 "root (hd0,4)"。接着，决定在哪里安装 GRUB -- 在硬盘的 MBR，或者如果与 GRUB 一起使用另一个“主”引导装入器，则安装在特定分区的引导记录中。如果安装到 MBR，则可以指定整个磁盘而不必指定分区，如下（对于 hda）： <BR><BR>grub&gt; setup (hd0) <BR><BR>如果要将 GRUB 安装到 /dev/hda5 的引导记录中，应输入： <BR><BR>grub&gt; setup (hd0,4) <BR><BR>现在，已安装 GRUB。引导系统时，应该立即以 GRUB 的控制台方式结束（如果安装到 MBR）。现在，应创建引导菜单，这样就不必在每次引导系统时都输入那些命令。 <BR><BR>小结：在这里只是介绍了 GRUB 的一部分。例如，可以使用 GRUB 来执行网络引导，引导 BSD 文件系统，或更多操作。另外，GRUB 有许多配置和安全性命令也很有用。如需所有 GRUB 功能的完整描述，请阅读 GRUB 出色的 GNU 文档。只要在 bash 提示中输入 "info grub" 就可以阅读该文档 <BR><BR>
<HR>
</TD></TR><!--printpage.asp##{$bbslist}循环部分-->
<TR>
<TD vAlign=center align=top>--&nbsp;&nbsp;作者：DSICO<BR>--&nbsp;&nbsp;发布时间：2003-8-6 23:01:14<BR><BR>--&nbsp;&nbsp;<BR>扫盲行动之五：在Linux中共享Windows系统资源 <BR>在一部电脑上装有Linux和Windows的用户，可能需要在Linux中用到包括软盘、光盘以及Windows分区中的文件，如当你在Windows操作系统中上网，却下载了Linux的应用软件的时候。下面我就向大家详细介绍这方面的知识和具体的应用。 <BR>　　一、装载（mount） <BR>　　（一）准备知识 <BR>　　在Linux系统中想要使用软驱的话，要先把它们装载到系统中，装载指令mount的格式如下： <BR>　　mount －t 文件系统类型 设备文件名 装载目录 <BR>　　1．文件系统类型 <BR>　　文件系统类型一般来说就是分区格式，依操作系统的不同而不同。下面将Linux系统支持的文件系统类型择要分列如表一： <BR>　　2．设备文件名 <BR>　　在Linux系统中，各个驱动器设备的命名和Windows中的规则（如A：、C：等）有很大的区别。所有的系统硬件设备都可以在/dev目录下找到对应的设备文件名。例如/dev/mouse就用来表示系统中的鼠标。磁盘驱动器的各个不同分区所对应的设备文件名列表如表二： <BR>　　假设第一个IDE硬盘驱动器被分成数个分区，通常，第一个分区可以肯定其设备文件名为/dev/hda1，但其它分区的设备文件名就不是可以依次类推得到的（特别是当用户使用了诸如PTM之类的磁盘分区工具的时候）。如果你想要知道硬盘各分区所对应的设备文件名，可以在控制台下执行cfdisk的指令，则各分区对应的设备文件名就可以一目了然了。 <BR>　　3．装载目录 <BR>　　通常我们都会在/mnt目录下面为需要装载的磁盘驱动器创建一个目录，不过这并不意味着它们不可以被装载于其它未被使用的目录中。再者，所谓装载目录，并不是将被装载的磁盘驱动器整个复制到本地，而仅仅是在本地提供一个装载点用以联系其它需要装载的磁盘驱动器。 <BR>　　Linux系统对字符的大小写是敏感的，但是在Windows中就不是这样。而当你装载一个Windows系统中的驱动器后，对其上的文件操作时，字符的大小写就变得不敏感了，这意味着用户不可以在装载的Windows驱动器的同一目录中建立这么两个文件：ABC.TXT和abc.txt，因为在Windows系统中，它们代表同一个文件。 <BR>　　（二）装载实例 <BR>　　1．装载软盘 <BR>　　首先在/mnt目录下为软磁盘创建一个目录floppy（有时Linux系统在安装的时候已经为你做了这一步）： <BR>　　mkdir /mnt/floppy <BR>　　接着用装载指令将软盘中的内容装载到这一目录中： <BR>　　mount －t msdos /dev/fd0 /mnt/floppy <BR>　　此后你就可以在/mnt/floppy下完全访问到软磁盘中的内容了。当然你亦可以用vfat这一文件系统类型代替msdos以使你能正确访问到软盘上的长文件名字，或者是用ext2代替它以使你可以访问到Linux文件格式的软盘。 <BR>　　2．装载FAT 32格式的C盘 <BR>　　在/mnt目录下为之创建一个目录winc（winc以及其他的装载目录是笔者的假设，你可以用自己喜欢的其他合法字符去命名这些目录）： <BR>　　mkdir /mnt/winc <BR>　　用装载指令将C盘内容装载入该目录： <BR>　　mount －t vfat /dev/hda1 /mnt/winc <BR>　　3．装载光盘 <BR>　　在/mnt目录下为你的光盘驱动器创建一个目录cdrom（有的Linux系统安装时已为你完成了这件事）： <BR>　　mkdir /mnt/cdrom <BR>　　如果你的光盘驱动器安装在primary slave上，设备文件名就是/dev/hdb；如果安装在secondary master上，设备文件名就是/dev/hdc。假设你的光盘驱动器挂在secondary master，就用下面的装载指令： <BR>　　mount －t iso9600 /dev/hdc /mnt/cdrom <BR>　　由于Linux版本的不同，你所用的系统或者会使用这样的指令来装载光盘： <BR>　　mount /dev/cdrom <BR>　　或mount /mnt/cdrom <BR>　　二、卸载（umount） <BR>　　如果你已经用指令将软盘装载进相应目录，就不要直接将它们从软驱中取出来，否则可能会导致信息丢失；已经装载的光盘驱动器更是会令面板上的弹出键暂时失效以阻止你将光盘直接取出。 <BR>　　在你取出它们之前，首先要确定已经没有用户对它们进行访问操作，包括没有工作窗口处在该磁盘驱动器被装载的目录。卸载指令的格式如下： <BR>　　umount 卸载目录名 <BR>　　如用户想要卸载软盘，可以用如下指令： <BR>　　umount /mnt/floppy <BR>　　三、进阶使用 <BR>　　（一）为装载指令增加简捷的形式 <BR>　　用户可以在系统的配置文件/etc/fstab中指定一些常用的需要装载的驱动器，以便用更简捷的指令来装载它们，下面给出一个添加Windows系统的C盘进配置文件的例子：用文件编辑工具vi（也可以用其它你熟悉的文件编辑工具）打开/etc/fstab，我们会看到系统已经为根目录“/”、软磁盘驱动器、光盘驱动器等指定了文件系统类型和设备文件名以及装载目录（可能会因为所用的Linux版本不同，被指定的驱动器会有出入，不过用户可以参照下面的例子做出改动）。你既可以改变它们的预设值，也可以在其中添加新的驱动器。用箭头将光标移到最后一行，将下面一行加入文件： <BR>　　/dev/hda1 /mnt/winc vfat defaults 0 0 <BR>　　如果还想装载硬盘内的其它分区，可以依次加入。完成后保存文件并退出。不要忘记相应的创建目录/mnt/winc。此后你就可以用如下指令装载Windows系统的C盘了： <BR>　　mount /mnt/winc <BR>　　（二）启动时自动装载磁盘驱动器 <BR>　　用户可能会希望系统在启动时可以将你惯用的磁盘驱动器（如硬盘中的Windows分区或者是光盘驱动器）自动装载，以减轻每次启动后都要手动装载的麻烦。你可以用以下方法实现自动装载的功能： <BR>　　用文件编辑工具打开/etc/fstab，将刚才我们加入的那一行中的defaults改成auto，如： <BR>　　/dev/hda1 /mnt/winc vfat auto 0 0 <BR>　　对于其它想在启动时就装载的磁盘驱动器，都可以将defaults改成auto。需要注意的是，对于硬盘上的Linux分区（包括ext2和swap）并不需要作出这样的改动。 <BR>　　重新启动系统以后，在/mnt/winc目录下，你就可以看到系统在启动时就装载好的Windows系统的C盘了。 <BR>　　四、使用Mtools <BR>　　在Linux中想要共享Windows系统资源，除了上面介绍的装载（mount）命令之外，还有一组名为Mtools的软件包可以实现这一功能。和大多数实用工具一样，Mtools软件包亦是Linux系统默认的安装套件。你可以在http://mtools.ltnb.lu/下载到它的最新版本mtools－3.9.7.tar.gz。 <BR>　　（一）Mtools命令 <BR>　　对于熟悉DOS命令的用户来说，Mtools命令会让他们感觉到像回到了DOS的世界。在下面的表格中我们可以看出它们有多么相似： <BR>　　很显然，Mtools命令是简单地在DOS命令前加上了一个m前缀，功能还是和在DOS下一样的。须要注意的是，更改目录路径在DOS命令中可以有cd和chdir两种命令格式，但是在Mtools中只有mcd一种格式。类似的在Mtools中没有对应的命令还有mkdir、rmdir、rename等等。 <BR>　　（二）mtools.conf文档的配置 <BR>　　因为Mtools是一个仿真DOS命令的软件包，所以它也保存着DOS系统下的磁盘驱动器概念。Mtools不但可以实现A盘 、B盘、 C盘的概念，对于一些特殊驱动器（如ZIP驱动器）更有专门的命令（mzip）以让用户方便地进行操作。在默认的环境下，A盘和B盘分别对应着两个软盘驱动器（如果有的话），N盘则对应着DOSMU启动盘的镜像文件。 <BR>　　我们可以通过修改/etc/mtools.conf文档来改变默认的配置，当然在做出修改之前你仍要对Linux下的设备文件名有相当的认识才行（请参考上一期本版有关设备及其文件名的介绍）。假设你的机器有一个1.44MB软驱（A），硬盘挂在第一个IDE接口的主位置上，在第一个分区（C盘）中安装有Windows操作系统，光驱挂在第二个IDE接口的主位置上（D盘），以及SCSI盘等，则可以对mtools.conf文档简单配置如下： <BR>　　mtools.conf <BR>　　drive a: file=″/dev/fd0″ exclusive 1.44m <BR>　　drive c: file=″/dev/hda1″ <BR>　　drive d: file=″/dev/hdc″ <BR>　　drive X: file=″/dev/rdsk/c0t5d0s2″ partition=4 scsi=1 nodelay <BR>　　（三）Mtools命令的使用 <BR>　　Mtools的命令系统和DOS有很多相似之处，比如在命令后面可以加不同的参数以实现更为丰富的功能（Mtools命令的参数使用方法是在命令后面加“－&lt;参数&gt;”，你可以把它想象成用“－”代替了DOS命令使用的“/”），可以使用通配符“?”和“＊”。更为方便的是，使用Mtools不需要事先装载（mount）和事后卸载（umount）。 <BR>　　下面我们以目录列表命令mdir为例作说明(想知道其它命令的使用格式和更多的可用参数可以用man 命令查看相关帮助系统)。mdir的功能是对DOS目录和其中的文档进行列表操作。命令格式如下： <BR>　　mdir [－/] [－f] [－w] [－a] [－X] msdosfile [msdosfile] <BR>　　各命令参数的功能分述如下： <BR>　　/：输出当前路径下的所有目录和文档，相当于DOS命令dir中的“s”参数； <BR>　　f：列表时不尝试计算当前分区的自由空间，对于大硬盘来说，这样做可以节省一些读入和扫描分区表（FAT）的时间； <BR>　　w：列表时在一行中显示多个目录文档，这种输出格式将不显示文档的大小和创建时间，相当于DOS命令dir中的“w”参数； <BR>　　a：列出隐藏的目录文档； <BR>　　X：简要列表， 列出路径名而不列出其它附加的信息。 <BR>　　一些具体的应用实例： <BR>　　1．列表A盘下的所有目录文档（不包括隐藏的目录文档） <BR>　　mdir －/ a: <BR>　　2．列表C盘Windows目录下含有“abc”的目录文档 <BR>　　mdir c:/windows/＊abc＊ <BR>　　或者mdir c:\\windows\\＊Abc＊ <BR>　　注：在Mtools命令中，/和\\是可以混用的。又因为列表的是DOS系统下的文档，对大小写并不敏感，所以“abc”和“Abc”在这里是等价的。而通配符“＊”的用法和DOS命令亦有所不同。 <BR>　　五、在Windows系统中共享Linux系统的文档 <BR>　　无论对于刚接触Linux的新手还是对之已经熟悉的高手，恐怕都免不了想在Windows系统中调阅Linux系统的文档。这可以使用在Windows系统下观看Linux分区的小软件（如fsdext2等）。这里再向各位推荐两种可以实现这种功能的小软件。 <BR>　　(一)Linuxindos <BR>　　Linuxindos是一个不到500KB的小程序，笔者找到的版本是Beta 0.9的DEMO版，用户可以在http://best.163.com/～linux/soft/下载试用。程序不需安装就可以在Windows系统下运行，使用也非常简单。 <BR>　　在Windows系统下将linuxindos.zip解压到硬盘目录，用户就会看到一个带着企鹅图标的可执行程序LiD95Demo.exe。启动程序后，会出现一个接口非常简单的窗口，当用户挑选一个含有Linux系统文档的磁盘驱动器以后，Linux分区中的所有文档就会显示出来，左边窗口列出的是Linux分区中的目录，而右窗口就列出了目录内的文档，感觉就像在Windows的资源管理器中一样。 <BR>　　(二）Explore2fs <BR>　　这是一个更为小巧的自由软件，大小约308KB，用户也可以在http://best.163.com/～linux/soft/下载使用。正如它的名字一样，这是一个有着类似资源管理器窗口的程序，通过它你可以读取Linux系统下的分区。和Linuxindos一样，Explore2fs的主视窗亦分为左右两个，左边显示的是Linux中的目录，而右边显示的就是目录中的文档了，可以简单地将右边视窗中的文档直接拖到Windows系统分区中。 <BR>　　在右边视窗中选择好文档后，按下鼠标右键可以选择相应的操作，“Export file”是直接导出文档，而“Export to Text”则是除去文档的分隔符后导出和TEXT兼容的文本文档。　　 <BR>
<HR>
</TD></TR><!--printpage.asp##{$bbslist}循环部分-->
<TR>
<TD vAlign=center align=top>--&nbsp;&nbsp;作者：DSICO<BR>--&nbsp;&nbsp;发布时间：2003-8-6 23:01:37<BR><BR>--&nbsp;&nbsp;<BR>扫盲行动之六：Linux下其它格式文件系统的自动挂装 <BR>不同的操作系统使用不同的文件系统格式，MS-DOS支持FAT16文件系统，Windows98支持FAT16、FAT32文件系统，WindowsNT支持FAT16、NTFS文件系统，Windows2000则支持FAT16、FAT32、NTFS三种文件系统格式，而Linux差不多支持所有的文件系统格式，但一般使用ext2文件系统。 <BR>　　对于普通的PC用户，使用的大多是Microsoft的windows98操作系统，如果想同时使用Linux操作系统的话，一般使用多重启动。这时，用户可能希望在Linux下访问Windows文件，比如原来Windows下的mp3、电影等等。通常，有多种方法实现Linux下对其它文件系统的访问，下面就以在Linux下对Windows98文件系统的访问为例进行介绍： <BR>　　（一）在安装Linux时进行设置。由于Linux和Windows98两种操作系统使用不同的文件格式，所以在Windows98下安装Linux必须为Linux建立单独的分区，安装过程中有设置分区的步骤，此时，不但可以建立Linux分区，还可以对原FAT文件系统进行挂装，因为Linux把磁盘设备也看成是特殊的文件，这时如果为Windows98操作系统的逻辑分区C、D分别设立如/DOSC、/DOSD的挂装点，那么在安装完Linux后就可以在/DOSC、/DOSD目录下访问原来C、D分区上的文件了。 <BR>　　（二）如果在安装时没有为Windows98分区设立挂装点，也可以在安装完成后进入Linux运行mount命令（必须以root身份登录）挂装，mount命令的具体格式是： <BR>mount〔-afFhnrvVm〕〔-l&lt;标签&gt;〕〔-o&lt;选项&gt;〕〔-t&lt;文件系统&gt;〕〔设备名称〕〔挂入点〕 <BR>比如，用户的Windows98在系统IDE设备上的第一个分区上，如果要将它挂装在/DOSC下，可以使用下面的命令： <BR>mount -t vfat /dev/hda1 /dosc <BR>使用mount挂装设备时会记录信息在/etc/mtab文件中，运行unmount命令时将记录清除。 <BR>　　（三）手动修改/etc/fstab文件。/etc/fstab文件的内容被用来在Linux 下自动挂装各种文件系统，文件中的每一行都提供了一种设备的信息，这种设备可以被挂装在Linux文件系统下的一个目录中。在Linux启动过程中，init进程执行一个脚本文件，该脚本调用带有-a参数的mount命令，用mount来读/etc/fstab，并挂装所有列出的文件系统（带noauto选项的除外）。下面是一个典型的/etc/fstab文件(其中Windows98安装在第一块硬盘上，Linux安装在第二块硬盘上)： <BR>　　ABEL=/ / ext2 defaults 1 1 <BR>　　/dev/hda1 /dosc vfat defaults 0 0 <BR>　　/dev/fd0 /mnt/floppy auto noauto,owner 0 0 <BR>　　none /proc proc defaults 0 0 <BR>　　none /devpts devpts sid=5,mode=620 0 0 <BR>　　/dev/hdb5 swap swap defaults 0 0 <BR>　　/dev/cdrom /mnt/cdrom iso9660 noauto,owner,kud2ure 0/0 <BR>　　/etc/fstab文件的第一列是设备名，第二列是挂装点，第三列表示在设备上的文件系统的类型，第四列是应用于特定设备的一组选项，通常为defaults，表示的含义有：这个设备在引导阶段被挂装、只有root用户可以挂装它、挂装后可以进行读或写操作，此选项如是noauto，则表示引导时该设备不会被自动挂装，而user选项表示任何用户都可以挂装该设备。以上面的etc/fstab文件为例，如果想在Linux下挂装Windows98操作系统的D分区(假设挂装点为/dosd)，则可以在上文件中加入一行： <BR>　　/dev/hda5 /dosd vfat defaults 0 0 <BR>　　然后存盘，重新启动后就可以访问Windows98操作系统的D分区了。 <BR><BR>补充：fstab 栏位说明 <BR>第一栏(fs_spec)： 实际的 device 名称 <BR>第二栏(fs_file)： 对应到的目录结构(mount point) <BR>第三栏(fs_vfstype)：该 partition 的档案系统，常见的有： <BR>minix、ext、ext2、msdos、iso9660、nfs、swap <BR>第四栏(fs_mntops)： 在 mount 时的参数 <BR>第五栏(fs_freq)： 在使用 dump 时是否记录，不需要则输入0 <BR>第六栏(fs_passno)： 决定在开机时执行 fsck 的先后顺序 <BR></TD></TR></TBODY></TABLE>]]></description>
</item><item>
<title><![CDATA[P2P之UDP穿透NAT的原理与实现 - 增强篇(附源代码)]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=20006</link>
<author>eaglebetter</author>
<pubDate>2006/11/13 14:57:34</pubDate>
<description><![CDATA[
<P>关于UDP穿透NAT的中文资料在网络上是很少的，仅有&lt;&lt;P2P之UDP穿透NAT的原理与实现(shootingstars)&gt;&gt;这篇文章有实际的参考价值。本人近两年来也一直从事P2P方面的开发工作，比较有代表性的是个人开发的BitTorrent下载软件 - FlashBT(变态快车). 对P2P下载或者P2P的开发感兴趣的朋友可以访问软件的官方主页: <A href="http://www.hwysoft.com/chs/">http://www.hwysoft.com/chs/</A> 下载看看，说不定有收获。写这篇文章的主要目的是懒的再每次单独回答一些网友的提问, 一次性写下来, 即节省了自己的时间，也方便了对于P2P的UDP穿透感兴趣的网友阅读和理解。对此有兴趣和经验的朋友可以给我发邮件或者访问我的个人Blog留言: <A href="http://hwycheng.blogchina.com/">http://hwycheng.blogchina.com</A>. <BR>您可以自由转载此篇文章，但是请保留此说明。</P>
<P>再次感谢shootingstars网友的早期贡献. 表示谢意。</P>
<P>------------------------------------------------------------------------------------------------------------</P>
<P>NAT(The IP Network Address Translator) 的概念和意义是什么?</P>
<P>NAT, 中文翻译为网络地址转换。具体的详细信息可以访问RFC 1631 - <A href="http://www.faqs.org/rfcs/rfc1631.html">http://www.faqs.org/rfcs/rfc1631.html</A>, 这是对于NAT的定义和解释的最权威的描述。网络术语都是很抽象和艰涩的，除非是专业人士，否则很难从字面中来准确理解NAT的含义。</P>
<P>要想完全明白NAT 的作用，我们必须理解IP地址的两大分类，一类是私有IP地址，在这里我们称作内网IP地址。一类是非私有的IP地址，在这里我们称作公网IP地址。关于IP地址的概念和作用的介绍参见我的另一篇文章: <A href="http://hwycheng.blogchina.com/2402121.html">http://hwycheng.blogchina.com/2402121.html</A></P>
<P>内网IP地址: 是指使用A/B/C类中的私有地址, 分配的IP地址在全球不惧有唯一性，也因此无法被其它外网主机直接访问。<BR>公网IP地址: 是指具有全球唯一的IP地址，能够直接被其它主机访问的。</P>
<P>NAT 最初的目的是为使用内网IP地址的计算机提供通过少数几台具有公网的IP地址的计算机访问外部网络的功能。NAT 负责将某些内网IP地址的计算机向外部网络发出的IP数据包的源IP地址转换为NAT自己的公网的IP地址，目的IP地址不变, 并将IP数据包转发给路由器，最终到达外部的计算机。同时负责将外部的计算机返回的IP数据包的目的IP地址转换为内网的IP地址，源IP地址不变，并最终送达到内网中的计算机。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ port:2809&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^port: 1827&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | NAT device&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | NAT device&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.99.86&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.77.66&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V port:80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; V port: 80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.202.88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.76.102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图一: NAT 实现了私有IP的计算机分享几个公网IP地址访问Internet的功能。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>随着网络的普及，IPv4的局限性暴露出来。公网IP地址成为一种稀缺的资源，此时NAT 的功能局限也暴露出来，同一个公网的IP地址，某个时间只能由一台私有IP地址的计算机使用。于是NAPT(The IP Network Address/Port Translator)应运而生，NAPT实现了多台私有IP地址的计算机可以同时通过一个公网IP地址来访问Internet的功能。这在很大程度上暂时缓解了IPv4地址资源的紧张。</P>
<P>NAPT 负责将某些内网IP地址的计算机向外部网络发出的TCP/UDP数据包的源IP地址转换为NAPT自己的公网的IP地址，源端口转为NAPT自己的一个端口。目的IP地址和端口不变, 并将IP数据包发给路由器，最终到达外部的计算机。同时负责将外部的计算机返回的IP数据包的目的IP地址转换内网的IP地址，目的端口转为内网计算机的端口，源IP地址和源端口不变，并最终送达到内网中的计算机。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; port: 2809&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ port: 1827<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | NAT device <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.99.86&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map port:9882 to 192.168.0.5:2809 ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ map port: 9881 to 192.168.0.6:1827<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; port:80&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp; port:80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.202.88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.76.102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图二: NAPT 实现了私有IP的计算机分享一个公网IP地址访问Internet的功能。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;<BR>在我们的工作和<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(6); onmouseover=kwE(event,6); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=生活','_blank');" onmouseout=kwL(event); target=_blank>生活</A></NOBR>中, NAPT的作用随处可见，绝大部分公司的网络架构，都是通过1至N台支持NAPT的路由器来实现公司的所有计算机连接外部的Internet网络的。包括本人在写这篇文章的时候，也是在家中使用一台IBM笔记本通过一台宽带连接的台式机来访问Internet的。我们本篇文章主要讨论的NAPT的问题。</P>
<P>NAPT(The IP Network Address/Port Translator) 为何阻碍了P2P软件的应用?</P>
<P>通过NAPT 上网的特点决定了只能由NAPT内的计算机主动向NAPT外部的主机发起连接，外部的主机想直接和NAPT内的计算机直接建立连接是不被允许的。IM(即时通讯)而言，这意味着由于NAPT内的计算机和NAPT外的计算机只能通过服务器中转数据来进行通讯。对于P2P方式的下载程序而言，意味着NAPT内的计算机不能接收到NAPT外部的连接，导致连接数用过少，下载速度很难上去。因此P2P软件必须要解决的一个问题就是要能够在一定的程度上解决NAPT内的计算机不能被外部连接的问题。</P>
<P>NAT(The IP Network Address Translator) 进行UDP穿透的原理是什么?</P>
<P>TCP/IP传输时主要用到TCP和UDP协议。TCP协议是可靠的，面向连接的传输协议。UDP是不可靠的，无连接的协议。根据TCP和UDP协议的实现原理，对于NAPT来进行穿透，主要是指的UDP协议。TCP协议也有可能，但是可行性非常小，要求更高，我们此处不作讨论，如果感兴趣可以到Google上<NOBR><A class=iAs oncontextmenu="return false;" onmousemove=kwM(5); onmouseover=kwE(event,5); style="CURSOR: hand; COLOR: #0000ff; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="window.open('http://114.vnet.cn/search_web.html?id=148&amp;kw=搜索','_blank');" onmouseout=kwL(event); target=_blank>搜索</A></NOBR>，有些文章对这个问题做了探讨性的描述。下面我们来看看利用UDP协议来穿透NAPT的原理是什么:</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp; Internat host<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UDP port: 2809&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ UDP port: 1827<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 192.168.0.1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | NAT device <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.99.86&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp; Session(192.168.0.6:1827 &lt;-&gt; 61.51.76.102:8098) ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ Session(192.168.0.6:1827 &lt;-&gt; 61.51.76.102:8098)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; map port:9882 to 192.168.0.5:2809 /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \map port: 9881 to 192.168.0.6:1827<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UDP port:8098 v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp; UDP port:8098&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.202.88&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 61.51.76.102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Internet host <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图三: NAPT 是如何将私有IP地址的UDP数据包与公网主机进行透明传输的。</P>
<P>UDP协议包经NAPT透明传输的说明:</P>
<P>NAPT为每一个Session分配一个NAPT自己的端口号，依据此端口号来判断将收到的公网IP主机返回的TCP/IP数据包转发给那台内网IP地址的计算机。在这里Session是虚拟的，UDP通讯并不需要建立连接，但是对于NAPT而言，的确要有一个Session的概念存在。NAPT对于UDP协议包的透明传输面临的一个重要的问题就是如何处理这个虚拟的Session。我们都知道TCP连接的Session以SYN包开始，以FIN包结束，NAPT可以很容易的获取到TCP Session的生命周期，并进行处理。但是对于UDP而言，就麻烦了，NAPT并不知道转发出去的UDP协议包是否到达了目的主机，也没有办法知道。而且鉴于UDP协议的特点，可靠很差，因此NAPT必须强制维持Session的存在，以便等待将外部送回来的数据并转发给曾经发起请求的内网IP地址的计算机。NAPT具体如何处理UDP Session的超时呢？不同的厂商提供的设备对于NAPT的实现不近相同，也许几分钟，也许几个小时，些NAPT的实现还会根据设备的忙碌状态进行智能计算超时时间的长短。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [192.168.0.6:1827]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | UDP Packet[src ip:192.168.0.6 src port:1827 dst ip:61.51.76.102 dst port 8098]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [pub ip: 61.51.99.86]NAT[priv ip: 192.168.0.1]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | UDP Packet[src ip:61.51.99.86 src port:9881 dst ip:61.51.76.102 dst port 8098]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [61.51.76.102:8098]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图四: NAPT 将内部发出的UDP协议包的源地址和源端口改变传输给公网IP主机。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [192.168.0.6:1827]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | UDP Packet[src ip:61.51.76.102 src port:8098 dst ip:192.168.0.6 dst port 1827]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [pub ip: 61.51.99.86]NAT[priv ip: 192.168.0.1]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | UDP Packet[src ip:61.51.76.102 src port:8098 dst ip:61.51.99.86 dst port 9881]&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [61.51.76.102:8098]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图五: NAPT 将收到的公网IP主机返回的UDP协议包的目的地址和目的端口改变传输给内网IP计算机。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>现在我们大概明白了NAPT如何实现内网计算机和外网主机间的透明通讯。现在来看一下我们最关心的问题，就是NAPT是依据什么策略来判断是否要为一个请求发出的UDP数据包建立Session的呢？主要有一下几个策略: </P>
<P>A. 源地址(内网IP地址)不同，忽略其它因素, 在NAPT上肯定对应不同的Session<BR>B. 源地址(内网IP地址)相同，源端口不同，忽略其它的因素，则在NAPT上也肯定对应不同的Session<BR>C. 源地址(内网IP地址)相同，源端口相同，目的地址(公网IP地址)相同，目的端口不同，则在NAPT上肯定对应同一个Session<BR>D. 源地址(内网IP地址)相同，源端口相同，目的地址(公网IP地址)不同，忽略目的端口，则在NAPT上是如何处理Session的呢？</P>
<P>D的情况正式我们关心和要讨论的问题。依据目的地址(公网IP地址)对于Session的建立的决定方式我们将NAPT设备划分为两大类:</P>
<P>Symmetric NAPT:<BR>对于到同一个IP地址，任意端口的连接分配使用同一个Session; 对于到不同的IP地址, 任意端口的连接使用不同的Session. <BR>我们称此种NAPT为 Symmetric NAPT. 也就是只要本地绑定的UDP端口相同， 发出的目的IP地址不同，则会建立不同的Session.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [202.223.98.78:9696] [202.223.98.78:9696] [202.223.98.78:9696]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9883&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9882&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9881<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \ [NAT] /<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [192.168.0.6:1827]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图六: Symmetric 的英文意思是对称。多个端口对应多个主机，平行的，对称的!<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>Cone NAPT:<BR>对于到同一个IP地址，任意端口的连接分配使用同一个Session; 对于到不同的IP地址，任意端口的连接也使用同一个Session.<BR>我们称此种NAPT为 Cone NAPT. 也就是只要本地绑定的UDP端口相同， 发出的目的地址不管是否相同， 都使用同一个Session.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [202.223.98.78:9696] [202.223.98.78:9696] [202.223.98.78:9696]</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 9881<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [NAT]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; v&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [192.168.0.6:1827]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图七: Cone 的英文意思是锥。一个端口对应多个主机，是不是像个锥子?</P>
<P>现在绝大多数的NAPT属于后者，即Cone NAT。本人在测试的过程中，只好使用了一台日本的Symmetric NAT。还好不是自己的买的，我从不买日货, 希望看这篇文章的朋友也自觉的不要购买日本的东西。Win9x/2K/XP/2003系统自带的NAPT也是属于 Cone NAT的。这是值的庆幸的，因为我们要做的UDP穿透只能在Cone NAT间进行，只要有一台不是Cone NAT，对不起，UDP穿透没有希望了，服务器转发吧。后面会做详细分析!</P>
<P>下面我们再来分析一下NAPT 工作时的一些数据结构，在这里我们将真正说明UDP可以穿透Cone NAT的依据。这里描述的数据结构只是为了说明原理，不具有实际参考价值，真正感兴趣可以阅读Linux的中关于NAT实现部分的源码。真正的NAT实现也没有利用数据库的，呵呵，为了速度！</P>
<P>Symmetric NAPT 工作时的端口映射数据结构如下:</P>
<P>内网信息表:</P>
<P>[NAPT 分配端口] [ 内网IP地址 ] [ 内网端口 ] [ 外网IP地址 ] [ SessionTime 开始时间 ]</P>
<P>PRIMARY KEY( [NAPT 分配端口] ) -&gt; 表示依据[NAPT 分配端口]建立主键，必须唯一且建立索引，加快查找.<BR>UNIQUE( [ 内网IP地址 ], [ 内网端口 ] ) -&gt; 表示这两个字段联合起来不能重复.<BR>UNIQUE( [ 内网IP地址 ], [ 内网端口 ], [ 外网IP地址 ] ) -&gt; 表示这三个字段联合起来不能重复.</P>
<P>映射表:</P>
<P>[NAPT 分配端口] [ 外网端口 ]</P>
<P>UNIQUE( [NAPT 分配端口], [ 外网端口 ] ) -&gt; 表示这两个字段联合起来不能重复.</P>
<P>Cone NAPT 工作时的端口映射数据结构如下:</P>
<P>内网信息表:</P>
<P>[NAPT 分配端口] [ 内网IP地址 ] [ 内网端口 ] [ SessionTime 开始时间 ]</P>
<P>PRIMARY KEY( [NAPT 分配端口] ) -&gt; 表示依据[NAPT 分配端口]建立主键，必须唯一且建立索引，加快查找.<BR>UNIQUE( [ 内网IP地址 ], [ 内网端口 ] ) -&gt; 表示这两个字段联合起来不能重复.</P>
<P>外网信息表:</P>
<P>[ wid 主键标识 ] [ 外网IP地址 ] [ 外网端口 ]</P>
<P>PRIMARY KEY( [ wid 主键标识 ] ) -&gt; 表示依据[ wid 主键标识 ]建立主键，必须唯一且建立索引，加快查找.<BR>UNIQUE( [ 外网IP地址 ], [ 外网端口 ] ) -&gt; 表示这两个字段联合起来不能重复.</P>
<P>映射表: 实现一对多，的</P>
<P>[NAPT 分配端口] [ wid 主键标识 ]</P>
<P>UNIQUE( [NAPT 分配端口], [ wid 主键标识 ] ) -&gt; 表示这两个字段联合起来不能重复.<BR>UNIQUE( [ wid 主键标识 ] ) -&gt; 标识此字段不能重复.</P>
<P>看完了上面的数据结构是更明白了还是更晕了？ 呵呵! 多想一会儿就会明白了。通过NAT,内网计算机计算机向外连结是很容易的，NAPT会自动处理，我们的应用程序根本不必关心它是如何处理的。那么外部的计算机想访问内网中的计算机如何实现呢？我们来看一下下面的流程：</P>
<P>c 是一台在NAPT后面的内网计算机，s是一台有外网IP地址的计算机。c 主动向 s 发起连接请求，NAPT依据上面描述的规则在自己的数据结构中记录下来，建立一个Session. 然后 c 和 s 之间就可以实现双向的透明的数据传输了。如下面所示:</P>
<P>&nbsp;&nbsp; c[192.168.0.6:1827] &lt;-&gt; [priv ip: 192.168.0.1]NAPT[pub ip: 61.51.99.86:9881] &lt;-&gt; s[61.51.76.102:8098]</P>
<P>由此可见，一台外网IP地址的计算机想和NAPT后面的内网计算机通讯的条件就是要求NAPT后面的内网计算机主动向外网IP地址的计算机发起一个UDP数据包。外网IP地址的计算机利用收到的UDP数据包获取到NAPT的外网IP地址和映射的端口，以后就可以和内网IP的计算机透明的进行通讯了。<BR>&nbsp;&nbsp;&nbsp; <BR>现在我们再来分析一下我们最关心的两个NAPT后面的内网计算机如何实现直接通讯呢? 两者都无法主动发出连接请求，谁也不知道对方的NAPT的公网IP地址和NAPT上面映射的端口号。所以我们要靠一个公网IP地址的服务器帮助两者来建立连接。当两个NAPT后面的内网计算机分别连接了公网IP地址的服务器后，服务器可以从收到的UDP数据包中获取到这两个NAPT设备的公网IP地址和这两个连接建立的Session的映射端口。两个内网计算机可以从服务器上获取到对方的NAPT设备公网IP地址和映射的端口了。</P>
<P>我们假设两个内网计算机分别为A和B，对应的NAPT分别为AN和BN， 如果A在获取到B对应的BN的IP地址和映射的端口后，迫不急待的向这个IP<BR>地址和映射的端口发送了个UDP数据包，会有什么情况发生呢？依据上面的原理和数据结构我们会知道，AN会在自己的数据结构中生成一条记录，标识一个新Session的存在。BN在收到数据包后，从自己的数据结构中查询，没有找到相关记录，因此将包丢弃。B是个慢性子，此时才慢吞吞的向着AN的IP地址和映射的端口发送了一个UDP数据包，结果如何呢？当然是我们期望的结构了，AN在收到数据包后，从自己的数据结构中查找到了记录，所以将数据包进行处理发送给了A。A 再次向B发送数据包时，一切都时畅通无阻了。OK, 大工告成！且慢，这时对于Cone NAPT而言，对于Symmetric NAPT呢？呵呵，自己分析一下吧...</P>
<P>NAPT(The IP Network Address/Port Translator) 进行UDP穿透的具体情况分析!</P>
<P>首先明确的将NAPT设备按照上面的说明分为: Symmetric NAPT 和 Cone NAPT, Cone NAPT 是我们需要的。Win9x/2K/XP/2003 自带的NAPT也为Cone NAPT。</P>
<P>第一种情况, 双方都是Symmetric NAPT:</P>
<P>此情况应给不存在什么问题，肯定是不支持UDP穿透。</P>
<P>第二种情况, 双方都是Cone NAPT:</P>
<P>此情况是我们需要的，可以进行UDP穿透。</P>
<P>第三种情况, 一个是Symmetric NAPT, 一个是Cone NAPT:</P>
<P>此情况比较复杂，但我们按照上面的描述和数据机构进行一下分析也很容易就会明白了, 分析如下,</P>
<P>假设: A -&gt; Symmetric NAT, B -&gt; Cone NAT</P>
<P>1. A 想连接 B, A 从服务器那儿获取到 B 的NAT地址和映射端口, A 通知服务器，服务器告知 B A的NAT地址和映射端口, B 向 A 发起连接，A 肯定无法接收到。此时 A 向 B 发起连接， A 对应的NAT建立了一个新的Session，分配了一个新的映射端口， B 的 NAT 接收到UDP包后，在自己的映射表中查询，无法找到映射项，因此将包丢弃了。</P>
<P>2. B 想连接 A, B 从服务器那儿获取到 A 的NAT地址和映射端口, B 通知服务器, 服务器告知 A B的NAT地址和映射端口,A 向 B 发起连接, A 对应的NAT建立了一个新的Session，分配了一个新的映射端口B肯定无法接收到。此时 B 向 A 发起连接, 由于 B 无法获取 A 建立的新的Session的映射端口，仍是使用服务器上获取的映射端口进行连接， 因此 A 的NAT在接收到UDP包后，在自己的映射表中查询，无法找到映射项, 因此将包丢弃了。</P>
<P>根据以上分析，只有当连接的两端的NAT都为Cone NAT的情况下，才能进行UDP的内网穿透互联。</P>
<P><BR>NAPT(The IP Network Address/Port Translator) 进行UDP穿透如何进行现实的验证和分析!</P>
<P>需要的网络结构如下:</P>
<P>三个NAT后面的内网机器，两个外网服务器。其中两台Cone NAPT，一台 Symmetric NAPT。</P>
<P>验证方法:</P>
<P>可以使用本程序提供的源码，编译，然后分别运行服务器程序和客户端。修改过后的源码增加了客户端之间直接通过IP地址和端口发送消息的命令，利用此命令，你可以手动的验证NAPT的穿透情况。为了方便操作，推荐你使用一个远程登陆软件，可以直接在一台机器上操作所有的相关的计算机，这样很方便，一个人就可以完成所有的工作了。<IMG src="images/file/zip.gif" border=0><A href="uploadfile/2006111314595891.RAR" target=_blank>p2pNAT.rar</A></P>]]></description>
</item><item>
<title><![CDATA[搜索引擎设计]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=19440</link>
<author>eaglebetter</author>
<pubDate>2006/10/26 22:33:58</pubDate>
<description><![CDATA[<P>中科院软件所 张俊林 <BR>2005年11月 </P>
<P><STRONG><FONT color=#800080>查询处理以及分词技术</FONT></STRONG> <BR>随着搜索经济的崛起，人们开始越加关注全球各大搜索引擎的性能、技术和日流量。作为企业，会根据搜索引擎的知名度以及日流量来选择是否要投放广告等；作为普通网民，会根据搜索引擎的性能和技术来选择自己喜欢的引擎查找资料；作为技术人员，会把有代表性的搜索引擎作为研究对象. 搜索引擎经济的崛起，又一次向人们证明了网络所蕴藏的巨大商机。网络离开了搜索将只剩下空洞杂乱的数据，以及大量等待去费力挖掘的金矿。 <BR>但是,如何设计一个高效的搜索引擎?我们可以以百度所采取的技术手段来探讨如何设计一个实用的搜索引擎.搜索引擎涉及到许多技术点,比如查询处理,排序算法,页面抓取算法,CACHE机制,ANTI-SPAM等等.这些技术细节,作为商业公司的搜索引擎服务提供商比如百度,GOOGLE等是不会公之于众的.我们可以将现有的搜索引擎看作一个黑盒,通过向黑盒提交输入,判断黑盒返回的输出大致判断黑盒里面不为人知的技术细节. <BR>查询处理与分词是一个中文搜索引擎必不可少的工作,而百度作为一个典型的中文搜索引擎一直强调其”中文处理”方面具有其它搜索引擎所不具有的关键技术和优势.那么我们就来看看百度到底采用了哪些所谓的核心技术. <BR>我们分两个部分来讲述:查询处理/中文分词. <BR><BR>一. 查询处理 <BR><BR>用户向搜索引擎提交查询,搜索引擎一般在接受到用户查询后要做一些处理,然后在索引数据库里面提取相关的信息.那么百度在接受到用户查询后做了些什么工作呢? <BR><BR>1. 假设用户提交了不只一个查询串,比如”信息检索 理论 工具”.那么搜索引擎首先做的是根据分隔符比如空格,标点符号,将查询串分割成若干子查询串,比如上面的查询就会被解析为:&lt;信息检索,理论,工具&gt;三个子字符串;这个道理简单,我们接着往下看. <BR><BR>2. 假设提交的查询有重复的内容,搜索引擎怎么处理呢?比如查询”理论 工具 理论”,百度是将重复的字符串当作只出现过一次,也就是处理成等价的”理论 工具”,而GOOGLE显然是没有进行归并,而是将重复查询子串的权重增大进行处理.那么是如何得出这个结论的呢?我们可以将”理论 工具”提交给百度,返回341,000篇文档,大致看看第一页的返回内容.OK.继续,我们提交查询”理论 工具 理论”,在看看返回结果,仍然是那么多返回文档,当然这个不能说明太多问题,那看看第一页返回结果的排序,看出来了吗?顺序完全没有变化,而GOOGLE则排序有些变动,这说明百度是将重复的查询归并成一个处理的,而且字符串之间的先后出现顺序基本不予考虑(GOOGLE是考虑了这个顺序关系的). <BR><BR>3. 假设提交的中文查询包含英文单词,搜索引擎是怎么处理的?比如查询”电影BT下载”,百度的方法是将中文字符串中的英文当作一个整体保留,并以此为断点将中文切分开,这样上述的查询就切为&lt;电影,BT,下载&gt;,不论中间的英文是否一个字典里能查到的单词也好,还是随机的字符也好,都会当作一个整体来对待.至于为什么,你用查询” 电影dfdfdf下载”看看结果就知道了.当然如果查询中包含数字,也是如此办理. <BR><BR>到目前为止,一切很简单,也很清楚,百度怎么处理用户查询的呢?归纳如下:首先根据分割符号将查询分开,然后看看是否有重复的字符串,如果有,就抛弃多余的,只保留一个,接着判断是否有英文或者数字,如果有的话,把英文或者数字当作一个整体保留并把前后的中文切开. <BR><BR>接着该干什么呢?该考虑分词的问题了. <BR><BR>二. 中文分词 <BR><BR>首先,讲讲百度的分词时机或者条件问题,是否是个中文字符串百度就拿来切一下呢?非也,要想被百度的分词程序荣幸的切割一下也是要讲条件的,哪能是个字符串就切割啊?你当百度是卖锯条的么? <BR><BR>那么什么样的字符串才满足被切割的条件呢?简单说来,如果字符串只包含小于等于3个中文字符的话,那就保留不动,当字符串长度大于4个中文字符的时候,百度的分词程序才出马大干快上,把这个字符串肢解掉. <BR><BR>怎么证明呢?我们向百度提交”电影下载”,看看返回结果中标为红字的地方,不难看出来,查询已经被切割成&lt;电影,下载&gt;两个单词了,说明分词程序已经开工了,如果是比4个中文字符更长的字符串,那分词程序就更不客气了,一定大卸八块而后快.我们来看看三个字符的情况,提交查询”当然择”,看起来这个查询不伦不类,那是因为我希望看到这个字符串被切分为&lt;当然,择&gt;,返回结果365篇相关页面,翻到最后一页,发现标红的关键字都是”当然择”连续出现的情况,好像没有切分,但是还不确定,那么再提交人工分好的查询”当然 择”看看,返回结果1,090,000篇,基本上可以确定没有进行分词了,当然另外一种解释是:对于三个字符先切分,然后将切分后的结果当作一个短语查询,这样看到的效果和没有切分是相似的.但是我倾向于判断百度对于少于3个字符的串没有切分,奥卡姆不是说了么”如无必要,勿增实体”,干吗做无用功呢.那么如果没有切分,会有一个随之而来的问题,怎么从索引库里面提取未切分的字符串呢?这牵扯到索引的问题,我觉得百度应该采取了两套索引机制,一种是按照单词索引,一种是按照N-GRAM索引,至于索引的具体问题,以后在详细论述. <BR><BR>下面我们看看百度是采取的何种分词算法,现在分词算法已经算是比较成熟了,有简单的有复杂的,比如正向最大匹配,反向最大匹配,双向最大匹配,语言模型方法,最短路径算法等等,有兴趣的可以用GOOGLE去搜索一下以增加理解.这里就不展开说了.但是要记住一点的是:判断一个分词系统好不好,关键看两点,一个是消除歧义能力;一个是词典未登录词的识别比如人名,地名,机构名等. <BR><BR>那么百度用的是什么方法?我的判断是用双向最大匹配算法.至于怎么推理得出的,让我们一步步来看.当然,这里首先有个假设,百度不会采取比较复杂的算法,因为考虑到速度问题. <BR><BR>我们提交一个查询”毛泽东北京华烟云”,又一个不知所云的查询,尽管不知所云但是自有它的道理,我想看看百度的分词是如何消歧以及是否有词典未登录词的识别的功能,如果是正向最大匹配算法的话,那么输出应该是:”毛泽东/北京/华/烟云”,如果是反向最大匹配算法的话,那么输出应该是:”毛/泽/东北/京华烟云”,我们看看百度的分词结果:”毛泽东/北/京华烟云”,一个很奇怪的输出,跟我们的期望相差较多,但是从中我们可以获得如下信息:百度分词可以识别人名,也可以识别”京华烟云”,这说明有词典未登录词的识别的功能,我们可以假设分词过程分为两个阶段:第一阶段,先查找一个特殊词典,这个词典包含一些人名,部分地名以及一些普通词典没有的新词,这样首先将”毛泽东”解析出来,剩下了字符串”北京华烟云”,而”北/京华烟云”,可以看作是反向最大匹配的分词结果.这样基本说得通.为了证明这一点,我们提交查询”发毛泽东北”,我们期望两种分词结果,一个是正向最大匹配&lt;发毛,泽,东北&gt;,一个是上述假设的结果&lt;发,毛泽东,北&gt;,事实上百度输出是第二种情况,这样基本能确定百度分词采取了至少两个词典,一个是普通词典,一个是专用词典(人名等).而且是专用词典先切分,然后将剩余的片断交由普通词典来切分. <BR><BR>继续测验,提交查询”古巴比伦理”,如果是正向最大匹配,那么结果应该是&lt;古巴比伦,理&gt;,如果是反向最大匹配,那么结果应该是&lt;古巴,比,伦理&gt;,事实上百度的分词结果是&lt;古巴比伦,理&gt;,从这个例子看,好像用了正向最大匹配算法;此外还有一些例子表明好像是使用正向最大匹配的;但是且慢,我们看这个查询”北京华烟云”,正向最大匹配期望的结果是&lt;北京,华,烟云&gt;,而反向最大匹配期望的结果是&lt;北,京华烟云&gt;,事实上百度输出的是后者,这说明可能采用的反向最大匹配;从这点我们可以猜测百度采用的是双向最大匹配分词算法,如果正向和反向匹配分词结果一致当然好办,直接输出即可;但是如果两者不一致,正向匹配一种结果,反向匹配一种结果,此时该如何是好呢?从上面两个例子看,在这种情况下,百度采取最短路径方法,也就是切分的片断越少越好,比如&lt;古巴,比,伦理&gt;和&lt;古巴比伦,理&gt;相比选择后者,&lt;北京,华,烟云&gt;和&lt;北,京华烟云&gt;相比选择后者.还有类似的一些例子,这样基本可以解释这些输出结果. <BR><BR>但是仍然遗留的问题是:如果正向反向分词不一致,而且最短路径也相同,那怎么办?输出正向的还是反向的结果?我们再来看一个例子.提交查询”遥远古古巴比伦”,这个查询被百度切分为&lt;遥远,古古,巴比伦&gt;,说明词典里面有”巴比伦”,但是是否有”古巴比伦”这个词汇不确定,此时看不出是正向切分还是反向切分得出的结果,换查询为”遥远古巴比伦”,此时被切分为”遥远/古巴比伦”,这说明词典里面有”古巴比伦”这个词汇,这说明了”遥远古古巴比伦”是正向最大匹配的结果.那为什么”遥远古古巴比伦”不会被反向切分为”遥/远古/古巴比伦”呢,百度的可能选择是这种情况下选择单字少的那组切分结果. <BR><BR>当然还可以继续追问:如果切分后单字也一样多,那怎么办?最后看一个例子,查询”王强大小:”,百度将其切分为”王/强大/小”,是正向切分的结果,如果是反向的会被切分为”王/强/大小”,这说明有歧义而且单字也相同则选择正向切分结果. <BR><BR>OK,看到这里可能头已经有些晕了,最后总结一下百度的分词算法,当然里面还是有猜测的成分,算法如下: <BR><BR>首先查询专用词典(人名,部分地名等),将专有名称切出,剩下的部分采取双向分词策略,如果两者切分结果相同,说明没有歧义,直接输出分词结果.如果不一致,则输出最短路径的那个结果,如果长度相同,则选择单字词少的那一组切分结果.如果单字也相同,则选择正向分词结果.. <BR><BR>百度一直宣传自己在中文处理方面的优势,从上面看,分词算法并无特殊之处,消歧效果并不理想,即使百度采取比上述分词算法复杂些的算法也难以说成是优势,如果说百度有优势的话,唯一的优势就是那个很大的专用词典,这个专用词典登录了人名(比如大长今),称谓(比如老太太),部分地名(比如阿联酋等),估计百度采用学术界公布的比较新的命名实体识别算法从语料库里面不断识别出词典未登录词,逐渐扩充这个专门词典.如果这就是优势的话,那么这个优势能够保持多久就是个很明显的问题.</P>
<P><STRONG><FONT color=#800080>Spelling Checker拼写检查错误提示(以及拼音提示功能)</FONT></STRONG> </P>
<P><FONT size=2>拼写检查错误提示是搜索引擎都具备的一个功能,也就是说用户提交查询给搜索引擎,搜索引擎检查看是否用户输入的拼写有错误,对于中文用户来说一般造成的错误是输入法造成的错误.那么我们就来分析看看百度是怎么实现这一功能的。 <BR>　　我们分析拼写检查系统关注以下几个问题: <BR>　　(1)系统如何判断用户的输入是有可能发生错误的查询呢? <BR>　　(2)如果判断是可能错误的查询输入,如何提示正确的词汇呢? <BR><BR>　　那么百度是如何做的呢?百度判断用户输入是否错误的标准,我觉得应该是查字典,如果发现字典里面不包含这个词汇,那么很有可能是个错误的输入,此时启动错误提示功能,这个很好判断,因为如果是一个正常词汇的话,百度一般不会有错误提示,而你故意输入一个词典不可能包含的所谓词汇,此时百度一般会提示你正确的检索词汇. <BR><BR>　　那么百度是怎么提示正确词汇的呢?很明显是通过拼音的方式,比如我输入查询” 制才”,百度提供的提示词汇为: “:制裁 质材 纸材”,都是同音字.所以百度必然维持着一个同音词词典,里面保留着同音词信息,比如可能包含着下面这条词条: “ zhi cai à制裁,质材,纸材”,另外还有一个标注拼音程序,现在能够看到的基本流程是: 用户输入” 制才”,查词典,发现没有这个词汇,OK,启动标注拼音程序,将” 制才”标注为拼音”zhi cai”,然后查找同音词词典,发现同音词” 制裁,质材,纸材”,那么提示用户可能的正确拼写. <BR><BR>　　整体流程看起来很简单,但是还有一些遗留的小问题,比如是否将词表里面所有同音词都作为用户的提示信息呢?比如 某个拼音有10个同音词,是否都输出呢?百度并没有将所有同音词都输出而是选择一定筛选标准,选择其中几个输出.怎么证明这一点?我们看看拼音”liu li”的同音词,紫光输入法提示同音词汇有” 流丽 流离 琉璃 流利”4个,我们看看百度返回几个,输入”流厉”作为查询,这里是故意输入一个词典不包含的词汇,这样百度的拼写检查才开始工作,百度提示: "琉璃 刘丽 刘莉 ",这说明什么?说明不是所有同音词都输出,而是选择输出,那么选择的标准是什么?我能够猜测到的方法是对于用户查询LOG进行统计,提取用户查询次数多的那些同音词输出,如果是这样的话,上面的例子说明用户搜索”琉璃”次数比其它的都要高些,次之是” 刘丽”,再次是” 刘莉”,看来大家都喜欢查询自己或者认识的人的名字. <BR><BR>　　另外一个小问题:同音词词典包含2字词,3字词,那么是否包含4字词以及更长的词条?是否包含一字词? 这里一字词好回答,不用测试也能知道肯定不包含,因为你输入一个字,谁知道是否是错误的呢?反正只要是汉字就能在词表里面找到,所以没有判断依据.二字词是包含的,上面有例子,三字词也包含,比如查询 "中城药"百度错误提示:”中成药”,修改查询为"重城药",还是提示”中成药” ,再次修改查询 "重城要",百度依然提示”中成药”. 那么4字词汇呢? <BR><BR>　　百度还是会给你提示的,下面是个例子: <BR><BR>　　输入:静华烟云 提示 京华烟云 <BR><BR>　输入 静话烟云 提示 京华烟云 <BR><BR>　　输入 静话阎晕 提示 京华烟云 <BR><BR>　　那么更长的词汇是否提示呢?也提示,比如我输入: "落花世界有风军",这个查询是什么意思,估计读过古诗的都知道,看看百度的提示”落花时节又逢君”,这说明什么?说明同音词词典包含不同长度的同音词信息,另外也说明了百度的核心中文处理技术,也就是那个词典,还真挺大的. <BR><BR>　　但是,如果用户输入的查询由两个或者两个以上子字符串构成,那么百度的错误提示功能就罢工了,比如输入查询"哀体",百度提示”艾提 挨踢”,但是.输入为 "我 哀体",则没有任何错误提示. <BR><BR>　　还有一个比较重要的问题:如果汉字是多音字那么怎么处理?百度呢比较偷懒,它根本就没有对多音字做处理.我们来看看百度的一个标注拼音的错误,在看这个错误前先看看对于多音字百度是怎么提示错误的,我们输入查询"俱长",百度提示”剧场 局长”, “俱长”的拼音有两个:”ju zhang /ju chang” ,可见如果是多音字则几种情况都提示..现在我们来看看错误的情况, 我们输入查询”剧常”,百度提示”:剧场 局长”,提示为”剧场"当然好解释,因为是同音字,但是为什么 "局长"也会被提示呢?这说明百度的同音字词典有错误,说明在”ju chang”这个词条里面包含”局长”这个错误的同音词.让我们顺藤摸瓜,这个错误又说明什么问题呢?说明百度的同音词典是自动生成的,而且没有人工校对.还说明在自动生成同音词典的过程中,百度不是根据对一篇文章标注拼音然后在抽取词汇和对应的拼音信息获得的,而是完全按照某个词典的词条来标注音节的,所以对于多音字造成的错误无法识别出来,如果是对篇章进行拼音标注,可能就不会出现这种很容易发现的错误标注. 当然还有另外一种解释,就是"局长"是故意被百度提示出来可能的正确提示词汇,因为考虑到南方人"zh'和 "ch"等前后鼻音分不清么,那么是这样的么?我们继续测试到底是何种情况.是百度有错误还是这是百度的先进的算法? <BR><BR>　　我们考虑词汇"长大",故意错误输入为"赃大",如果百度考虑到了前后鼻音的问题,那么应该会提示"长大",但是百度提示是"藏大".这说明什么?说明百度并没有考虑前后鼻音问题,根本就是系统错误. 我们输入查询"悬赏",故意将之错误输入为"悬桑",没有错误提示,说明确实没有考虑这种情况.前鼻音没有考虑,那么后鼻音考虑了么,我们输入”:经常”,故意改为后鼻音 "经缠",百度提示为"经产 经忏",还是没有考虑后鼻音.这基本可以确定是百度系统的错误导致. <BR><BR>　　根据以上推导,我们可以得出如下结论:百度是将分词词典里面每个词条利用拼音标注程序标注成拼音,然后形成同音词词典,所以两个词典是同样大的,而且这个词典也随着分词词典的增长而在不断增长. 至于标注过程中多音字百度没有考虑,如果是多音字就标注成多个发音组合,通过这种方式形成同音词词典.这样的同音词词典显然包含着很多错误. <BR><BR>　　最后一个问题:百度对于英文进行拼写检查么?让我们试试看,输入查询”china”,不错,搜到不少结果,专注中文搜索的百度还能搜索到英文,真是意外的惊喜.变换一下查询”chine”,会更加意外惊喜的给我们提示”china”吗?百度提示的是: 吃呢 持呢,原来是不小心触发了百度的拼音搜索功能了.那么拼音搜索和中文检查错误是否采用同一套同音词词典呢,让我们来实验一下,搜索”rongji”,百度提示” 榕基 溶剂 容积”,OK,换个中文查询”容机”,百度提示” 榕基 溶剂 容积”,看来使用的是同一套同音词词典.也就是说百度的中文纠错和拼音检索使用的机制相同,中文纠错多了一道拼音注音的过程而已.难道这就是传说中那个百度的”事实上是一个无比强大的拼音输入法”的拼音提示功能么? <BR><BR>　　最后让我们总结归纳一下百度的拼写检查系统: <BR><BR>　　后台作业: (1)前面的文章我们说过,百度分词使用的词典至少包含两个词典一个是普通词典,另外一个是专用词典(专名等),百度利用拼音标注程序依次扫描所有词典中的每个词条,然后标注拼音,如果是多音字则把多个音都标上,比如”长大”,会被标注为”zhang da/chang da”两个词条. <BR><BR>　　(2)通过标注完的词条,建立同音词词典,比如上面的”长大”,会有两个词条: zhang daà长大” , chang daà长大. <BR><BR>　　(3)利用用户查询LOG频率信息给予每个中文词条一个权重; <BR><BR>　　(4)OK,同音词词典建立完成了,当然随着分词词典的逐步扩大,同音词词典也跟着同步扩大; <BR><BR>　　拼写检查: <BR>　　(1)用户输入查询,如果是多个子字符串,不作拼写检查; <BR>　　(2)对于用户查询,先查分词词典,如果发现有这个单词词条,OK,不作拼写检查; <BR>　　(3)如果发现词典里面不包含用户查询,启动拼写检查系统;首先利用拼音标注程序对用户输入进行拼音标注; <BR>　　(4)对于标注好的拼音在同音词词典里面扫描,如果没有发现则不作任何提示; <BR>　　(5)如果发现有词条,则按照顺序输出权重比较大的几个提示结果；</FONT></P>
<P><FONT size=2><STRONG><FONT color=#800080>对百度分词算法的进一步分析</FONT></STRONG> </FONT></P>
<P>上面说过，经过分析得出百度的分词系统采用双向最大匹配分词，但是后来发现推理过程中存在一个漏洞，而且推导出来的百度分词算法步骤还是过于繁琐，所以进一步进行分析，看看是否前面的推导有错误。 <BR><BR>　　那么以前的分析有什么漏洞呢?我们推导百度分词有反向最大匹配的依据是百度将“北京华烟云”分词为&lt;北，京华烟云&gt;，从这里看好像采用了反向最大匹配，因为正向最大匹配的结果应该是&lt;北京，华，烟云&gt;，但是由此就推论说百度采用了双向最大匹配还是太仓促了，前面文章我们也讲过，百度有两个词典，一个普通词典，一个专有词典，而且是专有词典的词汇先切分，然后将剩余片断交给普通词典去切分。所以上面的“北京华烟云”之所以被切分成&lt;北，京华烟云&gt;，另外一个可能是:京华烟云这个词汇是在专有词典里面存储的，所以先分析，这样得出“京华烟云”，剩下“北”，没什么好切分的，所以输出&lt;北，京华烟云&gt;。 <BR>这里只是假设，那么是否确实“京华烟云”在专有词典呢?我们再看一个例子“山东北京华烟云”，百度切分的结果是&lt;山东，北，京华烟云&gt;，如果“京华烟云”在普通词典，如果是反向切分，那么结果应该是&lt;山，东北，京华烟云&gt;，如果是正向切分应该是&lt;山东，北京，华，烟云&gt;，无论如何都分不出&lt;山东，北，京华烟云&gt;。这说明什么?说明“京华烟云”是在那个专有词典，所以先切分出”京华烟云”，然后剩下的“山东北”交由普通词典切分，明显是正向最大匹配的结果输出&lt;山东，北&gt;。当然按照我们在第一篇文章的算法推导“山东北”的切分也会得出&lt;山东，北&gt;的结论，但是明显比正向最大匹配多几个判断步骤，既然效果一样，另外一个更加简洁的方法也能说得通，那当然选择简便的方法了。所以初步判断百度采取的是正向最大匹配。 <BR>　　我们继续测试采用何种分词算法，为了减少专有词典首先分词造成的影响，那么查询里面不能出现相对特殊的词汇，构筑查询“天才能量级”，这里应该没有专有词典出现过的词汇，百度切分为&lt;天才，能量，级&gt;，看来是正向最大匹配的结果。另外，如果所有查询词汇都出现在专有词典，那么采取的是何种方法?这样首先就得保证词汇都出现在专有词典，这么保证这一点呢?我们构造查询“铺陈晓东方”，百度切分为&lt;铺，陈晓东，方&gt;，可以看出“陈晓东”是在专有词典的所以先切分出来。另外一个例子 “山东京城”，百度切分为&lt;山东，京城&gt;，说明”东京”是在普通词典的。OK，构造查询“陈晓东京华烟云”，通过前面分析可以看出两个词汇都在专有词典里面，百度切分为&lt;陈晓东，京华烟云&gt;，说明对于专有词典词汇也是采取正向最大匹配或者双向最大匹配。那么使用反向最大匹配了吗?构造查询例子“陈晓东方不败”，首先我们肯定“陈晓东”和“东方不败”都是在专有词典出现的，如果是正向切分，那么应该是&lt;陈晓东，方，不败&gt;或者&lt;陈晓东，方，不，败&gt;如果是反向切分则是&lt;陈，晓，东方不败&gt;，可以看出百度的切分是&lt;陈晓东，方，不败&gt;或者&lt;陈晓东，方，不，败&gt;，说明采用的是正向最大匹配。通过分析，百度的词典不包含”不败”这个单词，所以实际上百度的切分结果是&lt;陈晓东，方，不，败&gt;，很明显这和我们以前推导的算法是有矛盾的，所以以前的分析算法确实有问题，所以结论是百度采取的是正向最大匹配算法。 <BR>重新归纳一下百度的分词系统:首先用专有词典采用最大正向匹配分词，切分出部分结果，剩余没有切分交给普通词典，同样采取正向最大匹配分词，最后输出结果。 <BR>　　另外，GOOGLE也是采用正向最大匹配分词算法，不过好像没有那个专用词典，所以很多专名都被切碎了。 <BR>　　从这点讲，GOOGLE在中文词典构建上比百度差些，还需要加把子力气才行，不过这也不是什么多难的事。</P>]]></description>
</item><item>
<title><![CDATA[重载、覆盖、多态与函数隐藏[3,4]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=19412</link>
<author>eaglebetter</author>
<pubDate>2006/10/25 20:15:30</pubDate>
<description><![CDATA[<a>　<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">例8-2</span></p><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>virtual</strong> void fun(int i){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;<strong>&nbsp;&nbsp;&nbsp;&nbsp; </strong></span><strong>void fun(int i){ cout &lt;&lt;"Derive::fun(int i)"&lt;&lt; endl; }<o:p></o:p></strong></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(<strong>double</strong> <strong>d</strong>){ cout &lt;&lt;"Derive::fun(double d)"&lt;&lt; endl; }<span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pb-&gt;fun(1);//<strong>Derive::fun(int i)<o:p></o:p></strong></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pb-&gt;fun((double)0.01);//<strong>Derive::fun(int i)<o:p></o:p></strong></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>
</span>&nbsp;</span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">9<o:p></o:p></span></p></span></span>
</a>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>virtual</strong> void fun(int i){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){ cout &lt;&lt;"Derive::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>void fun(char c){ cout &lt;&lt;"Derive::fun(char c)"&lt;&lt; endl; }<span style="">&nbsp; </span><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>void fun(double d){ cout &lt;&lt;"Derive::fun(double d)"&lt;&lt; endl; }<span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pb-&gt;fun(1);//<b>Derive::fun(int i)</b><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pb-&gt;fun('a');//<b><font color="#ff0000">Derive::fun(int i)</font></b><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pb-&gt;fun((double)0.01);//<b><font color="#ff0000">Derive::fun(int i)</font></b><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Derive *pd =new Derive();</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">pd-&gt;fun(1);//<b>Derive::fun(int i)</b></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload</span><o:p></o:p></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pd-&gt;fun('a');//<b>Derive::fun(char c)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </b></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload</span></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pd-&gt;fun(0.01);//<b>Derive::fun(double d)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </b></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>delete pd;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"></span>&nbsp;</p>
<span lang="EN-US">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">7-1</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">和例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">8-1</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">很好理解，我把这两个例子放在这里，是让大家作一个比较摆了，也是为了帮助大家更好的理解：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">7-1</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中，派生类没有覆盖基类的虚函数，此时派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中的函数指针指向的地址就是基类的虚函数地址。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">8-1</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中，派生类覆盖了基类的虚函数，此时派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中的函数指针指向的地址就是派生类自己的重写的虚函数地址。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">7-2</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">和</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">8-2</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">看起来有点怪怪，其实，你按照上面的原则对比一下，答案也是明朗的：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">7-2</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中，我们为派生类重载了一个函数版本：</span><span style="background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-weight: normal; font-family: Arial;">double d</span></strong>)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"> <span style="">&nbsp;</span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">其实，这只是一个障眼法。我们具体来分析一下，基类共有几个函数，派生类共有几个函数：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;">
<table class="MsoTableGrid" style="border: medium none ; border-collapse: collapse;" border="1" cellpadding="0" cellspacing="0">
<tbody>
<tr style="">
<td style="border: 1pt solid windowtext; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 50.4pt;" valign="top" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">类型</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: solid solid solid none; border-color: windowtext windowtext windowtext rgb(236, 233, 216); border-width: 1pt 1pt 1pt medium; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 180pt;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: solid solid solid none; border-color: windowtext windowtext windowtext rgb(236, 233, 216); border-width: 1pt 1pt 1pt medium; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 195.7pt;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">派生类</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td></tr>
<tr style="">
<td style="border-style: none solid solid; border-color: rgb(236, 233, 216) windowtext windowtext; border-width: medium 1pt 1pt; padding: 0cm 5.4pt; width: 50.4pt; background-color: transparent;" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">部分</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 180pt; background-color: transparent;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">int</span></strong> <strong><span style="font-family: Arial;">i</span></strong>)</span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 195.7pt; background-color: transparent;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">指向基类版的虚函数</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">int</span></strong> <strong><span style="font-family: Arial;">i</span></strong>)</span></p></td></tr>
<tr style="height: 33.15pt;">
<td style="border-style: none solid solid; border-color: rgb(236, 233, 216) windowtext windowtext; border-width: medium 1pt 1pt; padding: 0cm 5.4pt; width: 50.4pt; height: 33.15pt; background-color: transparent;" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">静态部分</span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 180pt; height: 33.15pt; background-color: transparent;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p><font color="#000000" face="Times New Roman" size="3">&nbsp;</font></o:p></span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 195.7pt; height: 33.15pt; background-color: transparent;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">double d</span></strong>)</span></p></td></tr></tbody></table></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;"></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;">我们再来分析一下以下三句代码</span><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">Base *pb = new Derive();<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">pb-&gt;fun(1);//<b>Base::fun(int i)</b></span><b><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;" lang="EN-US"><o:p></o:p></span></b></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">pb-&gt;fun((double)0.01);//<b>Base::fun(int i)</b></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US"><b><o:p></o:p></b></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">这第一句是关键，基类指针指向派生类的对象，我们知道这是多态调用；接下来第二句，运行时基类指针根据运行时对象的类型，发现是派生类对象，所以首先到派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中去查找派生类的虚函数版本，发现派生类没有覆盖基类的虚函数，派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">只是作了一个指向基类虚函数地址的一个指向，所以理所当然地去调用基类版本的虚函数。最后一句，程序运行仍然埋头去找派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">，发现根本没有这个版本的虚函数，只好回头调用自己的仅有一个虚函数。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">这里还值得一提的是：如果此时基类有多个虚函数，此时程序编绎时会提示</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">”</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">调用不明确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">”</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。示例如下</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>virtual void fun(int i){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="">&nbsp;</span>virtual void fun(char c){ cout &lt;&lt;"Base::fun(char c)"&lt;&lt; endl; }<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>void fun(double d){ cout &lt;&lt;"Derive::fun(double d)"&lt;&lt; endl; }<span style="">&nbsp; </span><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="">&nbsp;</span>pb-&gt;fun(0.01);//</span><span style="font-size: 9pt; color: red; font-family: Arial;" lang="EN-US">error C2668: 'fun' : ambiguous call to overloaded function</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">好了，我们再来分析一下例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">8-2</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例8</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">-2</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中，我们也为派生类重载了一个函数版本：</span><span style="background: rgb(217, 217, 217) none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-weight: normal; font-family: Arial;">double d</span></strong>)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"> <span style="">&nbsp;</span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">，同时覆盖了基类的虚函数，我们再来具体来分析一下，基类共有几个函数，派生类共有几个函数：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;">
<table class="MsoTableGrid" style="border: medium none ; border-collapse: collapse;" border="1" cellpadding="0" cellspacing="0">
<tbody>
<tr style="">
<td style="border: 1pt solid windowtext; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 50.4pt;" valign="top" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">类型</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: solid solid solid none; border-color: windowtext windowtext windowtext rgb(236, 233, 216); border-width: 1pt 1pt 1pt medium; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 180pt;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: solid solid solid none; border-color: windowtext windowtext windowtext rgb(236, 233, 216); border-width: 1pt 1pt 1pt medium; padding: 0cm 5.4pt; background: gray none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial; width: 195.7pt;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">派生类</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td></tr>
<tr style="">
<td style="border-style: none solid solid; border-color: rgb(236, 233, 216) windowtext windowtext; border-width: medium 1pt 1pt; padding: 0cm 5.4pt; width: 50.4pt; background-color: transparent;" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">部分</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 180pt; background-color: transparent;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">int</span></strong> <strong><span style="font-family: Arial;">i</span></strong>)</span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 195.7pt; background-color: transparent;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">int</span></strong> <strong><span style="font-family: Arial;">i</span></strong>)</span></p></td></tr>
<tr style="height: 33.15pt;">
<td style="border-style: none solid solid; border-color: rgb(236, 233, 216) windowtext windowtext; border-width: medium 1pt 1pt; padding: 0cm 5.4pt; width: 50.4pt; height: 33.15pt; background-color: transparent;" width="67">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: center;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">静态部分</span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 180pt; height: 33.15pt; background-color: transparent;" valign="top" width="240">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p><font color="#000000" face="Times New Roman" size="3">&nbsp;</font></o:p></span></p></td>
<td style="border-style: none solid solid none; border-color: rgb(236, 233, 216) windowtext windowtext rgb(236, 233, 216); border-width: medium 1pt 1pt medium; padding: 0cm 5.4pt; width: 195.7pt; height: 33.15pt; background-color: transparent;" valign="top" width="261">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">double d</span></strong>)</span></p></td></tr></tbody></table></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;"></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;">从表中我们可以看到，派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;">中函数指针指向的是自己的重写的虚函数地址。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;"></span><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US"><o:p></o:p></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;">我们再来分析一下以下三句代码</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: 宋体;"></span><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US"><o:p></o:p></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">Base *pb = new Derive();<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">pb-&gt;fun(1);//<b>Derive::fun(int i)</b></span><b><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;" lang="EN-US"><o:p></o:p></span></b></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; line-height: 150%; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); line-height: 150%; font-family: Arial;" lang="EN-US">pb-&gt;fun((double)0.01);//<b>Derive::fun(int i)<o:p></o:p></b></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">第一句不必多说了，第二句，理所当然调用派生类的虚函数版本，第三句，嘿，感觉又怪怪的，其实呀，</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">程序很笨的了，在运行时，埋头闯进派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">表中，只眼一看，靠，竞然没有想要的版本，真是想不通，基类指针为什么不四处转转再找找呢</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">?</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">呵呵，原来是眼力有限，基类年纪这么老了，想必肯定是老花了，它那双眼睛看得到的仅是自己的非</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">部分</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">即静态部分</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">和自己要管理的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Vtable</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">部分，派生类的</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">void fun(<strong><span style="font-family: Arial;">double d</span></strong>)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">那么远，看不到呀!再说了，派生类什么都要管，难道派生类没有自己的一点权力吗?哎，不吵了，各自管自己的吧</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">^_^<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">唉</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">!你是不是</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">要叹气了，基类指针能进行多态调用，但是始终不能进行派生类的重载调用啊</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">参考例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">6)~~~<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;">&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;">再来看看例9，</p></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">本例的效果同例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">6，异曲同工。想必你理解了上面的这些例子后，这个也是小Kiss了。<br>
<br>
</span>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="center"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">小结：</span><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><font color="#000000"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US">&nbsp;</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></font></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">重载</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">是根据函数的参数列表来选择要调用的函数版本，而多态是根据运行时对象的实际类型来选择要调用的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本，多态的实现是通过派生类对基类的虚</span><b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数进行覆盖</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">来实现的，若派生类没有对基类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数进行覆盖</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">的话，则派生类会自动继承基类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本，此时无论基类指针指向的对象是基类型还是派生类型，都会调用基类版本的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数；如果派生类对基类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数进行覆盖</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">的话，则会在运行时根据对象的实际类型来选择要调用的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本，例如基类指针指向的对象类型为派生类型，则会调用派生类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本，从而实现多态。</span><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 12pt; font-family: 宋体;" lang="EN-US"><font color="#000000">&nbsp;<o:p></o:p></font></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">使用多态的本意是要我们在基类中声明函数为</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">，并且是要在派生类中覆盖</span><b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函
数版本，注意，此时的函数原型与基类保持一致，即同名同参数类型；如果你在派生类中新添加函数版本，你不能通过基类指针动态调用派生类的新的函数版本，这
个新的函数版本只作为派生类的一个重载版本。还是同一句话，重载只有在当前类中有效，不管你是在基类重载的，还是在派生类中重载的，两者互不牵连。如果明
白这一点的话，在例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">6</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">、例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">9</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中，我们也会对其的输出结果顺利地理解。</span><span style="font-size: 12pt; color: rgb(51, 51, 51); font-family: 宋体;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 12pt; color: rgb(51, 51, 51); font-family: 宋体;" lang="EN-US">&nbsp;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">重载是静态联编的，多态是动态联编的。进一步解释，重载与指针实际指向的对象类型无关，多态与指针实际指向的对象类型相关。若基类的指针调用派生类的重载版本，</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">编绎认为是非法的，</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">编绎器只认为基类指针只能调用基类的重载版本，重载只在当前类的名字空间作用域内有效，继承会失去重载的特性，当然，若此时的基类指针调用的是一个虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数，那么它还会进行动态选择基类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本还是派生类的虚</span><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数版本来进行具体的操作，这是通过基类指针实际指向的对象类型来做决定的，所以说重载与指针实际指向的对象类型无关，多态与指针实际指向的对象类型相关。</span><span style="font-size: 12pt; color: rgb(51, 51, 51); font-family: 宋体;" lang="EN-US">&nbsp;</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 12pt; color: rgb(51, 51, 51); font-family: 宋体;" lang="EN-US">&nbsp;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;&nbsp;&nbsp; </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">最后阐明一点，虚</span><b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数同样可以进行重载，但是重载只能是在当前自己名字空间作用域内有效</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">请再次参考例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">6)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left;" align="left"><span style="font-size: 12pt; color: rgb(51, 51, 51); font-family: 宋体;" lang="EN-US">&nbsp;</span></p>
<br>
]]></description>
</item><item>
<title><![CDATA[重载、覆盖、多态与函数隐藏[1,2]]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=19411</link>
<author>eaglebetter</author>
<pubDate>2006/10/25 20:10:16</pubDate>
<description><![CDATA[<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">首先，我们来看一个非常简单的例子，理解一下什么叫函数隐藏<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide<o:p></o:p></span>。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>void fun() { cout &lt;&lt; "Base::fun()" &lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>void fun(<strong>int i</strong>) { cout &lt;&lt; "Derive::fun()" &lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">下面一句错误，故屏蔽掉</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span><font color="#ff0000">//d.fun();error C2660: 'fun' : function does not take 0 parameters<o:p></o:p></font></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>d.fun(1);<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive *pd =new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">下面一句错误，故屏蔽掉</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;<font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><font color="#ff0000">//pd-&gt;fun();error C2660: 'fun' : function does not take 0 parameters<o:p></o:p></font></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pd-&gt;fun(1);<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pd;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">/*</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在不同的非命名空间作用域里的函数不构成重载</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">子类和父类是不同的两个作用域。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在本例中</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">两个函数在不同作用域中</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">故不够成重载</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">除非这个作用域是命名空间作用域。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">*/<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在这个例子中，函数不是重载</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">，也不是覆盖<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span>，而是隐藏<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide<o:p></o:p></span>。</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">接下来的5个例子具体说明一下什么叫隐藏</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">1<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(){cout &lt;&lt; "Base::fun()" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive :public Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun<strong>2</strong>(){cout &lt;&lt; "Derive::fun2()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun();//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确，派生类没有与基类同名函数声明，则基类中的所有同名重载函数都会作为候选函数。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun(1);//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确，派生类没有与基类同名函数声明，则基类中的所有同名重载函数都会作为候选函数。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">2<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(){cout &lt;&lt; "Base::fun()" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive :public Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">新的函数版本</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类所有的重载版本都被屏蔽</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在这里</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">我们称之为函数隐藏</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">派生类中有基类的同名函数的声明，则基类中的同名函数不会作为候选函数，即使基类有不同的参数表的多个版本的重载函数。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(<strong>int i,int j</strong>){cout &lt;&lt; "Derive::fun(int i,int j)" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun2(){cout &lt;&lt; "Derive::fun2()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun(1,2);<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">下面一句错误，故屏蔽掉</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;<font color="#f70909">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><font color="#f70909">//d.fun();error C2660: 'fun' : function does not take 0 parameters<o:p></o:p></font></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">3<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(){cout &lt;&lt; "Base::fun()" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive :public Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">覆盖</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类的其中一个函数版本</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">,</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">同样基类所有的重载版本都被隐藏</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">派生类中有基类的同名函数的声明，则基类中的同名函数不会作为候选函数，即使基类有不同的参数表的多个版本的重载函数。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void <strong>fun()</strong>{cout &lt;&lt; "Derive::fun()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun2(){cout &lt;&lt; "Derive::fun2()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">下面一句错误，故屏蔽掉</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><font color="#ff0000"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//d.fun(1);error C2660: 'fun' : function does not take 1 parameters<o:p></o:p></font></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">4<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(){cout &lt;&lt; "Base::fun()" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive :public Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>using Basic::fun;<o:p></o:p></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void <strong>fun()</strong>{cout &lt;&lt; "Derive::fun()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun2(){cout &lt;&lt; "Derive::fun2()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun();//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun(1);//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">/*<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">输出结果</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Base::fun(int i)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Press any key to continue<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">*/<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">5<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(){cout &lt;&lt; "Base::fun()" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl;}//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive :public Basic{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>using Basic::fun;<o:p></o:p></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(<strong>int i,int j</strong>){cout &lt;&lt; "Derive::fun(int i,int j)" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun2(){cout &lt;&lt; "Derive::fun2()" &lt;&lt; endl;}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Derive d;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun();//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun(1);//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>d.fun(1,2);//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">正确</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">/*<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">输出结果</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Base::fun()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Base::fun(int i)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun(int i,int j)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Press any key to continue<o:p></o:p></span></p>

<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">*/<br>
</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">好了，我们先来一个小小的总结重载与覆盖两者之间的特征</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span>&nbsp;</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">重载<font face="Arial">overload</font>的特征：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">相同的范围<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在同一个类中</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span>；</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数名相同参数不同；</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字可有可无。</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">&nbsp;</span></span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">覆盖<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">override</span>是指派生类函数覆盖基类函数，覆盖的特征是：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"></span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">不同的范围<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span>分别位于派生类与基类<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span>；</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数名和参数都相同；</span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">基类函数必须有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">若没有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字则称之为隐藏<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide<o:p></o:p></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span></span></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"></span></span></span>&nbsp;</p></span></span>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">如果基类有某个函数的多个重载<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"><font face="Arial">)</font>版本，而你在派生类中重写</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(override)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">了基类中的一个或多个函数版本，或是在派生类中重新添加了新的函数版本</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数名相同，参数不同</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">，则所有基类的重载版本都被屏蔽，在这里我们称之为隐藏</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">hide</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。所以，在一般情况下，你想在派生类中使用新的函数版本又想使用基类的函数版本时，你应该在派生类中重写基类中的所有重载版本。你若是不想重写基类的重载的函数版本，则你应该使用例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">4</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">或例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">5</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">方式，显式声明基类名字空间作用域。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">事实上，</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">编译器认为，相同函数名不同参数的函数之间根本没有什么关系，它们根本就是两个毫不相关的函数。只是</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">语
言为了模拟现实世界，为了让程序员更直观的思维处理现实世界中的问题，才引入了重载和覆盖的概念。重载是在相同名字空间作用域下，而覆盖则是在不同的名字
空间作用域下，比如基类和派生类即为两个不同的名字空间作用域。在继承过程中，若发生派生类与基类函数同名问题时，便会发生基类函数的隐藏。当然，这里讨
论的情况是基类函数前面没有<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字。在有<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual </span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字关键字时的情形我们另做讨论。</span></span></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">继承类重写了基类的某一函数版本，以产生自己功能的接口。此时</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">编绎器认为，你现在既然要使用派生类的自己重新改写的接口，那我基类的接口就不提供给你了</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">当然你可以用显式声明名字空间作用域的方法，见<font face="Arial">[C++基础]重载、覆盖、多态与函数隐藏(1)</font></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。而不会理会你基类的接口是有重载特性的。若是你要在派生类里继续保持重载的特性，那你就自己再给出接口重载的特性吧。所以在派生类里，只要函数名一样，基类的函数版本就会被无情地屏蔽。在编绎器中，屏蔽是通过名字空间作用域实现的。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">所以，在派生类中要保持基类的函数重载版本，就应该重写所有基类的重载版本。重载只在当前类中有效，继承会失去函数重载的特性。也就是说，要把基类的重载函数放在继承的派生类里，就必须重写。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数，具体规则我们也来做一小结：</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">如果派生类的函数与基类的函数同名，但是参数不同。此时，若基类无</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字，基类的函数将被隐藏。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">注意别与重载混淆，虽然函数名相同参数不同应称之为重载，但这里不能理解为重载，因为派生类和基类不在同一名字空间作用域内。这里理解为隐藏</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">如果派生类的函数与基类的函数同名，但是参数不同。此时，若基类有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字，基类的函数将被隐式继承到派生类的<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable中</span>。此时派生类<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">vtable中的函数指向基类版本的函数地址。</span>同时这个新的函数版本添加到派生类中，作为派生类的重载版本。但在基类指针实现多态调用函数方法时，这个新的派生类函数版本将会被隐藏。</span></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">如果派生类的函数与基类的函数同名，并且参数也相同，但是基类函数没有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字。此时，基类的函数被隐藏。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">注意别与覆盖混淆，这里理解为隐藏</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Wingdings;" lang="EN-US"><span style="">n<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">如果派生类的函数与基类的函数同名，并且参数也相同，但是基类函数有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字。此时，基类的函数不会被“隐藏”。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">(</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">在这里，你要理解为覆盖哦</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">^_^)</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">。</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt 21pt; text-indent: -21pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;"></span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span>&nbsp;</p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">插曲：基类函数前没有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字时，我们要重写更为顺口些，在有</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">关键字时，我们叫覆盖更为合理些，戒此，我也希望大家能够更好的理解</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">C++</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">中一些微妙的东西。费话少说，我们举例说明吧。</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span lang="EN-US"><o:p><font size="3">&nbsp;</font></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">6<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><b style="">virtual</b> void fun() { cout &lt;&lt; "Base::fun()" &lt;&lt; endl; }//<strong>overload<o:p></o:p></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span><b style="">virtual</b> void fun(int i) { cout &lt;&lt; "Base::fun(int i)" &lt;&lt; endl; }//<strong>overload<o:p></o:p></strong></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun() { cout &lt;&lt; "Derive::fun()" &lt;&lt; endl; }<b style="">//override<o:p></o:p></b></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp; </span></span></b><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;</span>void fun(int i) { cout &lt;&lt; "Derive::fun(int i)" &lt;&lt; endl; }<b style="">//override<o:p></o:p></b></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i,int j){ cout&lt;&lt; "Derive::fun(int i,int j)" &lt;&lt;endl;}//overload<o:p></o:p></span></b></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span><b style="">Base</b> *pb<span style="">&nbsp; </span>= new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>pb-&gt;fun();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>pb-&gt;fun(1);<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>//</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">下面一句错误，故屏蔽掉</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><font color="#ff0000"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>//pb-&gt;fun(1,2);virtual</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">函数不能进行</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">overload,error C2661: 'fun' : no overloaded function takes 2 parameters<o:p></o:p></span></font></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>cout &lt;&lt; endl;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span><b style="">Derive</b> *pd<span style="">&nbsp; </span>= new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>pd-&gt;fun();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>pd-&gt;fun(1);<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>pd-&gt;fun(1,2);//overload<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>delete pb;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>delete pd;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">/*<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">输出结果</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun(int i)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun(int i)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Derive::fun(int i,int j)<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">Press any key to continue<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">*/<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">7-1<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>virtual</strong> void fun(<strong>int i</strong>){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><strong><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></strong></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pb-&gt;fun(1);//<b style="">Base::fun(int i)</b><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}</span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">例7-2</span></p>
<span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>
</o:p><p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong>virtual</strong> void fun(<strong>int</strong> <strong>i</strong>){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp; </span>void fun(<strong>double d</strong>){ cout &lt;&lt;"Derive::fun(double d)"&lt;&lt; endl; }<span style="">&nbsp; </span><o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pb-&gt;fun(1);//<strong>Base::fun(int i)<o:p></o:p></strong></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>pb-&gt;fun((double)0.01);//<strong>Base::fun(int i)<o:p></o:p></strong></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}</span></p></span>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: 宋体;">例</span><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">8-1<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">#include &lt;iostream&gt; <o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">using namespace std;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><b style="">virtual</b> void fun(int i){ cout &lt;&lt;"Base::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">class Derive : public Base{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">public:<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><b style=""><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>void fun(int i){ cout &lt;&lt;"Derive::fun(int i)"&lt;&lt; endl; }<o:p></o:p></span></b></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">};<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><o:p>&nbsp;</o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">int main()<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">{<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Base *pb = new Derive();<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pb-&gt;fun(1);//<b style="">Derive::fun(int i)</b><o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>delete pb;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>return 0;<o:p></o:p></span></p>

<p class="MsoNormal" style="margin: 0cm 0cm 0pt;"><span style="font-size: 9pt; color: rgb(51, 51, 51); font-family: Arial;" lang="EN-US">}</span></p>
<br>]]></description>
</item><item>
<title><![CDATA[使用VC6.0实现窗口的任意分割]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=18994</link>
<author>eaglebetter</author>
<pubDate>2006/10/8 19:00:34</pubDate>
<description><![CDATA[
<P><B>一、关于CSplitterWnd类</B><BR>&nbsp;我们在使用CuteFtp或者NetAnt等工具的时候，一般都会被其复杂的界面所吸引，在这些界面中窗口被分割为若干的区域，真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面，也实现窗口的任意的分割呢 ？在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口，每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口，但是自动加入的分割条总是不能让我们满意，因此我们还是通过手工增加代码来熟悉这个类。 <BR>&nbsp;CSplitterWnd的构造函数主要包括下面三个。 <BR><FONT color=#663333>BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,CCreateContext* pContext,DWORD dwStyle,UINT nID);</FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;功能描述：该函数用来创建动态切分窗口。<BR>&nbsp;&nbsp;&nbsp;&nbsp;参数含义：pParentWnd 切分窗口的父框架窗口。<BR>&nbsp;&nbsp;&nbsp;&nbsp;nMaxRows,nMaxCols是创建的最大的列数和行数。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;sizeMin是窗格的现实大小。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;pContext 大多数情况下传给父窗口。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;nID是字窗口的ID号. <BR>&nbsp;&nbsp;<FONT color=#663333>BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID) </FONT><BR>&nbsp;&nbsp;功能描述：用来创建切分窗口。 <BR>&nbsp;&nbsp;参数含义同上。 <BR><FONT color=#663333>BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,CcreateContext* pContext);</FONT> <BR>&nbsp;&nbsp;功能描述：为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必 须先将切分窗口创建好。 <BR>&nbsp;&nbsp;参数含义：同上。<BR>&nbsp;&nbsp;从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic，在函数中都调用了一个保护函数CreateCommon，从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗口。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#663333>DWORD dwCreateStyle = dwStyle &amp; ~(WS_HSCROLL|WS_VSCROLL);<BR>&nbsp;&nbsp;&nbsp;&nbsp;if (afxData.bWin4) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwCreateStyle &amp;= ~WS_BORDER; // create with the same wnd-class as MDI-Frame (no erase bkgnd) <BR>&nbsp;&nbsp;&nbsp;&nbsp;if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle, 0, 0, 0, 0,pParentWnd-&gt;m_hWnd, (HMENU)nID, NULL)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE; // create invisible </FONT><BR><BR><B>二、创建嵌套分割窗口</B> <BR><B>&nbsp;&nbsp;2.1创建动态分割窗口</B><BR>&nbsp;&nbsp;动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。 <BR>&nbsp;&nbsp;<FONT color=#663333>m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);</FONT> <BR>&nbsp;&nbsp;但是动态创建的分割窗口的窗格数目不能超过2x2，而且对于所有的窗格，都必须共享同一个视图，所受的限制也比较多，因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。 <BR>&nbsp;&nbsp;<B>2.2创建静态分割窗口</B><BR>与动态创建相比，静态创建的代码要简单许多，而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。 <BR>在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下：</P>
<DIV align=center>
<TABLE height=223 cellSpacing=1 cellPadding=0 width=446 bgColor=#666666 border=0>
<TBODY>
<TR bgColor=#eeeeee>
<TD colSpan=2 height=46>
<DIV align=center><FONT face="Arial, Helvetica, sans-serif" size=2>CCuteFTPView</FONT></DIV></TD></TR>
<TR bgColor=#eeeeee>
<TD height=123>
<DIV align=center><FONT face="Arial, Helvetica, sans-serif" size=2>CView2</FONT></DIV></TD>
<TD height=123>
<DIV align=center><FONT face="Arial, Helvetica, sans-serif" size=2>CView3</FONT></DIV></TD></TR>
<TR bgColor=#eeeeee>
<TD colSpan=2 height=44>
<DIV align=center><FONT face="Arial, Helvetica, sans-serif" size=2>CView4</FONT></DIV></TD></TR></TBODY></TABLE></DIV>
<P>&nbsp;&nbsp;创建步骤： <BR>&nbsp;&nbsp;▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP，生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2,CView3 CView4. <BR>&nbsp;&nbsp;▲ <B>增加成员：</B> <BR>&nbsp;&nbsp;在Cmainfrm.h中我们将增加下面的代码： <BR>&nbsp;&nbsp;<FONT color=#663333>CSplitterWnd wndSplitter1;<BR>&nbsp;&nbsp;CSplitterWnd wndSplitter2;</FONT><BR>&nbsp;&nbsp;▲ <B>重载CMainFrame::OnCreateClient()函数：</B><BR>&nbsp;&nbsp;<FONT color=#663333>BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext) <BR>&nbsp;&nbsp;{ //创建一个静态分栏窗口，分为三行一列 <BR>&nbsp;&nbsp;&nbsp;&nbsp;if(m_wndSplitter1.CreateStatic(this,3,1)==NULL) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE;<BR>&nbsp;&nbsp;&nbsp;&nbsp;//将CCuteFTPView连接到0行0列窗格上<BR>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext); <BR>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext); //将CView4连接到0行2列<BR>&nbsp;&nbsp;&nbsp;&nbsp;if(m_wndSplitter2.CreateStatic(&amp;m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE, m_wndSplitter.IdFromRowCol(1, 0))==NULL) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return FALSE; //将第1行0列再分开1行2列 <BR>&nbsp;&nbsp;&nbsp;&nbsp;//将CView2类连接到第二个分栏对象的0行0列<BR>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext); //将CView3类连接到第二个分栏对象的0行1列<BR>&nbsp;&nbsp;&nbsp;&nbsp;m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext); <BR>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <BR>&nbsp;&nbsp;} </FONT><BR><B>2.3实现各个分割区域的通信</B> <BR>■<B>有文档相连的视图之间的通信<BR></B>由AppWizard生成的CCuteFTPView是与文档相连的，同时我们也让CView2与文档相连，因此我们需要修改CCuteFTPApp的InitInstance()函数，我们将增加下面的部分。<BR><FONT color=#663333>AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE, <BR>RUNTIME_CLASS(CMainDoc), <BR>RUNTIME_CLASS(CMDIChildWnd), <BR>RUNTIME_CLASS(CView2))); </FONT><BR>我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类 是不能安全的与除文档类之外的其余的视图类通信的。因此我们只能让他们都与文档 类通信。在文档中我们设置相应的指针以用来获的各个视图。我们重载 CCuteFTPView::OnOpenDocument()函数； <BR><FONT color=#663333>CCuteFTPView* pCuteFTPView;<BR>CView2* pView2;<BR>POSITION pos;<BR>CView* pView;<BR>while(pos!=NULL)<BR>{<BR>&nbsp;&nbsp;pView=GetNextView(pos); <BR>&nbsp;&nbsp;if(pView-&gt;IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL) <BR>&nbsp;&nbsp;&nbsp;&nbsp;pCuteFTPView=(CCuteFTPView*)pView; <BR>&nbsp;&nbsp;else(pView-&gt;IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL) <BR>&nbsp;&nbsp;&nbsp;&nbsp;pView2=(CView2*)pView; <BR>} </FONT><BR>这样我们在文档类中就获的了跟它相连的所有的视图的指针。<BR>如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下： <BR><FONT color=#663333>CCuteFTPDoc* pDoc=GetDocument();<BR>CView2* pView2=pDoc-&gt;pView3;<BR>pView3.DoIt(); </FONT><BR>■<B>无文档视图与文档关联视图之间的通信<BR></B>CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说，CView2只能安全的与CCuteFTPDoc通信，因此，CView3如果需要跟CView2通信，也必须借助于文档类。因此程序的关键是如何在CView3中获得文档的指针。视图类中没有这样的类成员可以用来直接访问文档类。但是我们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获得程序主窗口了的指针，就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。<BR><BR>CView3中的代码如下： <BR><BR><FONT color=#663333>CMainFrame* MainFrame=(CMainFrame*)this-&gt;GetParent()-&gt;GetParent(); <BR>CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame-&gt;GetActiveDocument();<BR>if(Doc!=NULL) Doc-&gt;DoIt(); <BR><BR>CCuteFTPDoc中的相应的处理函数DoIt()代码如下： <BR><BR>CView2* pView2; <BR>POSITION pos; <BR>CView* pView; <BR>while(pos!=NULL) <BR>{ <BR>&nbsp;&nbsp;pView=GetNextView(pos);<BR>&nbsp;&nbsp;if(pView-&gt;IsKindOf(RUNTIME_CLASS(CView2))==NULL) <BR>&nbsp;&nbsp;pView2=(CView2*)pView; <BR>} <BR>pView2-&gt;DoIt(); </FONT><BR>■<B>无文档关联视图之间的通信<BR></B>CView3和CView4都是不跟文档相连的，如何实现他们之间的通信呢。 正如我们在上面所说的那样，由于在主框架中我们可以访问任意的视图，因此我们的主要任 务还是在程序中获得主框架的指针。在CView3中访问CView4中的方法DoIt()。 <BR><FONT color=#663333>CMainFrame* MainFrame=(CMainFrame*)this-&gt;GetParent()-&gt;GetParent(); <BR>CView4* View4=(CView4*)MainFrame-&gt;m_wndSplitter1.GetPane(2,0); <BR>View4-&gt;DoIt(); </FONT><BR><BR>到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以实现其他的一些流行界面例如NetAnts，Foxmail的分割。 <BR><BR><B>三、关于对话框的分割</B> <BR>到目前为止，只有基于文档/视图的程序才能使用CSplitterWnd，而基于对话框的应用程序却不支持CSplitterWnd,但是如果我们在继承类中重载一些虚拟方法，也能使CSplitterWnd 在对话框程序中使用。从MFC的源程序WinSplit.cpp中可以看出，为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame(),因此如果在对话框中使用，我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。<BR>&nbsp;&nbsp;<FONT color=#663333>virtual void StartTracking(int ht); <BR>&nbsp;&nbsp;virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL); <BR>&nbsp;&nbsp;virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL ); <BR>&nbsp;&nbsp;virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); <BR>&nbsp;&nbsp;virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult ); <BR>&nbsp;&nbsp;virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult ); </FONT><BR>具体实现如下，实现中我将给出原有代码的主要部分以及修改后的代码以作对比。<BR>在cpp文件中加入下面的枚举类型。 <BR><FONT color=#663333>enum HitTestValue <BR>{ <BR>&nbsp;&nbsp;noHit = 0,//表示没有选中任何对象<BR>&nbsp;&nbsp;vSplitterBox = 1,<BR>&nbsp;&nbsp;hSplitterBox = 2,<BR>&nbsp;&nbsp;bothSplitterBox = 3,<BR>&nbsp;&nbsp;vSplitterBar1 = 101,//代表各个方向的水平分割条<BR>&nbsp;&nbsp;vSplitterBar15 = 115,<BR>&nbsp;&nbsp;hSplitterBar1 = 201,//代表垂直方向的各个分割条<BR>&nbsp;&nbsp;hSplitterBar15 = 215,<BR>&nbsp;&nbsp;splitterIntersection1 = 301,//代表各个交叉点<BR>&nbsp;&nbsp;splitterIntersection225 = 525<BR>};<BR><BR>CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)<BR>{<BR>&nbsp;&nbsp;ASSERT_VALID(this); <BR>&nbsp;&nbsp;//获得当前的获得焦点的窗口<BR>&nbsp;&nbsp;//下面注释粗体的是原有的代码的主要部分。<BR>&nbsp;&nbsp;// CWnd* pView = NULL;<BR>&nbsp;&nbsp;//CFrameWnd* pFrameWnd = GetParentFrame();<BR>&nbsp;&nbsp;//ASSERT_VALID(pFrameWnd);<BR>&nbsp;&nbsp;//pView = pFrameWnd-&gt;GetActiveView();<BR>&nbsp;&nbsp;//if (pView == NULL)<BR>&nbsp;&nbsp;// pView = GetFocus();<BR>&nbsp;&nbsp;CWnd* pView = GetFocus();<BR>&nbsp;&nbsp;if (pView != NULL &amp;&amp; !IsChildPane(pView, pRow, pCol))<BR>&nbsp;&nbsp;&nbsp;&nbsp;pView = NULL;<BR>&nbsp;&nbsp;return pView; <BR>} <BR><BR>void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd) <BR>{<BR>&nbsp;&nbsp;CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd; <BR>&nbsp;&nbsp;//下面加注释粗体的是原有代码的主要部分。<BR>&nbsp;&nbsp;//FrameWnd* pFrameWnd = GetParentFrame();<BR>&nbsp;&nbsp;//ASSERT_VALID(pFrameWnd); <BR>&nbsp;&nbsp;//pFrameWnd-&gt;SetActiveView((CView*)pPane); <BR>&nbsp;&nbsp;pPane-&gt;SetFocus();//修改后的语句 <BR>}<BR><BR>void CxSplitterWnd::StartTracking(int ht)<BR>{<BR>&nbsp;&nbsp;ASSERT_VALID(this); <BR>&nbsp;&nbsp;if (ht == noHit) <BR>&nbsp;&nbsp;&nbsp;&nbsp;return;<BR>&nbsp;&nbsp;// GetHitRect will restrict 'm_rectLimit' as appropriate <BR>&nbsp;&nbsp;GetInsideRect(m_rectLimit);<BR>&nbsp;&nbsp;if (ht &gt;= splitterIntersection1 &amp;&amp; ht &lt;= splitterIntersection225) <BR>&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;&nbsp;&nbsp;// split two directions (two tracking rectangles) <BR>&nbsp;&nbsp;&nbsp;&nbsp;int row = (ht - splitterIntersection1) / 15; <BR>&nbsp;&nbsp;&nbsp;&nbsp;int col = (ht - splitterIntersection1) % 15; <BR>&nbsp;&nbsp;&nbsp;&nbsp;GetHitRect(row + vSplitterBar1, m_rectTracker); <BR>&nbsp;&nbsp;&nbsp;&nbsp;int yTrackOffset = m_ptTrackOffset.y; <BR>&nbsp;&nbsp;&nbsp;&nbsp;m_bTracking2 = TRUE; <BR>&nbsp;&nbsp;&nbsp;&nbsp;GetHitRect(col + hSplitterBar1, m_rectTracker2); <BR>&nbsp;&nbsp;&nbsp;&nbsp;m_ptTrackOffset.y = yTrackOffset; <BR>&nbsp;&nbsp;} <BR>&nbsp;&nbsp;else if (ht == bothSplitterBox) <BR>&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;// hit on splitter boxes (for keyboard) <BR>&nbsp;&nbsp;GetHitRect(vSplitterBox, m_rectTracker); <BR>&nbsp;&nbsp;int yTrackOffset = m_ptTrackOffset.y; <BR>&nbsp;&nbsp;m_bTracking2 = TRUE; <BR>&nbsp;&nbsp;GetHitRect(hSplitterBox, m_rectTracker2); <BR>&nbsp;&nbsp;m_ptTrackOffset.y = yTrackOffset; // center it <BR>&nbsp;&nbsp;m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2); m_rectTracker2.OffsetRect(m_rectLimit.Width()/2, 0); <BR>&nbsp;&nbsp;} <BR>&nbsp;&nbsp;else<BR>&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;// only hit one bar <BR>&nbsp;&nbsp;GetHitRect(ht, m_rectTracker); <BR>&nbsp;&nbsp;} <BR><BR>//下面加注释的将从程序中删去。 <BR>//CView* pView = (CView*)GetActivePane(); <BR>//if (pView != NULL &amp;&amp; pView-&gt;IsKindOf(RUNTIME_CLASS(CView))) <BR>//{ <BR>// ASSERT_VALID(pView); <BR>// CFrameWnd* pFrameWnd = GetParentFrame(); <BR>//ASSERT_VALID(pFrameWnd); <BR>//pView-&gt;OnActivateFrame(WA_INACTIVE, pFrameWnd); <BR>// } <BR>// steal focus and capture<BR>&nbsp;&nbsp;SetCapture();<BR>&nbsp;&nbsp;SetFocus();<BR>&nbsp;&nbsp;// make sure no updates are pending <BR>&nbsp;&nbsp;RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW); <BR>&nbsp;&nbsp;// set tracking state and appropriate cursor<BR>&nbsp;&nbsp;m_bTracking = TRUE;<BR>&nbsp;&nbsp;OnInvertTracker(m_rectTracker); <BR>&nbsp;&nbsp;if (m_bTracking2) <BR>&nbsp;&nbsp;&nbsp;&nbsp;OnInvertTracker(m_rectTracker2); <BR>&nbsp;&nbsp;m_htTrack = ht; <BR>&nbsp;&nbsp;SetSplitCursor(ht); <BR>&nbsp;&nbsp;}<BR><BR>BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam) <BR>{ <BR>&nbsp;&nbsp;if (CWnd::OnCommand(wParam, lParam)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <BR>&nbsp;&nbsp;//下面粗体的是原程序的语句 <BR>//<B>return GetParentFrame()-&gt;SendMessage(WM_COMMAND, wParam, lParam); </B><BR>&nbsp;&nbsp;return GetParent()-&gt;SendMessage(WM_COMMAND, wParam, lParam); <BR>}<BR>BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )<BR>{<BR>&nbsp;&nbsp;if (CWnd::OnNotify(wParam, lParam, pResult)) <BR>&nbsp;&nbsp;&nbsp;&nbsp;return TRUE; <BR>&nbsp;&nbsp;//下面粗体的是源程序的语句<BR>&nbsp;&nbsp;//<B>*pResult = GetParentFrame()-&gt;SendMessage(WM_NOTIFY, wParam, lParam);</B><BR>&nbsp;&nbsp;*pResult = GetParent()-&gt;SendMessage(WM_NOTIFY, wParam, lParam);<BR>&nbsp;&nbsp;return TRUE;<BR>} <BR><BR>BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) <BR>{ <BR>&nbsp;&nbsp;// The code line below is necessary if using CxSplitterWnd in a regular dll <BR>&nbsp;&nbsp;// AFX_MANAGE_STATE(AfxGetStaticModuleState());<BR>&nbsp;&nbsp;return CWnd::OnWndMsg(message, wParam, lParam, pResult); <BR>} </FONT><BR>这样我们就可以在对话框中使用CxSplitterWnd类了。 <BR><BR><B>四、CSplitterWnd的扩展</B> <BR>CSplitterWnd扩展话题是很多的，我们可以通过对原有方法的覆盖或者增加新的方法来扩展CSplitterWnd。我们在此仅举两个方面的例子。 <BR><B>4.1锁定切分条</B><BR>当用户创建好分割窗口后，有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。锁定切分条的最简单的方法莫过于不让CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息，而是将这些消息交给CWnd窗口进行处理，从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。修改为如下： <BR><FONT color=#663333>void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point) <BR>{ <BR>&nbsp;&nbsp;CWnd::OnLButtonDown(nFlags,point);<BR>} </FONT><BR>其余的处理方法类似。 <BR><B>4.2切分条的定制</B> <BR>由Window自己生成的切分条总是固定的，没有任何的变化，我们在使用一些软件比如ACDSee的时候却能发现它们的切分条却是和自动生成的切分条不一样的。那么如何定制自己的切分条呢？通过重载CSplitterWnd的虚方法OnDrawSplitter和OnInvertTracker可以达到这样的目的。下面的代码生成的效果是分割窗口的边界颜色为红色，分割条的颜色为绿色.代码如下：<BR><FONT color=#663333>void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &amp;rectArg)<BR>{<BR>&nbsp;&nbsp;if(pDC==NULL) <BR>&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);<BR>&nbsp;&nbsp;return;<BR>&nbsp;&nbsp;} <BR>&nbsp;&nbsp;ASSERT_VALID(pDC);<BR>&nbsp;&nbsp;CRect rc=rectArg;<BR>&nbsp;&nbsp;switch(nType) <BR>&nbsp;&nbsp;{ <BR>&nbsp;&nbsp;case splitBorder:<BR>&nbsp;&nbsp;//重画分割窗口边界,使之为红色 <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER); <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;return; <BR>&nbsp;&nbsp;case splitBox:<BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER); <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-CX_BORDER,-CY_BORDER);<BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(0,0,0)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));<BR>&nbsp;&nbsp;&nbsp;&nbsp;return; <BR>&nbsp;&nbsp;case splitBar: <BR>&nbsp;&nbsp;//重画分割条，使之为绿色 <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(255,255,255));<BR>&nbsp;&nbsp;&nbsp;&nbsp;rc.InflateRect(-5,-5); <BR>&nbsp;&nbsp;&nbsp;&nbsp;pDC-&gt;Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0)); <BR>&nbsp;&nbsp;&nbsp;&nbsp;return; <BR>&nbsp;&nbsp;default: <BR>&nbsp;&nbsp;&nbsp;&nbsp;ASSERT(FALSE); <BR>&nbsp;&nbsp;} <BR>&nbsp;&nbsp;pDC-&gt;FillSolidRect(rc,RGB(0,0,255));<BR>} <BR>void CSplitterWndEx::OnInvertTracker(CRect &amp;rect) <BR>{ <BR>&nbsp;&nbsp;ASSERT_VALID(this);<BR>&nbsp;&nbsp;ASSERT(!rect.IsRectEmpty()); <BR>&nbsp;&nbsp;ASSERT((GetStyle()&amp;WS_CLIPCHILDREN)==0);<BR>&nbsp;&nbsp;CRect rc=rect; <BR>&nbsp;&nbsp;rc.InflateRect(2,2);<BR>&nbsp;&nbsp;CDC* pDC=GetDC(); <BR>&nbsp;&nbsp;CBrush* pBrush=CDC::GetHalftoneBrush();<BR>&nbsp;&nbsp;HBRUSH hOldBrush=NULL;<BR>&nbsp;&nbsp;if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC-&gt;m_hDC,pBrush-&gt;m_hObject);<BR>&nbsp;&nbsp;pDC-&gt;PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS); <BR>&nbsp;&nbsp;if(hOldBrush!=NULL) <BR>&nbsp;&nbsp;SelectObject(pDC-&gt;m_hDC,hOldBrush);<BR>&nbsp;&nbsp;ReleaseDC(pDC); <BR>} </FONT><BR>同样我们只要继承CSplitterWnd中的其余的一些虚拟方法就可以生成具有自己个性的分割窗口了。</P>]]></description>
</item><item>
<title><![CDATA[标准的C函数库]]></title>
<link>http://blogger.org.cn/blog/more.asp?name=eaglebetter&amp;id=18675</link>
<author>eaglebetter</author>
<pubDate>2006/9/23 21:46:01</pubDate>
<description><![CDATA[
<DIV id=msgcns!99802c383ef99240!200>
<DIV><FONT color=#ff0000 size=4><STRONG>C-1 标准输出输入函数<BR>C -2 字元检查函数<BR>C -3 字串函数<BR>C -4 数学函数<BR>C -5 日期/时间函数<BR>C -6 工具函数</STRONG></FONT></DIV>
<DIV>&nbsp;</DIV>
<DIV><BR><FONT style="BACKGROUND-COLOR: #ffff00" size=4>C-1 标准输出输入函数</FONT><BR>FILE* fopen(const char* filename, const char* mode):使用mode模式开启参数filename的档案,传回档案串流,失败传回NULL.<BR>FILE* freopen(const char* filename, const char* mode, FILE* stream):关闭档案后重新开启档案.<BR>int fflush(FILE* stream):清除缓冲区的内容,成功传回0,失败传回EOF.<BR>int fclose(FILE* stream):关闭档案.<BR>int remove(const char* filename):删除参数的档案,失败传回非零值.<BR>int rename(const char* oldname, const char* newname):将档案名称oldname改为newname,失败传回非零值.<BR>FILE* tmpfile():建立"wb+"模式的暂存档案,当结束程式后就会关闭且删除此档案.<BR>char* tmpname(char s[L_tmpnam]):指定暂存档案的名称为s.<BR>int setvbuf(FILE* stream, char* buf, int mode, size_t size):指定串流暂存区尺寸size,使用mode参数值_IOFBF为完整暂存区,_IOLBF是线性暂存区或_IONBF没有暂存区.<BR>void setbuf(FILE* stream, char* buf):指定串流的暂存区为参数buf.<BR>int fprintf(FILE* stream, const char* format, ...):将格式化字串写入档案串流.<BR>int printf(const char* format, ...):在标准输出显示格式化字串.<BR>int sprintf(char* s, const char* format, ...):将格式化字串输出到字串s.<BR>int fscanf(FILE* stream, const char* format, ...):从档案串流读取指定格式的资料.<BR>int scanf(const char* format, ...):从标准输入读取指定格式的资料.<BR>int sscanf(char* s, const char* format, ...):从字串s读取指定格式的资料.<BR>int fgetc(FILE* stream):从档案串流读取一个字元.<BR>char* fgets(char* s, int n, FILE* stream):从档案串流读取一个字串.<BR>int fputc(int c, FILE* stream):写入一个字元到档案.<BR>char* fputs(const char* s, FILE* stream):写入一个字串到档案.<BR>int getc(FILE* stream):从档案串流读取一个字元.<BR>int getchar(void):从标准输入读取一个字元.<BR>char* gets(char* s):从标准输入读取一个字串.<BR>int putc(int c, FILE* stream):写入一个字元到档案.<BR>int putchar(int c):在标准输出显示一个字元.<BR>int puts(const char* s):在标准输出显示一个字串.<BR>int ungetc(int c, FILE* stream):将一个字元放回档案串流.<BR>size_t fread(void* ptr, size_t size, size_t nobj, FILE* stream):从档案读取指定大小的资料.<BR>size_t fwrite(const void* ptr, size_t size, size_t nobj, FILE* stream):将指定大小的资料写入档案.<BR>int fseek(FILE* stream, long offset, int origin):移动档案指标到offset位移量,其方向是origin参数值SEEK_SET的档案开头,SEEK_CUR是目前位置或SEEK_END档尾.<BR>long ftell(FILE* stream):目前档案指标的位置.<BR>void rewind(FILE* stream):重设档案指标到档头.<BR>int feof(FILE* stream):是否到达档尾.<BR>int ferror(FILE* stream):是否档案串流产生错误.<BR></DIV>
<DIV><FONT style="BACKGROUND-COLOR: #ffff00" size=4>C-2 字元检查函数</FONT><BR>int isalnum(int c):isalpha(c)或isdigit(c)的字元.<BR>int isalpha(int c):isupper(c)或islower(c)的字元.<BR>int iscntrl(int c):是否是ASCII控制字元.<BR>int isdigit(int c):是否是数字.<BR>int isgraph(int c):是否是显示字元,不含空白字元.<BR>int islower(int c):是否是小写字元.<BR>int isprint(int c):是否是显示字元0x20 (' ')到0x7E ('~').<BR>int ispunct(int c):是否是显示字元,不包含空白,字母,数字字元.<BR>int isspace(int c):是否是空白字元.<BR>int isupper(int c):是否是大写字元.<BR>int isxdigit(int c):是否是十六进位字元.<BR>int tolower(int c):转换成小写字元.<BR>int toupper(int c):转换成大写字元.</DIV>
<DIV><BR><FONT style="BACKGROUND-COLOR: #ffff00"><FONT size=4>C-3 字串函数</FONT><BR></FONT>char* strcpy(char* s, const char* ct):将字串ct复制到字串s.(String Copy)<BR>char* strncpy(char* s, const char* ct, size_t n):将字串ct前n个字元复制到字串s.<BR>char* strcat(char* s, const char* ct):连结字串ct到字串s之后.(String Catanation)<BR>char* strncat(char* s, const char* ct, size_t n):连结字串ct前n个字元到字串s.<BR>int strcmp(const char* cs, const char* ct):比较字串cs和ct.<BR>int strncmp(const char* cs, const char* ct, size_t n):比较字串cs和ct的前n个字元.<BR>char* strchr(const char* cs, int c):传回字元c第一次出现在字串cs位置的指标.<BR>char* strrchr(const char* cs, int c):传回字元c第后一次出现在字串cs位置的指标.<BR>char* strpbrk(const char* cs, const char* ct):传回字串ct任何字元在字串cs第一次出现的位置指标.<BR>char* strstr(const char* cs, const char* ct):传回字串ct在字串cs第一次出现的位置指标.<BR>size_t strlen(const char* cs):传回字串cs的长度.<BR>char* strerror(int n):传回指定错误代码的说明文字内容.<BR>char* strtok(char* s, const char* t):以字串t的任何字元为分隔字元,找寻字串s中下一个token记号.<BR>void* memcpy(void* s, const void* ct, size_t n):从位置ct复制n个字元到位置s,传回s.<BR>void* memmove(void* s, const void* ct, size_t n):从位置ct搬移n个字元到位置s,传回s.<BR>int memcmp(const void* cs, const void* ct, size_t n):比较位置ct和位置cs的前n个字元.<BR>void* memchr(const void* cs, int c, size_t n):传回cs位置开始前n个字元第一次出现字元c的位置指标.<BR>void* memset(void* s, int c, size_t n):取代cs位置开始前n个字元成为字元c,传回位置指标s.</DIV>
<DIV><BR><FONT style="BACKGROUND-COLOR: #ffff00" size=4>C-4 数学函数</FONT><BR>double exp(double x):自然数的指数e^x.<BR>double log(double x):自然对数logx<BR>double log10(double x):十为底的对数log10x.<BR>double pow(double x, double y):传回参数x为底,参数y的次方值x^y.<BR>double sqrt(double x):参数x的平方根.<BR>double ceil(double x):传回大於或等於参数x的最小double整数.<BR>double floor(double x):传回小於或等於参数x的最大double整数.<BR>double fabs(double x):传回参数x的绝对值.<BR>hypot(double x, double y):传回√(x^2+y^2)公式的值<BR>double ldexp(double x, int n):x乘以2的n次方是x*2^n.<BR>double frexp(double x, int* exp):将参数x的浮点数分解成尾数和指标,x = m*2^exp,传回m值的尾数,将指数存入参数exp.<BR>double modf(double x, double* ip):将浮点数x分解成整数和小数部分,传回小数部分,将整数部分存入参数ip.<BR>double fmod(double x, double y):如果y为非零值,传回浮点数x/y的余数.<BR>double sin(double x):正弦函数.<BR>double cos(double x):余弦函数.<BR>double tan(double x):正切函数.<BR>double asin(double x):反正弦函数.<BR>double acos(double x):反余弦函数.<BR>double atan(double x):反正切函数.<BR>double atan2(double y, double x):参数y/x的反正切函数值.<BR>double sinh(double x):hyperbolic正弦函数,sinh(x)=(e^x-e^(-x))/2.<BR>double cosh(double x):hyperbolic余弦函数,cosh(x)=(e^x+e^(-x))/2.<BR>double tanh(double x):hyperbolic正切函数,tanh(x)=(e^x-e^(-x))/(e^2+e^(-x)).</DIV>
<DIV><BR><FONT style="BACKGROUND-COLOR: #ffff00" size=4>C-5 日期/时间函数</FONT><BR>clock_t clock(void):传回程式开始执行后所使用的CPU时间,以ticks为单位,除以常数CLK_TCK就是秒数.<BR>time_t time(time_t* tp):传回目前的历法时间(Calendar Time),也会指定给参数的tp指标,如为无效时间,传回-1.<BR>double difftime(time_t time2, time_t time1):传回参数time2和time1的时间差,即time2-time1.<BR>time_t mktime(struct tm* tp):将参数*tp的当地时间改为历法时间, 如果不能转换传回-1.<BR>char* asctime(const struct tm* tp):传回参数tm结构指标转换成日期/时间格式的字串,字串最后有新行字元\n.<BR>char* ctime(const time_t* tp):传回参数time_t指标转换成当地日期/时间的字串,字串最后有新行字元\n.<BR>struct tm* gmtime(const time_t* tp):传回将参数的time_t指标转换成UTC(Coordinated Universal Time)日期/时间的tm结构指标.<BR>struct tm* localtime(const time_t* tp):传回将参数的time_t指标转换成当地日期/时间的tm结构指标.<BR>size_t strftime(char* s, size_t smax, const char* fmt, const struct tm* tp):将参数tp的日期/时间以格式化字串fmt输出到字串s,s最多储存smax个字元.</DIV>
<DIV><BR><FONT style="BACKGROUND-COLOR: #ffff00" size=4>C-6 工具函数</FONT><BR>int abs(int n),long labs(long n):传回整数n的绝对值.<BR>double atof(const char* s):将参数字串s转换成浮点数,如果字串不能转换传回0.0.<BR>int atoi(const char* s):将参数字串s转换成整数,如果字串不能转换传回0.(Char to integer)<BR>int itoa():将整数转换成参数字串s.(Integer to Char)<BR>long atol(const char* s):将参数字串s转换成长整数,如果字串不能转换传回0.<BR>double strtod(const char* s, char** endp):函数忽略字串s前的空白字元,将数字部分转换成浮点数,如果尚有未转换的部分字串,则设成参数endp指标.<BR>long strtol(const char* s, char** endp, int base):函数忽略字串s前的空白字元,将数字部分转换成长整数,如果尚有未转换的部分字串,则设成参数endp指标.<BR>unsigned long strtoul(const char* s, char** endp, int base):如同strtol函数,其传回值是无符号长整数.<BR>void* calloc(size_t nobj, size_t size):传回一块参数nobj阵列大小的记忆体指标,nobj元素大小为size初值为0,错误传回NULL.<BR>void* malloc(size_t size):传回大小size记忆体指标,没有指定初值,错误传回NULL.<BR>void* realloc(void* p, size_t size):将指标p的记忆体改为size大小,不会更改原记忆体的值,多配置部分初值为0,错误传回NULL.<BR>void free(void* p):释放参数p指标的记忆体空间.<BR>void abort():强迫程式以不正常方式结束,如同呼叫raise(SIGABRT)函数.<BR>void exit(int status):程式以正常方式结束,传回系统环境状态值,0表示正常结束.<BR>int system(const char* s):将字串s的指令传给环境来执行,也就是执行MS-DOS的指令.<BR>char* getenv(const char* name):传回参数name的环境字串,如果没有传回NULL.<BR>void* bsearch(const void* key, const void* base, size_t n, size_t size, int (*cmp)(const void* keyval, const void* datum)):阵列基础的二元搜寻函数,阵列是参数base,键值是参数key,n是阵列大小,size是每个元素的大小,最后的参数是指向函数的指标,这是比较元素大小的函数,找到传回该元素指标,没有找到传回NULL.<BR>void qsort(void* base, size_t n, size_t size, int (*cmp)(const void*, const void*)):阵列基础的快速排序法函数,阵列是参数base,n是阵列大小,size是每个元素的大小,最后的参数是指向函数的指标,这是比较元素大小的函数.<BR>int rand(void):传回乱数的整数值,其值的范围是0到RAND_MAX常数,其值为0x7FFF.<BR>void srand(unsigned int seed):指定乱数的种子数,参数是无符号整数,如果没有指定,预设的种子数为1.</DIV></DIV>]]></description>
</item>
</channel>
</rss>