﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-onlinewan:决定了，就不会放弃</title><link>http://www.cppblog.com/onlinewan/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 07 Apr 2026 22:05:58 GMT</lastBuildDate><pubDate>Tue, 07 Apr 2026 22:05:58 GMT</pubDate><ttl>60</ttl><item><title>嵌入式程序员应知道的几个基本问题</title><link>http://www.cppblog.com/onlinewan/archive/2007/03/28/20756.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Wed, 28 Mar 2007 03:56:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/28/20756.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20756.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/28/20756.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20756.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20756.html</trackback:ping><description><![CDATA[
		<a href="http://www.qqread.com/keywords/c-language.html" target="_blank">C语言</a>测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年，我既参加也组织了许多这种测试，在这过程中我意识到这些测试能为面试者和被面试者提供许多有用信息，此外，撇开面试的压力不谈，这种测试也是相当有趣的。 
<table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　从被面试者的角度来讲，你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术<a href="http://www.qqread.com/link.html" target="_blank">技巧</a>而<a href="http://www.qqread.com/keywords/photoshop_e.html" target="_blank">设计</a>吗？这是个愚蠢的问题吗？如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗？这标志着出题者也许花时间在微机上而不是在嵌入式系统上。如果上述任何问题的答案是"是"的话，那么我知道我得认真考虑我是否应该去做这份工作。 </p><p>　　从面试者的角度来讲，一个测试也许能从多方面揭示应试者的素质：最基本的，你能了解应试者C语言的水平。不管怎么样，看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择，还是只是瞎蒙呢？当应试者在某个问题上卡住时是找借口呢，还是表现出对问题的真正的好奇心，把这看成学习的机会呢？我发现这些信息与他们的测试成绩一样有用。 </p><p>　　有了这些想法，我决定出一些真正针对嵌入式系统的考题，希望这些令人头痛的考题能给正在找工作的人一点帮助。这些问题都是我这些年实际碰到的。其中有些题很难，但它们应该都能给你一点启迪。 </p><p>　　这个测试适于不同水平的应试者，大多数初级水平的应试者的成绩会很差，经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好，每个问题没有分配分数，如果选择这些考题为你所用，请自行按你的意思分配分数。 </p><p>　　<strong>预处理器(Preprocessor)</strong></p><p>　　1 . 用预处理指令#define 声明一个常数，用以表明1年中有多少秒(忽略闰年问题) </p><p>　　　　　　　　#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL</p><p>　　我在这想看到几件事情： </p><p>　　1) #define 语法的基本知识(例如：不能以分号结束，括号的使用，等等) </p><p>　　2)懂得预处理器将为你计算常数表达式的值，因此，直接写出你是如何计算一年中有多少秒而不是计算出实际的值，是更清晰而没有代价的。 </p><p>　　3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 </p><p>　　4) 如果你在你的表达式中用到UL(表示无符号长整型)，那么你有了一个好的起点。记住，第一印象很重要。 </p><p>　　2 . 写一个"标准"宏MIN ，这个宏输入两个参数并返回较小的一个。 </p><p>　　　　　　　　#define MIN(A,B) ((A) &lt;= (B) ? (A) : (B))</p><p>　　<strong>这个测试是为下面的目的而设的：</strong></p><p>　　1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变为标准C的一部分之前，宏是方便产生嵌入代码的唯一方法，对于嵌入式系统来说，为了能达到要求的性能，嵌入代码经常是必须的方法。 </p><p>　　2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码，了解这个用法是很重要的。 </p><p>　　3) 懂得在宏中小心地把参数用括号括起来 </p><p>　　4) 我也用这个问题开始讨论宏的副作用，例如：当你写下面的代码时会发生什么事？ </p><p>　　3. 预处理器标识#error的目的是什么？ </p><p>　　如果你不知道答案，请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子，那么应试者最好希望自己不要知道答案。 </p><p>　　<strong>死循环(Infinite loops)</strong></p><p>　　4. 嵌入式系统中经常要用到无限循环，你怎么样用C编写死循环呢？ </p><p>　　这个问题用几个<a href="http://www.qqread.com/z/specia/solution.html" target="_blank">解决方案</a>。我首选的方案是： </p><p>　　　　while(1)</p><p>　　　　{</p><p>　　　　}</p><p>　　一些程序员更喜欢如下方案： </p><p>　　　　for(;;)</p><p>　　　　{</p><p>　　　　}</p><p>　　这个实现方式让我为难，因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案，我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是："我被教着这样做，但从没有想到过为什么。"这会给我留下一个坏印象。第三个方案是用 goto </p><p>　　　　Loop:</p><p>　　　　...</p><p>　　　　goto Loop;</p><p>　　应试者如给出上面的方案，这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。 <br /><strong>数据声明(Data declarations)</strong></p><p>　　5. 用变量a给出下面的定义 </p><p>　　a) 一个整型数(An integer)。 </p><p>　　b)一个指向整型数的指针( A pointer to an integer)。 </p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"><iframe name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-1572879403720716&amp;dt=1175053997796&amp;hl=zh-CN&amp;lmt=1170771489&amp;alternate_ad_url=http%3A%2F%2Fwww.qqread.com%2F0000js%2Fgoogle336.htm&amp;prev_fmts=468x15_0ads_al_s&amp;format=336x280_as&amp;output=html&amp;channel=6686853775&amp;url=http%3A%2F%2Fwww.qqread.com%2Fcpp%2Fr296751_2.html&amp;color_bg=EDF0F5&amp;color_text=000000&amp;color_link=0000FF&amp;color_url=008000&amp;color_border=EDF0F5&amp;ad_type=text_image&amp;ref=http%3A%2F%2Fwww.qqread.com%2Fcpp%2Fr296751.html&amp;cc=15&amp;u_h=768&amp;u_w=1024&amp;u_ah=738&amp;u_aw=1024&amp;u_cd=32&amp;u_tz=480&amp;u_his=1&amp;u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency=""></iframe></span></td></tr></tbody></table><p>　　c)一个指向指针的的指针，它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r 。 </p><p>　　d)一个有10个整型数的数组( An array of 10 integers)。 </p><p>　　e) 一个有10个指针的数组，该指针是指向一个整型数的(An array of 10 pointers to integers)。 </p><p>　　f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)。 </p><p>　　g) 一个指向函数的指针，该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)。 </p><p>　　h) 一个有10个指针的数组，该指针指向一个函数，该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )。 </p><p>　　<strong>答案是：</strong></p><p>　　　　a) int a; // An integer </p><p>　　　　b) int *a; // A pointer to an integer </p><p>　　　　c) int **a; // A pointer to a pointer to an integer </p><p>　　　　d) int a[10]; // An array of 10 integers </p><p>　　　　e) int *a[10]; // An array of 10 pointers to integers </p><p>　　　　f) int (*a)[10]; // A pointer to an array of 10 integers </p><p>　　　　g) int (*a)(int); // A pointer to a function a that </p><p>　　　　takes an integer argument and returns an integer </p><p>　　　　h) int (*a[10])(int); // An array of 10 pointers </p><p>　　　　to functions that take an integer argument and return </p><p>　　　　an integer</p><p>　　人们经常声称这里有几个问题是那种要翻一下书才能回答的问题，我同意这种说法。当我写这篇文章时，为了确定语法的正确性，我的确查了一下书。但是当我被面试的时候，我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里，我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案)，那么也就没有为这次面试做准备，如果该面试者没有为这次面试做准备，那么他又能为什么出准备呢？</p><p>　　<strong>Static </strong></p><p>　　6. 关键字static的作用是什么？ </p><p>　　这个简单的问题很少有人能回答完全。在<a href="http://www.qqread.com/keywords/c-language.html" target="_blank">C语言</a>中，关键字static有三个明显的作用： </p><p>　　1)在函数体，一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 </p><p>　　2) 在模块内(但在函数体外)，一个被声明为静态的变量可以被模块内所用函数访问，但不能被模块外其它函数访问。它是一个本地的全局变量。 </p><p>　　3) 在模块内，一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是，这个函数被限制在声明它的模块的本地范围内使用。 </p><p>　　大多数应试者能正确回答第一部分，一部分能正确回答第二部分，同是很少的人能懂得第三部分。这是一个应试者的严重的缺点，因为他显然不懂得本地化数据和代码范围的好处和重要性。 </p><p>　　<strong>Const</strong></p><p>　　7.关键字const有什么含意？ </p><p>　　我只要一听到被面试者说："const意味着常数"，我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const 的所有用法，因此ESP(译者：Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么. 如果你从没有读到那篇文章，只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案，但我接受它作为一个正确的答案。(如果你想知道更详细的答案，仔细读一下Saks的文章吧。) </p><p>　　如果应试者能正确回答这个问题，我将问他一个附加的问题： </p><p>　　下面的声明都是什么意思？ </p><p>　　　　const int a;</p><p>　　　　int const a;</p><p>　　　　const int *a;</p><p>　　　　int * const a;</p><p>　　　　int const * a const;</p><p>　　　　/******/</p><p>　　前两个的作用是一样，a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是，整型数是不可修改的，但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说，指针指向的整型数是可以修改的，但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说，指针指向的整型数是不可修改的，同时指针也是不可修改的)。如果应试者能正确回答这些问题，那么他就给我留下了一个好印象。顺带提一句，也许你可能会问，即使不用关键字 const，也还是能很容易写出功能正确的程序，那么我为什么还要如此看重关键字const呢？我也如下的几下理由： </p><p>　　1) 关键字const的作用是为给读你代码的人传达非常有用的信息，实际上，声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾，你就会很快学会感谢这点多余的信息。(当然，懂得用const的程序员很少会留下的垃圾让别人来清理的。) </p><p>　　2) 通过给优化器一些附加的信息，使用关键字const也许能产生更紧凑的代码。 </p><p>　　3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数，防止其被无意的代码修改。简而言之，这样可以减少bug的出现。<br /><strong>Volatile </strong></p><p>　　8. 关键字volatile有什么含意?并给出三个不同的例子。 </p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　一个定义为volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。精确地说就是，优化器在用到这个变量时必须每次都小心地重新读取这个变量的值，而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子： </p><p>　　1) 并行设备的硬件寄存器(如：状态寄存器) </p><p>　　2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) </p><p>　　3) 多线程应用中被几个任务共享的变量 </p><p>　　回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道，所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。 </p><p>　　假设被面试者正确地回答了这是问题(嗯，怀疑是否会是这样)，我将稍微深究一下，看一下这家伙是不是直正懂得volatile完全的重要性。 </p><p>　　1)一个参数既可以是const还可以是volatile吗？解释为什么。 </p><p>　　2); 一个指针可以是volatile 吗？解释为什么。 </p><p>　　3); 下面的函数有什么错误： </p><p>　　　　int square(volatile int *ptr)</p><p>　　　　{</p><p>　　　　　return *ptr * *ptr;</p><p>　　　　}</p><p>　　<strong>下面是答案：</strong></p><p>　　1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 </p><p>　　2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 </p><p>　　3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方，但是，由于*ptr指向一个volatile型参数，编译器将产生类似下面的代码： </p><p>　　　　int square(volatile int *ptr) </p><p>　　　　{</p><p>　　　　int a,b;</p><p>　　　　a = *ptr;</p><p>　　　　b = *ptr;</p><p>　　　　return a * b;</p><p>　　　　}</p><p>　　由于*ptr的值可能被意想不到地该变，因此a和b可能是不同的。结果，这段代码可能返不是你所期望的平方值！正确的代码如下： </p><p>　　　　long square(volatile int *ptr) </p><p>　　　　{</p><p>　　　　int a;</p><p>　　　　a = *ptr;</p><p>　　　　return a * a;</p><p>　　　　}</p><p>　　位操作(Bit manipulation) </p><p>　　9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a 的bit 3。在以上两个操作中，要保持其它位不变。 </p><p>　　<strong>对这个问题有三种基本的反应：</strong></p><p>　　1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 </p><p>　　2) 用bit fields。Bit fields是被扔到<a href="http://www.qqread.com/keywords/c-language.html" target="_blank">C语言</a>死角的东西，它保证你的代码在不同编译器之间是不可移植的，同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的<a href="http://www.qqread.com/z/telecom/index.html" target="_blank">通信</a>芯片写的<a href="http://down.qqread.com/" target="_blank">驱动程序</a>，它用到了bit fields因此完全对我无用，因为我的编译器用其它的方式来实现bit fields的。从道德讲：永远不要让一个非嵌入式的家伙粘实际硬件的边。 </p><p>　　3) 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法，是应该被用到的方法。最佳的<a href="http://www.qqread.com/z/specia/solution.html" target="_blank">解决方案</a>如下： </p><p>　　　　#define BIT3 (0x1 &lt;&lt; 3)</p><p>　　　　static int a;</p><p>　　　　void set_bit3(void) </p><p>　　　　{</p><p>　　　　a |= BIT3;</p><p>　　　　}</p><p>　　　　void clear_bit3(void) </p><p>　　　　{</p><p>　　　　a &amp;= ~BIT3;</p><p>　　　　}</p><p>　　一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数，这也是可以接受的。我希望看到几个要点：说明常数、|=和&amp;=~操作。 </p><p>　　访问固定的内存位置(Accessing fixed memory locations) </p><p>　　10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中，要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。 </p><p>　　这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下： </p><p>　　　　int *ptr;</p><p>　　　　ptr = (int *)0x67a9;</p><p>　　　　*ptr = 0xaa55;</p><p>　　　　A more obscure approach is:</p><p>　　一个较晦涩的方法是： </p><p>　　　　*(int * const)(0x67a9) = 0xaa55;</p><p>　　即使你的品味更接近第二种方案，但我建议你在面试时使用第一种方案。<br /><strong>中断(Interrupts)</strong></p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table>　　11. 中断是嵌入式系统中重要的组成部分，这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是，产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR)，请评论一下这段代码的。 <p>　　　　__interrupt double compute_area (double radius) </p><p>　　　　{</p><p>　　　　double area = PI * radius * radius;</p><p>　　　　printf("\nArea = %f", area);</p><p>　　　　return area;</p><p>　　　　}</p><p>　　这个函数有太多的错误了，以至让人不知从何说起了： </p><p>　　1)ISR 不能返回一个值。如果你不懂这个，那么你不会被雇用的。 </p><p>　　2) ISR 不能传递参数。如果你没有看到这一点，你被雇用的机会等同第一项。 </p><p>　　3) 在许多的处理器/编译器中，浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈，有些处理器/编译器就是不允许在ISR中做浮点运算。此外，ISR应该是短而有效率的，在ISR中做浮点运算是不明智的。 </p><p>　　4) 与第三点一脉相承，printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点，我不会太为难你的。不用说，如果你能得到后两点，那么你的被雇用前景越来越光明了。 </p><p>　　代码例子(Code examples) </p><p>　　12 . 下面的代码输出是什么，为什么？ </p><p>　　　　void foo(void)</p><p>　　　　{</p><p>　　　　unsigned int a = 6;</p><p>　　　　int b = -20;</p><p>　　　　(a+b &gt; 6) ? puts("&gt; 6") : puts("&lt;= 6");</p><p>　　　　}</p><p>　　这个问题测试你是否懂得<a href="http://www.qqread.com/keywords/c-language.html" target="_blank">C语言</a>中的整数自动转换原则，我发现有些开发者懂得极少这些东西。不管如何，这无符号整型问题的答案是输出是 "&gt; 6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数，所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题，你也就到了得不到这份工作的边缘。 </p><p>　　13. 评价下面的代码片断： </p><p>　　　　unsigned int zero = 0;</p><p>　　　　unsigned int compzero = 0xFFFF; </p><p>　　　　/*1's complement of zero */</p><p>　　对于一个int型不是16位的处理器为说，上面的代码是不正确的。应编写如下： </p><p>　　　　unsigned int compzero = ~0;</p><p>　　这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里，好的嵌入式程序员非常准确地明白硬件的细节和它的局限，然而PC机程序往往把硬件作为一个无法避免的烦恼。 </p><p>　　到了这个阶段，应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好，那么这个测试就在这里结束了。但如果显然应试者做得不错，那么我就扔出下面的追加问题，这些问题是比较难的，我想仅仅非常优秀的应试者能做得不错。提出这些问题，我希望更多看到应试者应付问题的方法，而不是答案。不管如何，你就当是这个娱乐吧... </p><p>　　动态内存分配(Dynamic memory allocation) </p><p>　　14. 尽管不像非嵌入式计算机那么常见，嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中，动态分配内存可能发生的问题是什么？ </p><p>　　这里，我期望应试者能提到内存碎片，碎片收集的问题，变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释)，所有回过头看一下这些杂志吧！让应试者进入一种虚假的安全感觉后，我拿出这么一个小节目： </p><p>　　下面的代码片段的输出是什么，为什么？ </p><p>　　　　char *ptr;</p><p>　　　　if ((ptr = (char *)malloc(0)) == NULL) </p><p>　　　　puts("Got a null pointer");</p><p>　　　　else</p><p>　　　　puts("Got a valid pointer");</p><p>　　这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc，得到了一个合法的指针之后，我才想到这个问题。这就是上面的代码，该代码的输出是"Got a valid pointer"。我用这个来开始讨论这样的一问题，看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要，但解决问题的方法和你做决定的基本原理更重要些。 </p><p>　　Typedef </p><p>　　15 Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如，思考一下下面的例子： </p><p>　　　　#define dPS struct s *</p><p>　　　　typedef struct s * tPS;</p><p>　　以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢？(如果有的话)为什么？ </p><p>　　这是一个非常微妙的问题，任何人答对这个问题(正当的原因)是应当被恭喜的。答案是：typedef更好。思考下面的例子： </p><p>　　　　dPS p1,p2;</p><p>　　　　tPS p3,p4;</p><p>　　第一个扩展为 </p><p>　　　　struct s * p1, p2;</p><p>　　　　.</p><p>　　上面的代码定义p1为一个指向结构的指，p2为一个实际的结构，这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。 </p><p>　　晦涩的语法 </p><p>　　16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗，如果是它做些什么？ </p><p>　　　　int a = 5, b = 7, c;</p><p>　　　　c = a+++b;</p><p>　　这个问题将做为这个测验的一个愉快的结尾。不管你相不相信，上面的例子是完全合乎语法的。问题是编译器如何处理它？水平不高的编译作者实际上会争论这个问题，根据最处理原则，编译器应当能处理尽可能所有合法的用法。因此，上面的代码被处理成： </p><p>　　　　c = a++ + b;</p><p>　　因此, 这段代码持行后a = 6, b = 7, c = 12。 </p><p>　　如果你知道答案，或猜出正确答案，做得好。如果你不知道答案，我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格，代码的可读性，代码的可修改性的好的话题。 </p><p>　　好了，伙计们，你现在已经做完所有的测试了。这就是我出的C语言测试题，我怀着愉快的心情写完它，希望你以同样的心情读完它。如果是认为这是一个好的测试，那么尽量都用到你的找工作的过程中去吧。天知道也许过个一两年，我就不做现在的工作，也需要找一个。 </p><p>　　<strong>作者介绍:</strong></p><p>　　Nigel Jones 是一个顾问，现在住在Maryland，当他不在水下时，你能在多个范围的嵌入项目中找到他。 他很高兴能收到读者的来信，他的email地址是: NAJones@compuserve.com </p><p>　　<strong>参考文献</strong></p><p>　　1) Jones, Nigel, "In Praise of the #error directive," Embedded Systems Programming, September 1999, p. 114. </p><p>　　2) Jones, Nigel, " Efficient C Code for Eight-bit MCUs ," Embedded Systems Programming, November 1998, p. 66. </p><img src ="http://www.cppblog.com/onlinewan/aggbug/20756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-28 11:56 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/28/20756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++词汇解析集锦 编程开发人员必备</title><link>http://www.cppblog.com/onlinewan/archive/2007/03/28/20754.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Wed, 28 Mar 2007 03:54:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/28/20754.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20754.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/28/20754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20754.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20754.html</trackback:ping><description><![CDATA[1. 保留字
<p>　　<a href="http://www.qqread.com/keywords/cpp.html" target="_blank">C++</a>中，保留字也称关键字，它是预先定义好的标识符。见关键字的解释。</p><p>　　2.关键字</p><p>　　C++中已经被系统定义为特殊含义的一类标识符。C++中的关键字有：</p><p></p><table style="BORDER-RIGHT: #99cc00 1pt outset; BORDER-TOP: #99cc00 1pt outset; BORDER-LEFT: #99cc00 1pt outset; WIDTH: 550px; BORDER-BOTTOM: #99cc00 1pt outset" cellspacing="0" cellpadding="4" width="550" align="center" border="1"><tbody><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">auto</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">double</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">int</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">struct </font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">break </font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">else</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">long</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">switch</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">case</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">enum</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">register</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">typedef</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">char</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">extern</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">return</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">union</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">const</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">float</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">short</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">unsigned</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">continue</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">for</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">signed</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">void</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">default</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">goto</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">sizeof</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">volatile</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">do</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">if</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">static</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">while</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">asm</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">_cs</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">_ds</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">_es</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">_ss</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">cdecl</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">far</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">huge</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">interrupt</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">near</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">pascal</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">class</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">public</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">private</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">catch</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">protected</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">delete</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">new</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">template</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">friend</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">this</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">inline</font></div></td></tr><tr><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">throw</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">try</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">operator</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset"><div align="left"><font size="1">virtual</font></div></td><td style="BORDER-RIGHT: #99cc00 1pt inset; PADDING-RIGHT: 0cm; BORDER-TOP: #99cc00 1pt inset; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: #99cc00 1pt inset; PADDING-TOP: 0cm; BORDER-BOTTOM: #99cc00 1pt inset" colspan="2"><div align="left"><font size="1">overload（现不用）</font></div></td></tr></tbody></table><p>　　3.标识符</p><p>　　对变量、函数、标号和其它各种用户自定义对象的命名。在C++中，标识符长度没有限制，第一个字符必须是字母或下划线，其后若有字符则必须为字母、数字或下划线。例如count2，_x是正确的标识符形式，而hello!，3th则是错误的。在C++中标识符区分大小写，另外标识符不能和C++中的关键字相同，也不能和函数同名。</p><p>　　4.声明</p><p>　　将一个标识符引入一个作用域，此标识符必须指明类型，如果同时指定了它所代表的实体，则声明也是定义。</p><p>　　5.定义</p><p>　　给所声明的标识符指定所代表的实体。</p><p>　　6.变量</p><p>　　某个作用域范围内的命名对象。</p><p>　　7.常量</p><p>　　常量是不接受程序修改的固定值，可以是任意数据类型。可以用后缀准确的描述所期望的常量类型，如浮点类型常量在数字后加F，无符号整型常量加后缀U等等。此外还有串常量如"Please input year："，反斜线字符常量如\n表示回车符。</p><p>　　8. const说明符</p><p>　　const是在变量声明或函数声明时所用到的一个修饰符，用它所修饰的实体具有只读属性。</p><p>　　9. 输入</p><p>　　当程序需要执行键盘输入时，可以使用抽取操作付"&gt;&gt;"从cin输入流中抽取字符。如：</p><p>　　int myAge;</p><p>　　cin &gt;&gt; myAge;</p><p>　　10.输出</p><p>　　当程序需要在屏幕上显示输出时，可以使用插入操作符"&lt;&lt;"向cout 输出流中插入字符。如：</p><p>　　cout &lt;&lt; "This is a program. \n ";</p><p>　　11.流</p><p>　　流是既产生信息又消费信息的逻辑设备，通过C++系统和物理设备关联。C++的I/O系统是通过流操作的。有两种类型的流：文本流，二进制流。</p><p>　　12.标准输入输出库</p><p>　　它是C++标准库的组成部分，为C++语言提供了输入输出的能力。</p><p>　　13.内置数据类型</p><p>　　由C++直接提供的类型，包括int、float、double、char 、bool、指针、数组和引用。</p><p>　　14.字符类型</p><p>　　包括 char、signed char、unsigned char三种类型。</p><p>　　15.整数类型</p><p>　　包括 short、 int、long 三种类型。<br />16.long</p><p>　　只能修饰 int , double.</p><p>　　long int 指一种整数类型，它的长度大于等于int型.</p><p>　　long double 指长双精度类型,长度大于等于double型。</p><p>　　17.short</p><p>　　一种长度少于或等于int型的整数类型。</p><p>　　18.signed</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　由它所修饰的类型是带符号的. 只能修饰 int 和 char .</p><p>　　19.布尔型</p><p>　　一种数据类型，其值可为：true, false 两种。</p><p>　　20.浮点类型</p><p>　　包括float, double , long double 三种类型。其典型特征表现为有尾数或指数。</p><p>　　21.双精度类型</p><p>　　浮点类型中的一种。在基本数据类型中它是精度最高，表示范围最大的一种数据类型。</p><p>　　22.void类型</p><p>　　关键字之一，指示没有返回信息。</p><p>　　23.结构类型</p><p>　　类的一种，其成员默认为public型。大多用作无成员函数的<a href="http://www.qqread.com/keywords/data-structure.html" target="_blank">数据结构</a>。</p><p>　　24.枚举类型</p><p>　　一种用户自定义类型，由用户定义的值的集合组成。</p><p>　　25.类型转换</p><p>　　一种数据类型转换为另一种，包括显式,隐式两种方式。</p><p>　　26.指针</p><p>　　一个保存地址或0的对象。</p><p>　　27. 函数指针</p><p>　　每个函数都有地址，指向函数地址的指针称为函数指针，函数指针指向代码区中的某个函数，通过函数指针可以调用相应的函数。其定义形式为：</p><p>　　int ( * func ) ( char a, char b);</p><p>　　28.引用</p><p>　　为一个对象或函数提供的另一个名字。</p><p>　　29.链表</p><p>　　一种数据结构，由一个个有序的结点组成，每个结点都是相同类型的结构，每个结点都有一个指针成员指向下一个结点。</p><p>　　30.数组</p><p>　　数组是一个由若干同类型变量组成的集合。<br />31.字符串</p><p>　　标准库中的一种数据类型，一些常用操作符如+=，==支持其操作。</p><p>　　32.运算符</p><p>　　内置的操作常用符号，例如+,* ,&amp; 等。</p><p>　　33.单目运算符</p><p>　　只能对一个操作数进行操作</p><p>　　34.双目运算符</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　可对两个操作数进行操作</p><p>　　35.三目运算符</p><p>　　可对三个操作数进行操作</p><p>　　36.算术运算符</p><p>　　执行算术操作的运算符，包括：+，-，*，/，%。</p><p>　　37.条件运算符</p><p>　　即"?: " 。</p><p>　　其语法为：</p><p>　　(条件表达式)?(条件为真时的表达式)：(条件为假时的表达式)</p><p>　　如：x = a &lt; b ? a : b;</p><p>　　相当于:</p><p>　　if ( a &lt; b)</p><p>　　x = a;</p><p>　　else</p><p>　　x = b;</p><p>　　38.赋值运算符</p><p>　　即：" = "及其扩展赋值运算符</p><p>　　39.左值</p><p>　　能出现在赋值表达式左边的表达式。</p><p>　　40.右值</p><p>　　能出现在赋值表达式右边的表达式。</p><p>　　41.运算符的结合性</p><p>　　指表达式中出现同等优先级的操作符时该先做哪个的规定。</p><p>　　42.位运算符</p><p>　　" &amp; "," | " , " ^ "，" &gt;&gt; "，" &lt;&lt; "</p><p>　　43.逗号运算符</p><p>　　即" ， "</p><p>　　44.逻辑运算符</p><p>　　" &amp;&amp; ", " || " ," ! "</p><p>　　45.关系运算符</p><p>　　"&gt;","&gt;=","&lt;=","&lt; "," &lt;= ","== "<br /><br />46.new运算符</p><p>　　对象创建的操作符。</p><p>　　47.delete运算符</p><p>　　对象释放操作符，触发析构函数。</p><p>　　48.内存泄露</p><p>　　操作堆内存时，如果分配了内存，就有责任回收它，否则这块内存就无法重新使用，称为内存泄漏。</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"><iframe name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-1572879403720716&amp;dt=1175053778750&amp;hl=zh-CN&amp;lmt=1170771489&amp;alternate_ad_url=http%3A%2F%2Fwww.qqread.com%2F0000js%2Fgoogle336.htm&amp;prev_fmts=468x15_0ads_al_s&amp;format=336x280_as&amp;output=html&amp;channel=6686853775&amp;url=http%3A%2F%2Fwww.qqread.com%2Fcpp%2Fp296884_4.html&amp;color_bg=EDF0F5&amp;color_text=000000&amp;color_link=0000FF&amp;color_url=008000&amp;color_border=EDF0F5&amp;ad_type=text_image&amp;ref=http%3A%2F%2Fwww.qqread.com%2Fcpp%2Fp296884_3.html&amp;cc=21&amp;u_h=768&amp;u_w=1024&amp;u_ah=738&amp;u_aw=1024&amp;u_cd=32&amp;u_tz=480&amp;u_his=8&amp;u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency=""></iframe></span></td></tr></tbody></table><p>　　49.sizeof运算符</p><p>　　获得对象在内存中的长度，以字节为单位。</p><p>　　50.表达式</p><p>　　由操作符和标识符组合而成，产生一个新的值。</p><p>　　51.算术表达式</p><p>　　用算术运算符和括号将运算对象(也称操作数)连接起来，符合<a href="http://www.qqread.com/keywords/cpp.html" target="_blank">C++</a>语法规则的式子。</p><p>　　52.关系表达式</p><p>　　用关系运算符和括号将运算对象(也称操作数)连接起来，符合C++语法规则的式子。</p><p>　　53.逻辑表达式</p><p>　　用逻辑运算符和括号将运算对象(也称操作数)连接起来，符合C++语法规则的式子。</p><p>　　54.赋值表达式</p><p>　　由赋值运算符将一个变量和一个表达式连接起来，符合C++语法规则的式子。</p><p>　　55.逗号表达式</p><p>　　由逗号操作符将几个表达式连接起来，符合C++语法规则的式子。</p><p>　　56.条件表达式</p><p>　　由条件运算符将运算对象连接起来，符合C++语法规则的式子。</p><p>　　57.语句</p><p>　　在函数中控制程序流程执行的基本单位，如if语句,while语句,switch语句, do语句, 表达式语句等。</p><p>　　58.复合语句</p><p>　　封闭于大括号{}内的语句序列。</p><p>　　59.循环语句</p><p>　　for 语句, while 语句, do 语句三种。</p><p>　　60.条件语句</p><p>　　基于某一条件在两个选项中选择其一的语句称为条件语句。<br />61.成员函数</p><p>　　在类中说明的函数称为成员函数。</p><p>　　62.全局函数</p><p>　　定义在所有类之外的函数。</p><p>　　63.main函数</p><p>　　由系统自动调用开始执行<a href="http://www.qqread.com/keywords/cpp.html" target="_blank">C++</a>程序的第一个函数</p><p>　　64.外部函数</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　在定义函数时，如果冠以关键字extern，表示此函数是外部函数。</p><p>　　65.内联函数</p><p>　　在函数前加上关键字inline说明了一个内联函数，这使一个函数在程序行里进行代码扩展而不被调用。这样的好处是减少了函数调用的开销，产生较快的执行速度。但是由于重复编码会产生较长代码，所以内联函数通常都非常小。如果一个函数在类说明中定义，则将自动转换成内联函数而无需用inline说明。</p><p>　　66.函数重载</p><p>　　在同一作用域范围内，相同的函数名通过不同的参数类型或参数个数可以定义几个函数，编译时编译器能够识别实参的个数和类型来决定该调用哪个具体函数。需要注意的是，如果两个函数仅仅返回类型不同，则编译时将会出错，因为返回类型不足以提供足够的信息以使编译程序判断该使用哪个函数。所以函数重载时必须是参数类型或者数量不同。</p><p>　　67.函数覆盖</p><p>　　对基类中的虚函数，派生类以相同的函数名及参数重新实现之。</p><p>　　68.函数声明</p><p>　　在C++中，函数声明就是函数原型，它是一条程序语句，即它必须以分号结束。它有函数返回类型，函数名和参数构成，形式为：</p><p>　　返回类型 function (参数表);</p><p>　　参数表包含所有参数的数据类型，参数之间用逗号分开。如下函数声明都是合法的。</p><p>　　int Area(int length , int width ) ;</p><p>　　或 int Area ( int , int ) ;</p><p>　　69.函数定义</p><p>　　函数定义与函数声明相对应，指函数的具体实现，即包括函数体。如：</p><p>　　int Area( int length , int width )</p><p>　　{</p><p>　　// other program statement</p><p>　　}</p><p>　　70.函数调用</p><p>　　指定被调用函数的名字和调用函数所需的信息(参数)。</p><p>　　71.函数名</p><p>　　与函数体相对，函数调用时引用之</p><p>　　72.函数类型</p><p>　　(1) 获取函数并返回值。</p><p>　　(2) 获取函数但不返回值。</p><p>　　(3) 没有获取参数但返回值。</p><p>　　(4) 没有获取参数也不返回值。</p><p>　　73.形式参数</p><p>　　函数中需要使用变元时，将在函数定义时说明需要接受的变元，这些变元称为形式参数。形式参数对应于函数定义时的参数说明。其使用与局部变量类似。</p><p>　　74.实际参数</p><p>　　当需要调用函数时，对应该函数需要的变元所给出的数据称为实际参数。</p><p>　　75.值传递</p><p>　　函数调用时形参仅得到实参的值，调用结果不会改变实参的值.<br /><br />76.引用传递</p><p>　　函数调用时形参为实参的引用，调用结果会改变实参的值。</p><p>　　77.递归</p><p>　　函数的自我调用称为递归。每次调用是应该有不同的参数，这样递归才能终止。</p><p>　　78.函数体</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　与函数名相对，指函数最外边由{}括起来的部分。</p><p>　　79.作用域</p><p>　　指标识符在程序中有效的范围，与声明位置有关，作用域开始于标识符的生命处。分：局部作用域，函数作用域，函数原型作用域，文件作用域，类作用域。</p><p>　　80.局部作用域</p><p>　　当标识符的声明出现在由一对花括号所括起来的一段程序内时，该标示符的作用域从声明点开始到块结束处为止，此作用域的范围具有局部性。</p><p>　　81.全局作用域</p><p>　　标识符的声明出现在函数，类之外，具有全局性。</p><p>　　82.类作用域</p><p>　　指类定义和相应的成员函数定义范围。</p><p>　　83.全局变量</p><p>　　定义在任何函数之外，可以被任一模块使用，在整个程序执行期间保持有效。当几个函数要共享同一数据时全局变量将十分有效，但是使用全局变量是有一定弊端的：全局变量将在整个程序执行期间占有执行空间，即使它只在少数时间被用到;大量使用全局变量将导致程序混乱，特别是在程序较复杂时可能引起错误。</p><p>　　84.局部变量</p><p>　　定义在函数内部的变量。局部变量只在定义它的模块内部起作用，当该段代码结束，这个变量就不存在了。也就是说一个局部变量的生命期就是它所在的代码块的执行期，而当这段代码再次被执行时该局部变量将重新被初始化而不会保持上一次的值。需要注意的是，如果主程序和它的一个函数有重名的变量，当函数被调用时这个变量名只代表当前函数中的变量，而不会影响主程序中的同名变量。</p><p>　　85.自动变量</p><p>　　由auto修饰，动态分配<a href="http://www.qqread.com/z/server/storage/" target="_blank">存储</a>空间，存储在动态存储区中，对他们分配和释放存储空间的工作是由编译系统自动处理的。</p><p>　　86.寄存器变量</p><p>　　存储在运算器中的寄存器里的变量，可提高执行效率。</p><p>　　87.静态变量</p><p>　　由连接器分配在静态内存中的变量。</p><p>　　88.类</p><p>　　一种用户自定义类型，有成员数据，成员函数，成员常量，成员类型组成。类是描叙<a href="http://www.qqread.com/keywords/cpp.html" target="_blank">C++</a>概念的三个基本机制之一。</p><p>　　89.外部变量</p><p>　　由extern修饰的变量</p><p>　　90.堆</p><p>　　即自由存储区，new 和delete 都是在这里分配和释放内存块。<br />91.栈</p><p>　　有两个含义：(1)指内存中为函数维护局部变量的区域。(2)指先进后处的序列。</p><p>　　92.抽象类</p><p>　　至少包含一个纯虚函数的类。抽象类不能创建对象，但可以创建指向抽象类的指针，多态机制将根据基类指针选择相应的虚函数。</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　93.嵌套类</p><p>　　在一个类里可以定义另一个类，被嵌入类只在定义它的类的作用域里有效。</p><p>　　94.局部类</p><p>　　在函数中定义的类。注意在函数外这个局部类是不可知的。由于局部类的说明有很多限制，所以并不常见。</p><p>　　95.基类</p><p>　　被继承的类称为基类，又称父类、超类或范化类。它是一些共有特性的集合，可以有其它类继承它，这些类只增加它们独有的特性。</p><p>　　96.派生类</p><p>　　继承的类称为派生类。派生类可以用来作为另一个派生类的基类，实现多重继承。一个派生类也可以有两个或两个以上的基类。定义时在类名后加"：被继承类名"即可。</p><p>　　97.父类</p><p>　　即基类。见95基类的解释。</p><p>　　98.子类</p><p>　　即派生类。见96派生类的解释。</p><p>　　99.对象</p><p>　　有两重含义：</p><p>　　1. 内存中含有某种数据类型值的邻近的区域。</p><p>　　2. 某种数据类型的命名的或未命名的变量。一个拥有构造函数的类型对象在构造函数完成构造之前不能认为是一个对象，在析构函数完成析构以后也不再认为它是一个对象。</p><p>　　100. 数据成员</p><p>　　指类中<a href="http://www.qqread.com/z/server/storage/" target="_blank">存储</a>数据的变量。</p><p>　　101.实例化</p><p>　　即建立类的一个对象。</p><p>　　102.构造函数</p><p>　　是一个类的实例的初始化函数，将在生成类的实例时被自动调用，用于完成预先的初始化工作。一个类可以有几个构造函数，以不同的参数来区别，即构造函数可以被重载，以便不同的情况下产生不同的初始化;也可以没有构造函数，此时系统将调用缺省的空构造函数。需要注意的是构造函数没有返回类型。</p><p>　　103.成员初始化表</p><p>　　成员初始化表可用于初始化类中的任何数据成员，放在构造函数头与构造函数体之间，用"："与构造函数头分开，被初始化的数据成员的值出现在一对括弧之间，它们之间用逗号分开。</p><p>　　104.析构函数</p><p>　　是一个类的实例的回收函数，将在该实例结束使用前被自动调用，用于完成资源的释放。一个类只可以有一个析构函数，当析构函数执行后,该实例将不复存在。析构函数同样没有返回值。</p><p>　　105.虚析构函数</p><p>　　由virtual 修饰的析构函数，当用基类指针释放派生类对象时可根据它所指向的派生类对象释放准确的对象。<br />106.继承</p><p>　　面向对象的程序<a href="http://www.qqread.com/keywords/photoshop_e.html" target="_blank">设计</a>语言的特点之一。即一个对象获得另一个对象的特性的过程。如将公共属性和服务放到基类中，而它的各派生类除了有各自的特有属性和服务外还可以共享基类的公共属性和服务。这样的好处是容易建立体系，增强代码重复性。</p><p></p><table style="MARGIN: 10px 7px 3px 0px" cellspacing="0" cellpadding="0" align="right" border="0"><tbody><tr><td><span id="ad_qqread_mid_big"></span></td></tr></tbody></table><p>　　107.单继承</p><p>　　一个派生类只有一个基类，成为单继承。</p><p>　　108.重继承</p><p>　　一个派生类拥有多个基类，成为多继承。</p><p>　　109.虚函数</p><p>　　在基类中说明为virtual并在派生类中重定义的函数。重定义将忽略基类中的函数定义，指明了函数执行的实际操作。当一个基类指针指向包含虚函数的派生对象时，<a href="http://www.qqread.com/keywords/cpp.html" target="_blank">C++</a>将根据指针指向的对象类型来决定调用哪一个函数，实现了运行时的多态性。这里的重定义类似于函数重载，不同的是重定义的虚函数的原型必须和基类中指定的函数原型完全匹配。构造函数不能是虚函数，而析构函数则可以是。</p><p>　　110.纯虚函数</p><p>　　在基类中只有声明没有实现的虚函数。形式为：</p><p>　　virtual type funname(paralist)=0。这时基函数只提供派生类使用的接口，任何类要使用必须给出自己的定义。</p><p>　　111.多态性</p><p>　　给不同类型的实体提供单一接口。虚函数通过基类接口实现动态多态性，重载函数和模板提供了静态多态性。</p><p>　　112.复制构造函数</p><p>　　以自身类对象为参数的构造函数，如Z::Z(const Z&amp;). 用在同类对象间进行初始化。</p><p>　　113.运算符重载</p><p>　　C++中可以重载双目(如+，×等)和单目(如++)操作符，这样可以使用户像使用基本数据类型那样对自定义类型(类)的变量进行操作，增强了程序的可读性。当一个运算符被重载后，它将具有和某个类相关的含义，同时仍将保持原有含义。</p><p>　　114.静态成员函数</p><p>　　成员函数通过前面加static说明为静态的，但是静态成员函数只能存取类的其他静态成员，而且没有this指针。静态成员函数可以用来在创建对象前预初始化专有的静态数据。</p><p>　　115.静态成员变量</p><p>　　在成员变量之前加static关键字将使该变量称为静态成员变量，该类所有的对象将共享这个变量的同一拷贝。当对象创建时，所有静态变量只能被初始化为0。使用静态成员变量可以取代全局变量，因为全局变量是违背面向对象的程序设计的封装性的。</p><p>　　116.私有成员</p><p>　　只能由自身类访问的成员。</p><p>　　117.保护成员</p><p>　　只能由自身类及其派生类访问的成员。</p><p>　　118.友元</p><p>　　被某类明确授权可访问其成员的函数和类。</p><p>　　119.友元函数</p><p>　　在函数前加上关键字friend即说明了一个友元函数，友元函数可以存取类的所有私有和保护成员。友元在重载运算符时有时是很有用的。</p><p>　　120.友元类</p><p>　　被某类明确授权可访问其成员的类</p><p>　　121.例外处理</p><p>　　报告局部无法处理某错误的基本方式。由try.， throw , catch组成。</p><p>　　122.文件</p><p>　　是用于从磁盘文件到终端或打印机的任何东西。流通过完成打开操作与某文件建立联系。</p><img src ="http://www.cppblog.com/onlinewan/aggbug/20754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-28 11:54 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/28/20754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用宏有什么问题？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20515.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:53:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20515.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20515.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20515.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20515.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20515.html</trackback:ping><description><![CDATA[宏不遵循C++中关于范围和类型的规则。这经常导致一些微妙的或不那么微妙的问题。因此，C++提供更适合其他的 C++（译注：原文 the rest of C++，当指 C++除了兼容 C 以外的部分）的替代品，例如内联函数、模板与名字空间。 <br />  <br />考虑一下： <br />  <br />    #include "someheader.h" <br />  <br />    struct S { <br />        int alpha; <br />        int beta; <br />    }; <br />  <br />如果某人（不明智地）地写了一个叫“alpha”或“beta”的宏，那么它将不会被编译，<br />或者被错误地编译，产生不可预知的结果。例如，“someheader.h”可能包含： <br />  <br />    #define alpha 'a' <br />    #define beta b[2] <br />  <br />将宏（而且仅仅是宏）全部大写的习惯，会有所帮助，但是对于宏并没有语言层次上的保护<br />机制。例如，虽然成员的名字包含在结构体的内部，但这无济于事：在编译器能够正确地辨<br />别这一点之前，宏已经将程序作为一个字符流进行了处理。顺便说一句，这是 C 和 C++程<br />序开发环境和工具能够被简化的一个主要原因：人与编译器看到的是不同的东西。 <br />  <br />不幸的是，你不能假设别的程序员总是能够避免这种你认为“相当白痴”的事情。例如，最<br />近有人报告我，他们遇到了一个包含 goto 的宏。我也见过这种情况，而且听到过一些——<br />在很脆弱的时候——看起来确实有理的意见。例如： <br />  <br />    #define prefix get_ready(); int ret__ <br />    #define Return(i) ret__=i; do_something(); goto exit <br />    #define suffix exit: cleanup(); return ret__ <br />  <br />    void f() <br />    { <br />        prefix; <br />        // ... <br />        Return(10); <br />        // ... <br />        Return(x++); <br />        //... <br />        suffix; <br />    } <br />  <br />作为一个维护的程序员，就会产生这种印象；将宏“隐藏”到一个头文件中——这并不罕见<br />——使得这种“魔法”更难以被辨别。 <br />  <br />一个常见的微妙问题是，一个函数风格的宏并不遵守函数参数传递的规则。例如： <br />  <br />    #define square(x) (x*x) <br />  <br />    void f(double d, int i) <br />    { <br />        square(d);  // 好 <br />        square(i++);    // 糟糕：这表示 (i++*i++) <br />        square(d+1);    //糟糕：这表示(d+1*d+1); 也就是 (d+d+1) <br />        // ... <br />    } <br />  <br />“d+1”的问题，可以通过在“调用”时或宏定义时添加一对圆括号来解决： <br />  <br />    #define square(x) ((x)*(x)) /*这样更好 */ <br />  <br />但是， i++被执行了两次（可能并不是有意要这么做）的问题仍然存在。 <br />  <br />是的，我确实知道有些特殊的宏并不会导致 C/C++预处理宏这样的问题。但是，我无心去<br />发展 C++中的宏。作为替代，我推荐使用 C++语言中合适的工具，例如内联函数，模板，<br />构造函数（用来初始化），析构函数（用来清除），异常（用来退出上下文环境），等等。 <br /><img src ="http://www.cppblog.com/onlinewan/aggbug/20515.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:53 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20515.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么我不能重载点符号，::，sizeof，等等？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20514.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:52:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20514.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20514.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20514.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20514.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20514.html</trackback:ping><description><![CDATA[  <br />大多数的运算符能够被程序员重载。例外的是： <br />  <br />    . (点符号)  ::  ?:  sizeof <br />  <br />并没有什么根本的原因要禁止重载?:。仅仅是因为，我没有发现有哪种特殊的情况需要重<br />载一个三元运算符。注意一个重载了 表达式1？表达式2：表达式 3 的函数，不能够保证<br />表达式 2：表达式3 中只有一个会被执行。 <br />  <br />Sizeof 不能够被重载是因为内建的操作（built-in operations），诸如对一个指向<br />数组的指针进行增量操作，必须依靠它。考虑一下： <br />  <br />    X a[10]; <br />    X* p = &amp;a[3]; <br />    X* q = &amp;a[3]; <br />    p++;    // p指向a[4] <br />        // 那么p 的整型值必须比 q的整型值大出一个 sizeof(X) <br />  <br />所以，sizeof(X)不能由程序员来赋予一个不同的新意义，以免违反基本的语法。 <br />  <br />在 N::m 中，无论 N 还是 m 都不是值的表达式；N 和 m 是编译器知道的名字，::执行一个<br />（编译期的）范围解析，而不是表达式求值。你可以想象一下，允许重载 x::y的话，x 可<br />能是一个对象而不是一个名字空间（namespace）或者一个类，这样就会导致——与原来<br />的表现相反——产生新的语法（允许 表达式 1::表达式 2）。很明显，这种复杂性不会带<br />来任何好处。 <br />  <br />理论上来说，.（点运算符）可以通过使用和-&gt;一样的技术来进行重载。但是，这样做会导<br />致一个问题，那就是无法确定操作的是重载了.的对象呢，还是通过.引用的一个对象。例<br />如： <br />  <br />  <br />    class Y { <br />    public: <br />        void f(); <br />        // ... <br />    }; <br />  <br />    class X {   // 假设你能重载. <br />        Y* p; <br />        Y&amp; operator.() { return *p; } <br />        void f(); <br />        // ... <br />    }; <br />  <br />    void g(X&amp; x) <br />    { <br />        x.f();  // X::f还是Y::f还是错误？ <br />    } <br />  <br />这个问题能够用几种不同的方法解决。在标准化的时候，哪种方法最好还没有定论。更多的<br />细节，请参见《C++语言的设计和演变》。 <br /><img src ="http://www.cppblog.com/onlinewan/aggbug/20514.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:52 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20514.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么 C++不提供“finally”的构造？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20513.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:52:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20513.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20513.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20513.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20513.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20513.html</trackback:ping><description><![CDATA[
		<br />  <br />因为 C++提供了另外一种方法，它几乎总是更好的：“资源获得即初始化”（resource <br />acquisiton is initialization）技术。基本的思路是，通过一个局部对象来表现资<br />源，于是局部对象的析构函数将会释放资源。这样，程序员就不会忘记释放资源了。举例来<br />说： <br />  <br />    class File_handle { <br />        FILE* p; <br />    public: <br />        File_handle(const char* n, const char* a) <br />            { p = fopen(n,a); if (p==0) throw Open_error(errno); } <br />        File_handle(FILE* pp) <br />            { p = pp; if (p==0) throw Open_error(errno); } <br />  <br />        ~File_handle() { fclose(p); } <br />  <br />        operator FILE*() { return p; } <br />  <br />        // ... <br />    }; <br />  <br />    void f(const char* fn) <br />    { <br />        File_handle f(fn,"rw"); //打开fn进行读写 <br />        // 通过f 使用文件 <br />    } <br />  <br />在一个系统中，需要为每一个资源都使用一个“资源句柄”类。无论如何，我们不需要为每<br />一个资源获得都写出“finally”语句。在实时系统中，资源获得要远远多于资源的种类，<br />因此和使用“finally”构造相比，“资源获得即初始化”技术会产生少得多的代码。<img src ="http://www.cppblog.com/onlinewan/aggbug/20513.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:52 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20513.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>怎样从输入中读取一个字符串？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20512.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20512.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20512.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20512.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20512.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20512.html</trackback:ping><description><![CDATA[
		<br />  <br />你可以用这种方式读取一个单独的以空格结束的词： <br />  <br />    #include&lt;iostream&gt; <br />    #include&lt;string&gt; <br />    using namespace std; <br />  <br />    int main() <br />    { <br />        cout &lt;&lt; "Please enter a word:\n"; <br />  <br />        string s; <br />        cin&gt;&gt;s; <br />     <br />        cout &lt;&lt; "You entered " &lt;&lt; s &lt;&lt; '\n'; <br />    } <br />  <br />注意，这里没有显式的内存管理，也没有可能导致溢出的固定大小的缓冲区。 <br />  <br />如果你确实想得到一行而不是一个单独的词，可以这样做： <br />  <br />  <br />    #include&lt;iostream&gt; <br />    #include&lt;string&gt; <br />    using namespace std; <br />  <br />    int main() <br />    { <br />        cout &lt;&lt; "Please enter a line:\n"; <br />  <br />        string s; <br />        getline(cin,s); <br />     <br />        cout &lt;&lt; "You entered " &lt;&lt; s &lt;&lt; '\n'; <br />    } <br /><img src ="http://www.cppblog.com/onlinewan/aggbug/20512.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:51 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20512.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我为什么在捕获一个异常之后就不能继续？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20511.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20511.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20511.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20511.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20511.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20511.html</trackback:ping><description><![CDATA[
		<br />  <br />换句话说，C++为什么不提供一种简单的方式，让程序能够回到异常抛出点之后，并继续执<br />行？ <br />  <br />主要的原因是，如果从异常处理之后继续，那么无法预知掷出点之后的代码如何对待异常处<br />理，是否仅仅继续执行，就象什么也没有发生一样。异常处理者无法知道，在继续之前，有<br />关的上下文环境（context）是否是“正确”的。要让这样的代码正确执行，抛出异常的<br />编写者与捕获异常的编写者必须对彼此的代码与上下文环境都非常熟悉才行。这样会产生非<br />常复杂的依赖性，因此无论在什么情况下，都会导致一系列严重的维护问题。 <br />  <br />当我设计C++的异常处理机制时，我曾经认真地考虑过允许这种继续的可能性，而且在标准<br />化的过程中，这个问题被非常详细地讨论过。请参见《C++语言的设计和演变》中的异常处<br />理章节。<img src ="http://www.cppblog.com/onlinewan/aggbug/20511.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:51 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20511.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我应该如何对付内存泄漏？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20510.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20510.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20510.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20510.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20510.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20510.html</trackback:ping><description><![CDATA[
		<br />  <br />写出那些不会导致任何内存泄漏的代码。很明显，当你的代码中到处充满了 new 操作、delete操作和指针运算的话，你将会在某个地方搞晕了头，导致内存泄漏，指针引用错误，以及诸如此类的问题。这和你如何小心地对待内存分配工作其实完全没有关系：代码的复杂性最终总是会超过你能够付出的时间和努力。于是随后产生了一些成功的技巧，它们依赖于将内存分配（allocations）与重新分配（deallocation）工作隐藏在易于管理的类型之后。标准容器（standard containers）是一个优秀的例子。它们不是通过你而是自己为元素管理内存，从而避免了产生糟糕的结果。想象一下，没 string 和 vector 的<br />帮助，写出这个： <br />  <br />    #include&lt;vector&gt; <br />    #include&lt;string&gt; <br />    #include&lt;iostream&gt; <br />    #include&lt;algorithm&gt; <br />    using namespace std; <br />  <br />    int main()  // small program messing around with strings <br />    { <br />        cout &lt;&lt; "enter some whitespace-separated words:\n"; <br />        vector&lt;string&gt; v; <br />        string s; <br />        while (cin&gt;&gt;s) v.push_back(s); <br />  <br />        sort(v.begin(),v.end()); <br />  <br />        string cat; <br />        typedef vector&lt;string&gt;::const_iterator Iter; <br />        for (Iter p = v.begin(); p!=v.end(); ++p) cat += *p+"+"; <br />        cout &lt;&lt; cat &lt;&lt; '\n'; <br />    } <br />  <br />你有多少机会在第一次就得到正确的结果？你又怎么知道你没有导致内存泄漏呢？ <br />  <br />注意，没有出现显式的内存管理，宏，造型，溢出检查，显式的长度限制，以及指针。通过<br />使用函数对象和标准算法（standard algorithm），我可以避免使用指针——例如使用<br />迭代子（iterator），不过对于一个这么小的程序来说有点小题大作了。 <br />  <br />这些技巧并不完美，要系统化地使用它们也并不总是那么容易。但是，应用它们产生了惊人<br />的差异，而且通过减少显式的内存分配与重新分配的次数，你甚至可以使余下的例子更加容<br />易被跟踪。早在 1981 年，我就指出，通过将我必须显式地跟踪的对象的数量从几万个减少<br />到几打，为了使程序正确运行而付出的努力从可怕的苦工，变成了应付一些可管理的对象，<br />甚至更加简单了。 <br />  <br />如果你的程序还没有包含将显式内存管理减少到最小限度的库，那么要让你程序完成和正确<br />运行的话，最快的途径也许就是先建立一个这样的库。 <br />  <br />模板和标准库实现了容器、资源句柄以及诸如此类的东西，更早的使用甚至在多年以前。异<br />常的使用使之更加完善。 <br />  <br />如果你实在不能将内存分配/重新分配的操作隐藏到你需要的对象中时，你可以使用资源句<br />柄（resource handle），以将内存泄漏的可能性降至最低。这里有个例子：我需要通过<br />一个函数，在空闲内存中建立一个对象并返回它。这时候可能忘记释放这个对象。毕竟，我<br />们不能说，仅仅关注当这个指针要被释放的时候，谁将负责去做。使用资源句柄，这里用了<br />标准库中的 auto_ptr，使需要为之负责的地方变得明确了。 <br />  <br />    #include&lt;memory&gt; <br />    #include&lt;iostream&gt; <br />    using namespace std; <br />  <br />    struct S { <br />        S() { cout &lt;&lt; "make an S\n"; } <br />        ~S() { cout &lt;&lt; "destroy an S\n"; } <br />        S(const S&amp;) { cout &lt;&lt; "copy initialize an S\n"; } <br />        S&amp; operator=(const S&amp;) { cout &lt;&lt; "copy assign an S\n"; } <br />    }; <br />  <br />    S* f() <br />    { <br />        return new S;   // 谁该负责释放这个 S？ <br />    }; <br />  <br />    auto_ptr&lt;S&gt; g() <br />    { <br />        return auto_ptr&lt;S&gt;(new S);  // 显式传递负责释放这个S <br />    } <br />  <br />    int main() <br />    { <br />        cout &lt;&lt; "start main\n"; <br />        S* p = f(); <br />        cout &lt;&lt; "after f() before g()\n"; <br />    //  S* q = g(); // 将被编译器捕捉 <br />        auto_ptr&lt;S&gt; q = g(); <br />        cout &lt;&lt; "exit main\n"; <br />        // *p产生了内存泄漏 <br />        // *q被自动释放 <br />    } <br />  <br />在更一般的意义上考虑资源，而不仅仅是内存。 <br />  <br />如果在你的环境中不能系统地应用这些技巧（例如，你必须使用别的地方的代码，或者你的<br />程序的另一部分简直是原始人类（译注：原文是 Neanderthals，尼安德特人，旧石器时<br />代广泛分布在欧洲的猿人）写的，如此等等），那么注意使用一个内存泄漏检测器作为开发<br />过程的一部分，或者插入一个垃圾收集器（garbage collector）。<img src ="http://www.cppblog.com/onlinewan/aggbug/20510.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:51 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20510.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是函数对象（function object）？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20509.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:50:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20509.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20509.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20509.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20509.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20509.html</trackback:ping><description><![CDATA[  <br />顾名思义，就是在某种方式上表现得象一个函数的对象。典型地，它是指一个类的实例，这<br />个类定义了应用操作符operator()。 <br />  <br />函数对象是比函数更加通用的概念，因为函数对象可以定义跨越多次调用的可持久的部分<br />（类似静态局部变量），同时又能够从对象的外面进行初始化和检查（和静态局部变量不同）。<br />例如： <br />  <br />class Sum { <br />    int val; <br />public: <br />    Sum(int i) :val(i) { } <br />    operator int() const { return val; }        // 取得值 <br />  <br />    int operator()(int i) { return val+=i; }    // 应用 <br />}; <br />  <br />void f(vector v) <br />{ <br />    Sum s = 0;  // initial value 0 <br />    s = for_each(v.begin(), v.end(), s);    // 求所有元素的和 <br />    cout &lt;&lt; "the sum is " &lt;&lt; s &lt;&lt; "\n"; <br />     <br />    //或者甚至： <br />    cout &lt;&lt; "the sum is " &lt;&lt; for_each(v.begin(), v.end(), Sum(0)) &lt;&lt; <br />"\n"; <br />} <br />  <br />注意一个拥有应用操作符的函数对象可以被完美地内联化（inline），因为它没有涉及到<br />任何指针，后者可能导致拒绝优化。与之形成对比的是，现有的优化器几乎不能（或者完全<br />不能？）将一个通过函数指针的调用内联化。 <br />  <br />在标准库中，函数对象被广泛地使用以获得弹性。 <br /><img src ="http://www.cppblog.com/onlinewan/aggbug/20509.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:50 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20509.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么不能为模板参数定义约束（constraints）？ </title><link>http://www.cppblog.com/onlinewan/archive/2007/03/24/20508.html</link><dc:creator>阿刚</dc:creator><author>阿刚</author><pubDate>Sat, 24 Mar 2007 01:49:00 GMT</pubDate><guid>http://www.cppblog.com/onlinewan/archive/2007/03/24/20508.html</guid><wfw:comment>http://www.cppblog.com/onlinewan/comments/20508.html</wfw:comment><comments>http://www.cppblog.com/onlinewan/archive/2007/03/24/20508.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/onlinewan/comments/commentRss/20508.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/onlinewan/services/trackbacks/20508.html</trackback:ping><description><![CDATA[
		<br />  <br />可以的，而且方法非常简单和通用。 <br />  <br />看看这个： <br />  <br />        template&lt;class Container&gt; <br />        void draw_all(Container&amp; c) <br />        { <br />                for_each(c.begin(),c.end(),mem_fun(&amp;Shape::draw)); <br />        } <br /><br />如果出现类型错误，可能是发生在相当复杂的 for_each()调用时。例如，如果容器的元<br />素类型是int，我们将得到一个和for_each()相关的含义模糊的错误(因为不能够对对一<br />个int值调用Shape::draw 的方法)。 <br />  <br />为了提前捕捉这个错误，我这样写： <br />  <br />        template&lt;class Container&gt; <br />        void draw_all(Container&amp; c) <br />        { <br />                Shape* p = c.front(); // accept only containers of <br />Shape*s <br />  <br />                for_each(c.begin(),c.end(),mem_fun(&amp;Shape::draw)); <br />        } <br />  <br />对于现在的大多数编译器，中间变量 p 的初始化将会触发一个易于了解的错误。这个窍门<br />在很多语言中都是通用的，而且在所有的标准创建中都必须这样做。在成品的代码中，我也<br />许可以这样写： <br />  <br />    template&lt;class Container&gt; <br />        void draw_all(Container&amp; c) <br />        { <br />                typedef typename Container::value_type T; <br />                Can_copy&lt;T,Shape*&gt;(); // accept containers of only <br />Shape*s <br />  <br />                for_each(c.begin(),c.end(),mem_fun(&amp;Shape::draw)); <br />        } <br />  <br />这样就很清楚了，我在建立一个断言(assertion)。Can_copy 模板可以这样定义： <br />  <br />    template&lt;class T1, class T2&gt; struct Can_copy { <br />        static void constraints(T1 a, T2 b) { T2 c = a; b = a; } <br />        Can_copy() { void(*p)(T1,T2) = constraints; } <br />    }; <br />  <br />Can_copy(在运行时)检查 T1 是否可以被赋值给 T2。Can_copy&lt;T,Shape*&gt;检查 T 是<br />否是 Shape*类型，或者是一个指向由 Shape 类公共继承而来的类的对象的指针，或者是<br />被用户转换到Shape*类型的某个类型。注意这个定义被精简到了最小： <br />  <br />一行命名要检查的约束，和要检查的类型 <br />一行列出指定的要检查的约束(constraints()函数) <br />一行提供触发检查的方法(通过构造函数) <br />  <br />注意这个定义有相当合理的性质： <br />  <br />你可以表达一个约束，而不用声明或复制变量，因此约束的编写者可以用不着去设想变量如<br />何被初始化，对象是否能够被复制，被销毁，以及诸如此类的事情。(当然，约束要检查这<br />些属性的情况时例外。) <br />使用现在的编译器，不需要为约束产生代码 <br />定义和使用约束，不需要使用宏 <br />当约束失败时，编译器会给出可接受的错误信息，包括“constraints”这个词（给用户<br />一个线索），约束的名字，以及导致约束失败的详细错误（例如“无法用 double*初始化<br />Shape*”）。 <br />  <br />那么，在C++语言中，有没有类似于 Can_copy——或者更好——的东西呢？在《C++语言<br />的设计和演变》中，对于在 C++中实现这种通用约束的困难进行了分析。从那以来，出现了<br />很多方法，来让约束类变得更加容易编写，同时仍然能触发良好的错误信息。例如，我信任<br />我在 Can_copy 中使用的函数指针的方式，它源自 Alex Stepanov 和 Jeremy Siek。<br />我并不认为 Can_copy()已经可以标准化了——它需要更多的使用。同样，在 C++社区中，<br />各种不同的约束方式被使用；到底是哪一种约束模板在广泛的使用中被证明是最有效的，还<br />没有达成一致的意见。 <br />  <br />但是，这种方式非常普遍，比语言提供的专门用于约束检查的机制更加普遍。无论如何，当<br />我们编写一个模板时，我们拥有了C++提供的最丰富的表达力量。看看这个： <br />  <br />template&lt;class T, class B&gt; struct Derived_from { <br />    static void constraints(T* p) { B* pb = p; } <br />    Derived_from() { void(*p)(T*) = constraints; } <br />}; <br />  <br />template&lt;class T1, class T2&gt; struct Can_copy { <br />    static void constraints(T1 a, T2 b) { T2 c = a; b = a; } <br />    Can_copy() { void(*p)(T1,T2) = constraints; } <br />}; <br />  <br />template&lt;class T1, class T2 = T1&gt; struct Can_compare { <br />    static void constraints(T1 a, T2 b) { a==b; a!=b; a&lt;b; } <br />    Can_compare() { void(*p)(T1,T2) = constraints; } <br />}; <br />  <br />template&lt;class T1, class T2, class T3 = T1&gt; struct Can_multiply { <br />    static void constraints(T1 a, T2 b, T3 c) { c = a*b; } <br />    Can_multiply() { void(*p)(T1,T2,T3) = constraints; } <br />}; <br />  <br />struct B { }; <br />struct D : B { }; <br />struct DD : D { }; <br />struct X { }; <br />  <br />int main() <br />{ <br />    Derived_from&lt;D,B&gt;(); <br />    Derived_from&lt;DD,B&gt;(); <br />    Derived_from&lt;X,B&gt;(); <br />    Derived_from&lt;int,B&gt;(); <br />    Derived_from&lt;X,int&gt;(); <br />  <br />    Can_compare&lt;int,float&gt;(); <br />    Can_compare&lt;X,B&gt;(); <br />    Can_multiply&lt;int,float&gt;(); <br />    Can_multiply&lt;int,float,double&gt;(); <br />    Can_multiply&lt;B,X&gt;(); <br />     <br />    Can_copy&lt;D*,B*&gt;(); <br />    Can_copy&lt;D,B*&gt;(); <br />    Can_copy&lt;int,B*&gt;(); <br />} <br />  <br />// 典型的“元素必须继承自Mybase*”约束: <br />  <br />template&lt;class T&gt; class Container : Derived_from&lt;T,Mybase&gt; { <br />    // ... <br />}; <br />  <br />事实上，Derived_from并不检查来源（derivation），而仅仅检查转换（conversion），<br />不过这往往是一个更好的约束。为约束想一个好名字是很难的。 <br /><img src ="http://www.cppblog.com/onlinewan/aggbug/20508.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/onlinewan/" target="_blank">阿刚</a> 2007-03-24 09:49 <a href="http://www.cppblog.com/onlinewan/archive/2007/03/24/20508.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>