﻿<?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>BlogJava-weidagang2046的专栏-文章分类-C/C++</title><link>http://www.blogjava.net/weidagang2046/category/1276.html</link><description>物格而后知致</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 08:47:16 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 08:47:16 GMT</pubDate><ttl>60</ttl><item><title>C语言中对文件的随机存取</title><link>http://www.blogjava.net/weidagang2046/articles/91918.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 05 Jan 2007 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/91918.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/91918.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/91918.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/91918.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/91918.html</trackback:ping><description><![CDATA[C语言中对文件的随机存取<br />C语言中要实现对文件的随机存 取，需要用到两个函数fseek()、ftell()。下面通过 一个反转显示指定文件的程序来介绍这两个函数的用法。<br />r eserve.c：<br /><br /><br /><br /><br /><br />#include &lt;<br />stdio.h&gt;<br />#include &lt;<br />stdlib.h&gt;<br /><br /><br />#define CNTL_Z '\032' / /DOS 文本中的文件结尾标记 <br />#define SL EN 50<br /><br />int main(int arg c, char *argv[])<br />{<br />c har file[SLEN];<br /><br />char ch ;<br /><br />FILE *fp;<br /><br />long count, last;<br /><br /><br />p uts("Enter the name of the fil e to be processed: ");<br /><br />gets(file);<br /><br />if( (fp = f open(file, "rb")) == NULL ) //只读和二进制模式 <br />{<br />printf("reverse can't open %s\n", file);<br /><br />exit( 1);<br /><br />} <br /><br />f seek(fp, 0L, SEEK_SET);<br />//定位在文件开头处 <br />la st = ftell(fp);<br /><br />printf( "fseek(fp, 0L, SEEK_SET) , fte el(p): %d\n", last);<br /><br />fseek(fp, 0L, SEEK_END);<br />//定位在文件结尾处 <br />last = ftell(fp);<br /><br />printf("fseek(fp, 0L, SEEK_END ) , fteel(p): %d\n", last);<br /><br /><br />for(count = 1L;<br />c ount &lt;<br />= last;<br />count++)<br />{<br />fseek(fp, -cou nt, SEEK_END);<br /><br />ch = getc(fp);<br /><br /><br />if(ch != CNTL_Z &amp;<br />&amp;<br />ch != '\r')<br />{<br />putchar(ch);<br /><br />}<br />}<br />putchar ('\n');<br /><br />fclose(fp);<br /><br /><br />system("PAUSE");<br /><br />return 0;<br /><br />}<br /><br />假定一个文 件test.txt内容为：<br /><br /><br /><br /><br /><br />1234567890<br />12345678 90<br />1234567890<br />1111111112 <br />2222222223<br />3333333334 <br />执行reserve来进行反转显示：<br />&lt; br&gt;<br /><br /><br /><br />Enter the n ame of the file to be processe d:<br />test.txt<br />fseek(fp, 0L , SEEK_SET) , fteel(p): 0<br />f seek(fp, 0L, SEEK_END) , fteel (p): 72<br /><br />4333333333<br />3222222222<br />2111111111<br />09 87654321<br />0987654321<br />0987 654321<br /><br />下面，我们来解释一下fseek ()和ftell()是如何工作的。<br />l fseek()函数<br /><br /><br /><br /><br />fseek（移动文件流的读写位置）<br /><br /><br />相 关函数 <br /><br />rewind，ftell，fgetp os，fsetpos，lseek<br /><br /><br />表头 文件 <br /><br />#include&lt;<br />stdio.h &gt;<br /><br /><br /><br />定义函数 <br /><br />i nt fseek(FILE * stream,long of fset,int whence);<br /><br /><br /><br />函 数说明 <br /><br />fseek()用来移动文件流的读写位 置。参数stream为已打开的文件指针，参数offset为根 据参数whence来移动读写位置的位移数。<br /><br />&lt; br&gt;参数 <br /><br />whence为下列其中一种:SE EK_SET从距文件开头offset位移量为新的读写位置。S EEK_CUR 以目前的读写位置往后增加offset个位移量 。SEEK_END将读写位置指向文件尾后再增加offset个 位移量。当whence值为SEEK_CUR 或SEEK_EN D时，参数offset允许负值的出现。下列是较特别的使用方式 :1) 欲将读写位置移动到文件开头时:fseek(FILE *stream,0,SEEK_SET);<br />2) 欲将读写位置移 动到文件尾时:fseek(FILE *stream,0,0S EEK_END);<br /><br /><br /><br />返回值 <br />&lt; br&gt;当调用成功时则返回0，若有错误则返回-1，errno会 存放错误代码。<br /><br /><br />附加说明 <br />fseek()不像lseek()会返回读写位置，因此必须 使用ftell()来取得目前读写的位置。<br /><br />l ftell()函数<br /><br /><br /><br /><br />ftell（取得文件流的读取位置）<br />&lt; br&gt;<br />相关函数 <br /><br />fseek，rewi nd，fgetpos，fsetpos<br /><br /><br />表头文件 <br /><br />#include&lt;<br />stdio .h&gt;<br /><br /><br /><br />定义函数 <br /><br />long ftell(FILE * stream);<br /><br /><br /><br />函数说明 <br /><br />ftell()用 来取得文件流目前的读写位置。参数stream为已打开的文件指 针。<br /><br /><br />返回值 <br /><br />当调用成 功时则返回目前的读写位置，若有错误则返回-1，errno会存 放错误代码。<br /><br /><br />错误代码 <br /><br />EBADF 参数stream无效或可移动读写位置的文件流。 <br /><br /><br />范例 <br /><br />参考fseek ()。<br /><br />通过fseek()、ftell()两 个函数，我们就可以随意访问文件的任何位置了，想了想好像操作文 件就这么easy，实在也没有更多可说的了。对了，fseek( )和ftell()存在一个潜在的问题就是他们限制文件的大小只 能在long类型的表示范围以内，也就是说通过这种方式，只能打 开2,000,000,000字节的文件，不过在绝大多数情况下 似乎也已经够用了。如果需要打开更大的文件，你需要用到fget pos()、fsetpos()函数了，那是另一个命题了。<br /><br />from: <a href="http://www.aonet.cn/artical/26/2005045581.htm">http://www.aonet.cn/artical/26/2005045581.htm</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/91918.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2007-01-05 10:49 <a href="http://www.blogjava.net/weidagang2046/articles/91918.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言学习进程:fseek与ftell函数</title><link>http://www.blogjava.net/weidagang2046/articles/91915.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 05 Jan 2007 02:46:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/91915.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/91915.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/91915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/91915.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/91915.html</trackback:ping><description><![CDATA[
		<blockquote class="bbquote">fseek函数是 用来设定文件的当前读写位置.<br /><br />函数原型: int fseek(FILE *fp,long offset,int origin);<br /><br />函数功能:把fp的文件读写位置指针移到指定的位置.<br /><br />fseek(fp,20,SEEK_SET); 意思是把fp文件读写位置指针从文件开始后移20个字节.<br /><br /><br /><br />ftell函数是用来获取文件的当前读写位置;<br /><br />函数原型: long ftell(FILE *fp)<br /><br />函数功能:得到流式文件的当前读写位置,其返回值是当前读写位置偏离文件头部的字节数.<br /><br />ban=ftell(fp); 是获取fp指定的文件的当前读写位置,并将其值传给变量ban.<br /><br /><br /><br />fseek函数与ftell函数综合应用:<br /><br />分析:可以用fseek函数把位置指针移到文件尾,再用ftell函数获得这时位置指针距文件头的字节数,这个字节数就是文件的长度.<br /><br /><pre>#include &lt;stdio.h&gt;

main()

{

   FILE *fp;

   char filename[80];

   long length;

   printf("输入文件名:");

   gets(filename);

   //以二进制读文件方式打开文件

   fp=fopen(filename,"rb");

   if(fp==NULL)

      printf("file not found!\n");

   else

      {

         //把文件的位置指针移到文件尾

          fseek(fp,OL,SEEK_END);

         //获取文件长度;

          length=ftell(fp);

          printf("该文件的长度为%1d字节\n",length);

          fclose(fp);

      }

}<br /><br /><br />from: <a href="http://my.opera.com/lau_jia/blog/show.dml/380421">http://my.opera.com/lau_jia/blog/show.dml/380421</a></pre></blockquote>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/91915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2007-01-05 10:46 <a href="http://www.blogjava.net/weidagang2046/articles/91915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++标准I/O重定向</title><link>http://www.blogjava.net/weidagang2046/articles/82106.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 19 Nov 2006 13:38:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/82106.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/82106.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/82106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/82106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/82106.html</trackback:ping><description><![CDATA[#include &lt;iostream&gt;<br />#include &lt;fstream&gt;<br />int main()<br />{<br />    std::ofstream logFile("out.txt");<br />    std::streambuf *outbuf = std::cout.rdbuf(logFile.rdbuf());<br />    std::streambuf *errbuf = std::cerr.rdbuf(logFile.rdbuf());<br /><br />    // do the actual work of the program;<br />    // GUI code and event loop would go here<br />    std::cout &lt;&lt; "This would normally go to cout but goes to the log file\n";<br />    std::cerr &lt;&lt; "This would normally go to cerr but goes to the log file \n";<br />    logFile &lt;&lt; "This goes to the log file\n";<br />    // end of program body<br /><br />    // restore the buffers<br />    std::cout.rdbuf(outbuf);<br />    std::cerr.rdbuf(errbuf);<br />}<br /><br />rdbuf函数返回一个由基类basic_ios管理的流缓冲区的指针。重载版本允许你替换流缓冲区，返回值是原始的流缓冲区。解决方法很简单—用你的log文件的流缓冲区替换cout和cerr的流缓冲区。程序结束时，改回原来的流缓冲区。<img src ="http://www.blogjava.net/weidagang2046/aggbug/82106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-19 21:38 <a href="http://www.blogjava.net/weidagang2046/articles/82106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC++2005快速构建安全的应用程序</title><link>http://www.blogjava.net/weidagang2046/articles/80282.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 09 Nov 2006 15:24:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/80282.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/80282.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/80282.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/80282.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/80282.html</trackback:ping><description><![CDATA[　　<b>一、 简介</b><br /><br />　　微软的Visual C++2005发布版本对于有志于轻松、迅速地编写安全可靠的应用程序的编程爱好者来说是正确地选择。正如你所听到的那样，Visual C++中语言和库的新特点使开发安全、可靠的应用程序比以前更容易。它即提供了功能强大并且灵活的标准C++，又提供了适于.NET框架下编程的最强大的开发语言。<br /><br />　　本文中，我主要探讨Visual C++2005发布版本中部分语言和库的新特色，无论是对于教学项目还是大的应用工程，这都将帮助你在编写安全可靠的代码时提高工作效率。<br /><br />　　<b>二、C运行时库的安全特点</b><br /><br />　　如果你正在使用Visual C++创建使用C运行时库的应用程序，你将非常欣慰地了解到现在你所依赖的许多库函数都有了更安全的版本。对于需要一个或多个缓冲作为输入的函数来说，已经添加了长度参数，以此让函数来确信不会超越缓冲的边界。现在更多的函数开始对参数进行合法性检查，必要时将调用无效参数处理器。让我们来看一些简单的例子：<br /><br />　　C运行时库中最不可靠的是gets函数，它从标准输入中读取一行。思考下面的一个简单的例子：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>char buffer[10] = { 0 }; <br />gets(buffer);</td></tr></tbody></table><br />　　第一行代码声明了一个缓冲变量，并将缓冲区中的字符初始化设置为0。为避免意外情况发生将变量初始化为一个众所周知的值是一个非常好的主意。紧接着，看似清白无辜的gets函数从标准的输入流中读取一行并且写入到buffer缓冲区内。这有什么错误吗？对于函数来说C类型的数组不能实现值传递，而是传递了指向数组第一个元素的指针。所以在函数看来，char[ ]相当于char*指针，并且是一个不附带可以决定所指向的缓冲区大小尺寸的任何额外信息的原始指针。那么gets函数是怎么作的呢？它假设缓冲区无限大（UINT_MAX 是有精确尺寸的），并将持续地从输入流中拷贝字符到缓冲区内。攻击者可以轻易地使用这个弱点，这种不广为人知的类型错误被称为缓冲溢出。<br /><br />　　很多最初的C运行时库函数遭受同样的与参数确认有关的问题，并且现在因此受到抨击。一定要牢记对于当前所要写的应用程序来说，性能处于次要地位，我们现在生活在一个安全第一的世界。每一个受到批评的函数已经被一个提供同样函数功能，但添加了安全特点的函数所代替。当然，根据你在已经存在的代码中所使用的旧的库函数的多少，你可能需要花费一些时间来代码更替到新的、更安全的版本。这些新的函数有一个_s后缀，例如，gets函数被gets_s函数代替；遭受抨击的strcpy函数被strcpy_s函数代替。这里有一个例子：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>char buffer[10] = { 0 };<br />gets_s(buffer, sizeof (buffer) / sizeof (buffer[0]));</td></tr></tbody></table><br />　　gets_s函数有一个额外的参数，用来显示可以写入的最大字符数量，这里包括一个NULL终结符。我使用了sizeof操作符，它能决定数组的长度，因为编译器在编译时决定sizeof操作符返回的结果。记住，sizeof返回操作数的长度是以字节为单位的，所以用数组长度来除以数组中第一个元素的长度将返回数组中元素的个数。这种简单的方法可以移植到Unicode编码下使用_getws_s的情况，这个函数也需要得知以字节为单位的缓冲区长度。<br /><br />　　正如我所提到的，另外一个接受安全检查的常用函数strcpy函数，就象gets函数一样，它没有方法来保证有效的缓冲区尺寸，所以它只能假定缓冲足够大来容纳要拷贝的字符串。在程序运行时，这将导致不可预料的行为，正如我所提到的，为了安全需要避免这些不可预料的行为，这有一个使用安全的strcpy_s函数的例子。<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>char source[] = Hello world!; <br />char destination[20] = { 0 }; <br />strcpy_s(destination, sizeof (destination) / sizeof (destination[0]), source);</td></tr></tbody></table><br />　　有很多原因来喜欢这个新的strcpy_s函数。最明显的区别是的额外的、以字节为单位的参数，它用来确认缓冲区大小。这允许strcpy_s函数可以进行运行时检查，以确定写入的字符没有超过目标缓冲区的边界。还有一些其它的检查方法来确定参数的有效性。在调试版本中这些检测方法，包括显示调试报告的断言（assertions）方法，如果它们的条件没有满足，它们将显示调试报告。无论是调试还是发行版本，如果一个特定的条件没有得到满足，一个无效的参数管理器将被调用，它默认的行为是抛出一个访问冲突来终止应用程序。这非常好的实现了让你的应用程序持续运行，而不会产生不可预期的结果。当然，这种情况完全可以通过确保类似于strcpy_s的函数不调用无效参数来避免。<br /><br />　　前一个例子可以通过新的_countof宏来进一步简化，这个宏移抛开了对有错误倾向的sizeof操作符的需要。_countof宏返回C类型数组的元素数量。这个宏本身对应了一个模版，如果传递一个原始指针的话，它将无法通过编译。这有一个例子：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>strcpy_s(destination, _countof(destination), source);</td></tr></tbody></table><br /><br /><br /><b>三、使用C++标准库</b><br /><br />　　已经看了C运行时库新增强的安全特性，让我们来看一下如何使用C++标准库来进一步减少你的代码中的相似错误。<br /><br />　　当你从C运行时库转向C++的标准库，让你从C++开始受益的一个最有效的方法是使用库中的矢量类（Vector class）。矢量类是C++标准库中的一个模仿一维T数组的容器类，这里T可以是事实上的任何类型，你的代码中使用缓冲区的地方都可以用矢量对象来代替。让我们来考虑上一节的例子，第一个例子我们使用gets_s函数来从标准输入中读取一个行，考虑用下面的代码代替：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>std::vector&lt;char&gt; buffer(10); <br />gets_s(&amp;buffer[0], buffer.size());</td></tr></tbody></table><br />　　最值得注意的一个区别是缓冲区变量现在是一个带有可用方法和操作符的矢量对象，这个矢量对象初始化为10个字节长度，并且构造函数将每个元素都初始化为0，表达式&amp;buffer[0]用于得到矢量对象的第一个元素的地址，向期待一个简单缓冲区的C函数传递一个矢量对象是一种正确的方法。与sizeof操作符不同的是，所有的容器的尺寸测量是基于元素的，而不是基与字节的。例如，矢量的size方法返回的是容器的元素数量。<br /><br />　　在上节的第二个例子里，我们使用strcpy_s函数从源缓冲区向目标缓冲区拷贝字符。应该清楚矢量对象是如何代替原始的C类型的数组，为了更好的说明这一点，让我们来考虑另外一个非常有用的C++标准库的容器。<br /><br />　　提供的basic_string类使得字符串在C++中可以作为正常的类型来操作。它提供了各种各样的重载操作符，为C++程序开发人员提供了自然的编程模式。由于优于strcopy_s及其它操作字符串的函数，你应该首选basic_string函数。basic_string以字节为单位的T类型容器。这里T是字符类型。C++标准类库对于常用的字符类型提供类型定义。string和wstring中的元素类型分别被定义为char和wchar类型。下面的例子说明basic_string类是多么简单和安全：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>std::string source = Hello world!; <br />std::string destination = source;</td></tr></tbody></table><br />　　basic_string类也提供了你所希望的、常用的字符串操作的方法和操作符，象字符串联合及子串的搜索。<br /><br />　　最后，C++标准库提供了一个功能非常强大的I/O库，用来安全、简单地与标准输入输出、文件流进行交互操作。虽然对于gets_s函数来说使用矢量对象比使用C类型的数组更好，但你可以通过使用定义的basic_istream 和 basic_ostream类进一步简化。实际上，你可以书写简单并且类型安全的代码从流中来读取包括字符串在内的任何类型。<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>std::string word;<br />int number = 0; <br />std::cin &gt;&gt; word &gt;&gt; number; <br />std::cout &lt;&lt; word<br />&lt;&lt; std::endl <br />&lt;&lt; number <br />&lt;&lt; std::endl;</td></tr></tbody></table><br />　　cin被定义成一个basic_istream流，从标准的输入中提取字符类型的元素。wcin是用于wchar_t元素。另一方面，cout被定义为一个basic_ostream流，用于向标准的输出流写入操作。正如你能想象的，这种模式比起gets_s和puts函数来可以无限的扩展。但是，真正的价值是在于它非常难以产生让你的应用程序出现安全裂痕的错误。<br /><br />　　<b>四、C++标准库中的边界检查</b><br /><br />　　默认情况，C++标准库中大量的容器对象和迭代对象没有提供边界检查。例如，矢量的下标操作符通常是一个比较快，但有潜在的危险性的操作单独元素的方法。如果你正在寻找得到确认检查的操作方法，你可以转向at方法。安全性的增加是以牺牲性能为代价的。当然，绝大情况下性能的降低是可以忽略不计的，但是对于性能要求第一位的代码来说，这可能是非常有害的，思考一下下面的简单函数：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>void PrintAll(const std::vector&lt;int&gt;&amp; numbers) <br />{ <br />　for (size_t index = 0; index &lt; numbers.size(); ++index)<br />　{<br />　　std::cout &lt;&lt; numbers[index] &lt;&lt; std::endl;<br />　} <br />} <br />void PrintN(const std::vector&lt;int&gt;&amp; numbers, size_t index) <br />{ <br />　std::cout &lt;&lt; numbers.at(index) &lt;&lt; std::endl; <br />}</td></tr></tbody></table><br />　　PrintAll函数使用了下标操作符，因为索引由函数控制，并且可以确认是安全的。另一方面，PrintN函数不能保证索引的有效性，所以它使用了更安全的at方法来代替。当然，并不是所有的容器的存取操作都象这么简洁明了。<br /><br />　　在保证C++标准库的安全特性的同时，Visual C++2005继续坚持并在很多情况下改进了C++标准库的运行特性，同时提供了调节C++标准库安全性的特色。一项受人欢迎的改进是在调试版本中添加了范围检查，这对你的发行版本性能并不构成影响。但这确实帮助你在调试阶段捕获越界错误，甚至是使用传统上不安全的下标操作符的代码。<br /><br />　　不安全的函数，象vector的下标操作算子，和其他的函数，象它的front函数，如果不恰当的调用，通常会导致不明确的行为。如果你幸运的话，它将很快导致一个存取冲突，这将使你的应用程序崩溃。如果你不那么走运的话，它可能默默地持续运转并导致不可预知的副效应，这将破坏数据并可能被进攻者利用。为了保护你的发行版本的应用程序，Visual C++2005引入了_SECURE_SCL符号，用来给那些非安全的函数添加运行时检查。象下面的代码那样在你的应用程序中简单地定义这个符号可以添加额外的实时检查并阻止不确切的行为。<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>#define _SECURE_SCL 1</td></tr></tbody></table><br />　　紧记定义这个符号对你的程序冲击很大，大量的合法的，但是具有潜在非安全的操作将在编译时将无法通过，以避免在运行时出现潜在BUG。思考下面的使用Copy运算的例子：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>std::copy(first, last, destination);</td></tr></tbody></table><br />　　其中，first和last是定义拷贝范围的迭代参数，destination是输出迭代参数，指示了目标缓冲区的位置，这个位置用来拷贝范围之内的第一个元素。这里有一个危险是destination所对应的目标缓冲区或容器不足够大，无法容纳所要拷贝的元素。如果Destination是一个需要安全检查的迭代参数，类似的错误将被捕获。但是，这仅仅是一个假设。如果destination是一个简单的指针，将无法保证copy运算函数正确运转。这时当然会想到_SECURE_SCL符号来避免这一问题，这种情况下，代码甚至是不能编译，以此避免任何可能的运行时错误。就象你想象的那样，这将需要重写更完美有效的代码。所以，这是一个更好的理由支持C++标准库容器，避免使用C类型数组。<br /><br /><br /><b>五、编译器的安全特点</b><br /><br />　　虽然对于Visual C++2005来说并不是全新的，但大量编译器特色仍然需要了解。与以前版本的显著区别是编译器的安全检查当前默认情况下是打开的，让我们来看一下编译器的特点及在某些情况下它们是如何阻止在某些情况下受到攻击。<br /><br />　　Visual C++编译器很久以前就开始提供严格的运行时安全检查选项，包括栈校验，下溢和上溢检查以及未初始化变量的识别。这些运行时检查由编译器的/RTC选项来控制。虽然在早期的发展中捕获错误非常有用，但是对于发布版本性能上的损失却是不能接受的。微软的Visual C++.NET引入了/GS编译开关，对于发行版本来说它添加了有限的运行时安全检查。/GS开关在编译开关中插入代码，通过检测函数的栈数据来检测通常基于栈的缓冲溢出。如果发现问题，应用程序将被终止。为了减少运行时检查对性能的影响，编译器辨别哪个函数易于攻击并且仅针对这些函数来进行安全检查。安全检查涉及到在函数的栈框架上增加一个cookie，在缓冲溢出的情况下它将被重写。函数指令的前后都添加了汇编指令。在函数执行以前，源自cookie模块的函数cookie先执行计算。当函数结束但在栈空间被收回前，cookie的栈拷贝被检索以判断它是否被更改。如果cookie未被更改，函数结束并继续执行程序的下一步，如果cookie被更改了，一个安全错误句柄将被调用，它将结束应用程序。<br />为了在Visual C++ 2005发布版本中控制这些编译选项，打开工程的属性页，单击C/C++标签，在代码发生属性页中，你将发现两个属性对应于我刚刚描述的特点。Basic Runtime Checks属性对应于开发时/RTC编译选项，在编译版本中应设置为BOTH。Buffer Security Check属性相当于编译器的/GS选项，对于发布版本应设置为YES。<br /><br />　　对于使用Visual C++ 2005的开发人员来说，这些编译特点在默认情况下打开，这意味着你可以确信编译器正在尽其可能阻止你代码中的漏洞。然而，这并不意味着我们可以完全不关心安全问题。开发人员需要继续为正确的代码而努力，并且要考虑各种不同的、可能发生的安全威胁。编译器仅仅可以阻止部分类型的错误发生。<br /><br />　　要牢记这些编译器提供的特殊的安全检查仅适用于本地代码，幸运的是，托管代码很少犯此类的错误。这里甚至于有更好的消息，Visual C++ 2005引进了C++/CLI设计语言，它提供了.NET框架下最强有力的开发语言。<br /><br />　　<b>六、新的C++编程语言</b><br /><br />　　Visual C++ 2005发布版本提供了C++/CLI设计语言的一流的实现。C++/CLI是为.NET设计的系统编程语言。相对于其他语言来说，它在创建和使用.NET模块和汇编上有更多的控制。C++/CLI对于C++开发人员来说更精细和自然，无论你是否熟悉C++或.NET框架，你将发现使用C++书写托管代码是对ANSI C++自然文雅的扩展，学习起来非常容易。<br /><br />　　对于开发应用程序来说，有许多强制性的原因让你来选择托管代码而不是本地C++。两个最重要的原因是安全性和效率。通用运行时语言（CLR）给你的代码提供了一个安全的运行环境。作为一个程序开发人员，你不需要关心缓冲区溢出及因为你在使用前未初始化变量等问题。安全问题没有完全消失，但是使用托管可以避免通常发生的一些错误。<br /><br />　　另外一个使用托管的原因是.NET框架下丰富的类库。虽然标准C++库更适合于C++类型编程，但是.NET框架包含了一个功能强大的函数库，这是标准C++库所无法比拟的。.NET框架包括很多有用的集合类、一个强大的数据操作库、执行很多流行的通讯协议的类，从SOCKETS到HTTP和网络服务等等。虽然本地C++程序开发人员可以以各种形似使用这些服务，但通过使用.NET框架获取的生产力主要因为它的统一性和连贯性。无论你是用System::Net::Sockets还是用System::Web名字空间，你将面对同样的类型，描述广泛应用的概念，例如流和字符串。这是.NET框架具有开发高效率的最主要的原因。这让程序人员更快速地书写更强有力的应用程序，同时代码更可靠。<br /><br />　　Visual C++ 2005自然地准许你在一个工程中混合本地与托管代码，你可以继续使用已经存在的本地函数及类的同时，开始使用越来越多的.NET框架下的类库，甚至是写你的托管类型。你可以将你的托管类型定义为一个引用类型或一个值类型，虽然Visual C++编译器允许你为了方面选择使用栈语法或是为了控制管理资源使用通常的作用域规则，但值类型在栈上而引用类型位于CLR的托管堆上。 <br /><br />　　通过在你定义的class 和 struct 前添加ref来形成一个关键词，定义了一个引用类型。获取和释放资源按通常的方式完成，通过使用构造和析构函数，正如这里说明的：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>ref class Connection<br />{ <br />　public: Connection(String^ server) <br />　{<br />　　Server = server; <br />　　Console::WriteLine(Aquiring connection to server.);<br />　}<br />　~Connection() <br />　{<br />　　Console::WriteLine(Disconnecting from server.); <br />　}<br />　property String^ Server; <br />};</td></tr></tbody></table><br />　　编译器负责Connection引用类型的IDisposable接口的实现，所以使用类似C#、Visual Basic.NET的开发人员可以使用任何对他们可用的资源管理结构。对于C++开发人员，有着与以前一样的选择。为了简化资源管理，并书写异常安全代码，你可以简单地在栈上声明一个Connection对象。当一个对象超过其作用范围后，执行Dispose方法的析构函数将被调用。下面是一个例子：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>void UseStackConnection()<br />{<br />　Connection connection(sample.kennyandkarin.com); <br />　Console::WriteLine(Connection to {0} established!, <br />　connection.Server);<br />}</td></tr></tbody></table><br />　　这个例子中，通过在函数返回调用前调用析构函数来关闭这个Connection，这正如你在C++希望的那样。如果你希望自己控制对象的生命期，仅仅需要使用gcnew这个关键词来获取connection对象的句柄。这个指针可以看作通常的指针（不含有通常的缺陷），并且这个对象的析构函数可以简单地通过delete操作来调用。这个例子代码如下 ：<br /><br /><table class="txcode" width="90%" align="center" bgcolor="#dadacf" border="1"><tbody><tr><td>void UseHeapConnection()<br />{<br />　Connection^ connection = gcnew Connection(sample.kennyandkarin.com);<br />　try<br />　{<br />　　Console::WriteLine(Connection to {0} established!,<br />　　connection-&gt;Server);<br />　}<br />　finally<br />　{<br />　　delete connection;<br />　}<br />}</td></tr></tbody></table><br />　　正如你所看到的，从本地C++到托管代码，Visual C++ 2005带来了简单灵活的资源管理方式，可以书写强壮的资源管理代码对于书写正确、安全的代码是非常重要的。<br /><br />　　<b>七、小结</b><br /><br />　　无论是对于一个小的程序还是一个大的应用，Visual C++ 2005发布版本都是一个功能强大的开发工具，C运行时库和C++标准库提供了一个强大的工具集，来发布功能强大的、强壮的本地应用程序，同时，对用C++书写托管代码有着一流的支持，Visual C++ 2005在微软的Windows开发平台上是独一无二的强大的开发工具。<br /><br />from: <a href="http://www.97t.org/ArticleView/2005-9-4/Article_View_241.Htm">http://www.97t.org/ArticleView/2005-9-4/Article_View_241.Htm</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/80282.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-09 23:24 <a href="http://www.blogjava.net/weidagang2046/articles/80282.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用C++实现简单的文件I/O操作</title><link>http://www.blogjava.net/weidagang2046/articles/74898.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 13 Oct 2006 00:45:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/74898.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/74898.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/74898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/74898.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/74898.html</trackback:ping><description><![CDATA[   文件 I/O 在C++中比烤蛋糕简单多了。 在这篇文章里，我会详细解释ASCII和二进制文件的输入输出的每个细节，值得注意的是，所有这些都是用C++完成的。<br /><br />　　一、ASCII 输出<br /><br />　　为了使用下面的方法, 你必须包含头文件&lt;fstream.h&gt;(译者注：在标准C++中，已经使用&lt;fstream&gt;取代&lt; fstream.h&gt;，所有的C++标准头文件都是无后缀的。)。这是 &lt;iostream.h&gt;的一个扩展集, 提供有缓冲的文件输入输出操作. 事实上, &lt;iostream.h&gt; 已经被&lt;fstream.h&gt;包含了, 所以你不必包含所有这两个文件, 如果你想显式包含他们，那随便你。我们从文件操作类的设计开始, 我会讲解如何进行ASCII I/O操作。如果你猜是"fstream," 恭喜你答对了！ 但这篇文章介绍的方法,我们分别使用"ifstream"?和 "ofstream" 来作输入输出。<br /><br />　　如果你用过标准控制台流"cin"?和 "cout," 那现在的事情对你来说很简单。 我们现在开始讲输出部分，首先声明一个类对象。ofstream fout;<br /><br />　　这就可以了，不过你要打开一个文件的话, 必须像这样调用ofstream::open()。<br /><br />fout.open("output.txt");<br /><br />　　你也可以把文件名作为构造参数来打开一个文件.<br /><br />ofstream fout("output.txt");<br /><br />　　这是我们使用的方法, 因为这样创建和打开一个文件看起来更简单. 顺便说一句, 如果你要打开的文件不存在，它会为你创建一个, 所以不用担心文件创建的问题. 现在就输出到文件，看起来和"cout"的操作很像。 对不了解控制台输出"cout"的人, 这里有个例子。<br /><br />int num = 150;<br />char name[] = "John Doe";<br />fout &lt;&lt; "Here is a number: " &lt;&lt; num &lt;&lt; "\n";<br />fout &lt;&lt; "Now here is a string: " &lt;&lt; name &lt;&lt; "\n";<br /><br />　　现在保存文件，你必须关闭文件，或者回写文件缓冲. 文件关闭之后就不能再操作了, 所以只有在你不再操作这个文件的时候才调用它，它会自动保存文件。 回写缓冲区会在保持文件打开的情况下保存文件, 所以只要有必要就使用它。回写看起来像另一次输出, 然后调用方法关闭。像这样：<br /><br />fout &lt;&lt; flush; fout.close();<br /><br />　　 现在你用文本编辑器打开文件，内容看起来是这样：<br /><br />　　Here is a number: 150 Now here is a string: John Doe<br /><br />　　很简单吧! 现在继续文件输入, 需要一点技巧, 所以先确认你已经明白了流操作，对 "&lt;&lt;" 和"&gt;&gt;" 比较熟悉了, 因为你接下来还要用到他们。继续…<br /><br />　　二、ASCII 输入<br /><br />　　输入和"cin" 流很像. 和刚刚讨论的输出流很像, 但你要考虑几件事情。在我们开始复杂的内容之前, 先看一个文本：<br /><br />　　12 GameDev 15.45 L This is really awesome!<br /><br />　　为了打开这个文件，你必须创建一个in-stream对象,?像这样。<br /><br />ifstream fin("input.txt");<br /><br />　　现在读入前四行. 你还记得怎么用"&lt;&lt;" 操作符往流里插入变量和符号吧？好,?在 "&lt;&lt;" (插入)?操作符之后，是"&gt;&gt;" (提取) 操作符. 使用方法是一样的. 看这个代码片段.<br /><br />int number;<br />float real;<br />char letter, word[8];<br />fin &gt;&gt; number; fin &gt;&gt; word; fin &gt;&gt; real; fin &gt;&gt; letter;<br /><br />　　也可以把这四行读取文件的代码写为更简单的一行。<br /><br />fin &gt;&gt; number &gt;&gt; word &gt;&gt; real &gt;&gt; letter;<br /><br />　　它是如何运作的呢? 文件的每个空白之后, "&gt;&gt;" 操作符会停止读取内容, 直到遇到另一个&gt;&gt;操作符. 因为我们读取的每一行都被换行符分割开(是空白字符), "&gt;&gt;" 操作符只把这一行的内容读入变量。这就是这个代码也能正常工作的原因。但是，可别忘了文件的最后一行。<br /><br />　　This is really awesome!<br /><br />　　如果你想把整行读入一个char数组, 我们没办法用"&gt;&gt;"?操作符，因为每个单词之间的空格（空白字符）会中止文件的读取。为了验证：<br /><br />char sentence[101]; fin &gt;&gt; sentence;<br /><br />　　我们想包含整个句子, "This is really awesome!" 但是因为空白, 现在它只包含了"This". 很明显, 肯定有读取整行的方法, 它就是getline()。这就是我们要做的。<br /><br />fin.getline(sentence, 100);<br /><br />　　这是函数参数. 第一个参数显然是用来接受的char数组. 第二个参数是在遇到换行符之前，数组允许接受的最大元素数量. 现在我们得到了想要的结果：“This is really awesome!”。<br /><br />　　你应该已经知道如何读取和写入ASCII文件了。但我们还不能罢休，因为二进制文件还在等着我们。<br /><br />　　三、二进制 输入输出<br /><br />　　二进制文件会复杂一点, 但还是很简单的。首先你要注意我们不再使用插入和提取操作符(译者注：&lt;&lt; 和 &gt;&gt; 操作符). 你可以这么做，但它不会用二进制方式读写。你必须使用read() 和write() 方法读取和写入二进制文件. 创建一个二进制文件, 看下一行。<br /><br />ofstream fout("file.dat", ios::binary);<br /><br />　　这会以二进制方式打开文件, 而不是默认的ASCII模式。首先从写入文件开始。函数write() 有两个参数。 第一个是指向对象的char类型的指针, 第二个是对象的大小（译者注：字节数）。 为了说明，看例子。<br /><br />int number = 30; fout.write((char *)(&amp;number), sizeof(number));<br /><br />　　第一个参数写做"(char *)(&amp;number)". 这是把一个整型变量转为char *指针。如果你不理解，可以立刻翻阅C++的书籍，如果有必要的话。第二个参数写作"sizeof(number)". sizeof() 返回对象大小的字节数. 就是这样!<br /><br />　　二进制文件最好的地方是可以在一行把一个结构写入文件。 如果说，你的结构有12个不同的成员。 用ASCII?文件，你不得不每次一条的写入所有成员。 但二进制文件替你做好了。 看这个。<br /><br />struct OBJECT { int number; char letter; } obj;<br />obj.number = 15;<br />obj.letter = ‘M’;<br />fout.write((char *)(&amp;obj), sizeof(obj));<br /><br />　　这样就写入了整个结构! 接下来是输入. 输入也很简单，因为read()?函数的参数和 write()是完全一样的, 使用方法也相同。<br /><br />ifstream fin("file.dat", ios::binary); fin.read((char *)(&amp;obj), sizeof(obj));<br /><br />　　我不多解释用法, 因为它和write()是完全相同的。二进制文件比ASCII文件简单, 但有个缺点是无法用文本编辑器编辑。 接着, 我解释一下ifstream 和ofstream 对象的其他一些方法作为结束.<br /><br />　　四、更多方法<br /><br />　　我已经解释了ASCII文件和二进制文件, 这里是一些没有提及的底层方法。<br /><br />　　检查文件<br /><br />　　你已经学会了open() 和close() 方法, 不过这里还有其它你可能用到的方法。<br /><br />　　方法good() 返回一个布尔值，表示文件打开是否正确。<br /><br />　　类似的，bad() 返回一个布尔值表示文件打开是否错误。 如果出错，就不要继续进一步的操作了。<br /><br />　　最后一个检查的方法是fail(), 和bad()有点相似, 但没那么严重。<br /><br />　　读文件<br /><br />　　方法get() 每次返回一个字符。<br /><br />　　方法ignore(int,char) 跳过一定数量的某个字符, 但你必须传给它两个参数。第一个是需要跳过的字符数。 第二个是一个字符, 当遇到的时候就会停止。 例子,<br /><br />fin.ignore(100, ‘\n’);<br /><br />　　会跳过100个字符，或者不足100的时候，跳过所有之前的字符，包括 ‘\n’。<br /><br />　　方法peek() 返回文件中的下一个字符, 但并不实际读取它。所以如果你用peek() 查看下一个字符, 用get() 在peek()之后读取，会得到同一个字符, 然后移动文件计数器。<br /><br />　　方法putback(char) 输入字符, 一次一个, 到流中。我没有见到过它的使用，但这个函数确实存在。<br /><br />　　写文件<br /><br />　　只有一个你可能会关注的方法.?那就是 put(char), 它每次向输出流中写入一个字符。<br /><br />　　打开文件<br /><br />　　当我们用这样的语法打开二进制文件:<br /><br />ofstream fout("file.dat", ios::binary);<br /><br />　　"ios::binary"是你提供的打开选项的额外标志. 默认的, 文件以ASCII方式打开, 不存在则创建, 存在就覆盖. 这里有些额外的标志用来改变选项。<br /><br />　　ios::app 添加到文件尾<br />　　ios::ate 把文件标志放在末尾而非起始。<br />　　ios::trunc 默认. 截断并覆写文件。<br />　　ios::nocreate 文件不存在也不创建。<br />　　ios::noreplace 文件存在则失败。<br /><br />　　文件状态<br /><br />　　我用过的唯一一个状态函数是eof(), 它返回是否标志已经到了文件末尾。 我主要用在循环中。 例如, 这个代码断统计小写‘e’ 在文件中出现的次数。<br /><br />ifstream fin("file.txt");<br />char ch; int counter;<br />while (!fin.eof()) {<br />ch = fin.get();<br />if (ch == ‘e’) counter++;<br />}<br />fin.close();<br /><br />　　我从未用过这里没有提到的其他方法。 还有很多方法，但是他们很少被使用。参考C++书籍或者文件流的帮助文档来了解其他的方法。<br /><br />　　结论<br /><br />　　你应该已经掌握了如何使用ASCII文件和二进制文件。有很多方法可以帮你实现输入输出，尽管很少有人使用他们。我知道很多人不熟悉文件I/O操作，我希望这篇文章对你有所帮助。 每个人都应该知道. 文件I/O还有很多显而易见的方法,?例如包含文件 &lt;stdio.h&gt;. 我更喜欢用流是因为他们更简单。 祝所有读了这篇文章的人好运, 也许以后我还会为你们写些东西。  <br /><br />from: <a href="http://www.cnsdn.com.cn/inc/show.asp?id=3462">http://www.cnsdn.com.cn/inc/show.asp?id=3462</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/74898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-13 08:45 <a href="http://www.blogjava.net/weidagang2046/articles/74898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的多态性实现机制剖析</title><link>http://www.blogjava.net/weidagang2046/articles/74179.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 09 Oct 2006 14:02:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/74179.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/74179.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/74179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/74179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/74179.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: C++																																的多态性实现机制剖析																																																																																																																										――即...&nbsp;&nbsp;<a href='http://www.blogjava.net/weidagang2046/articles/74179.html'>阅读全文</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/74179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-09 22:02 <a href="http://www.blogjava.net/weidagang2046/articles/74179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学STL Iterator，traits笔记</title><link>http://www.blogjava.net/weidagang2046/articles/71698.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 25 Sep 2006 03:48:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/71698.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/71698.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/71698.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/71698.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/71698.html</trackback:ping><description><![CDATA[
		<p>最近看侯杰老师的《STL源码剖析》有一点收获，特把我对STL iterator设计的认识草草记录下来，大部分内容来自那本书(看原书更好)。欢迎大家跟我讨论，里面也有问题希望您能提供宝贵看法！</p>
		<p>
				<strong>一. Iterator认识</strong>
				<br />如果需要构造一组通用容器，提供一套统一的算法，构造底层数据结构类库，iterator的设计无疑是非常重要的。iterator可以方便容器元素的遍历，提供算法的统一参数接口。怎么说？首先，让我们考虑一个算法。<br /><font color="#006400">Template &lt;class T&gt; ptrdiff_t<br />distance(T p1, T p2)<br />{<br />  //计算p1和p2之间的距离<br />}</font><br />显然这个函数是想计算两个“位置”之间距离。这里表示“位置”的类型T，可以是指向数组中某个元素的（原生）指针，可以是指向链表节点的指针，也可以是用来记录位置的任何对象（例如我们要谈的iterator）。不管这两个位置是对应在数组，vector，链表或是其他任何容器，我们当然希望设计好的类库中最好只要一个这样的distance函数，而不需要每种容器都有不同的“位置”记录方式，从而导致需要很多个这样的distance算法。对，我们完全可以抽象出一种表示“位置”的概念，它像游标，像智能的指针，可以方便的遍历容器中的元素，记录位置，而不用管你作用的是什么类型的容器，这就是现在被容器设计者普遍接受的iterator概念。<br /><br /><br /><br /><br /></p>
		<p>二. STL iterator的设计：<br /><strong>为什么不用继承构造iterator？</strong><br />容器抽象出5种iterator 类型，input&lt;--forward&lt;--bidrectional&lt;--random access iterator加上output iterator，我们能不能通过refinement关系设计出具有继承关系的几个iterator类？然后各个容器的iterator类去继承这些基类。那么上面的disatance函数可以设计两个版本<br /><font color="#006400">ptrdiff_t distance(InputIterator p1, InputIterator p2)<br />{<br /> //InputIterator只能一个一个前进,例如链表<br />  ptrdiff_t n=0;<br />  while(p1 != p2)<br />  {<br />   ++p1; ++n;   <br />  }<br />  return n;<br />} </font></p>
		<p>
				<font color="#006400">ptrdiff_t distance(RandomAccessIterator p1, RandomAccessIterator p2)<br />{<br /> //RandomAccessIterator可以直接计算差距，例如数组，vector等<br /> return p2-p1;<br />}</font>
		</p>
		<p>这样看来是可行的对吗？但为什么STL不采用这种方式呢？（各位帮我想想啊，我实在是菜，想不出很好的理由啊）我所能想到的有：iterator可以是原生指针的类型，而原生指针是不会继承InputIterator基类的。（是不是还有效率问题？)</p>
		<p>不讨论STL为什么不这么作，还是看看它漂亮的处理方法吧：先提醒你，它用的是函数模板（function template）的参数推导机制来确定函数返回值和参数类型的。</p>
		<p>
				<strong>(1)</strong>  通过不同的iterator概念，先作几个表明iterator类型的tag类。input_iterator_tag&lt;--forward_iterator_tag&lt;--bidrectonal_iterator_tag&lt;--random_access_iterator_tag。还有output_iterator_tag，这几个类都是空的，前面4个有继承关系。</p>
		<p>
				<strong>(2)</strong>  STL设计的iterator类都需要typedef一个重要的类型iterator_category用来表明这个iterator是什么类型的iterator，它必须是上面tag类中的一个。例如list&lt;T&gt;的iterator类有：<br />    typedef bidrectonal_iterator_tag iterator_category;<br />    <br />    另外需要有一个value_type类型表明iterator是指向什么类型的元素。例如list&lt;T&gt;的iterator类有：<br />    typedef T value_type;</p>
		<p>
				<strong>(3)</strong>  设计iterator_traits&lt;class Iterator&gt;类，这个类的作用是可以提取出模板参数Iterator类的类型。也是通过typedef实现的。如下：<br /><font color="#006400">template &lt;class Iterator&gt;<br />struct <strong>iterator_traits</strong> {<br />  typedef typename Iterator::iterator_category iterator_category;<br />  typedef typename Iterator::value_type        value_type;<br />  //.....<br />};</font></p>
		<p>本来第二步中，我们设计的iterator类已经可以通过typedef别名来标志类型了，为什么要这层中间转换？原因是通常我们可以写Iterator::iterator_category作为一个typename，<strong>但如果Iterator是一个原生指针T*，</strong>我们写T*::iterator_category就得不到啦。利用partial specialization（模板偏特化）技术，可以通过中间层iterator_traits自己指定并得到原生指针的iterator_category类型，代码如下。（这么复杂的编译技术，真不知他们咋整的...吾辈只能望洋兴叹55）</p>
		<p>
				<font color="#006400">template &lt;class T&gt;<br />struct iterator_traits&lt;T*&gt; {<br />  typedef random_access_iterator_tag iterator_category;<br />  typedef T                          value_type;<br />  //........<br />};</font>
		</p>
		<p>
				<strong>(4)</strong>  设想要设计一个算法template &lt;class Iterator&gt; RET_TYPE gen_fun(Iterator p1, Iterator p2, ...)。<br />一般这么处理：<br /><font color="#006400">template &lt;class Iterator&gt; RET_TYPE gen_fun(Iterator p1, Iterator p2, ...)<br />{<br />   <strong> typedef iterator_traits&lt;Iterator&gt;::iterator_category category;</strong>    <br />    typedef iterator_traits&lt;Iterator&gt;::value_type type;  //这个type用于实际运算中得知iterator指向的对象(*运算符返回类型)<br />   __gen_fun(Iterator p1, Iterator p2, ...,<strong> category()</strong>);<br />}</font></p>
		<p>category()构造一个iterator_tag类型的临时变量，该临时变量只用于区分调用函数，不参与实际算法实现。具体实现方法如下：(注意最后的参数不用变量名)</p>
		<p>
				<font color="#006400">template &lt;class Iterator&gt; RET_TYPE __gen_fun(Iterator p1, Iterator p2, ..., <strong>input_iterator_tag</strong>)<br />{<br />   //input_iterator类型的实际实现方法<br />   //并且由于tag类继承机制，forward_iterator类型也会调用本方法<br />}</font>
				<br /> <br /><font color="#006400">template &lt;class Iterator&gt; RET_TYPE __gen_fun(Iterator p1, Iterator p2, ..., <strong>bidrectonal_iterator_tag</strong>)<br />{<br />   //bidrectonal_iterator类型的实际实现方法<br />}</font></p>
		<p>
				<font color="#006400">template &lt;class Iterator&gt; RET_TYPE __gen_fun(Iterator p1, Iterator p2, ..., <strong>random_access_iterator_tag</strong>)<br />{<br />   //random_access_iterator类型的实际实现方法<br />}</font>
		</p>
		<p>这样通过定义iterator tag和函数模板的参数推导机制，就实现了参数类型识别，达到了构造继承关系的iterator类实现的功能。并且没有继承要求那么严格，而且typedef是在编译时候完成的工作，丝毫不影响程序运行速度。如果增加iterator中typedef的类型，如pointer等，可以增强参数类型识别的功能。</p>
		<p>另外需要提醒的是，在STL代码中，如果是random_access_iterator类型的方法，它通常写<br /><font color="#006400">template &lt;class <strong>RandomAccessIterator</strong>&gt; RET_TYPE __gen_fun(<strong>RandomAccessIterator</strong> p1, <strong>RandomAccessIterator</strong> p2, ..., random_access_iterator_tag)</font><br />是input_iterator类型的方法，它通常写<br />template &lt;class InputIterator&gt; RET_TYPE __gen_fun(InputIterator p1, InputIterator p2, ..., input_iterator_tag)<br />但是，<strong>别被这里的RandomAccessIterator和InputIterator迷惑了</strong>，它们只是模板参数而已，并没有继承关系，也不存在这样的类！（我就被这个迷惑了好久:( ）也不是我开头提的构造一组继承关系的Iterator类。模板参数写成RandomAccessIterator并不能表示该RandomAccessIterator类型就是random_access_iterator的，它写成T，Type，Iter都没有关系。只有通过iterator_traits得到iterator_tag才能表明iterator的真正类型。我想它那样写，只是为了提醒你调用函数的iterator类型吧。<br /></p>
		<p>
				<br />
				<br />
				<strong>三. 最后看看开头提的distance()算法实际实现</strong>：</p>
		<p>
				<font color="#006400">template &lt;class Iterator&gt; inline <br /> typename iterator_traits&lt;Iterator&gt;::iterator_category<br />  iterator_category(const Iterator&amp;)  <br />  //提取Iterator的iterator_category类型<br />{<br />  typedef typename iterator_traits&lt;Iterator&gt;::iterator_category category;<br />  return category();<br />}</font>
		</p>
		<p>
				<br />
				<font color="#006400">template &lt;class InputIterator, class Distance&gt;<br />inline void distance(InputIterator first, InputIterator last, Distance&amp; n)  <br />{<br />  __distance(first, last, n, <strong>iterator_category(first)</strong>);<br />  //根据提取的iterator_category类型选择实际执行函数<br />  //Distance是通过引用传递，相当于函数返回值<br />}</font>
		</p>
		<p>
				<br />
				<font color="#006400">template &lt;class InputIterator, class Distance&gt;<br />inline void __distance(InputIterator first, InputIterator last, Distance&amp; n, <br />                       <strong>input_iterator_tag</strong>) <br />{<br />  //input_iterator类型的实现，根据input_iterator_tag的继承关系，forward_iterator<br />  //和bidrectonal_iterator也会调用此实现函数。<br />  while (first != last) { ++first; ++n; }<br />}</font>
		</p>
		<p>
				<font color="#006400">template &lt;class RandomAccessIterator, class Distance&gt;<br />inline void __distance(RandomAccessIterator first, RandomAccessIterator last, <br />                       Distance&amp; n, <strong>random_access_iterator_tag</strong>) <br />{<br />  //random_access_iterator类型的实现<br />  n += last - first;<br />}</font>
		</p>
		<p>                                                                              2004.11.10<br />from: <a href="http://www.zahui.com/html/9/35875.htm">http://www.zahui.com/html/9/35875.htm</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/71698.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-25 11:48 <a href="http://www.blogjava.net/weidagang2046/articles/71698.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The Standard Librarian: What Are Allocators Good For?</title><link>http://www.blogjava.net/weidagang2046/articles/70038.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sat, 16 Sep 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/70038.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/70038.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/70038.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/70038.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/70038.html</trackback:ping><description><![CDATA[
		<div class="Bspace15">
				<em>Matthew H. Austern</em>
		</div>
		<!-- teaser -->
		<div class="Bspace15">
				<span class="greenBlurb">Most of us who use the C++ Standard library tend to forget about allocators, those mysterious things specified by default template parameters for STL containers. In most situations you will not need to call an allocator explicitly or write one of your own. But there are occasions when you might want to substitute your own custom allocator for the default version, for example, to allocate objects from a special memory pool. In this column Matt Austern discusses what you can use allocators for and how you can define your own.</span>
		</div>
		<!-- end teaser -->
		<p>
		</p>
		<p>
		</p>
		<hr />
		<p>
		</p>
		<p>Allocators are one of the most mysterious parts of the C++ Standard library. Allocators are rarely used explicitly; the Standard doesn't make it clear when they should ever be used. Today's allocators are substantially different from those in the original STL proposal, and there were two other designs in between — all of which relied on language features that, until recently, were available on few compilers. The Standard appears to make promises about allocator functionality with one hand and then take those promises away with the other.</p>
		<p>This column will discuss what you can use allocators for and how you can define your own. I'm only going to discuss allocators as defined by the C++ Standard: bringing in pre-Standard designs, or workarounds for deficient compilers, would just add to the confusion.</p>
		<p>
		</p>
		<h3>
				<font size="3">When Not to Use Allocators</font>
		</h3>
		<p>
		</p>
		<p>Allocators in the C++ Standard come in two pieces: a set of generic requirements, specified in 20.1.5 (Table 32), and the class <b>std::allocator</b>, specified in 20.4.1. We call a class an allocator if it conforms to the requirements of Table 32. The <b>std::allocator</b> class conforms to those requirements, so it is an allocator. It is the only predefined allocator class in the standard library. 
</p>
		<p>
		</p>
		<p>Every C++ programmer already knows about dynamic memory allocation: you write <b>new X</b> to allocate memory and create a new object of type <b>X</b>, and you write <b>delete p</b> to destroy the object that <b>p</b> points to and return its memory. You might reasonably think that allocators have something to do with <b>new</b> and <b>delete </b>— but they don't. (The Standard describes <b>::operator new</b> as an "allocation function," but, confusingly, that's not the same as an allocator.) 
</p>
		<p>
		</p>
		<p>The most important fact about allocators is that they were intended for one purpose only: encapsulating the low-level details of STL containers' memory management. You shouldn't invoke allocator member functions in your own code, unless you're writing an STL container yourself. You shouldn't try to use allocators to implement <b>operator new[]</b>; that's not what they're for. If you aren't sure whether you need to use allocators, then you don't. 
</p>
		<p>
		</p>
		<p>An allocator is a class with member functions <b>allocate</b> and <b>deallocate</b>, the rough equivalents of <b>malloc</b> and <b>free</b>. It also has helper functions for manipulating the memory that it allocated and typedefs that describe how to refer to the memory — names for pointer and reference types. If an STL container allocates all of its memory through a user-provided allocator (which the predefined STL containers all do; each of them has a template parameter that defaults to <b>std::allocator</b>), you can control its memory management by providing your own allocator. 
</p>
		<p>
		</p>
		<p>This flexibility is limited: a container still decides for itself how much memory it's going to ask for and how the memory will be used. You get to control which low-level functions a container calls when it asks for more memory, but you can't use allocators to make a <b>vector</b> act like a <b>deque</b>. Sometimes, though, even this limited flexibility is useful. If you have a special <b>fast_allocator</b> that allocates and deallocates memory quickly, for example (perhaps by giving up thread safety, or by using a small local heap), you can make the standard list class use it by writing <b>std::list&lt;T, fast_allocator&lt;T&gt; &gt;</b> instead of plain <b>std::list&lt;T&gt;</b>. 
</p>
		<p>
		</p>
		<p>If this seems esoteric to you, you're right. There is no reason to use allocators in normal code. 
</p>
		<p>
		</p>
		<p>
		</p>
		<h3>
				<font size="3">Defining an Allocator</font>
		</h3>
		<p>
		</p>
		<p>This already shows you something about allocators: they're templates. Allocators, like containers, have value types, and an allocator's value type must match the value type of the container it's used with. This can sometimes get ugly: <b>map</b>'s value type is fairly complicated, so a <b>map</b> with an explicit allocator involves expressions like <b>std::map&lt;K, V, fast_allocator&lt;std::pair&lt;const K, V&gt; &gt; &gt;</b>. (As usual, typedefs help.) 
</p>
		<p>
		</p>
		<p>Let's start with a simple example. According to the C++ Standard, <b>std::allocator</b> is built on top of <b>::operator new</b>. If you're using an automatic tool to trace memory usage, it's often more convenient to have something a bit simpler than <b>std::allocator</b>. We can use <b>malloc</b> instead of <b>::operator new</b>, and we can leave out the complicated performance optimizations that you'll find in a good implementation of <b>std::allocator</b>. We'll call this simple allocator <b>malloc_allocator</b>. 
</p>
		<p>
		</p>
		<p>Since the memory management in <b>malloc_allocator</b> is simple, we can focus on the boilerplate that's common to all STL allocators. First, some types: an allocator is a class template, and an instance of that template allocates memory specifically for objects of some type <b>T</b>. We provide a series of typedefs that describe how to refer to objects of that type: <b>value_type</b> for <b>T</b> itself, and others for the various flavors of pointers and references. 
</p>
		<p>
		</p>
		<p>
		</p>
		<pre>template &lt;class T&gt; class malloc_allocator
{
public:
  typedef T                 value_type;
  typedef value_type*       pointer;
  typedef const value_type* const_pointer;
  typedef value_type&amp;       reference;
  typedef const value_type&amp; const_reference;
  typedef std::size_t       size_type;
  typedef std::ptrdiff_t    difference_type;
  ...
};
</pre>It's no accident that these types are so similar to those in an STL container: a container class usually gets those types directly from its allocator. Why so many typedefs? You might think that <b>pointer</b> is superfluous: it's just <b>value_type*</b>. Most of the time that's true, but you might occasionally want to define an unconventional allocator where <b>pointer</b> is some pointer-like class, or where it's some nonstandard vendor-specific type like <b>value_type __far*</b>; allocators are a standard hook for nonstandard extensions. Unusual pointer types are also the reason for the <b>address</b> member function, which in <b>malloc_allocator</b> is just an alternate spelling for <b>operator&amp;</b>: <pre>template &lt;class T&gt; class malloc_allocator
{
public:
  pointer address(reference x) const { return &amp;x; }
  const_pointer address(const_reference x) const { 
    return &amp;x; 
  }
  ...
};
</pre>Now we can get to the real work: <b>allocate</b> and <b>deallocate</b>. They're straightforward, but they don't look quite like <b>malloc</b> and <b>free</b>. We pass two arguments to <b>allocate</b>: the number of objects that we're allocating space for (<b>max_size</b> returns the largest request that might succeed), and, optionally, an address that can be used as a locality hint. A simple allocator like <b>malloc_allocator</b> makes no use of that hint, but an allocator designed for high performance might. The return value is a pointer to a block of memory that's large enough for <b>n</b> objects of type <b>value_type</b> and that has the correct alignment for that type. We also pass two arguments to <b>deallocate</b>: a pointer, of course, but also an element count. A container has to keep track of sizes on its own; the size arguments to <b>allocate</b> and <b>deallocate</b> must match. Again, this extra argument exists for reasons of performance, and again, <b>malloc_allocator</b> doesn't use it. <pre>template &lt;class T&gt; class malloc_allocator 
{
public:
  pointer allocate(size_type n, const_pointer = 0) {
    void* p = std::malloc(n * sizeof(T));
    if (!p)
      throw std::bad_alloc();
    return static_cast&lt;pointer&gt;(p);
  }

  void deallocate(pointer p, size_type) {
    std::free(p);
  }

  size_type max_size() const { 
    return static_cast&lt;size_type&gt;(-1) / sizeof(value_type);
  }
  ...
};
</pre>The <b>allocate</b> and <b>deallocate</b> member functions deal with uninitialized memory; they don't construct or destroy objects. An expression like <b>a.allocate(1)</b> is more like <b>malloc(sizeof(int))</b> than like <b>new int</b>. Before using the memory you get from <b>allocate</b>, you have to create some objects in that memory; before returning that memory with <b>deallocate</b>, you have to destroy those objects. C++ provides a mechanism for creating an object at a specific memory location: placement new. If you write <b>new(p) T(a, b)</b> then you are invoking <b>T</b>'s constructor to create a new object, just as if you had written <b>new T(a, b)</b> or <b>T t(a, b)</b>. The difference is that when you write <b>new(p) T(a, b)</b> you're specifying the location where that object is constructed: the address where <b>p</b> points. (Naturally, <b>p</b> has to point to a large enough region of memory, and it has to point to raw memory; you can't construct two different objects at the same address.) You can also call an object's destructor, without releasing any memory, by writing <b>p-&gt;~T()</b>. These features are rarely used, because usually memory allocation and initialization go together: it's inconvenient and dangerous to work with pointers to uninitialized memory. One of the few places where you need such low-level techniques is if you're writing a container class, so allocators decouple allocation from initialization. The member function <b>construct</b> performs placement new, and the member function <b>destroy</b> invokes the destructor. <pre>template &lt;class T&gt; class malloc_allocator
{
public:
  void construct(pointer p, const value_type&amp; x) { 
    new(p) value_type(x); 
  }
  void destroy(pointer p) { p-&gt;~value_type(); }
  ...
};
</pre>(Why do allocators have those member functions, when containers could use placement new directly? One reason is to hide the somewhat awkward syntax, and another is that if you're writing a more complicated allocator you might want <b>construct</b> and <b>destroy</b> to have some side effects beside object construction and destruction. An allocator might, for example, maintain a log of all currently active objects.) None of these member functions is static, so the first thing a container has to do before using an allocator is create allocator objects — and that means we should define some constructors. We don't need an assignment operator, though: once a container creates its allocator, the allocator isn't ever supposed to be changed. The allocator requirements in Table 32 don't include assignment. Just to be on the safe side, to make sure nobody uses an assignment operator accidentally, we'll disable the one that would otherwise be generated automatically. <pre>template &lt;class T&gt; class malloc_allocator
{
public:
  malloc_allocator() {}
  malloc_allocator(const malloc_allocator&amp;) {}
  ~malloc_allocator() {}
private:
  void operator=(const malloc_allocator&amp;);
  ...
};
</pre>None of these constructors actually does anything, because this allocator doesn't have any member variables to initialize. For the same reason, any two <b>malloc_allocator</b> objects are interchangeable; if <b>a1</b> and <b>a2</b> are both of type <b>malloc_allocator&lt;int&gt;</b>, we can freely allocate memory through <b>a1</b> and deallocate it through <b>a2</b>. We therefore define a comparison operator that says all <b>malloc_allocator</b> objects are equal: <pre>template &lt;class T&gt;
inline bool operator==(const malloc_allocator&lt;T&gt;&amp;, 
                       const malloc_allocator&lt;T&gt;&amp;) {
  return true;
}

template &lt;class T&gt;
inline bool operator!=(const malloc_allocator&lt;T&gt;&amp;, 
                       const malloc_allocator&lt;T&gt;&amp;) {
  return false;
}
</pre>Would you ever want to have an allocator where different objects <i>weren't</i> interchangeable? Certainly — but simple and useful examples are hard to come by. One obvious possibility is memory pools. It's common for large C programs to allocate memory from several different places ("pools"), instead of directly doing everything through <b>malloc</b>. This has several benefits, one of which is that it only takes a single function call to reclaim all of the memory associated with a particular phase of the program. A program that uses memory pools might define utility functions like <b>mempool_Alloc</b> and <b>mempool_Free</b>, where <b>mempool_Alloc(n, p)</b> allocates <b>n</b> bytes from pool <b>p</b>. It's easy to write a <b>mempool_allocator</b> that fits into such a framework: each <b>mempool_allocator</b> object would have a member variable to specify which pool it's associated with, and <b>mempool_allocator::allocate</b> would invoke <b>mempool_Alloc</b> to get memory from the appropriate pool <a href="http://www.ddj.com/dept/cpp/184403759#1">[1]</a>. Finally, we get to the one tricky part of defining an allocator: mapping between different types. The problem is that an allocator class, like <b>malloc_allocator&lt;int&gt;</b>, is all built around a single value type: <b>malloc_allocator&lt;int&gt;::pointer</b> is <b>int*</b>, <b>malloc_allocator&lt;int&gt;().allocate(1)</b> returns enough memory for one <b>int</b> object, and so on. In general, however, a container class that uses <b>malloc_allocator</b> may have to deal with more than one type. A list class, for example, doesn't allocate <b>int</b>s; internally, it allocates list nodes. (We'll see more about that in the next section.) Somehow, when you create an <b>std::list&lt;int, malloc_allocator&lt;int&gt; &gt;</b>, the list has to turn the <b>malloc_allocator&lt;int&gt;</b> into a <b>malloc_allocator</b> for list nodes. The mechanism for this is called <i>rebinding</i>, and it has two parts. First, given an allocator type <b>A1</b> whose value type is <b>X1</b>, you must be able to write down an allocator type <b>A2</b> that's just the same as <b>A1</b> except that its value type is <b>X2</b>. Second, given an object <b>a1</b> of type <b>A1</b>, you must be able to create an equivalent object <b>a2</b> of type <b>A2</b>. Both of these parts use member templates, which is why allocators were unsupported or poorly supported on older compilers. <pre>template &lt;class T&gt; class malloc_allocator
{
public:
  template &lt;class U&gt; 
  malloc_allocator(const malloc_allocator&lt;U&gt;&amp;) {}

  template &lt;class U&gt; 
  struct rebind { typedef malloc_allocator&lt;U&gt; other; };
  ...
};
</pre>What this really means is that an allocator class can't ever just be a single class; it has to be a family of related classes, each with its own value type. An allocator class must always have a <b>rebind</b> member, because that's what makes it possible to go from one class in that family to another. If you have an allocator class <b>A1</b>, the corresponding allocator class for a different value type is <b>typename A1::template rebind&lt;X2&gt;::other</b><a href="http://www.ddj.com/dept/cpp/184403759#2">[2]</a>. And just as you can convert from one type to another, the templatized converting constructor lets you convert values: you can write <b>malloc_allocator&lt;int&gt;(a)</b> whether <b>a</b> is an object of type <b>malloc_allocator&lt;int&gt;</b>, or <b>malloc_allocator&lt;double&gt;</b>, or <b>malloc_allocator&lt;string&gt;</b>. As usual, <b>malloc_allocator</b>'s converting constructor doesn't do anything because <b>malloc_allocator</b> has no member variables. Incidentally, while most allocators have a single template parameter (the allocator's value type) that's not a requirement — it just often happens to be convenient. The rebind mechanism would work just as well for allocators with multiple template parameters: <pre>template &lt;class T, int flags&gt; class my_allocator
{
public:
  template &lt;class U&gt;
  struct rebind { typedef my_allocator&lt;U, flags&gt; other; };
  ...
};
</pre>Finally, one last detail: what do we do about <b>void</b>? Sometimes a container has to refer to void pointers (again, we'll see more about that in the next section), and the rebind mechanism almost gives us what we need, but not quite. It doesn't work, because we would need to write something like <b>malloc_allocator&lt;void&gt;::pointer</b>, and we've defined <b>malloc_allocator</b> in such a way that instantiating it for <b>void</b> would be illegal. It uses <b>sizeof(T)</b>, and it refers to <b>T&amp;</b>; neither is legal when <b>T</b> is <b>void</b>. The solution is as simple as the problem: specialize <b>malloc_allocator</b> for <b>void</b>, leaving out everything except the bare minimum that we need for referring to void pointers. <pre>template&lt;&gt; class malloc_allocator&lt;void&gt;
{
  typedef void        value_type;
  typedef void*       pointer;
  typedef const void* const_pointer;

  template &lt;class U&gt; 
  struct rebind { typedef malloc_allocator&lt;U&gt; other; };
</pre>That's it! The complete source code for <b>malloc_allocator</b> is shown in <a href="http://www.ddj.com/showArticle.jhtml?documentID=cujcexp1812austern&amp;pgno=2">Listing 1</a>. 
<h3><font size="3">Using Allocators</font></h3>The easiest way to use allocators, of course, is to pass them as arguments to container classes; write <pre>std::vector&lt;char, malloc_allocator&lt;char&gt; &gt; V;
</pre>instead of plain <b>std::vector&lt;char&gt;</b>, or <pre>  typedef std::list&lt;int, mempool_allocator&lt;int&gt; &gt; List;
  List L(mempool_allocator&lt;int&gt;(p));
</pre>instead of plain <b>std::list&lt;int&gt;</b>. But you can do more than that. The whole point of the STL is that it's extensible: just as you can write your own allocators, you can also write your own container classes. If you are careful, and if you write a container class that uses its allocator for all memory-related functionality, then somebody else will be able to plug in their own custom-written allocators. Container classes like <b>std::vector</b> and <b>std::list</b> are complicated, and a lot of the complexity has nothing to do with memory management. Let's start with a simple example, so that we can focus just on the allocators. Consider a fixed-size array class, <b>Array</b>, where the number of elements is set in the constructor and can never change thereafter. (This is something like a simplified version of <b>std::valarray</b>.) We'll have two template parameters, the element type and an allocator type. Containers, like allocators, start with nested types: <b>value_type</b>, <b>reference</b>, <b>const_reference</b>, <b>size_type</b>, <b>difference_type</b>, <b>iterator</b>, and <b>const_iterator</b>. In general, most of those types can be taken directly from the container's allocator — thus illustrating why the container's value type and the allocator's must match. The iterator types, of course, don't usually come from the allocator; usually an iterator is some kind of class, closely tied to the container's internal representation. The <b>Array</b> class is simpler than usual because it's natural to store all of our elements in a single contiguous memory block; we'll maintain a pointer to the beginning of the block and a pointer to the end. The iterators will just be pointers. Before we go any further, we have to make a decision: how are we going to store the allocator? The constructor will take an allocator object as one of its arguments. We need to hold on to a copy of the allocator throughout the lifetime of the container, since we'll still need it in the destructor. In some sense, there's no problem here: we could just declare a member variable of type <b>Allocator</b> and be done with it. That solution would be correct, but annoying. Ninety-nine percent of the time, after all, users don't want to bother thinking about allocators; they'll just write <b>Array&lt;int&gt;</b> and use the default — and the default allocator is probably an empty class with no non-static member variables. The trouble is that a member variable of type <b>Allocator</b> will take up even when <b>Allocator</b> is an empty class. (This is required by the C++ Standard.) Our <b>Array</b> class will have three words of overhead, instead of two. Maybe an extra word of overhead isn't a big deal, but it's annoying to burden all users with that overhead for a feature that most of them will never use. There are a number of solutions to this problem, some of which rely on traits classes and partial specialization. Probably the simplest solution is to use a (private) base class of type <b>Allocator</b> instead of a member variable. Compilers are allowed to optimize away empty base classes, and nowadays most compilers do. We can finally write down a skeleton class definition: <pre>template &lt;class T, class Allocator = std::allocator&lt;T&gt; &gt;
class Array : private Allocator
{
public:
  typedef T value_type;
 
  typedef typename Allocator::reference reference;
  typedef typename Allocator::const_reference 
          const_reference;

  typedef typename Allocator::size_type size_type;
  typedef typename Allocator::difference_type 
          difference_type;

  typedef typename Allocator::pointer       iterator;
  typedef typename Allocator::const_pointer const_iterator;

  typedef Allocator allocator_type;
  allocator_type get_allocator() const {
    return static_cast&lt;const Allocator&amp;&gt;(*this);
  }

  iterator begin()             { return first; }
  iterator end()               { return last; }
  const_iterator begin() const { return first; }
  const_iterator end() const   { return last; }

  Array(size_type n = 0, 
        const T&amp; x = T(), 
        const Allocator&amp; a = Allocator());
  Array(const Array&amp;);
  ~Array();

  Array&amp; operator=(const Array&amp;);

private:
  typename Allocator::pointer first;
  typename Allocator::pointer last;
};
</pre>This doesn't yet have all of the boilerplate that we need if we are to satisfy the container requirements (see Table 65, in 23.1 of the C++ Standard, for the complete set of requirements), but most of that boilerplate has little to do with allocators. For our purposes, the most interesting member functions are the constructor, which allocates memory and creates objects, and the destructor, which destroys memory and frees memory. The constructor initializes the allocator base class, obtains a block of memory that's large enough for <b>n</b> elements (if we were writing something like <b>vector</b>, we might obtain a larger block to allow room for growth), and then loops through the block creating copies of the initial value. The only tricky part is exception safety: if one of the elements' constructors throws an exception, we have to undo everything we've done. <pre>template &lt;class T, class Allocator&gt;
Array&lt;T, Allocator&gt;::Array(size_type n, 
                           const T&amp; x, 
                           const Allocator&amp; a)
  : Allocator(a), first(0), last(0)
{
  if (n != 0) {
    first = Allocator::allocate(n);
    size_type i;
    try {
      for (i = 0; i &lt; n; ++i) {
        Allocator::construct(first + i, x);
      }
    }
    catch(...) {
      for(size_type j = 0; j &lt; i; ++j) {
        Allocator::destroy(first + j);
      }
      Allocator::deallocate(first, n);
      throw;
    }
  }
}
</pre>(You might wonder why we're writing out these loops by hand; doesn't <b>std::uninitialized_fill</b> already do what we need? Almost, but not quite. We have to use the allocator's <b>construct</b> member function instead of plain placement new. Perhaps a future version of the C++ Standard will include a version of <b>uninitialized_fill</b> that takes an allocator argument and make such explicit loops unnecessary.) The destructor is simpler, since we don't have to worry about exception safety: <b>T</b>'s destructor is never supposed to throw. <pre>template &lt;class T, class Allocator&gt;
Array&lt;T, Allocator&gt;::~Array() 
{
  if (first != last) {
    for (iterator i = first; i &lt; last; ++i)
      Allocator::destroy(i);
    Allocator::deallocate(first, last - first);
  }
}
</pre>Our simple array class doesn't have to use rebinding or conversion, but that's only because <b>Array&lt;T, Allocator&gt;</b> never creates objects of any type other than <b>T</b>. Other types come in when you define more complicated data structures. Consider, for example, a linked list class whose value type is <b>T</b>. A linked list typically consists of nodes, where each node holds an object of type <b>T</b> and a pointer to the next node. So, as a first attempt, we might define a list node as follows: <pre>template &lt;class T&gt; 
struct List_node
{
  T val;
  List_node* next;
};
</pre>The procedure for adding a new value to the list might look something like this: 
<ul><li>Using an allocator whose value type is <b>List_node&lt;T&gt;</b>, allocate memory for a new list node. 
</li><li>Using an allocator whose value type is <b>T</b>, construct the new list element in the node's <b>val</b> slot. 
</li><li>Link the node into place appropriately.</li></ul>This procedure requires dealing with two different allocators, one of which is obtained from the other via rebinding. It's good enough for almost all applications, even ones that use allocators for quite sophisticated purposes. What it doesn't do is support allocators with unusual pointer types. It explicitly relies on being able to use an ordinary pointer of type <b>List_node&lt;T&gt;*</b>. If you're extremely ambitious, and you want to support allocators with alternative pointer types, everything suddenly becomes much more complicated — the pointer from one list node to another can no longer be of type <b>List_node&lt;T&gt;*</b>, or even of type <b>void*</b>, but must be of some type taken from the allocator. Writing this without circularity is nontrivial: it's illegal to instantiate an allocator with an incomplete type, so there's no way to talk about pointers to <b>List_node</b> until after <b>List_node</b> has been fully defined. We need a delicate chain of declarations. <pre>template &lt;class T, class Pointer&gt;
struct List_node
{
  T val;
  Pointer next;
};

template &lt;class T, class Alloc&gt;
class List : private Alloc
{
private:
  typedef typename Alloc::template rebind&lt;void&gt;::other  
          Void_alloc;
  typedef typename Void_alloc::pointer Voidptr;
  typedef typename List_node&lt;T, Voidptr&gt; Node;
  typedef typename Alloc::template rebind&lt;Node&gt;::other 
          Node_alloc;
  typedef typename Node_alloc::pointer Nodeptr;
  typedef typename Alloc::template rebind&lt;Voidptr&gt;::other
          Voidptr_alloc;

  Nodeptr new_node(const T&amp; x, Nodeptr next) {
    Alloc a = get_allocator();
    Nodeptr p = Node_alloc(a).allocate(1);
    try {
      a.construct(p-&gt;val, x);
    }
    catch(...) {
      Node_alloc(a).deallocate(p, 1);
      throw;
    }
    Voidptr_alloc(a).construct(p-&gt;next, Voidptr(next));
    return p;
  }

  ...
};
</pre><p></p><p>And finally, in case you think that this is far too much effort for far too small a benefit, a reminder: just because you can write a container class that uses allocators doesn't mean that you have to, or that you should. Sometimes you might want to write a container class that relies on a specific allocation strategy, whether it's something as ambitious as a disk-based B-tree container or as simple as the <b>block</b> class that I describe in my book. Even if you do want to write a container class that uses allocators, you don't have to support alternate pointer types. You can write a container where you require that any user-supplied allocator uses ordinary pointers, and document that restriction. Not everything has to be fully general. 
</p><p></p><p></p><h3><font size="3">Future Directions</font></h3><p></p><p>If you want to write a simple allocator like <b>malloc_allocator</b>, you should have no difficulty. (Provided that you're using a reasonably modern compiler, that is.) If you have more ambitious plans, however — a memory pool allocator or an allocator with nonstandard pointer types for distributed computing — the situation is less satisfactory. 
</p><p></p><p>If you want to use some alternative pointer-like type, what operations does it have to support? Must it have a special null value, and, if so, how is that value written? Can you use casts? How can you convert between pointer-like objects and ordinary pointers? Do you have to worry about pointer operations throwing exceptions? I made some assumptions in the last section; the C++ Standard doesn't say whether those assumptions are right or wrong. These details are left to individual standard library implementations, and it's even legal for an implementation to ignore alternative pointer types altogether. The C++ Standard also leaves a few unanswered questions about what happens when different instances of an allocator aren't interchangeable. 
</p><p></p><p>Fortunately, the situation isn't quite as dire as the words in the Standard (20.1.5, paragraphs 4-5) might make it seem. The Standard left some questions unanswered because, at the time it was written, the C++ standardization committee wasn't able to agree on the answers; the necessary experience with allocators did not exist. Everyone involved in writing this section of the Standard considered it to be a temporary patch, and the vagueness will definitely be removed in a future revision. 
</p><p></p><p>For the moment, it's best to stay away from alternative pointer types if you're concerned about portability, but, if you're willing to accept a few limitations, you can safely use allocators like <b>mempool_allocator</b> where the differences between individual objects is important. All major standard library implementations now support such allocators in some way, and the differences between implementations are minor. 
</p><p></p><p>Just as the containers take allocator types as template parameters, so the containers' constructors take allocator objects as arguments. A container makes a copy of that argument and uses the copy for all of its memory management; once it is initialized in the constructor, the container's allocator is never changed. 
</p><p></p><p>The only question is what happens when you perform an operation that requires two containers to cooperate on memory management. There are exactly two such operations in the standard library: <b>swap</b> (all containers) and <b>std::list::splice</b>. In principle, an implementation could handle them in several different ways: 
</p><p></p><p></p><ul><li>Forbid <b>swap</b> or <b>splice</b> between two containers whose allocators aren't equal. 
</li><li>Put a test for allocator equality in <b>swap</b> and <b>splice</b>. If the allocators aren't equal, then fall back to some other operation like copying and assignment. 
</li><li>For <b>swap</b> only: swap the containers' allocators as well as their data. (It's hard to see how we could generalize this to <b>splice</b>. It also presents a problem: how do you swap things that don't have assignment operators?)</li></ul><p></p><p>If you just stay away from <b>swap</b> and <b>splice</b> whenever two containers might be using different allocators, you'll be safe. In practice, I haven't found this to be a serious restriction: you need tight discipline to use a feature like memory pools safely, and you probably won't want indiscriminate mixing between containers with different allocators. 
</p><p></p><p>Partly because of unfamiliarity and partly because of the unsatisfactory state of the C++ Standard's requirements, most uses of allocators today are simple. As the C++ community becomes more familiar with allocators, and as the Standard is clarified, we can expect more sophisticated uses to emerge. 
</p><p></p><p></p><h3><h4><a name="heading1">Listing 1: A sample allocator based on malloc</a></h4><p><font size="3"></font></p><pre><font size="3">template &lt;class T&gt; class malloc_allocator
{
public:
  typedef T                 value_type;
  typedef value_type*       pointer;
  typedef const value_type* const_pointer;
  typedef value_type&amp;       reference;
  typedef const value_type&amp; const_reference;
  typedef std::size_t       size_type;
  typedef std::ptrdiff_t    difference_type;
  
  template &lt;class U&gt; 
  struct rebind { typedef malloc_allocator&lt;U&gt; other; };

  malloc_allocator() {}
  malloc_allocator(const malloc_allocator&amp;) {}
  template &lt;class U&gt; 
  malloc_allocator(const malloc_allocator&lt;U&gt;&amp;) {}
  ~malloc_allocator() {}

  pointer address(reference x) const { return &amp;x; }
  const_pointer address(const_reference x) const { 
    return x;
  }

  pointer allocate(size_type n, const_pointer = 0) {
    void* p = std::malloc(n * sizeof(T));
    if (!p)
      throw std::bad_alloc();
    return static_cast&lt;pointer&gt;(p);
  }

  void deallocate(pointer p, size_type) { std::free(p); }

  size_type max_size() const { 
    return static_cast&lt;size_type&gt;(-1) / sizeof(T);
  }

  void construct(pointer p, const value_type&amp; x) { 
    new(p) value_type(x); 
  }
  void destroy(pointer p) { p-&gt;~value_type(); }

private:
  void operator=(const malloc_allocator&amp;);
};

template&lt;&gt; class malloc_allocator&lt;void&gt;
{
  typedef void        value_type;
  typedef void*       pointer;
  typedef const void* const_pointer;

  template &lt;class U&gt; 
  struct rebind { typedef malloc_allocator&lt;U&gt; other; };
};


template &lt;class T&gt;
inline bool operator==(const malloc_allocator&lt;T&gt;&amp;, 
                       const malloc_allocator&lt;T&gt;&amp;) {
  return true;
}

template &lt;class T&gt;
inline bool operator!=(const malloc_allocator&lt;T&gt;&amp;, 
                       const malloc_allocator&lt;T&gt;&amp;) {
  return false;
}</font></pre></h3><h3><font size="3">Notes</font></h3><p><a name="1"></a>[1] You can see an example of a pool allocator in the open source SGI Pro64<sup>TM</sup> compiler, http://oss.sgi.com/projects/Pro64/. 
</p><p></p><p><a name="2"></a>[2] Why the funny <b>template</b> keyword in that expression? It's an annoying little technicality; like <b>typename</b>, it helps the compiler resolve a parsing ambiguity. The problem is that when <b>A</b> is a template parameter, and the compiler sees an expression like <b>A::B&lt;T&gt;</b>, the compiler doesn't know anything about <b>A</b>'s members. Should it assume that <b>B&lt;T&gt;</b> is a member template, or should it assume that <b>B</b> is an ordinary member variable and that <b>&lt;</b> is just a less than sign? A human reader knows which way to interpret it, but the compiler doesn't. You need to put in <b>template</b> to force the first interpretation. Formally, the rule (in 14.2 of the C++ Standard) is: "When the name of a member template specialization appears after <b>.</b> or <b>-&gt;</b> in a <i>postfix-expression</i>, or after <i>nested-name-specifier</i> in a <i>qualified-id</i>, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2), the member template name must be prefixed by the keyword <b>template</b>. Otherwise the name is assumed to name a non-template." 
</p><p></p><p></p><p><i><b>Matt Austern</b> is the author of </i>Generic Programming and the STL<i> and the chair of the C++ standardization committee's library working group. He works at AT&amp;T Labs — Research and can be contacted at <a href="mailto:austern@research.att.com">austern@research.att.com</a>.</i></p><p><em></em> </p><img src ="http://www.blogjava.net/weidagang2046/aggbug/70038.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-16 15:08 <a href="http://www.blogjava.net/weidagang2046/articles/70038.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Using auto_ptr to Handle Memory</title><link>http://www.blogjava.net/weidagang2046/articles/69527.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 14 Sep 2006 00:46:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/69527.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/69527.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/69527.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/69527.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/69527.html</trackback:ping><description><![CDATA[For instance, it is common to write code such as <pre class="example">void myFunction()
{
    myClass = new myClass();
    // body of function
    delete myClass;
}
</pre>and this may work. But what if somewhere in the function body an <a href="http://cprogramming.com/tutorial/exceptions.html">exception</a> gets thrown? Suddenly, the delete code never gets called! What's more, you may never have intended to throw an exception into the function but one of the functions you call may do so, or you may later need to modify your code to do so. In both cases, this is a memory leak waiting to happen. <br /><br />On the other hand, by letting the auto_ptr class manage your memory for you, as soon as an exception gets thrown and the auto_ptr you declared has gone out of scope, the memory allocated will automatically be freed. First, let's look at how to use auto_ptr, and then I'll explain the exact mechanics of how this works. <br /><br />To take advantage of auto_ptr, you will need to include &lt;memory&gt;. This will give you access to the std <a href="http://www.cprogramming.com/tutorial/namespaces.html">namespace</a>, in which resides the templated class auto_ptr&lt;<i>type</i>&gt;. For <i>type</i>, you should put the type you want your <a href="http://www.cprogramming.com/tutorial/lesson6.html">pointer</a> to point to. For instance, if you would ordinarily have declared an int*, you should use int for the type. When you actually declare an instance of the templated class, the constructor should take a pointer to the specified type. The object will then take care of managing the memory associated with that pointer. For instance, to create an auto_ptr object that manages the memory of a pointer to an integer, you could use this declaration: <pre class="example">std::auto_ptr&lt;int&gt; int_memory_manager(new int);
// Alternately, you could include "using namespace std;" or 
// "using namespace std::auto_ptr" at the top of your code to avoid having to
// prefix all declarations with std::auto_ptr
</pre>To actually use the pointer stored in the auto_ptr container, you can just treat the auto_ptr object you create as though it were the pointer. For instance, you can dereference it: <pre class="example">*int_memory_manager = 5;
</pre>and if you wanted to access a member function (or variable) of a struct or class, you can simply use the same arrow operator as normal. I'll use a fictitious class calle "myClass" to illustrate this. <pre class="example">std::auto_ptr&lt;myClass&gt; myClassManager(new myClass);
myClass-&gt;variable = 50;
</pre>which sets a field called variable to 50 in the instance of myClass whose pointer is stored in myClassManager. <br /><br />The great benefit of all of this is that you simply don't need to worry about calling delete at all! As soon as int_memory_manager goes out of scope, its destructor will be invoked. Its destructor, in turn, will call delete on the pointer. This brings up a subtle point: you must use ato_ptr with memory allocated with new, not malloc, and, moreover, not new[]. Each of these memory allocators must be properly paired: malloc with free, new with delete, and new[] with delete[] (new[] is used to allocate arrays). If they aren't matched correctly, your program may crash. <br /><br />The consequence of auto_ptr's always calling delete on the pointer it stores means that you can't store arrays in an auto_ptr. For instance, the following is a mistake: <pre class="example">std::auto_ptr&lt;int&gt; int_memory_manager(new int[10]);
</pre>What happens when int_memory_manager goes out of scope? The program invokes delete on an array, and this is illegal. Some compilers may let you get away with it, but it's not portable. Just don't do it! If you really need to store a collection of items with constant random access times, just use a <a href="http://www.cprogramming.com/tutorial/stl/vector.html">vector</a> from the <a href="http://www.cprogramming.com/tutorial/stl/stlintro.html">Standard Template Library (STL)</a>. <br /><br />If you just need to retrieve the address of the pointer held in an auto_ptr object, you can use the get method. The following code demonstrates a function that returns a pointer to an integer as stored in an auto_ptr object: <pre class="example">int* example()
{
std::auto_ptr&lt;int&gt; int_memory_manager(new int);
return int_memory_manager.release(); // we'll see release in more depth below 
</pre>Of course, you'd probably be better off simply returning an auto_ptr object in the first place. <br /><br /><b>Some Caveats</b><br /><br />While you can assign one auto_ptr object to another, when you do so, the actual assignment results in the transferral of the pointer from one object to the other. <br /><br />For instance, <pre class="example">std::auto_ptr&lt;int&gt; int_memory_manager(new int);
std::auto_ptr&lt;int&gt; int_memory_manager2;
cout&lt;&lt;"Contents of first is "&lt;&lt;int_memory_manager.get()&lt;&lt;endl;
int_memory_manager2 = int_memory_manager;
cout&lt;&lt;"Contents of first is "&lt;&lt;int_memory_manager.get()&lt;&lt;endl;
cout&lt;&lt;"Contents of second is "&lt;&lt;int_memory_manager2.get()&lt;&lt;endl;
</pre>This sample program demonstrates that the overloaded copy operator for auto_ptr actually removes the pointer from the object being copied! It's set to NULL. This means that only one auto_ptr object can hold a pointer at any time. The act of assigning one auto_ptr to another changes the auto_ptr being assigned. Moreover, this situation holds true even when implicitly copying auto_ptr objects -- for instance, if you make a function call and pass an auto_ptr object, when the function returns, the contents of auto_ptr will be changed to NULL, as demonstrated by the following code: <pre class="example">using namespace std;
void aFunction(std::auto_ptr&lt;int&gt; x)
{
}

int main()
{
    std::auto_ptr&lt;int&gt; int_manager(new int);
    aFunction(int_manager);
    cout&lt;&lt;"Content of int_manager is "int_manager.get()&lt;&lt;endl;
    // Expected output: 0
}
</pre>In truth, this behavior is quite beneficial because it means that two auto_ptr objects will not both try to delete the same pointer. Deleting the same pointer twice is not a valid operation, and can lead to program crashes, whereas deleting NULL is a valid operation (though it doesn't change anything). <br /><br />A subtle consequence of this behavior is that auto_ptrs don't work well in all scenarios. For instance, using auto_ptr objects with the <a href="http://www.cprogramming.com/tutorial/stl/stlintro.html">standard template library</a> can lead to problems as some functions in the STL may make copies of the objects in containers such as the <a href="http://www.cprogramming.com/tutorial/stl/vector.html">vector container class</a>. One example is the sort function, which makes copies of some of the objects in the container being sorted. As a consequence, this copy can blithely delete the data in the container! <br /><br />If for some reason you did want to store an auto_ptr object in an STL container, you should probably either write your own memory manager container that counts the number of references to a pointer or handle freeing the memory somewhere else. If you wanted to take the second approach, you'll need to be able to tell an auto_ptr object that you're finished using it to manage your pointer. You can do this with the release function. For instance, the following code requests that an auto_ptr object allow us to handle the memory for the object again and resets the pointer stored by the auto_ptr object to NULL so that it doesn't get deleted twice. <pre class="example">std::auto_ptr&lt;int&gt; int_memory_manager(new int);
// Calling release sets the pointer managed by int_memory_manager to point 
// to NULL
int *need_to_delete_ptr = int_memory_manager.release();
delete need_to_delete_ptr;
</pre>Finally, if you happen to want to reuse an auto_ptr object, you can simply call the reset function to free the old memory and set the new memory: <pre class="example">std::auto_ptr&lt;int&gt; int_memory_manager(new int);
int_memory_manager.reset(new int);
</pre>Using auto_ptr won't solve all of your problems. First, you could still write code that manually handles pointers that are also managed by auto_ptr objects. Doing this could result in double deletes. Whenever you request manual control of a pointer from an auto_ptr, you open yourself up to the requirement to call delete on that pointer -- consequently, the possibility of a memory leak exists. <br /><br /><b>Summary</b><br /><br />The auto_ptr class is a templated class that takes a type and stores a <a href="http://www.cprogramming.com/tutorial/lesson6.html">pointer</a> of that type, which can be set by the constructor or by using the reset function to pass in a new address to store. Pointers can be returned to manual control by the release function; otherwise, the pointer will be freed when the auto_ptr leaves scope. <br /><br /><b>The Good</b><ul><li>auto_ptr objects store pointers and handle deleting the pointer when the auto_ptr object goes out of scope 
</li><li>Using auto_ptr helps avoid memory leaks associated with exceptions and minimizes the amount of cleanup code required </li></ul><b>The Gotchas</b><ul><li>Copying an auto_ptr changes the object being copied by setting the contests to NULL 
</li><li>auto_ptr objects are not guaranteed to work correctly with the standard template library containers </li></ul><img src ="http://www.blogjava.net/weidagang2046/aggbug/69527.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-14 08:46 <a href="http://www.blogjava.net/weidagang2046/articles/69527.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>const使用详解</title><link>http://www.blogjava.net/weidagang2046/articles/63553.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 14 Aug 2006 16:01:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/63553.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/63553.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/63553.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/63553.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/63553.html</trackback:ping><description><![CDATA[
		<div class="comText">一 const基础<br /><br />如果const关键字不涉及到指针，我们很好理解，下面是涉及到指针的情况：<br /><br />int b = 500;<br />const int* a = &amp;b; [1]<br />int const *a = &amp;b; [2]<br />int* const a = &amp;b; [3]<br />const int* const a = &amp;b; [4]<br /><br />如果你能区分出上述四种情况，那么，恭喜你，你已经迈出了可喜的一步。不知道，也没关系，我们可以参考《Effective c++》Item21上的做法，如果const位于星号的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；如果const位于星号的右侧，const就是修饰指针本身，即指针本身是常量。因此，[1]和[2]的情况相同，都是指针所指向的内容为常量（const放在变量声明符的位置无关），这种情况下不允许对内容进行更改操作，如不能*a = 3 ；[3]为指针本身是常量，而指针所指向的内容不是常量，这种情况下不能对指针本身进行更改操作，如a++是错误的；[4]为指针本身和指向的内容均为常量。<br />另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中，const 可以修饰函数的返回值，或某个参数；对于成员函数，还可以修饰是整个函数。有如下几种情况，以下会逐渐的说明用法：<br /><br />A&amp; operator=(const A&amp; a);<br />void fun0(const A* a ); <br />void fun1( ) const; // fun1( ) 为类成员函数<br />const A fun2( );<br /><br />二 const的初始化<br /><br />先看一下const变量初始化的情况<br />1) 非指针const常量初始化的情况：<br /><br />A b;<br />const A a = b;<br /><br />2) 指针(引用)const常量初始化的情况：<br /><br />A* d = new A();<br />const A* c = d;<br />或者：const A* c = new A();<br />引用：<br />A f;<br />const A&amp; e = f; // 这样作e只能访问声明为const的函数，而不能访问一般的成员函数；<br /><br />[思考1]： 以下的这种赋值方法正确吗？<br />const A* c=new A();<br />A* e = c;<br />[思考2]： 以下的这种赋值方法正确吗？<br />A* const c = new A();<br />A* b = c;<br /><br />三 作为参数和返回值的const修饰符<br /><br />其实，不论是参数还是返回值，道理都是一样的，参数传入时候和函数返回的时候，初始化const变量<br />1 修饰参数的const，如 void fun0(const A* a ); void fun1(const A&amp; a);<br />调用函数的时候，用相应的变量初始化const常量，则在函数体中，按照const所修饰的部分进行常量化，如形参为const A* a，则不能对传递进来的指针的内容进行改变，保护了原指针所指向的内容；如形参为const A&amp; a，则不能对传递进来的引用对象进行改变，保护了原对象的属性。<br />[注意]：参数const通常用于参数为指针或引用的情况;<br />2 修饰返回值的const，如const A fun2( ); const A* fun3( );<br />这样声明了返回值后，const按照"修饰原则"进行修饰，起到相应的保护作用。<br /><br />const Rational operator*(const Rational&amp; lhs, const Rational&amp; rhs)<br />{<br />return Rational(lhs.numerator() * rhs.numerator(),<br />lhs.denominator() * rhs.denominator());<br />}<br /><br />返回值用const修饰可以防止允许这样的操作发生:<br /><br />Rational a,b;<br />Radional c;<br />(a*b) = c;<br /><br />一般用const修饰返回值为对象本身（非引用和指针）的情况多用于二目操作符重载函数并产生新对象的时候。<br />[总结] 一般情况下，函数的返回值为某个对象时，如果将其声明为const时，多用于操作符的重载。通常，不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。<br />原因如下：<br />如果返回值为某个对象为const（const A test = A 实例）或某个对象的引用为const（const A&amp; test = A实例），则返回值具有const属性，则返回实例只能访问类A中的公有（保护）数据成员和const成员函数，并且不允许对其进行赋值操作，这在一般情况下很少用到。<br /><br />[思考3]： 这样定义赋值操作符重载函数可以吗？<br />const A&amp; operator=(const A&amp; a);<br /><br />四 类成员函数中const的使用<br /><br />一般放在函数体后，形如：void fun() const;<br />如果一个成员函数的不会修改数据成员，那么最好将其声明为const，因为const成员函数中不允许对数据成员进行修改，如果修改，编译器将报错，这大大提高了程序的健壮性。<br /><br />五 使用const的一些建议<br /><br />1 要大胆的使用const，这将给你带来无尽的益处，但前提是你必须搞清楚原委；<br />2 要避免最一般的赋值操作错误，如将const变量赋值，具体可见思考题；<br />3 在参数中使用const应该使用引用或指针，而不是一般的对象实例，原因同上；<br />4 const在成员函数中的三种用法（参数、返回值、函数）要很好的使用；<br />5 不要轻易的将函数的返回值类型定为const;<br />6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;<br /><br /><br />本人水平有限，欢迎批评指正，可以联系 kangjd@epri.ac.cn<br /><br /><br />[思考题答案]<br />1 这种方法不正确，因为声明指针的目的是为了对其指向的内容进行改变，而声明的指针e指向的是一个常量，所以不正确；<br />2 这种方法正确，因为声明指针所指向的内容可变；<br />3 这种做法不正确；<br />在const A::operator=(const A&amp; a)中，参数列表中的const的用法正确，而当这样连续赋值的时侯，问题就出现了：<br />A a,b,c:<br />(a=b)=c;<br />因为a.operator=(b)的返回值是对a的const引用，不能再将c赋值给const常量。<br /><br />from: <a href="http://www.linux-ren.org/modules/newbb/viewtopic.php?topic_id=1110&amp;forum=14">http://www.linux-ren.org/modules/newbb/viewtopic.php?topic_id=1110&amp;forum=14</a></div>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/63553.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-08-15 00:01 <a href="http://www.blogjava.net/weidagang2046/articles/63553.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>All About: File I/O in C++</title><link>http://www.blogjava.net/weidagang2046/articles/47328.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 21 May 2006 12:43:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/47328.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/47328.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/47328.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/47328.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/47328.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: All About: File I/O in C++																								By 														Ilia Yordanov												, loobian@cpp-home.com																																				www.cpp-home.com...&nbsp;&nbsp;<a href='http://www.blogjava.net/weidagang2046/articles/47328.html'>阅读全文</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/47328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-05-21 20:43 <a href="http://www.blogjava.net/weidagang2046/articles/47328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中动态分配二维数组的方法</title><link>http://www.blogjava.net/weidagang2046/articles/46487.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 16 May 2006 13:39:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/46487.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/46487.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/46487.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/46487.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/46487.html</trackback:ping><description><![CDATA[
		<p>
				<font face="宋体" size="1"> </font>
		</p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<span style="COLOR: #000000">#include<br /><br /></span>
				<span style="COLOR: #0000ff">#define</span>
				<span style="COLOR: #000000"> N 4</span>
				<span style="COLOR: #000000">
						<br />typedef </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> p[N];</span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">方法一用到</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">
						<br />
				</span>
				<span style="COLOR: #0000ff">using</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">namespace</span>
				<span style="COLOR: #000000"> std;<br /><br /></span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> main()<br />{<br />    </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> n </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">;<br /><br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">方法一：使用typedef定义一个具有N个元素的数组类型</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    p </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">ptr1;      </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">定义二维数组??用法与二维数组相同</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    ptr1 </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">new</span>
				<span style="COLOR: #000000"> p[N];<br /><br />    </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000">(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> i </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">; i </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> N; i</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />        </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000">(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> j </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">; j </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> N; j</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />            ptr1[i][j] </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">n;<br /><br />    cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000">方法一：</span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl;<br />    </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000">(i </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">; i </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> N; i</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />    {<br />        </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000">(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> j</span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">;j </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> N; j</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />            cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> ptr1[i][j] </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">"</span>
				<span style="COLOR: #000000">;<br />        cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl;<br />    }<br />    delete[] ptr1;<br />    cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl;<br /><br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000"> 方法二：使用数组指针</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> row </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> N;     </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">二维数组的行数?</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> column </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> N;  </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">二维数组的列数<br />     <br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">分配一个指针数组，其首地址保存在pMatrix中</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">**</span>
				<span style="COLOR: #000000">pMatrix </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">new</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">[row];<br /><br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">为指针数组的每个元素分配一个数组</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000"> (</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> i </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">; i </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> row; i</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />        pMatrix[i] </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">new</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000">[column];<br /><br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">以上是分配，以下是释放</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    </span>
				<span style="COLOR: #0000ff">for</span>
				<span style="COLOR: #000000"> (</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> i </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">; i </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000"> row; i</span>
				<span style="COLOR: #000000">++</span>
				<span style="COLOR: #000000">)<br />        delete [column] pMatrix[i];<br />    delete [row] pMatrix;<br /><br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">这些技术可用于构造一个矩阵类</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">
						<br />    </span>
				<span style="COLOR: #0000ff">return</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">0</span>
				<span style="COLOR: #000000">;<br />}<br /></span>
		</div>from: <a href="http://www.zahui.com/html/9/20062.htm">http://www.zahui.com/html/9/20062.htm</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/46487.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-05-16 21:39 <a href="http://www.blogjava.net/weidagang2046/articles/46487.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>using Declaration</title><link>http://www.blogjava.net/weidagang2046/articles/34906.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 12 Mar 2006 05:44:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/34906.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/34906.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/34906.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/34906.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/34906.html</trackback:ping><description><![CDATA[<H4>Syntax</H4><PRE class=syntax><B>using</B> [<B>typename</B>][<B>::</B>] <I>nested-name-specifier</I> <I>unqualified-id</I>
<B>using</B> <B>::</B> <I>unqualified-id</I></PRE>
<P>The <B>using</B> declaration introduces a name into the declarative region in which the <B>using</B> declaration appears. The name becomes a synonym for an entity declared elsewhere. It allows an <I>individual</I> name from a specific namespace to be used without <A href="http://msdn.microsoft.com/library/en-us/vccelng/htm/tions_45.asp">explicit qualification</A>. This is in contrast to the <B>using</B> directive, which allows <I>all</I> the names in a namespace to be used without qualification. See <A href="http://msdn.microsoft.com/library/en-us/vccelng/htm/tions_44.asp">using Directive</A> for more information.</P>
<P>A <I>using-declaration</I> can be used in a class definition. For example:</P><PRE>class B
{
    void f(char);
    void g(char);
};

class D : B
{
    using B::f;
    void f(int) { f('c'); }        // calls B::f(char)
    void g(int) { g('c'); }        // recursively calls D::g(int)
                                   // only B::f is being used
};
</PRE>
<P>When used to declare a member, a <I>using-declaration</I> must refer to a member of a base class. For example:</P><PRE>class C
{
    int g();
};

class D2 : public B
{
    using B::f;          // ok: B is a base of D2
    using C::g;          // error: C isn't a base of D2
};
</PRE>
<P>Members declared with a <I>using-declaration</I> can be referenced using explicit qualification. The <B>::</B> prefix refers to the global namespace. For example:</P><PRE>void f();

namespace A
{
    void g();
}

namespace X
{
    using ::f;        // global f
    using A::g;       // A's g
}

void h()
{
    X::f();           // calls ::f
    X::g();           // calls A::g
}
</PRE>
<P>Just as with any declaration, a <I>using-declaration</I> can be used repeatedly only where multiple declarations are allowed. For example:</P><PRE>namespace A
{
    int i;
}

void f()
{
    using A::i;
    using A::i;        // ok: double declaration
}

class B
{
protected:
    int i;
};

class X : public B
{
public:
    using B::i;
    using B::i;        // error: class members cannot be multiply declared
};
</PRE>
<P>When a <I>using-declaration</I> is made, the synonym created by the declaration refers only to definitions that are valid at the point of the <I>using-declaration</I>. Definitions added to a namespace after the <I>using-declaration</I> are not valid synonyms. For example:</P><PRE>namespace A
{
    void f(int);
}

using A::f;        // f is a synonym for A::f(int) only

namespace A
{
    void f(char);
}

void f()
{
    f('a');            // refers to A::f(int), even though A::f(char) exists
}

void b()
{
    using A::f;        // refers to A::f(int) AND A::f(char)
    f('a');            // calls A::f(char);
}
</PRE>
<P>A name defined by a <I>using-declaration</I> is an alias for its original name. It does not affect the type, linkage or other attributes of the original declaration.</P>
<P>If a set of local declarations and <I>using-declaration</I>s for a single name are given in a declarative region, they must all refer to the same entity, or they must all refer to functions. For example:</P><PRE>namespace B
{
    int i;
    void f(int);
    void f(double);
}

void g()
{
    int i;
    using B::i;          // error: i declared twice
    void f(char);
    using B::f;          // ok: each f is a function
}
</PRE>
<P>In the example above, the <CODE>using B::i</CODE> statement causes a second <CODE>int i</CODE> to be declared in the <CODE>g()</CODE> function. The <CODE>using B::f</CODE> statement does not conflict with the <CODE>f(char)</CODE> function because the function names introduced by <CODE>B::f</CODE> have different parameter types.</P>
<P>A local function declaration cannot have the same name and type as a function introduced by a <I>using-declaration</I>. For example:</P><PRE>namespace B
{
    void f(int);
    void f(double);
}

namespace C
{
    void f(int);
    void f(double);
    void f(char);
}

void h()
{
    using B::f;          // introduces B::f(int) and B::f(double)
    using C::f;          // C::f(int), C::f(double), and C::f(char)
    f('h');              // calls C::f(char)
    f(1);                // error: ambiguous: B::f(int) or C::f(int)?
    void f(int);         // error: conflicts with B::f(int) and C::f(int)
}
</PRE>
<P>When a <I>using-declaration</I> introduces a name from a base class into a derived class scope, member functions in the derived class override virtual member functions with the same name and argument types in the base class. For example:</P><PRE>struct B
{
    virtual void f(int);
    virtual void f(char);
    void g(int);
    void h(int);
};

struct D : B
{
    using B::f;
    void f(int);         // ok: D::f(int) overrides B::f(int)

    using B::g;
    void g(char);        // ok: there is no B::g(char)

    using B::h;
    void h(int);         // error: D::h(int) conflicts with B::h(int)
                         // B::h(int) is not virtual
};

void f(D* pd)
{
    pd-&gt;f(1);            // calls D::f(int)
    pd-&gt;f('a');          // calls B::f(char)
    pd-&gt;g(1);            // calls B::g(int)
    pd-&gt;g('a');          // calls D::g(char)
}
</PRE>
<P>All instances of a name mentioned in a <I>using-declaration</I> must be accessible. In particular, if a derived class uses a <I>using-declaration</I> to access a member of a base class, the member name must be accessible. If the name is that of an overloaded member function, then all functions named must be accessible. For example:</P><PRE>class A
{
private:
    void f(char);
public:
    void f(int);
protected:
    void g();
};

class B : public A
{
    using A::f;          // error: A::f(char) is inaccessible
public:
    using A::g;          // B::g is a public synonym for A::g
};
<BR><BR>from: <A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/tions_43.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/tions_43.asp</A><BR></PRE><img src ="http://www.blogjava.net/weidagang2046/aggbug/34906.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-03-12 13:44 <a href="http://www.blogjava.net/weidagang2046/articles/34906.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Use Functor for Callbacks in C++</title><link>http://www.blogjava.net/weidagang2046/articles/28881.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sat, 21 Jan 2006 07:45:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/28881.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/28881.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/28881.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/28881.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/28881.html</trackback:ping><description><![CDATA[<DIV class="" style="MARGIN-TOP: 10px; FONT-WEIGHT: bold; FONT-SIZE: 18px; MARGIN-LEFT: 10px; COLOR: #ff6600; FONT-FAMILY: Arial, Helvetica, Sans-Serif">Use <I>Functor</I> for Callbacks in C++</DIV>
<DIV class="" style="PADDING-RIGHT: 10px; PADDING-LEFT: 10px; PADDING-BOTTOM: 10px; PADDING-TOP: 10px">Using the <SPAN class=pf>callback</SPAN> function in C is pretty straightforward, but in C++ it becomes little tricky. 
<P>If you want to use a <SPAN class=pf>member</SPAN> function as a <SPAN class=pf>callback</SPAN> function, then the <SPAN class=pf>member</SPAN> function needs to be associated with an object of the class before it can be called. In this case, you can use <SPAN class=pf>functor</SPAN>. 
<P>Suppose you need to use the <SPAN class=pf>member</SPAN> function <SPAN class=pf>get()</SPAN> of the class base as a callback function <PRE><CODE>
class base

{
public:
	int get ()
	{ return 7;}
};
</CODE></PRE>Then, you need to define a functor: <PRE><CODE>
class CallbackFunctor
{<BR><BR><BR>public:
	functor(const base&amp; b):m_base(b)
	{}
	int operator() ()
	{
		return m_base.get();
	}<BR>private:<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;base m_base;
};
</CODE></PRE>Now you can use an object of <SPAN class=pf>CallbackFunctor</SPAN> as a callback function as follows. 
<P>Define the function that needs a callback to take an argument of type <SPAN class=pf>CallbackFunctor</SPAN>: <PRE><CODE>
void call (CallbackFunctor&amp; f)
{
	cout &lt;&lt; f() &lt;&lt; endl;
}


int main ()
{
	base b;
	functor f(b);
	call(f);
}
</CODE></PRE></DIV>
<DIV class=articleDek style="MARGIN-BOTTOM: 10px; MARGIN-LEFT: 10px">Saurabh Ramya<BR><BR>from: <A href="http://www.devx.com/tips/Tip/27126">http://www.devx.com/tips/Tip/27126</A></DIV><img src ="http://www.blogjava.net/weidagang2046/aggbug/28881.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-01-21 15:45 <a href="http://www.blogjava.net/weidagang2046/articles/28881.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>A Crash Course in the C++ Standard Template Library</title><link>http://www.blogjava.net/weidagang2046/articles/24256.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 16 Dec 2005 09:35:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/24256.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/24256.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/24256.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/24256.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/24256.html</trackback:ping><description><![CDATA[<P><FONT size=4><STRONG>A Crash Course in the C++ Standard Template Library</STRONG></FONT><BR>Thursday, August 28, 2003</P>
<P>By&nbsp;<A class=bodyText href="http://www.topcoder.com/tc?module=MemberProfile&amp;cr=269554"><STRONG>Yarin</STRONG></A><BR><SPAN class=smallText><EM>TopCoder Member</EM></SPAN></P>
<P><B>Introduction</B><BR>This course tries to quickly teach how to use the Standard Template Library for the most common tasks without having to rely on other sources. It tells very little how things work, but instead gives several code snippets from which it should be possible to figure out how to use some parts of the STL. Since it's a crash course, a lot of things are left out and some things are simplified so much they're almost lies... </P>
<P>The STL is a collection of containers and simple algorithms. A container is something that holds several elements of the same type. The containers described in this course are <SPAN class=code>vector</SPAN>, <SPAN class=code>set</SPAN> and <SPAN class=code>map</SPAN>, but the STL have several more containers. The STL header files required in this course are <SPAN class=code>vector</SPAN>, <SPAN class=code>set</SPAN>, <SPAN class=code>map</SPAN> and <SPAN class=code>algorithm</SPAN>. I also use <SPAN class=code>iostream</SPAN> and <SPAN class=code>string</SPAN> (the latter is not part of the STL, even though it in many ways have the same functionality). All classes are declared in the namespace std, so having the following lines at the beginning of your code is a good idea: </P><PRE>#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;set&gt;
#include &lt;map&gt;
#include &lt;algorithm&gt;

using namespace std;
</PRE>
<H2>vector</H2>
<P>A vector corresponds to a 1-dimensional array in C. A vector is declared like this: </P><PRE>vector&lt;int&gt; a;       // Declare a vector of integers
vector&lt;MyStruct&gt; b;  // Declare a vector of MyStruct
</PRE>
<P>The vectors declared above both initially contain zero elements. This is no problem, since vectors (like all the STL containers) can grow dynamically by inserting elements. </P>
<P>It's also possible to initialize a vector with elements and also give those a specific value: </P><PRE>vector&lt;int&gt; c(50,1); // Declare a vector of integers with 50 elements, all set to 1
</PRE>
<P>For structs and classes, the initial value should be a constructor. If the initial value is omitted, the default constructor will be used (in case of simple types like int, double etc, the initial value is 0). </P>
<P>Elements in a vector are accessed the same way as elements in an array, using the [] operator. Elements can also be accessed using <SPAN class=code>at</SPAN>: </P><PRE>cout &lt;&lt; c[5] &lt;&lt; endl;
cout &lt;&lt; c.at(5) &lt;&lt; endl;
</PRE>
<P>The difference between the two is that <SPAN class=code>at</SPAN> will raise an exception if you try to access an element outside the vector while the [] operator won't. </P>
<P>Extending the size of a vector by appending an element to the end is done with the <SPAN class=code>push_back</SPAN> method: </P><PRE>vector&lt;int&gt; a(10,1);
a.push_back(2);      // Element 0 to 9 in a is now 1, and element 10 is 2.
</PRE>
<P>It's not possible to insert elements anywhere else in a vector. Other ways to resize a vector includes the <SPAN class=code>resize</SPAN> method: </P><PRE>vector&lt;int&gt; a(10,1);
a.resize(15);        // Element 0 to 9 in a is now 1, and element 10 to 14 is 0.
</PRE>
<P>If <SPAN class=code>resize</SPAN> increases the number of elements, the default constructor is assigned to the new elements. Decreasing the size of a vector removes elements from the end of the vector. </P>
<P>Other useful methods in the vector class are: (T is the type of each element) </P><PRE>size_t size();       // Returns the number of elements in the vector
void pop_back();     // Removes the last element
T back();            // Returns the value of the last element
void clear();        // Essentially the same thing as resize(0)
</PRE>
<H2>Sorting</H2>
<P>A vector can of course be sorted: (also works on arrays) </P><PRE>sort(&amp;a[0],&amp;a[N]);   // N = no elements in the vector (i.e. a.size())
</PRE>
<P>This will sort all elements in a in the default order. For built-in types, the default order is ascending. For structs and classes, you can define the default order by defining how the less-than operator should work: </P><PRE>bool operator&lt;(const MyStruct &amp;a, const MyStruct &amp;b)
{
  // Return true if A&lt;B, false if A&gt;=B
}
</PRE>
<P>The two parameters to sort specify the range in the vector to be sorted, by pointing to the first element and the element after the last one. So <SPAN class=code>sort(&amp;a[0],&amp;a[N])</SPAN> sorts elements 0,1,...,N-1. This way of specifying a range is used throughout all the STL, and is very practical. </P>
<P>Since it's most common you want to perform an operation on the whole vector, the start and endpoint have special names: <SPAN class=code>begin()</SPAN> and <SPAN class=code>end()</SPAN>. So instead of <SPAN class=code>sort(&amp;a[0],&amp;a[N])</SPAN> one can do <SPAN class=code>sort(a.begin(),a.end())</SPAN>. Actually, <SPAN class=code>&amp;a[0]</SPAN> and <SPAN class=code>a.begin()</SPAN> are not in general interchangeable as the former is a pointer and the latter an iterator (more about this later). </P>
<H2>pair</H2>
<P>A tiny class that is part of the STL is pair, which basically just contains (as the name implies) two data members - <SPAN class=code>first</SPAN> and <SPAN class=code>second</SPAN>. However, it's practical to use, especially when sorting a vector of pairs. The members are, respectively, used as the first and second sort key, which means that both data types used must have a less-than operator defined. For instance, if you want to sort coordinates first according to their x value and then their y value, then we can use the pair class like this: </P><PRE>int N,x,y;
vector&lt; pair&lt;int,int&gt; &gt; a; // It's essential that there is a space between &gt; and &gt;!
cin &gt;&gt; N;
for(int i=0;i&lt;N;++i) {
  cin &gt;&gt; x &gt;&gt; y;
  a.push_back(make_pair(x,y)); // make_pair creates a pair&lt;int,int&gt; object
}
sort(a.begin(),a.end()); // Sorts first according to the x-coordinate, then the y-coordinate
</PRE>
<P>Note the use of <SPAN class=code>make_pair</SPAN> (also part of the STL). Without it, we would have to type something like <SPAN class=code>a.push_back(pair&lt;int,int&gt;(x,y))</SPAN></P>
<H2>set</H2>
<P>One of the most fundamental ways to store information is to have a set of objects. Defining a set of integers is done with </P><PRE>set&lt;int&gt; a;
</PRE>
<P>To add, remove and check for single elements in a set: </P><PRE>a.insert(7);         // Insert integer 7 in the set
a.erase(5);          // Remove integer 5 (if it exist) from the set
if (a.find(7)!=a.end())
  ; // Integer 7 exists in the set
else
  ; // Integer 7 does not exist
cout &lt;&lt; a.size() &lt;&lt; endl; // Print the number of elements in the set a
</PRE>
<P>Other common set operations includes finding the union, intersection and difference between two sets. </P><PRE>set&lt;int&gt; a,b,un,in,di;
..
..
set_union(a.begin(),a.end(),b.begin(),b.end(),insert_iterator&lt;set&lt;int&gt; &gt;(un,un.begin()));
set_intersection(a.begin(),a.end(),b.begin(),b.end(),insert_iterator&lt;set&lt;int&gt; &gt;(in,in.begin()));
set_difference(a.begin(),a.end(),b.begin(),b.end(),insert_iterator&lt;set&lt;int&gt; &gt;(di,di.begin()));
</PRE>
<P>It's necessary to use the <SPAN class=code>insert_iterator</SPAN> thingy which insert all elements in the union (or intersection or difference) of the two sets <SPAN class=code>a</SPAN> and <SPAN class=code>b</SPAN> into set <SPAN class=code>c</SPAN>. If <SPAN class=code>c</SPAN> is not empty, you might want to clear the set first using the <SPAN class=code>clear()</SPAN> method. </P>
<P>To allow for fast operations, the internal representation of a set is a balanced binary tree. This means that the type of the values must be sortable, i.e. having a less-than operator defined (see above). Thus all elements in a set are always sorted, which can be quite practical. </P>
<H3>Conversions</H3>
<P>One can initialize a set from a vector: </P><PRE>set&lt;int&gt; b(a.begin(),a.end()); // a is a vector&lt;int&gt;
</PRE>
<P>Going the other way is also possible: </P><PRE>vector&lt;int&gt; c(b.begin(),b.end()); // b is a set&lt;int&gt;
</PRE>
<P>Doing these two operations after each other effectively sorts a vector of integers and removes all duplicate elements! </P>
<H2>map</H2>
<P>It's often desirable to have a 1-1 relation between two data types. One member in the relation is the key element, the lookup value. The other is the data value, retrieved by doing a lookup. For example, we might want to map names of cities (stored as strings) to serial number (integers): </P><PRE>map&lt;string,int&gt; a;
</PRE>
<P>Now, map can in some ways be used exactly as a vector: </P><PRE>a["New York"]=7;
a["Los Angeles"]=8;
a["Boston"]=10;
a["Los Angeles"]=3;
cout &lt;&lt; a["Los Angeles"] &lt;&lt; endl;     // Prints 3
cout &lt;&lt; a["San Francisco"] &lt;&lt; endl;   // Prints 0
</PRE>
<P>If no value is given to a key, it receives the default constructor (0 for simple types). </P>
<P>The map class can be thought of as a set of pairs (where first is the key value and second the data value) with the [] operator declared in a handy way, except that only the key value needs a less-than operator defined. </P>
<P>It might be worth noting that by defining a <SPAN class=code>map&lt;int,int&gt;</SPAN> one basically has an infinite large array of integers! </P>
<H2>Looping over elements</H2>
<P>Looping over all elements in a vector can be done by looping over all indexes, but that's not possible in a set or a map. Instead one can use one of the following two methods: </P>
<H3>Using iterators</H3>
<P>This basically means that instead of looping an index from 0 to N-1, we loop an iterator (think of it as a "smart pointer") from the first to the last element. For instance, to print out all elements in a set or a map: </P><PRE>set&lt;int&gt; a;
map&lt;string,string&gt; b;
..
..
for(set&lt;int&gt;::iterator i=a.begin();i!=a.end();++i)
  cout &lt;&lt; *i &lt;&lt; endl;
for(map&lt;string,string&gt;::iterator i=b.begin();i!=b.end();++i)
  cout &lt;&lt; i-&gt;first &lt;&lt; " =&gt; " &lt;&lt; i-&gt;second &lt;&lt; endl;
</PRE>
<P>Note that it's necessary to use <SPAN class=code>i!=a.end()</SPAN> and not <SPAN class=code>i&lt;a.end()</SPAN> as the elements don't necessary have to be stored in order in the memory. </P>
<P>We can here see that <SPAN class=code>i</SPAN> is not a pointer but an iterator: if <SPAN class=code>i</SPAN> was a pointer, <SPAN class=code>++i</SPAN> would cause <SPAN class=code>i</SPAN> to point to the next element in the memory, while with an iterator it will point to the next element in the container (which does not necessarily lie immediately after the previous element in the memory). </P>
<H3>Using for_each</H3>
<P>Instead of explicitly creating the for-loop, one can use a function from the algorithm library: </P><PRE>void doit(const int &amp;t) { cout &lt;&lt; t &lt;&lt; endl; }
..
..
set&lt;int&gt; a; // Can be a vector or map as well
for_each(a.begin(),a.end(),doit); 
</PRE>
<P>That is, for all elements in the range [<SPAN class=code>a.begin()</SPAN>,<SPAN class=code>a.end()</SPAN>), call the function <SPAN class=code>doit</SPAN>. The parameter in <SPAN class=code>doit</SPAN> must be a const reference of the data type in the container. </P>
<H2>More about the STL</H2>
<P>A complete reference to the STL as well as a more technical introduction to it can be found at <A href="http://www.sgi.com/tech/stl/" target=_blank>http://www.sgi.com/tech/stl/</A>. </P>
<P><BR></P>
<P>Would you like to <A class=bodyGeneric href="http://www.topcoder.com/?&amp;t=features&amp;c=feat_topics">write a feature?</A><BR><BR>from: <A href="http://www.topcoder.com/index?t=features&amp;c=feat_082803">http://www.topcoder.com/index?t=features&amp;c=feat_082803</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/24256.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-12-16 17:35 <a href="http://www.blogjava.net/weidagang2046/articles/24256.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你理解复杂的C/C++声明</title><link>http://www.blogjava.net/weidagang2046/articles/23697.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 13 Dec 2005 09:00:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/23697.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/23697.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/23697.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/23697.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/23697.html</trackback:ping><description><![CDATA[<DIV>介绍&nbsp; 
<P></P>
<P>曾经碰到过让你迷惑不解、类似于int&nbsp;*&nbsp;(*&nbsp;(*fp1)&nbsp;(int)&nbsp;)&nbsp;[10];这样的变量声明吗？本文将由易到难，一步一步教会你如何理解这种复杂的C/C++声明：我们将从每天都能碰到的较简单的声明入手，然后逐步加入const修饰符和typedef，还有函数指针，最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。需要强调一下的是，复杂的C/C++声明并不是好的编程风格；我这里仅仅是教你如何去理解这些声明。注意：为了保证能够在同一行上显示代码和相关注释，本文最好在至少1024x768分辨率的显示器上阅读。&nbsp;</P>
<P></P>
<P>基础&nbsp;</P>
<P>让我们从一个非常简单的例子开始，如下：&nbsp;</P>
<P>int&nbsp;n;&nbsp;</P>
<P>这个应该被理解为“declare&nbsp;n&nbsp;as&nbsp;an&nbsp;int”（n是一个int型的变量）。&nbsp;</P>
<P>接下去来看一下指针变量，如下：&nbsp;</P>
<P>int&nbsp;*p;&nbsp;</P>
<P>这个应该被理解为“declare&nbsp;p&nbsp;as&nbsp;an&nbsp;int&nbsp;*”（p是一个int&nbsp;*型的变量），或者说p是一个指向一个int型变量的指针。我想在这里展开讨论一下：我觉得在声明一个指针（或引用）类型的变量时，最好将*（或&amp;）写在紧靠变量之前，而不是紧跟基本类型之后。这样可以避免一些理解上的误区，比如：&nbsp;</P>
<P>int*&nbsp;&nbsp;p,q;&nbsp;</P>
<P>第一眼看去，好像是p和q都是int*类型的，但事实上，只有p是一个指针，而q是一个最简单的int型变量。&nbsp;</P>
<P>我们还是继续我们前面的话题，再来看一个指针的指针的例子：&nbsp;</P>
<P>char&nbsp;**argv;&nbsp;</P>
<P>理论上，对于指针的级数没有限制，你可以定义一个浮点类型变量的指针的指针的指针的指针...&nbsp;</P>
<P>再来看如下的声明：&nbsp;</P>
<P>int&nbsp;RollNum[30][4];&nbsp;<BR>int&nbsp;(*p)[4]=RollNum;&nbsp;<BR>int&nbsp;*q[5];&nbsp;</P>
<P>这里，p被声明为一个指向一个4元素（int类型）数组的指针，而q被声明为一个包含5个元素（int类型的指针）的数组。&nbsp;</P>
<P>另外，我们还可以在同一个声明中混合实用*和&amp;，如下：&nbsp;</P>
<P>int&nbsp;**p1;&nbsp;//&nbsp;p1&nbsp;is&nbsp;a&nbsp;pointer&nbsp;&nbsp;to&nbsp;a&nbsp;pointer&nbsp;&nbsp;to&nbsp;an&nbsp;int.&nbsp;<BR>int&nbsp;*&amp;p2;&nbsp;//&nbsp;p2&nbsp;is&nbsp;a&nbsp;reference&nbsp;to&nbsp;a&nbsp;pointer&nbsp;&nbsp;to&nbsp;an&nbsp;int.&nbsp;<BR>int&nbsp;&amp;*p3;&nbsp;//&nbsp;ERROR:&nbsp;Pointer&nbsp;&nbsp;to&nbsp;a&nbsp;reference&nbsp;is&nbsp;illegal.&nbsp;<BR>int&nbsp;&amp;&amp;p4;&nbsp;//&nbsp;ERROR:&nbsp;Reference&nbsp;to&nbsp;a&nbsp;reference&nbsp;is&nbsp;illegal.&nbsp;</P>
<P>注：p1是一个int类型的指针的指针；p2是一个int类型的指针的引用；p3是一个int类型引用的指针（不合法！）；p4是一个int类型引用的引用（不合法！）。&nbsp;</P>
<P></P>
<P>const修饰符&nbsp;</P>
<P>当你想阻止一个变量被改变，可能会用到const关键字。在你给一个变量加上const修饰符的同时，通常需要对它进行初始化，因为以后的任何时候你将没有机会再去改变它。例如：&nbsp;</P>
<P>const&nbsp;int&nbsp;n=5;&nbsp;<BR>int&nbsp;const&nbsp;m=10;&nbsp;</P>
<P>上述两个变量n和m其实是同一种类型的--都是const&nbsp;int（整形恒量）。因为C++标准规定，const关键字放在类型或变量名之前等价的。我个人更喜欢第一种声明方式，因为它更突出了const修饰符的作用。&nbsp;</P>
<P>当const与指针一起使用时，容易让人感到迷惑。例如，我们来看一下下面的p和q的声明：&nbsp;</P>
<P>const&nbsp;int&nbsp;*p;&nbsp;<BR>int&nbsp;const&nbsp;*q;&nbsp;</P>
<P>他们当中哪一个代表const&nbsp;int类型的指针（const直接修饰int），哪一个代表int类型的const指针（const直接修饰指针）？实际上，p和q都被声明为const&nbsp;int类型的指针。而int类型的const指针应该这样声明：&nbsp;</P>
<P>int&nbsp;*&nbsp;const&nbsp;r=&nbsp;&amp;n;&nbsp;//&nbsp;n&nbsp;has&nbsp;been&nbsp;declared&nbsp;as&nbsp;an&nbsp;int&nbsp;</P>
<P>这里，p和q都是指向const&nbsp;int类型的指针，也就是说，你在以后的程序里不能改变*p的值。而r是一个const指针，它在声明的时候被初始化指向变量n（即r=&amp;n;）之后，r的值将不再允许被改变（但*r的值可以改变）。&nbsp;</P>
<P>组合上述两种const修饰的情况，我们来声明一个指向const&nbsp;int类型的const指针，如下：&nbsp;</P>
<P>const&nbsp;int&nbsp;*&nbsp;const&nbsp;p=&amp;n&nbsp;//&nbsp;n&nbsp;has&nbsp;been&nbsp;declared&nbsp;as&nbsp;const&nbsp;int&nbsp;</P>
<P>下面给出的一些关于const的声明，将帮助你彻底理清const的用法。不过请注意，下面的一些声明是不能被编译通过的，因为他们需要在声明的同时进行初始化。为了简洁起见，我忽略了初始化部分；因为加入初始化代码的话，下面每个声明都将增加两行代码。&nbsp;</P>
<P>char&nbsp;**&nbsp;p1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;<BR>const&nbsp;char&nbsp;**p2;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;const&nbsp;char&nbsp;<BR>char&nbsp;*&nbsp;const&nbsp;*&nbsp;p3;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;const&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;<BR>const&nbsp;char&nbsp;*&nbsp;const&nbsp;*&nbsp;p4;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;const&nbsp;pointer&nbsp;to&nbsp;const&nbsp;char&nbsp;<BR>char&nbsp;**&nbsp;const&nbsp;p5;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;const&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;<BR>const&nbsp;char&nbsp;**&nbsp;const&nbsp;p6;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;const&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;to&nbsp;const&nbsp;char&nbsp;<BR>char&nbsp;*&nbsp;const&nbsp;*&nbsp;const&nbsp;p7;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;const&nbsp;pointer&nbsp;to&nbsp;const&nbsp;pointer&nbsp;to&nbsp;&nbsp;&nbsp;&nbsp;char&nbsp;<BR>const&nbsp;char&nbsp;*&nbsp;const&nbsp;*&nbsp;const&nbsp;p8;&nbsp;//&nbsp;const&nbsp;pointer&nbsp;to&nbsp;const&nbsp;pointer&nbsp;to&nbsp;const&nbsp;char&nbsp;</P>
<P>注：p1是指向char类型的指针的指针；p2是指向const&nbsp;char类型的指针的指针；p3是指向char类型的const指针；p4是指向const&nbsp;char类型的const指针；p5是指向char类型的指针的const指针；p6是指向const&nbsp;char类型的指针的const指针；p7是指向char类型const指针的const指针；p8是指向const&nbsp;char类型的const指针的const指针。&nbsp;</P>
<P></P>
<P>typedef的妙用&nbsp;</P>
<P>typedef给你一种方式来克服“*只适合于变量而不适合于类型”的弊端。你可以如下使用typedef：&nbsp;</P>
<P>typedef&nbsp;char&nbsp;*&nbsp;PCHAR;&nbsp;<BR>PCHAR&nbsp;p,q;&nbsp;</P>
<P>这里的p和q都被声明为指针。（如果不使用typedef，q将被声明为一个char变量，这跟我们的第一眼感觉不太一致！）下面有一些使用typedef的声明，并且给出了解释：&nbsp;</P>
<P>typedef&nbsp;char&nbsp;*&nbsp;a;&nbsp;//&nbsp;a&nbsp;is&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;</P>
<P>typedef&nbsp;a&nbsp;b();&nbsp;&nbsp;&nbsp;//&nbsp;b&nbsp;is&nbsp;a&nbsp;function&nbsp;that&nbsp;returns&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;</P>
<P>typedef&nbsp;b&nbsp;*c;&nbsp;&nbsp;&nbsp;//&nbsp;c&nbsp;is&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;function&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;that&nbsp;returns&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;</P>
<P>typedef&nbsp;c&nbsp;d();&nbsp;&nbsp;&nbsp;//&nbsp;d&nbsp;is&nbsp;a&nbsp;function&nbsp;returning&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;function&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;that&nbsp;returns&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;</P>
<P>typedef&nbsp;d&nbsp;*e;&nbsp;&nbsp;&nbsp;//&nbsp;e&nbsp;is&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;function&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;returning&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;function&nbsp;that&nbsp;returns&nbsp;a&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;</P>
<P>e&nbsp;var[10];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;var&nbsp;is&nbsp;an&nbsp;array&nbsp;of&nbsp;10&nbsp;pointers&nbsp;to&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;functions&nbsp;returning&nbsp;pointers&nbsp;to&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;functions&nbsp;returning&nbsp;pointers&nbsp;to&nbsp;chars.&nbsp;</P>
<P>typedef经常用在一个结构声明之前，如下。这样，当创建结构变量的时候，允许你不使用关键字struct（在C中，创建结构变量时要求使用struct关键字，如struct&nbsp;tagPOINT&nbsp;a；而在C++中，struct可以忽略，如tagPOINT&nbsp;b）。&nbsp;</P>
<P>typedef&nbsp;struct&nbsp;tagPOINT&nbsp;<BR>{&nbsp;<BR>&nbsp;&nbsp;int&nbsp;x;&nbsp;<BR>&nbsp;&nbsp;int&nbsp;y;&nbsp;<BR>}POINT;&nbsp;</P>
<P>POINT&nbsp;p;&nbsp;/*&nbsp;Valid&nbsp;C&nbsp;code&nbsp;*/&nbsp;</P>
<P></P>
<P>函数指针&nbsp;</P>
<P>函数指针可能是最容易引起理解上的困惑的声明。函数指针在DOS时代写TSR程序时用得最多；在Win32和X-Windows时代，他们被用在需要回调函数的场合。当然，还有其它很多地方需要用到函数指针：虚函数表，STL中的一些模板，Win&nbsp;NT/2K/XP系统服务等。让我们来看一个函数指针的简单例子：&nbsp;</P>
<P>int&nbsp;(*p)(char);&nbsp;</P>
<P>这里p被声明为一个函数指针，这个函数带一个char类型的参数，并且有一个int类型的返回值。另外，带有两个float类型参数、返回值是char类型的指针的指针的函数指针可以声明如下：&nbsp;</P>
<P>char&nbsp;**&nbsp;(*p)(float,&nbsp;float);&nbsp;</P>
<P>那么，带两个char类型的const指针参数、无返回值的函数指针又该如何声明呢？参考如下：&nbsp;</P>
<P>void&nbsp;*&nbsp;(*a[5])(char&nbsp;*&nbsp;const,&nbsp;char&nbsp;*&nbsp;const);&nbsp;</P>
<P></P>
<P>“右左法则”[重要！！！]&nbsp;</P>
<P>The&nbsp;right-left&nbsp;rule:&nbsp;Start&nbsp;reading&nbsp;the&nbsp;declaration&nbsp;from&nbsp;the&nbsp;innermost&nbsp;parentheses,&nbsp;go&nbsp;right,&nbsp;and&nbsp;then&nbsp;go&nbsp;left.&nbsp;When&nbsp;you&nbsp;encounter&nbsp;parentheses,&nbsp;the&nbsp;direction&nbsp;should&nbsp;be&nbsp;reversed.&nbsp;Once&nbsp;everything&nbsp;in&nbsp;the&nbsp;parentheses&nbsp;has&nbsp;been&nbsp;parsed,&nbsp;jump&nbsp;out&nbsp;of&nbsp;it.&nbsp;Continue&nbsp;till&nbsp;the&nbsp;whole&nbsp;declaration&nbsp;has&nbsp;been&nbsp;parsed.&nbsp;</P>
<P>这是一个简单的法则，但能让你准确理解所有的声明。这个法则运用如下：从最内部的括号开始阅读声明，向右看，然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续，直到整个声明都被分析完毕。&nbsp;</P>
<P>对上述“右左法则”做一个小小的修正：当你第一次开始阅读声明的时候，你必须从变量名开始，而不是从最内部的括号。&nbsp;</P>
<P>下面结合例子来演示一下“右左法则”的使用。&nbsp;</P>
<P>int&nbsp;*&nbsp;(*&nbsp;(*fp1)&nbsp;(int)&nbsp;)&nbsp;[10];&nbsp;</P>
<P>阅读步骤：&nbsp;<BR>1.&nbsp;从变量名开始&nbsp;--------------------------------------------&nbsp;fp1&nbsp;<BR>2.&nbsp;往右看，什么也没有，碰到了)，因此往左看，碰到一个*&nbsp;------&nbsp;一个指针&nbsp;<BR>3.&nbsp;跳出括号，碰到了(int)&nbsp;-----------------------------------&nbsp;一个带一个int参数的函数&nbsp;<BR>4.&nbsp;向左看，发现一个*&nbsp;---------------------------------------&nbsp;（函数）返回一个指针&nbsp;<BR>5.&nbsp;跳出括号，向右看，碰到[10]&nbsp;------------------------------&nbsp;一个10元素的数组&nbsp;<BR>6.&nbsp;向左看，发现一个*&nbsp;---------------------------------------&nbsp;指针&nbsp;<BR>7.&nbsp;向左看，发现int&nbsp;-----------------------------------------&nbsp;int类型&nbsp;</P>
<P><BR>总结：fp1被声明成为一个函数的指针,该函数返回指向指针数组的指针.&nbsp;</P>
<P><BR>再来看一个例子：&nbsp;</P>
<P>int&nbsp;*(&nbsp;*(&nbsp;*arr[5])())();&nbsp;</P>
<P>阅读步骤：&nbsp;<BR>1.&nbsp;从变量名开始&nbsp;--------------------------------------------&nbsp;arr&nbsp;<BR>2.&nbsp;往右看，发现是一个数组&nbsp;----------------------------------&nbsp;一个5元素的数组&nbsp;<BR>3.&nbsp;向左看，发现一个*&nbsp;---------------------------------------&nbsp;指针&nbsp;<BR>4.&nbsp;跳出括号，向右看，发现()&nbsp;--------------------------------&nbsp;不带参数的函数&nbsp;<BR>5.&nbsp;向左看，碰到*&nbsp;-------------------------------------------&nbsp;（函数）返回一个指针&nbsp;<BR>6.&nbsp;跳出括号，向右发现()&nbsp;------------------------------------&nbsp;不带参数的函数&nbsp;<BR>7.&nbsp;向左，发现*&nbsp;---------------------------------------------&nbsp;（函数）返回一个指针&nbsp;<BR>8.&nbsp;继续向左，发现int&nbsp;---------------------------------------&nbsp;int类型&nbsp;</P>
<P>总结：？？&nbsp;</P>
<P><BR>还有更多的例子：&nbsp;</P>
<P>float&nbsp;(&nbsp;*&nbsp;(&nbsp;*b())&nbsp;[]&nbsp;)();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;b&nbsp;is&nbsp;a&nbsp;function&nbsp;that&nbsp;returns&nbsp;a&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;pointer&nbsp;to&nbsp;an&nbsp;array&nbsp;of&nbsp;pointers&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;to&nbsp;functions&nbsp;returning&nbsp;floats.&nbsp;</P>
<P>void&nbsp;*&nbsp;(&nbsp;*c)&nbsp;(&nbsp;char,&nbsp;int&nbsp;(*)());&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;c&nbsp;is&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;function&nbsp;that&nbsp;takes&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;two&nbsp;parameters:&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;a&nbsp;char&nbsp;and&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&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;function&nbsp;that&nbsp;takes&nbsp;no&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;parameters&nbsp;and&nbsp;returns&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;an&nbsp;int&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;and&nbsp;returns&nbsp;a&nbsp;pointer&nbsp;to&nbsp;void.&nbsp;</P>
<P>void&nbsp;**&nbsp;(*d)&nbsp;(int&nbsp;&amp;,&nbsp;<BR>char&nbsp;**(*)(char&nbsp;*,&nbsp;char&nbsp;**));&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;d&nbsp;is&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;function&nbsp;that&nbsp;takes&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;two&nbsp;parameters:&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;a&nbsp;reference&nbsp;to&nbsp;an&nbsp;int&nbsp;and&nbsp;a&nbsp;pointer&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;to&nbsp;a&nbsp;function&nbsp;that&nbsp;takes&nbsp;two&nbsp;parameters:&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;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&nbsp;and&nbsp;a&nbsp;pointer&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;to&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;char&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;and&nbsp;returns&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;pointer&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;to&nbsp;a&nbsp;char&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;and&nbsp;returns&nbsp;a&nbsp;pointer&nbsp;to&nbsp;a&nbsp;pointer&nbsp;to&nbsp;void&nbsp;</P>
<P>float&nbsp;(&nbsp;*&nbsp;(&nbsp;*&nbsp;e[10])&nbsp;<BR>&nbsp;&nbsp;(int&nbsp;&amp;)&nbsp;)&nbsp;[5];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;e&nbsp;is&nbsp;an&nbsp;array&nbsp;of&nbsp;10&nbsp;pointers&nbsp;to&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;functions&nbsp;that&nbsp;take&nbsp;a&nbsp;single&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;reference&nbsp;to&nbsp;an&nbsp;int&nbsp;as&nbsp;an&nbsp;argument&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;and&nbsp;return&nbsp;pointers&nbsp;to&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;an&nbsp;array&nbsp;of&nbsp;5&nbsp;floats.&nbsp;&nbsp;<BR><BR><BR>from: <A href="http://www.ieee.org.cn/dispbbs.asp?boardID=61&amp;ID=21651">http://www.ieee.org.cn/dispbbs.asp?boardID=61&amp;ID=21651</A></P></DIV><img src ="http://www.blogjava.net/weidagang2046/aggbug/23697.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-12-13 17:00 <a href="http://www.blogjava.net/weidagang2046/articles/23697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Member Function Pointers and the Fastest Possible C++ Delegates</title><link>http://www.blogjava.net/weidagang2046/articles/17893.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Wed, 02 Nov 2005 20:32:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/17893.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/17893.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/17893.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/17893.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/17893.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: IntroductionStandard C++ does not have true object-oriented function pointers. This is unfortunate, because object-oriented function pointers, also called 'closures' or 'delegates', have proved thei...&nbsp;&nbsp;<a href='http://www.blogjava.net/weidagang2046/articles/17893.html'>阅读全文</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/17893.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-11-03 04:32 <a href="http://www.blogjava.net/weidagang2046/articles/17893.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Pointers to C++ Member Functions</title><link>http://www.blogjava.net/weidagang2046/articles/17892.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Wed, 02 Nov 2005 19:47:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/17892.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/17892.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/17892.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/17892.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/17892.html</trackback:ping><description><![CDATA[<H2><A name=abstract>Abstract</A></H2>
<P><I>Pointers to Member Functions</I> are one of C++'s more rarely used features, and are often not well understood even by experienced developers. This is understandable, as their syntax is necessarily rather clumsy and obscure.</P>
<P>While they do not have wide applicability, sometimes member function pointers are useful to solve certain problems, and when they do apply they are often the perfect choice, both for improved performance and to make the code sensible. They work very well to cache the result of a frequently made decision, and to implement a different sort of polymorphism.</P>
<P>I discuss what member function pointers are, how to declare and use them, and give some examples of problems that they solve very well.</P>
<H2><A name=contents>Contents</A></H2>
<UL>
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#abstract">Abstract</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#introduction">Introduction</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#not-addresses">Member Function Pointers Are Not Just Simple Addresses</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#decision-cache">Caching the Outcome of a Decision</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#performance">The Performance of Member Function Pointers</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#details">Details About Using Member Function Pointers</A> 
<LI><A href="http://linuxquality.dotsrc.org/articles/memberpointers/#polymorphism">A Different Sort of Polymorphism</A></LI></UL>
<H2><A name=introduction>Introduction</A></H2>
<P>I don't have any hard numbers on how frequently member function pointers are used. While Ido see others mention them sometimes in Usenet and mailing list posts, I have yet to find someoneelse use one in code I have worked with, so my impression is that they are not commonly applied.</P>
<P>Member function pointers are important because they provide an efficient way to cache the outcome of a decision over which member function to call. They can save time, and in some cases, provide a design alternative that avoids the need to implement such decision caching through memory allocation. I will return to this further on.</P>
<P>Member function pointers allow one to call one of several of an object's member functions indirectly. Each of the functions whose "address" is stored must share the same signature. </P>
<P>I put "address" in quotes because the information stored in a member function pointer is not simply the memory address of the start of the member function's code; conceptually it is an offset into the list of functions declared by the class, and in the case of virtual functions will include a real offset into the <CODE>vtbl</CODE>, or table of virtual function pointers.</P>
<P>Member function pointers cannot be dereferenced (have their function called) directly by themselves. They must be called on behalf of some object, that then provides the "this" pointer for use by the member functions.</P>
<P>To illustrate how to declare and call a member function pointer, I will start by giving an example ofdeclaring and dereferencing an ordinary pointer to a non-member function. You declare a functionpointer by giving the prototype of a function it can point to, with the name of the function replacedby <CODE>(*pointerName)</CODE>. Regular function pointers share the same syntax between C and C++:</P>
<BLOCKQUOTE><PRE>void Foo( int anInt, double aDouble );void Bar(){       <STRONG> void (*funcPtr)( int, double ) = &amp;Foo;</STRONG>        <STRONG>(*funcPtr)( 1, 2.0 );</STRONG>}</PRE></BLOCKQUOTE>
<P>For regular function pointers, it is optional to use the address-of operator <CODE>&amp;</CODE> when taking the address of a function, but it is required for taking the address of member functions. g++ will compile source that leaves it out, but emits a warning.</P>
<P>To declare a pointer to member function, you give the prototype of a function it can point to, as before, but the name of this function is replaced by a construction that scopes the pointer - you give it the name of the class whose member functions it can point to, as <CODE>(ClassName::*pointerName)</CODE>. Note that a given member function pointer can only point to functions that are members of the class it was declared with. It cannot be applied to an object of a different class even if it has member functions with the same signature.</P>
<P>You dereference a member function pointer by using <CODE>.*</CODE> or <CODE>-&gt;*</CODE>, supplying a reference or pointer to an object on the left, as appropriate, and the function pointer on the right.</P>
<P>Here is a simple example:</P>
<BLOCKQUOTE><PRE>class Foo{        public:                double One( long inVal );                double Two( long inVal );};void main( int argc, char **argv ){	<STRONG>double (Foo::*funcPtr)( long ) = &amp;Foo::One;</STRONG> 	Foo aFoo; 	<STRONG>double result =(aFoo.*funcPtr)( 2 ); </STRONG> 	return 0;}</PRE></BLOCKQUOTE>
<P>Declaring a member function pointer is clumsy at best and is hard to get right until you have used them for a while. Rather than declaring them using the full prototype each time, it is helpful to use a <CODE>typedef</CODE> as I show in the example below.</P>
<H2><A name=not-addresses>Member Function Pointers Are Not Just Simple Addresses</A></H2>
<P>Most C and C++ programmers know that it is bad style to assume that a pointer is the same size as an int, although this may often be the case. What is less well known is that pointers of different types may not be the same size as each other. For example, in 16-bit x86 programming near pointers and far pointers may have different sizes, where the far pointers consist of the segment and offset together, while near pointers just have the offset. Member function pointers are generally small structures, that encode information about a function's virtualness, multiple inheritance and so on.</P>
<P>In the case of the example shown below, compiled with g++ 2.95.2 on a PowerPC G3 Mac OS X iBook, I found that the size of the member function pointer I created was eight bytes.</P>
<P>This can result in surprises to the user. For example, Microsoft Visual C++ 6 allows the programmer to make an optimization (which is apparently enabled by default) which can cause member function pointers that are intended to be the same type but are declared in different circumstances to have different sizes. Using the wrong setting for your project may result in an apparently gross code generation bug, because a member function pointer returned by a function that supplies them may have a different size than the recipient function expects, causing bogus data to be overwritten on the stack.</P>
<P>There is an item in VC++'s settings labeled "representation" that has a choice between "best case always" and "most general always". If you work with member function pointers in Visual C++, check the documentation for what these settings do and select the right one; if in doubt, select "most general always".</P>
<H2><A name=decision-cache>Caching the Outcome of a Decision</A></H2>
<P>One of the best uses for member function pointers is caching the outcome of a decision over which ofseveral member functions should be called in a particular circumstance. If a decision is always going to yield the same result, then it may be faster and even cleaner to make the decision just once ahead of time, then store the outcome in the form of a member function pointer. This is especially advantageous when the decision will be made repeatedly in a loop.</P>
<P>Here is an admittedly silly (but hopefully clear) example, that shows a member function pointer being used to store the outcome of a decision. It also illustrates the use of <CODE>typedef</CODE>:</P>
<BLOCKQUOTE><PRE>#include &lt;stdlib.h&gt;#include &lt;iostream&gt;class Test{	public:		Test( long inVal )			: mVal( inVal ){}		long TimesOne() const;		long TimesTwo() const;		long TimesThree() const;	private:		long mVal;};<STRONG>typedef long (Test::*Multiplier)() const;</STRONG>int main( int argc, char **argv ){	using std::cerr;	using std::endl;	using std::cout;	if ( argc != 3 ){		cerr  &lt;&lt; "Usage: PtrTest value factor"  &lt;&lt; endl;		return 1;	}	<STRONG>Multiplier funcPtr;</STRONG>	switch( atol( argv[ 2 ] ) ){		case 1:			<STRONG>funcPtr = &amp;Test::TimesOne;</STRONG>			break;		case 2:			<STRONG>funcPtr = &amp;Test::TimesTwo;</STRONG>			break;		case 3:			<STRONG>funcPtr = &amp;Test::TimesThree;</STRONG>			break;		default:			cerr &lt;&lt; "PtrTest: factor must range from 1 to 3"  &lt;&lt; endl;			return 1;	}	cout  &lt;&lt; "sizeof( funcPtr )="  &lt;&lt; sizeof( funcPtr )  &lt;&lt; endl;	Test myTest( atol( argv[ 1 ] ) );	cout &lt;&lt; "result="  &lt;&lt; <STRONG>(myTest.*funcPtr)()</STRONG>  &lt;&lt;endl;	return 0;}long Test::TimesOne() const{	return mVal;}long Test::TimesTwo() const{	return 2 * mVal;}long Test::TimesThree() const{	return 3 * mVal;}</PRE></BLOCKQUOTE>
<P>Now I present an example that does not perform as well as it could because performs a <CODE>switch</CODE> decision many times inside a loop, always reaching the same decision. It is a good candidate to refactor by using a pointer to member function. Again it is a silly example but I wanted to be very clear:</P>
<BLOCKQUOTE><PRE>#include &lt;exception&gt;class Test{	public:		Test( long inFactor )			: mFactor( inFactor ){}		long TimesOne( long inToMultiply ) const;		long TimesTwo( long inToMultiply ) const;		long TimesThree( long inToMultiply ) const;		long MultiplyIt( long inToMultiply ) const;	private:		long mFactor;};long Test::MultiplyIt( long inToMultiply ) const{	<STRONG>switch( mFactor ){	// decision made repeatedly that always yields the same result</STRONG>		case 1:			return TimesOne( inToMultiply );			break;		case 2:			return TimesTwo( inToMultiply );			break;		case 3:			return TimesThree( inToMultiply );			break;		default:			throw std::exception();	}}void MultiplyThem( long inFactor ){	Test myTest( 2 );		long product;	// Call a function that makes the same decision many times	for ( long i = 0; i &lt; 1000000; ++i )		product = myTest.MultiplyIt( i );}</PRE></BLOCKQUOTE>
<P>In most cases where an identical decision is made inside a loop, it is better to refactor the code so that thedecision is outside the loop, and the loop is repeated in each branch of the loop (or packaged inside a subroutine):</P>
<BLOCKQUOTE><PRE>void Foo( long value ){	for ( long i = 0; i &lt; 1000000; ++i ){		<STRONG>switch( value ){		// BAD CODE: always reaches the same decision</STRONG>				case 1:				//...				break;			case 2:				//...				break;			case 3:				//...				break;		}	}}</PRE></BLOCKQUOTE>
<P>Instead we place the switch outside the loop:</P>
<BLOCKQUOTE><PRE>void Foo( long value ){	<STRONG>switch( value ){		// BETTER CODE: decision made only once</STRONG>		case 1:			for ( long i = 0; i &lt; 1000000; ++i ){				//...			}			break;		case 2:			for ( long i = 0; i &lt; 1000000; ++i ){				//...			}			break;		//...	}}</PRE></BLOCKQUOTE>
<P>If you want to avoid repeating the loop implementations and each branch of the decision has similar code, you can place them inside subroutines.</P>
<P><I>Member function pointers are the best solution when it is not practical to refactor this way.</I> Onereason might be that the loop and the decision are in code that belongs to different classes, and you do not want to expose the implementation of the class that makes the decision. Here is the <CODE>MultiplyIt</CODE> code above, refactored to use a pointer to member function:</P>
<BLOCKQUOTE><PRE>#include &lt;exception&gt;class Test{	public:		Test( long inFactor );		long TimesOne( long inToMultiply ) const;		long TimesTwo( long inToMultiply ) const;		long TimesThree( long inToMultiply ) const;		long MultiplyIt( long inToMultiply ) const;	private:		<STRONG>typedef long (Test::*Multiplier)( long inToMultiply ) const;</STRONG>		long       mFactor;		<STRONG>Multiplier mMultFuncPtr;		static Multiplier GetFunctionPointer( long inFactor );</STRONG>};Test::Test( long inFactor )	: mFactor( inFactor ),	 <STRONG> mMultFuncPtr( GetFunctionPointer( mFactor ) )</STRONG>{	return;}<STRONG>Test::Multiplier Test::GetFunctionPointer( long inFactor )</STRONG>{	<STRONG>switch ( inFactor ){	// Decision only made once!</STRONG>		case 1:			<STRONG>return &amp;Test::TimesOne;</STRONG>			break;		case 2:			<STRONG> return &amp;Test::TimesTwo;</STRONG>			break;		case 3:			<STRONG> return &amp;Test::TimesThree;</STRONG>			break;			default:			throw std::exception();	}}			long Test::MultiplyIt( long inToMultiply ) const{	<STRONG>// Using cached decision result	return (this-&gt;*mMultFuncPtr)( inToMultiply );	</STRONG>}void MultiplyThem( long inFactor ){	Test myTest( 2 );		long product;	for ( long i = 0; i &lt; 1000000; ++i )		product = myTest.MultiplyIt( i );}</PRE></BLOCKQUOTE>
<H2><A name=performance>The Performance of Member Function Pointers</A></H2>
<P>Unfortunately, calling a member function by dereferencing a member function is more complicated than simply doing a subroutine jump off a register. The pointers are actually small structures and a little bit of work is required to find the actual address of the subroutine to jump to.</P>
<P>I'm afraid I do not have the g++ source code at hand or I could show you the implementation. I know that in tracing through calls via member function pointers in <A href="http://www.metrowerks.com/">Metrowerks</A> CodeWarrior for Windows, I found that a call would run a small piece of assembly code provided by CodeWarrior's library. This is pretty fast code, and will run very fast in a tight loop if it stays in the CPU's L1 cache, but it is not as fast as a simple compare and conditional branch.</P>
<P>If the decision your code is making repeatedly is very quick to run, it may not be to your advantage to use a member function pointer. A simple <CODE>if</CODE> statement that compares two numeric values, or checks the value of a <CODE>bool</CODE>, or possibly a <CODE>switch</CODE> statement whose alternatives are all contained in a small range (so it is easy for the compiler to build a jump table) may be quicker than dereferencing a member function pointer.</P>
<P>However, if the decision is complicated or lengthy to arrive at, like string comparison or searching some data structure, then using a pointer to member function may be a big win.</P>
<H2><A name=details>Details About Using Member Function Pointers</A></H2>
<P>You may understand the reasons for implementing pointers to member functions as structures if you see that they can be assigned to the addresses of routines with different kinds of implementations, as long as they have the same calling convention:</P>
<BLOCKQUOTE><PRE>class Different{	public:		inline void InlineMember();		virtual void VirtualMember();		void OrdinaryMember();		static void StaticMember();		typedef void (Different::*FuncPtr)();};void Test(){	Different::FuncPtr ptr = &amp;Different::InlineMember;	ptr = &amp;Different::VirtualMember;	ptr = &amp;Different::OrdinaryMember;}</PRE></BLOCKQUOTE>
<P>(You may be surprised to see me creating a pointer to an inline function, but this is perfectly normal. If you do this, the compiler will place a normal subroutine version of the inline's implementation in an object file and give you the address of that, so the function pointer does not really point to an inline function at all.)</P>
<P>However, although a static member function may appear to have the same calling convention, it really does not because it is not passed the <CODE>this</CODE> pointer - <CODE>this</CODE> is passed to your member functions just like any other parameter, but it is not given explicitly in the member function's prototype. You cannot use pointers to member functions to store the address of a static function (use an ordinary, non-member function pointer for that):</P>
<BLOCKQUOTE><PRE>void Fails(){        Different::FuncPtr ptr = &amp;Different::StaticMember;}mike% c++ different.cppdifferent.cpp: In function `void Fails()':different.cpp:24: initialization to `void (Different::*)()' from `void (*)()'</PRE></BLOCKQUOTE>
<P>Pointers to virtual member functions work just like calling a virtual member function directly - the type whose member function gets called is the dynamic type of the object it is called on behalf of, not the static type of the member function pointer:</P>
<BLOCKQUOTE><PRE>#include &lt;iostream&gt;class Base{	public:		virtual void WhoAmI() const;		<STRONG>typedef void (Base::*WhoPtr)() const;</STRONG>};class Derived: public Base{	public:		virtual void WhoAmI() const;};void Base::WhoAmI() const{	std::cout &lt;&lt; "I am the Base" &lt;&lt; std::endl;}void Derived::WhoAmI() const{	std::cout &lt;&lt; "I am the Derived" &lt;&lt; std::endl;}int main( int argc, char **argv ){	<STRONG>Base::WhoPtr func = &amp;Base::WhoAmI;</STRONG>	Base theBase;	<STRONG>(theBase.*func)();</STRONG>	Derived theDerived;	<STRONG>(theDerived.*func)();</STRONG>	return 0;}</PRE></BLOCKQUOTE>
<P>Running the above program yields the following output:</P>
<BLOCKQUOTE><PRE>mike% ./virtual<STRONG>I am the BaseI am the Derived</STRONG></PRE></BLOCKQUOTE>
<H2><A name=polymorphism>A Different Sort of Polymorphism</A></H2>
<P>Polymorphism in C++ is usually regarded as always implemented in the form of class heirarchies containing virtual member functions. </P>
<P>An object of a derived class can be supplied to create a pointer or reference to what is apparently the base class; a function pointer lookup in the <CODE>vtbl</CODE> is done when calling a virtual member function off a pointer or reference, so that the function called will be based on the dynamic type that the pointer or reference denotes - that is, it will be from the actual type of the object that was allocated, rather than the static type that the base class pointer or reference is declared as.</P>
<P>However, the concept of polymorphism can take a more general meaning than that, and I have seen mailing list postings advocating that it should also include the use of templates that allow source code with identical syntax to be applied to objects of unrelated types. This <CODE>std::vector</CODE> can be regarded as a polymorphic container that is parameterized by the type supplied as a parameter when a vector object is declared.</P>
<P>Pointers to member functions can be used to implement a different kind of polymorphism. In the regular type, we determine which member function ultimately gets called by allocating objects of different types, that are related members in an inheritance tree. This is implemented by having the <CODE>vptr</CODE> that is a hidden member of the object point at the appropriate <CODE>vtbl</CODE>.</P>
<P>In this other form you create objects that are always of the same type, but determine which member function gets called by choosing which member function's address gets assigned to a member function pointer. One interesting advantage is that you can change the behaviour of an object during its lifetime without having to allocate a new one of a different type as you would with the regular sort of inheritance-based polymorphism.<BR><BR>from: <A href="http://linuxquality.dotsrc.org/articles/memberpointers/">http://linuxquality.dotsrc.org/articles/memberpointers/</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/17892.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-11-03 03:47 <a href="http://www.blogjava.net/weidagang2046/articles/17892.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中异常处理的游戏规则</title><link>http://www.blogjava.net/weidagang2046/articles/17747.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 01 Nov 2005 17:13:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/17747.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/17747.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/17747.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/17747.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/17747.html</trackback:ping><description><![CDATA[如果您喜欢玩一款游戏，您必须先要很好理解这款游戏的规则。同样主人公阿愚喜欢上C++中异常处理后，当然也首先关注它的游戏规则，这就是C++中异常处理的语法。 
<P>关键字<BR><BR>　　1、 try<BR>　　2、 catch<BR>　　3、 throw<BR>　　其中关键字try表示定义一个受到监控、受到保护的程序代码块；关键字catch与try遥相呼应，定义当try block（受监控的程序块）出现异常时，错误处理的程序模块，并且每个catch block都带一个参数（类似于函数定义时的数那样），这个参数的数据类型用于异常对象的数据类型进行匹配；而throw则是检测到一个异常错误发生后向外抛出一个异常事件，通知对应的catch程序块执行对应的错误处理。</P>
<P><BR>语法<BR><BR>　　1、还是给一个例子吧！如下：<BR><BR>int main()<BR>{<BR>cout &lt;&lt; "In main." &lt;&lt; endl;</P>
<P>//定义一个try block，它是用一对花括号{}所括起来的块作用域的代码块<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个异常." &lt;&lt; endl;</P>
<P>//这里抛出一个异常（其中异常对象的数据类型是int，值为1）<BR>//由于在try block中的代码是受到监控保护的，所以抛出异常后，程序的<BR>//控制流便转到随后的catch block中<BR>throw 1;</P>
<P>cout &lt;&lt; "在 try block 中, 由于前面抛出了一个异常，因此这里的代码是不会得以执行到的" &lt;&lt; endl;<BR>}<BR>//这里必须相对应地，至少定义一个catch block，同样它也是用花括号括起来的<BR>catch( int&amp; value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, 处理异常错误。异常对象value的值为："&lt;&lt; value &lt;&lt; endl;<BR>}</P>
<P>cout &lt;&lt; "Back in main. Execution resumes here." &lt;&lt; endl;<BR>return 0;</P>
<P>}</P>
<P>　　2、语法很简单吧！的确如此。另外一个try block可以有多个对应的catch block，可为什么要多个catch block呢？这是因为每个catch block匹配一种类型的异常错误对象的处理，多个catch block呢就可以针对不同的异常错误类型分别处理。毕竟异常错误也是分级别的呀！有致命的、有一般的、有警告的，甚至还有的只是事件通知。例子如下：<BR><BR>int main()<BR>{<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个int数据类型的异常." &lt;&lt; endl;<BR>throw 1;</P>
<P>cout &lt;&lt; "在 try block 中, 准备抛出一个double数据类型的异常." &lt;&lt; endl;<BR>throw 0.5;<BR>}<BR>catch( int&amp; value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}<BR>catch( double&amp; d_value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, double数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>return 0;<BR>}</P>
<P>　　3、一个函数中可以有多个trycatch结构块，例子如下：<BR><BR>int main()<BR>{<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个int数据类型的异常." &lt;&lt; endl;<BR>throw 1;<BR>}<BR>catch( int&amp; value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>//这里是二个trycatch结构块，当然也可以有第三、第四个，甚至更多<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个double数据类型的异常." &lt;&lt; endl;<BR>throw 0.5;<BR>}<BR>catch( double&amp; d_value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, double数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>return 0;<BR>}</P>
<P>　　4、上面提到一个try block可以有多个对应的catch block，这样便于不同的异常错误分类处理，其实这只是异常错误分类处理的方法之一（暂且把它叫做横向展开的吧！）。另外还有一种就是纵向的，也即是分层的、trycatch块是可以嵌套的，当在低层的trycatch结构块中不能匹配到相同类型的catch block时，它就会到上层的trycatch块中去寻找匹配到正确的catch block异常处理模块。例程如下：<BR><BR>int main()<BR>{<BR>try<BR>{<BR>//这里是嵌套的trycatch结构块<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个int数据类型的异常." &lt;&lt; endl;<BR>throw 1;<BR>}<BR>catch( int&amp; value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>cout &lt;&lt; "在 try block 中, 准备抛出一个double数据类型的异常." &lt;&lt; endl;<BR>throw 0.5;<BR>}<BR>catch( double&amp; d_value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, double数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>return 0;<BR>}</P>
<P>　　5、讲到是trycatch块是可以嵌套分层的，并且通过异常对象的数据类型来进行匹配，以找到正确的catch block异常错误处理代码。这里就不得不详细叙述一下通过异常对象的数据类型来进行匹配找到正确的catch block的过程。<BR><BR>　　（1） 首先在抛出异常的trycatch块中查找catch block，按顺序先是与第一个catch block块匹配，如果抛出的异常对象的数据类型与catch block中传入的异常对象的临时变量（就是catch语句后面参数）的数据类型完全相同，或是它的子类型对象，则匹配成功，进入到catch block中执行；否则到二步；<BR><BR>　　 （2） 如果有二个或更多的catch block，则继续查找匹配第二个、第三个，乃至最后一个catch block，如匹配成功，则进入到对应的catch block中执行；否则到三步；<BR><BR>　　 （3） 返回到上一级的trycatch块中，按规则继续查找对应的catch block。如果找到，进入到对应的catch block中执行；否则到四步；<BR><BR>　　 （4） 再到上上级的trycatch块中，如此不断递归，直到匹配到顶级的trycatch块中的最后一个catch block，如果找到，进入到对应的catch block中执行；否则程序将会执行terminate()退出。<BR><BR>　　另外分层嵌套的trycatch块是可以跨越函数作用域的，例程如下：<BR><BR>void Func() throw()<BR>{<BR>//这里实际上也是嵌套在里层的trycatch结构块<BR>try<BR>{<BR>cout &lt;&lt; "在 try block 中, 准备抛出一个int数据类型的异常." &lt;&lt; endl;<BR>//由于这个trycatch块中不能找到匹配的catch block，所以<BR>//它会继续查找到调用这个函数的上层函数的trycatch块。<BR>throw 1;<BR>}<BR>catch( float&amp; value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}<BR>}</P>
<P>int main()<BR>{<BR>try<BR>{<BR>Func();</P>
<P>cout &lt;&lt; "在 try block 中, 准备抛出一个double数据类型的异常." &lt;&lt; endl;<BR>throw 0.5;<BR>}<BR>catch( double&amp; d_value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, double数据类型处理异常错误。”&lt;&lt; endl;<BR>}<BR>catch( int&amp; value )<BR>{<BR>//这个例子中，Func()函数中抛出的异常会在此被处理<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>return 0;<BR>}</P>
<P>　　6、刚才提到，嵌套的trycatch块是可以跨越函数作用域的，其实这里面还有另外一层涵义，就是抛出异常对象的函数中并不一定必须存在trycatch块，它可以是调用这个函数的上层函数中存在trycatch块，这样这个函数的代码也同样是受保护、受监控的代码；当然即便是上层调用函数不存在trycatch块，也只是不能找到处理这类异常对象错误处理的catch block而已，例程如下：<BR><BR>void Func() throw()<BR>{<BR>//这里实际上也是嵌套在里层的trycatch结构块<BR>//由于这个函数中是没有trycatch块的，所以它会查找到调用这个函数的上<BR>//层函数的trycatch块中。<BR>throw 1;<BR>}</P>
<P>int main()<BR>{<BR>try<BR>{<BR>//调用函数，注意这个函数里面抛出一个异常对象<BR>Func();</P>
<P>cout &lt;&lt; "在 try block 中, 准备抛出一个double数据类型的异常." &lt;&lt; endl;<BR>throw 0.5;<BR>}<BR>catch( double&amp; d_value )<BR>{<BR>cout &lt;&lt; "在 catch block 中, double数据类型处理异常错误。”&lt;&lt; endl;<BR>}<BR>catch( int&amp; value )<BR>{<BR>//这个例子中，Func()函数中抛出的异常会在此被处理<BR>cout &lt;&lt; "在 catch block 中, int数据类型处理异常错误。”&lt;&lt; endl;<BR>}</P>
<P>//如果这里调用这个函数，那么由于main()已经是调用栈的顶层函数，因此不能找<BR>//到对应的catch block，所以程序会执行terminate()退出。<BR>Func();<BR>// [特别提示]：在C++标准中规定，可以在程序任何地方throw一个异常对象，<BR>// 并不要求一定只能是在受到try block监控保护的作用域中才能抛出异常，但<BR>// 如果在程序中出现了抛出的找不到对应catch block的异常对象时，C++标<BR>// 准中规定要求系统必须执行terminate()来终止程序。<BR>// 因此这个例程是可以编译通过的，但运行时却会异常终止。这往往给软件<BR>// 系统带来了不安全性。与此形成对比的是java中提供的异常处理模型却是不<BR>// 永许出现这样的找不到对应catch block的异常对象，它在编译时就给出错误<BR>// 提示，所以java中提供的异常处理模型往往比C++要更完善，后面的章节<BR>// 会进一步对这两种异常处理模型进行一个详细的分析比较。<BR>return 0;<BR>}</P>
<P>　　朋友们！C++中的异常处理模型的语法很简单吧！就是那么（one、two、three、…哈哈！数数呢！）简单的几条规则。怪不得主人公阿愚这么快就喜欢上她了，而且还居然像一个思想家一样总结出一条感想：好的东西往往都是简单的，简单就是美吗！哈哈！还挺臭美的。<BR><BR>　　下一集主人公阿愚愿和大家一起讨论一下C++中的异常处理中的一种特殊的catch用法，那就是关于catch(…)大探秘。<BR><BR>from: <A href="http://51cmm.csai.cn/ExpertEyes/No137.htm">http://51cmm.csai.cn/ExpertEyes/No137.htm</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/17747.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-11-02 01:13 <a href="http://www.blogjava.net/weidagang2046/articles/17747.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中几个比较不常用的关键字</title><link>http://www.blogjava.net/weidagang2046/articles/17744.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 01 Nov 2005 17:00:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/17744.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/17744.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/17744.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/17744.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/17744.html</trackback:ping><description><![CDATA[<P>mutable关键字</P>
<P>&nbsp;&nbsp;&nbsp; 关键字mutable是C++中一个不常用的关键字,他只能用于类的非静态和非常量数据成员。我们知道一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,对像的状态也会随之发生变化! </P>
<P>　　如果一个类的成员函数被声明为const类型,表示该函数不会改变对象的状态,也就是该函数不会修改类的非静态数据成员.但是有些时候需要在该类函数中对类的数据成员进行赋值.这个时候就需要用到mutable关键字了</P>
<P>　　例如:</P>
<P>&nbsp;　　class Demo<BR>　　{<BR>　　public:<BR>　　Demo(){}<BR>　　~Demo(){}<BR>　　public:<BR>　　bool getFlag() const<BR>　　{<BR>　　m_nAccess++;<BR>　　return m_bFlag;<BR>　　}<BR>　　private:<BR>　　int&nbsp; m_nAccess;<BR>　　bool m_bFlag;<BR>　　}; <BR>　　int main()<BR>　　{<BR>　　return 0;<BR>　　}<BR>&nbsp;</P>
<P><BR>　　编译上面的代码会出现 error C2166: l-value specifies const object的错误<BR>　　说明在const类型的函数中改变了类的非静态数据成员.</P>
<P>　　这个时候需要使用mutable来修饰一下要在const成员函数中改变的非静态数据成员<BR>　　m_nAccess,代码如下:</P>
<P><BR>&nbsp;　　class Demo<BR>　　{<BR>　　public:<BR>　　Demo(){}<BR>　　~Demo(){}<BR>　　public:<BR>　　bool getFlag() const<BR>　　{<BR>　　m_nAccess++;<BR>　　return m_bFlag;<BR>　　}<BR>　　private:<BR>　　mutable int&nbsp; m_nAccess;<BR>　　bool m_bFlag;<BR>　　}; <BR>　　int main()<BR>　　{<BR>　　return 0;<BR>　　}<BR>&nbsp;</P>
<P><BR>　　这样再重新编译的时候就不会出现错误了!</P>
<P>volatile关键字 </P>
<P>　　volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型</P>
<P>　　如：int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候，结构或者类的所有成员都会被视为volatile.</P>
<P>　　使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要</P>
<P>　　例如：</P>
<P>　　int i;<BR>　　i = i + 3;</P>
<P>　　无论如何，总是会有一小段时间，i会被放在一个寄存器中，因为算术运算只能在寄存器中进行。一般来说，volatitle关键字适用于行与行之间，而不是放在行内。</P>
<P>　　我们先来实现一个简单的函数，来观察一下由编译器产生出来的汇编代码中的不足之处，并观察volatile关键字如何修正这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法)</P>
<P><BR>&nbsp;　　void getKey(char* pch)<BR>　　{<BR>　　while (*pch == 0)<BR>　　;<BR>　　} </P>
<P><BR>　　当你在VC开发环境中将最优化选项都关闭之后，编译这个程序，将获得以下结果(汇编代码);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </P>
<P>&nbsp;&nbsp;&nbsp; while (*pch == 0)<BR>　　$L27<BR>　　; Load the address stored in pch<BR>　　mov eax, DWORD PTR _pch$[ebp]<BR>　　; Load the character into the EAX register<BR>　　movsx eax, BYTE PTR [eax]<BR>　　; Compare the value to zero<BR>　　test eax, eax<BR>　　; If not zero, exit loop<BR>　　jne $L28<BR>　　;<BR>　　jmp $L27<BR>　　$L28<BR>　　;} </P>
<P><BR>　　这段没有优化的代码不断的载入适当的地址，载入地址中的内容，测试结果。效率相当的低，但是结果非常准确</P>
<P>　　现在我们再来看看将编译器的所有最优化选项开关都打开以后，重新编译程序，生成的汇编代码，和上面的代码</P>
<P>　　比较一下有什么不同</P>
<P><BR>&nbsp;　　;{ <BR>　　; Load the address stored in pch<BR>　　mov eax, DWORD PTR _pch$[esp-4]<BR>　　; Load the character into the AL register<BR>　　movsx al, BYTE PTR [eax]<BR>　　; while (*pch == 0)<BR>　　; Compare the value in the AL register to zero<BR>　　test al, al<BR>　　; If still zero, try again<BR>　　je SHORT $L84<BR>　　;<BR>　　;} </P>
<P><BR>&nbsp;&nbsp;&nbsp; 从代码的长度就可以看出来，比没有优化的情况要短的多。需要注意的是编译器把MOV指令放到了循环之外。这在单线程中是一个非常好的优化，但是，在多线程应用程序中，如果另一个线程改变了变量的值，则循环永远不会结束。被测试的值永远被放在寄存器中，所以该段代码在多线程的情况下，存在一个巨大的BUG。解决方法是重新</P>
<P>　　写一次getKey函数，并把参数pch声明为volatile,代码如下： </P>
<P>&nbsp;　　void getKey(volatile char* pch)<BR>　　{<BR>　　while (*pch == 0)<BR>　　;<BR>　　} </P>
<P><BR>　　这次的修改对于非最优化的版本没有任何影响，下面请看最优化后的结果：</P>
<P><BR>&nbsp;　　;{<BR>　　; Load the address stored in pch<BR>　　mov eax, DWORD PTR _pch$[esp-4]<BR>　　;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (*pch == 0)<BR>　　$L84:<BR>　　; Directly compare the value to zero<BR>　　cmp BYTE PTR [eax], 0<BR>　　; If still zero, try again<BR>　　je SHORT $L84<BR>　　;<BR>　　;} </P>
<P><BR>　　这次的修改结果比较完美，地址不会改变，所以地址声明被移动到循环之外。地址内容是volatile,所以每次循环之中它不断的被重新检查。</P>
<P>　　把一个const volatile变量作为参数传递给函数是合法的。如此的声明意味着函数不能改变变量的值，但是变量的值却可以被另一个线程在任何时间改变掉。</P>
<P>　　explicit关键字</P>
<P>　　我们在编写应用程序的时候explicit关键字基本上是很少使用,它的作用是"禁止单参数构造函数"被用于自动型别转换,其中比较典型的例子就是容器类型,在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数.</P>
<P>　　例如:<BR>　　你可以声明这样一个构造函数</P>
<P><BR>&nbsp;　　class Array<BR>　　{<BR>　　public:<BR>　　explicit Array(int size);<BR>　　......<BR>　　}; </P>
<P>　　在这里explicit关键字起着至关重要的作用,如果没有这个关键字的话,这个构造函数有能力将int转换成Array.一旦这种情况发生,你可以给Array支派一个整数值而不会引起任何的问题,比如:</P>
<P><BR>&nbsp;　　Array arr;<BR>　　...<BR>　　arr = 40; </P>
<P>　　此时,C++的自动型别转换会把40转换成拥有40个元素的Array,并且指派给arr变量,这个结果根本就不是我们想要的结果.如果我们将构造函数声明为explicit,上面的赋值操作就会导致编译器报错,使我们可以及时发现错误.需要注意的是:explicit同样也能阻止"以赋值语法进行带有转型操作的初始化";</P>
<P>例如:<BR>　　Array arr(40);//正确<BR>　　Array arr = 40;//错误 </P>
<P><BR>　　看一下以下两种操作:<BR>　　X x;<BR>　　Y y(x);//显式类型转换<BR>　　另一种<BR>　　X x;<BR>　　Y y = x;//隐式类型转换</P>
<P>　　这两种操作存在一个小小的差别,第一种方式式通过显式类型转换,根据型别x产生了型别Y的新对象;第二种方式通过隐式转换产生了一个型别Y的新对象.<BR>　　explicit关键字的应用主要就是上面所说的构造函数定义种,参考该关键字的应用可以看看STL源代码,其中大量使用了该关键字</P>
<P>　　__based关键字</P>
<P>　　该关键字主要用来解决一些和共享内存有关的问题,它允许指针被定义为从某一点开始算的32位偏移值,而不是内存种的绝对位置</P>
<P>　　举个例子:</P>
<P><BR>&nbsp;　　typedef struct tagDEMOSTRUCT {<BR>　　int a;<BR>　　char sz[10];<BR>　　} DEMOSTRUCT, * PDEMOSTRUCT; <BR>　　HANDLE hFileMapping = CreateFileMapping(...);<BR>　　LPVOID lpShare = (LPDWORD)MapViewOfFile(...);</P>
<P>　　DEMOSTRUCT __based(lpShare)* lpDemo;<BR>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面的例子声明了一个指针lpDemo,内部储存的是从lpShare开始的偏移值,也就是lpHead是以lpShare为基准的偏移值.上面的例子种的DEMOSTRUCT只是随便定义的一个结构,用来代表任意的结构.</P>
<P>　　虽然__based指针使用起来非常容易,但是,你必须在效率上付出一定的代价.每当你用__based指针处理数据,CPU都必须为它加上基地址,才能指向真正的位置.</P>
<P>　　在这里我只是介绍了几个并不时很常见的关键字的意义即用法,其他那些常见的关键字介绍他们的文章已经不少了在这里就不再一一介绍了.希望这些内容能对大家有一定的帮助。<BR><BR><BR>from: <A href="http://www.softhouse.com.cn/html/200507/2005070811001700009110.html">http://www.softhouse.com.cn/html/200507/2005070811001700009110.html</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/17744.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-11-02 01:00 <a href="http://www.blogjava.net/weidagang2046/articles/17744.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ constructor trivia</title><link>http://www.blogjava.net/weidagang2046/articles/16504.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 23 Oct 2005 14:55:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/16504.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/16504.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/16504.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/16504.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/16504.html</trackback:ping><description><![CDATA[<P>We had a head-scratcher bug the other day. Our system is written in <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN>, and has a custom reference counting system for managing memory (see <A class=offsite href="http://www.nedbatchelder.com/blog/200311.html#e20031118T071446">Greenspun's Law</A>). We were seeing an <A class=offsite href="http://www.nedbatchelder.com/text/assert.html">assertion</A> that we had thought was long debugged and solid. Here's what we found. (Warning: lots of arcane <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> stuff ahead.) </P>
<P>Our reference counting implementation looks kind of like this: </P>
<BLOCKQUOTE class=code><TT><FONT face="Lucida Console"><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;1</FONT></SPAN><SPAN class=c_commentdoc>/**</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;2</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;*&nbsp;Reference-counted&nbsp;object.&nbsp;&nbsp;When&nbsp;its&nbsp;reference&nbsp;count&nbsp;goes&nbsp;to</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;3</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;*&nbsp;zero,&nbsp;it&nbsp;destroys&nbsp;itself.</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;4</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;*/</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;5</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>class</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;6</FONT></SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;7</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>public</FONT></STRONG></SPAN><SPAN class=c_operator>:</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;8</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>&nbsp;9</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>10</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_number>0</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>11</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>12</SPAN><BR><SPAN class=linenumber>13</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>virtual</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>~</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>14</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>15</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;CRefCountedObject's&nbsp;can&nbsp;be&nbsp;created&nbsp;on&nbsp;the&nbsp;stack,</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>16</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;where&nbsp;they&nbsp;will&nbsp;be&nbsp;destroyed&nbsp;by&nbsp;falling&nbsp;out&nbsp;of&nbsp;scope.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>17</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;This&nbsp;assert&nbsp;checks&nbsp;the&nbsp;implicit&nbsp;assumption&nbsp;that&nbsp;no&nbsp;one</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>18</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;is&nbsp;still&nbsp;interested&nbsp;in&nbsp;the&nbsp;object&nbsp;when&nbsp;it&nbsp;is&nbsp;destroyed.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>19</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>ASSERT</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>==</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_number>0</SPAN><SPAN class=c_operator>);</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>20</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>21</SPAN><BR><SPAN class=linenumber>22</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_commentdoc>/**&nbsp;Declare&nbsp;your&nbsp;interest&nbsp;in&nbsp;this&nbsp;object:&nbsp;ups&nbsp;the&nbsp;refcount.</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>23</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>24</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>void</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>Retain</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>25</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>26</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_operator>++;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>27</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>28</SPAN><BR><SPAN class=linenumber>29</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_commentdoc>/**&nbsp;Declare&nbsp;your&nbsp;lack&nbsp;of&nbsp;interest&nbsp;in&nbsp;this&nbsp;object:&nbsp;if&nbsp;you&nbsp;were</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>30</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;the&nbsp;last&nbsp;interested&nbsp;party,&nbsp;the&nbsp;object&nbsp;is&nbsp;destroyed.</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>31</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>32</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>void</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>Release</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>33</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>34</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_operator>--;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>35</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>if</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_operator>==</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_number>0</SPAN><SPAN class=c_operator>)</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>36</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>delete</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>this</FONT></STRONG></SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>37</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>38</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>39</SPAN><BR><SPAN class=linenumber>40</SPAN></FONT></FONT></FONT><SPAN class=c_word><STRONG><FONT color=#000088>private</FONT></STRONG></SPAN><SPAN class=c_operator>:</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>41</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>int</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>42</FONT></SPAN><SPAN class=c_operator>};</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>43</SPAN><BR><SPAN class=linenumber>44</SPAN></FONT></FONT></FONT><SPAN class=c_commentdoc>/**</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>45</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;*&nbsp;Smart&nbsp;pointer,&nbsp;templated&nbsp;to&nbsp;a&nbsp;particular&nbsp;object&nbsp;class.</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>46</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;*/</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>47</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>template</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>&lt;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>class</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>TObject</SPAN><SPAN class=c_operator>&gt;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>48</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>class</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>CPtr</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>49</FONT></SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>50</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>public</FONT></STRONG></SPAN><SPAN class=c_operator>:</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>51</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT color=#008800><FONT face="Lucida Console"><SPAN class=c_commentline>//&nbsp;..&nbsp;lots&nbsp;of&nbsp;stuff&nbsp;omitted&nbsp;..</SPAN><BR></FONT></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>52</SPAN><BR><SPAN class=linenumber>53</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_commentdoc>/**&nbsp;Assignment&nbsp;operator.&nbsp;&nbsp;Manage&nbsp;the&nbsp;refcounts&nbsp;on&nbsp;the&nbsp;old&nbsp;and</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>54</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;new&nbsp;objects.</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>55</FONT></SPAN><SPAN class=c_commentdoc>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>56</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>TObject</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>*</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>operator</FONT></STRONG></SPAN><SPAN class=c_operator>=(</SPAN><SPAN class=c_identifier>TObject</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>*</SPAN><SPAN class=c_identifier>p</SPAN><SPAN class=c_operator>)</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>57</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>58</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>if</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>m_p</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>!=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>p</SPAN><SPAN class=c_operator>)</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>59</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>if</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>p</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>!=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>NULL</SPAN><SPAN class=c_operator>)</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>60</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>p</SPAN><SPAN class=c_operator>-&gt;</SPAN><SPAN class=c_identifier>Retain</SPAN><SPAN class=c_operator>();</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>61</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>62</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>if</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>m_p</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>!=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>NULL</SPAN><SPAN class=c_operator>)</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>63</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>m_p</SPAN><SPAN class=c_operator>-&gt;</SPAN><SPAN class=c_identifier>Release</SPAN><SPAN class=c_operator>();</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>64</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>65</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>m_p</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>p</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>66</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>67</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>return</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>m_p</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>68</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>69</SPAN><BR><SPAN class=linenumber>70</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//..&nbsp;lots&nbsp;of&nbsp;stuff&nbsp;omitted&nbsp;..</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>71</FONT></SPAN><SPAN class=c_operator>};</SPAN><BR></FONT></TT></BLOCKQUOTE>
<P>(Note: this is a simplified version of our actual code, and some of the simplifications mean that it will not work properly, but not in ways that affect the narrative here. For example, don't try this code with multiple threads, or with multiple inheritance. I'm taking some expository license. Don't bug me about it!)</P>
<P>The assert that fired is on line 19: it's designed to protect against allocating reference counted objects on the stack. The problem with a stack-allocated refcounted object is that the object will be destroyed when it falls out of scope, regardless of the reference count. For our reference counting to work right, the only thing that should destroy an object is the delete on line 36. With a stack allocated object, the compiler destroys the object for us, so smart pointers can exist which point to freed memory: </P>
<BLOCKQUOTE class=code><TT><FONT face="Lucida Console"><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>100</FONT></SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>101</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>CPtr</SPAN><SPAN class=c_operator>&lt;</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><SPAN class=c_operator>&gt;</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>pObj</SPAN><SPAN class=c_operator>;</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>102</SPAN><BR><SPAN class=linenumber>103</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT color=#008800><FONT face="Lucida Console"><SPAN class=c_commentline>//..&nbsp;blah&nbsp;blah&nbsp;..</SPAN><BR></FONT></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>104</SPAN><BR><SPAN class=linenumber>105</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>if</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>blah</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>blah</SPAN><SPAN class=c_operator>)</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>106</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>stackobj</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>107</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>pObj</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>stackobj</SPAN><SPAN class=c_operator>;</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>108</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>109</SPAN><BR><SPAN class=linenumber>110</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;pObj&nbsp;is&nbsp;invalid&nbsp;at&nbsp;this&nbsp;point.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>111</FONT></SPAN><SPAN class=c_operator>}</SPAN><BR></FONT></TT></BLOCKQUOTE>
<P>The object stackobj is created at line 106, and then destroyed at line 108 (when it falls out of scope), but pObj still has a pointer to it. When stackobj is destroyed, its reference count is 1, so the assert in the CRefCountedObject destructor will fire. All is well. </P>
<P>So when that assertion fired the other day, we thought we understood the problem. Just look up the stack, find the stack allocated refcounted object, and change it to a heap allocated object. But when we looked into it, there was no stack allocated object. So who was destroying the refcounted object before its time? How does a heap allocated object get destroyed other than using delete on it? </P>
<P>Digging deeper, we rediscovered a <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> detail that we had forgotten. Turns out we had some code that boiled down to this: </P>
<BLOCKQUOTE class=code><TT><FONT face="Lucida Console"><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>200</FONT></SPAN><SPAN class=c_word><STRONG><FONT color=#000088>class</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>CBadObject</SPAN><SPAN class=c_operator>:</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>public</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>201</FONT></SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>202</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//..&nbsp;blah&nbsp;blah&nbsp;..</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>203</FONT></SPAN><SPAN class=c_operator>};</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>204</SPAN><BR><SPAN class=linenumber>205</SPAN></FONT></FONT></FONT><SPAN class=c_identifier>CPtr</SPAN><SPAN class=c_operator>&lt;</SPAN><SPAN class=c_identifier>CBadObject</SPAN><SPAN class=c_operator>&gt;</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_identifier>pGlobalBad</SPAN><SPAN class=c_operator>;</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>206</SPAN><BR><SPAN class=linenumber>207</SPAN></FONT></FONT></FONT><SPAN class=c_identifier>CBadObject</SPAN><SPAN class=c_operator>::</SPAN><SPAN class=c_identifier>CBadObject</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>208</FONT></SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>209</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>pGlobalBad</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>=</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>this</FONT></STRONG></SPAN><SPAN class=c_operator>;</SPAN><BR></FONT><FONT face="Lucida Console"><FONT size=2><FONT color=#aaaaaa><FONT style="BACKGROUND-COLOR: #f8f8f8"><SPAN class=linenumber>210</SPAN><BR><SPAN class=linenumber>211</SPAN></FONT></FONT></FONT><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>DoSomethingWhichThrowsAnException</SPAN><SPAN class=c_operator>();</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>212</FONT></SPAN><SPAN class=c_operator>}</SPAN><BR></FONT></TT></BLOCKQUOTE>
<P>This object's <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> assigned itself to a smart pointer outside itself, then threw an exception. <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> semantics state that if a <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> of a heap allocated object throws an exception, that the object's destructor is called. Since CBadObject derives from CRefCountedObject, the CRefCountedObject destructor is called. It checks the reference count, sees that it is not zero (it's one, because pGlobalBad has called Retain, but hasn't called Release), and fires the assertion. </P>
<P><I>Updated May 2005:</I> Actually, the correct statement of <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> semantics is that if a <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> throws an exception, a destructor is called for all the base classes that had been successfully constructed. In this case, the CBadObject destructor is not called (because the CBadObject <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> didn't finish), but the CRefCountedObject destructor <EM>is</EM> called, because its <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> completed. It's the CRefCountedObject destructor which causes the trouble here. More about this at <A class=offsite href="http://www.nedbatchelder.com/blog/20050512T191222.html">More <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN> trivia</A>.</P>
<P>So the lesson is: try not to throw exceptions from <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN>s, or at least understand that they will destroy the object for you.</P>
<P><I>Updated May 2005:</I> Again, this is not correct. The real lesson is that throwing exceptions from <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN>s is perfectly legal, and <SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN> is precisely engineered to specify exactly what will happen. But you may be surprised by what happens. In particular, you may have objects in "impossible" states.</P>
<P>It just goes to show you:</P>
<BLOCKQUOTE class=box>You learn something new every day, no matter how hard you try.</BLOCKQUOTE>
<P>By the way: there were two things we did to fix the problem. Do you know what they were? The first was to move most of the code out of the <SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN>, so that hard stuff happens in regular methods. The second was to add to the comment in CRefCountedObject's destructor:</P>
<BLOCKQUOTE class=code><TT><FONT face="Lucida Console"><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>13</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_word><STRONG><FONT color=#000088>virtual</FONT></STRONG></SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>~</SPAN><SPAN class=c_identifier>CRefCountedObject</SPAN><SPAN class=c_operator>()</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>14</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>{</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>15</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;CRefCountedObject's&nbsp;can&nbsp;be&nbsp;created&nbsp;on&nbsp;the&nbsp;stack,</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>16</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;where&nbsp;they&nbsp;will&nbsp;be&nbsp;destroyed&nbsp;by&nbsp;falling&nbsp;out&nbsp;of&nbsp;scope.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>17</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;This&nbsp;assert&nbsp;checks&nbsp;the&nbsp;implicit&nbsp;assumption&nbsp;that&nbsp;no&nbsp;one</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>18</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;is&nbsp;still&nbsp;interested&nbsp;in&nbsp;the&nbsp;object&nbsp;when&nbsp;it&nbsp;is&nbsp;destroyed.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>19</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;The&nbsp;other&nbsp;way&nbsp;this&nbsp;can&nbsp;happen&nbsp;is&nbsp;if&nbsp;a&nbsp;<SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN></SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>20</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;fails,&nbsp;but&nbsp;it&nbsp;has&nbsp;already&nbsp;assigned&nbsp;itself&nbsp;to&nbsp;a&nbsp;CPtr</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>21</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;someplace.&nbsp;&nbsp;When&nbsp;a&nbsp;<SPAN class=searchword0><FONT style="BACKGROUND-COLOR: #cfffb9">C++</FONT></SPAN>&nbsp;<SPAN class=searchword1><FONT style="BACKGROUND-COLOR: #ffcfcf">constructor</FONT></SPAN>&nbsp;fails,&nbsp;the&nbsp;compiler</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>22</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN></FONT><FONT face="Lucida Console"><FONT color=#008800><SPAN class=c_commentline>//&nbsp;automatically&nbsp;destroys&nbsp;the&nbsp;object.</SPAN><BR></FONT><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>23</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_identifier>ASSERT</SPAN><SPAN class=c_operator>(</SPAN><SPAN class=c_identifier>m_nRefCount</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_operator>==</SPAN><SPAN class=c_default>&nbsp;</SPAN><SPAN class=c_number>0</SPAN><SPAN class=c_operator>);</SPAN><BR><SPAN class=linenumber><FONT style="BACKGROUND-COLOR: #f8f8f8" color=#aaaaaa size=2>24</FONT></SPAN><SPAN class=c_default>&nbsp;&nbsp;&nbsp;&nbsp;</SPAN><SPAN class=c_operator>}</SPAN><BR></FONT></TT></BLOCKQUOTE>
<P>That way, future generations might be spared the head-scratching.<BR><BR>from: <A href="http://www.nedbatchelder.com/blog/20041202T070735.html">http://www.nedbatchelder.com/blog/20041202T070735.html</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/16504.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-23 22:55 <a href="http://www.blogjava.net/weidagang2046/articles/16504.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++箴言:绝不在构造或析构期调用虚函数</title><link>http://www.blogjava.net/weidagang2046/articles/16499.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 23 Oct 2005 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/16499.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/16499.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/16499.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/16499.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/16499.html</trackback:ping><description><![CDATA[你不应该在构造或析构期间<A class=bluekey href="http://www.yesky.com/key/1698/86698.html" target=_blank>调用</A>虚函数，因为这样的调用不会如你想象那样工作，而且它们做的事情保证会让你很郁闷。如果你转为 Java 或 C# <A class=bluekey href="http://www.yesky.com/key/1799/31799.html" target=_blank>程序员</A>，也请你密切关注本文，因为在 C++ 急转弯的地方，那些语言也紧急转了一个弯。 <BR><BR>　　假设你有一套模拟股票处理的类层次结构，例如，购入流程，出售流程等。对这样的处理来说可以核查是非常重要的，所以随时会创建一个 Transaction 对象，将这个创建记录在核查日志中是一个适当的要求。下面是一个看起来似乎合理的解决问题的方法：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>class Transaction { // base class for all<BR>　public: // transactions<BR>　　Transaction();<BR><BR>　　virtual void logTransaction() const = 0; // make type-dependent<BR>　　// log entry<BR>　　...<BR>};<BR><BR>Transaction::Transaction() // implementation of<BR>{ <BR>　// base class ctor<BR>　...<BR>　logTransaction(); // as final action, log this<BR>} // transaction<BR><BR>class BuyTransaction: public Transaction { <BR>　// derived class<BR>　public:<BR>　　virtual void logTransaction() const; // how to log trans-<BR>　　// actions of this type<BR>　　...<BR>};<BR><BR>class SellTransaction: public Transaction { <BR>// derived class<BR>public:<BR>　virtual void logTransaction() const; // how to log trans-<BR>　// actions of this type<BR>...<BR>};</TD></TR></TBODY></TABLE><BR>　　考虑执行这行代码时会发生什么：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>BuyTransaction b;</TD></TR></TBODY></TABLE><BR>　　很明显 BuyTransaction 的构造函数会被调用，但是首先，Transaction 的构造函数必须先被调用，<A class=bluekey href="http://www.yesky.com/key/2127/27127.html" target=_blank>派生类</A>对象中的<A class=bluekey href="http://www.yesky.com/key/4852/79852.html" target=_blank>基类</A>部分先于派生类部分被构造。Transaction 的构造函数的最后一行调用虚函数 logTransaction，但是结果会让你大吃一惊，被调用的 logTransaction 版本是在 Transaction 中的那个，而不是 BuyTransaction 中的——即使被创建的对象类型是 BuyTransaction。基类构造期间，虚函数从来不会向下匹配（go down）到派生类。取而代之的是，那个对象的行为就好像它的类型是基类。非正式地讲，基类构造期间，虚函数禁止。 这个表面上看起来匪夷所思的行为存在一个很好的理由。因为基类的构造函数在派生类构造函数之前执行，当基类构造函数运行时，派生类数据成员还没有被初始化。如果基类构造期间调用的虚函数向下匹配（go down）到派生类，派生类的函数理所当然会涉及到本地数据成员，但是那些数据成员还没有被初始化。这就会为未定义行为和悔之晚矣的<A class=bluekey href="http://www.yesky.com/key/1707/86707.html" target=_blank>调试</A>噩梦开了一张通行证。调用涉及到一个对象还没有被初始化的部分自然是危险的，所以 C++ 告诉你此路不通。<BR><BR>　　在实际上还有比这更多的更深层次的原理。在派生类对象的基类构造期间，对象的类型是那个基类的。不仅虚函数会解析到基类，而且语言中用到运行时类型信息（runtime type information）的配件（例如，dynamic_cast和 typeid），也会将对象视为基类类型。在我们的例子中，当 Transaction 构造函数运行初始化 BuyTransaction 对象的基类部分时，对象的类型是 Transaction。C++ 的每一个配件将以如下眼光来看待它，并对它产生这样的感觉：对象的 BuyTransaction 特有的部分还没有被初始化，所以安全的对待它们的方法就是视若无睹。在派生类构造函数运行之前，一个对象不会成为一个派生类对象。<BR><BR>　　同样的原因也适用于析构过程。一旦派生类析构函数运行，这个对象的派生类数据成员就被视为未定义的值，所以 C++ 就将它们视为不再存在。在进入基类析构函数时，对象就成为一个基类对象，C++ 的所有配件——虚函数，dynamic_casts 等——都如此看待它。<BR><BR>　　在上面的示例代码中，Transaction 的构造函数直接调用了虚函数，对本 Item 的规则的违例是显而易见的。这一违例是如此显见，以致一些编译器会给出警告。（其它的则不会）甚至除了这样的警告之外，这一问题几乎肯定会在运行之前暴露出来，因为 logTransaction 函数在 Transaction 中是一个纯虚函数。除非它被定义（看似不可能，但确实可能），否则程序将无法连接：连接程序无法找到 Transaction::logTransaction 的必需的实现。<BR><BR>　　在构造函数和析构函数中调用虚函数的问题并不总是如此容易被察觉。如果 Transaction 有多个构造函数，每一个都必须完成一些相同的工作，好的软件工程为避免代码重复，会将共用的初始化代码，包括对 logTransaction 的调用，放入一个私有的非虚的初始化函数，叫做 init：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>class Transaction {<BR>public:<BR>　Transaction()<BR>　{ init(); } // call to non-virtual...<BR><BR>　virtual void logTransaction() const = 0;<BR>　...<BR><BR>private:<BR>　void init()<BR>　{<BR>　　...<BR>　　logTransaction(); // ...that calls a virtual!<BR>　}<BR>};</TD></TR></TBODY></TABLE><BR>　　这个代码在概念上和早先那个版本相同，但是它更阴险，因为它很具代表性地会躲过编译器和连接程序的抱怨。在这种情况下，因为 logTransaction 在 Transaction 中是纯虚函数，大多数运行时系统在纯虚函数被调用时，程序会异常中止（典型的结果就是给出一条信息）。然而，如果 logTransaction 是一个“常规的”虚函数（也就是说，非纯的虚函数），而且在 Transaction 中有其实现，那个版本被调用，程序会继续一路小跑，让你想象不出为什么派生类对象创建的时候会调用 logTransaction 的错误版本。避免这个问题的唯一办法就是确保在你的构造函数和析构函数中，决不在你创建或销毁的对象上调用虚函数，构造函数和析构函数所调用的函数也要服从同样的约束。<BR><BR>　　但是，如何保证在任何时间 Transaction 层次结构中的对象被创建时，都能调用 logTransaction 的正确版本呢？显然，在 Transaction 的构造函数中在这个对象上调用虚函数的做法是错误的。<BR><BR>　　有不同的方法来解决这个问题。其中之一是将 Transaction 中的 logTransaction 转变为一个非虚函数，这就需要派生类的构造函数将必要的日志信息传递给 Transaction 的构造函数。那个函数就可以安全地调用非虚的 logTransaction。如下：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#e3e3e3 border=1>
<TBODY>
<TR>
<TD>class Transaction {<BR>public:<BR>　explicit Transaction(const std::string&amp; logInfo);<BR><BR>　void logTransaction(const std::string&amp; logInfo) const; // now a non-<BR>　// virtual func<BR>　...<BR>};<BR><BR>Transaction::Transaction(const std::string&amp; logInfo)<BR>{<BR>　...<BR>　logTransaction(logInfo); // now a non-<BR>} // virtual call<BR><BR>class BuyTransaction: public Transaction {<BR>public:<BR>　BuyTransaction( parameters )<BR>　: Transaction(createLogString( parameters )) // pass log info<BR>　{ ... } // to base class<BR>　... // constructor<BR><BR>private:<BR>　static std::string createLogString( parameters );<BR>};</TD></TR></TBODY></TABLE><BR>　　换句话说，因为在基类的构造过程中你不能使用虚函数，就改为由派生类传递必要的构造信息给基类的构造函数作为补偿。 在此例中，注意 BuyTransaction 中那个（私有的）static 函数 createLogString 的使用。使用一个辅助函数创建一个值传递给基类的构造函数，通常比通过在成员初始化列表给基类它所需要的东西更加便利（也更加具有可读性）。将那个函数做成 static，就不会有偶然涉及到一个初生的 BuyTransaction 对象的仍未初始化的数据成员的危险。这很重要，因为实际上那些数据成员在一个未定义状态，这就是为什么在基类构造和析构期间虚函数不能首先匹配到派生类的原因。<BR><BR>　　Things to Remember<BR><BR>　　·在构造和析构期间不要调用虚函数，因为这样的调用不会匹配到当前执行的构造函数或析构函数所属的类的更深的派生层次。 <BR><BR>from: <A href="http://dev.yesky.com/441/2033941.shtml">http://dev.yesky.com/441/2033941.shtml</A><img src ="http://www.blogjava.net/weidagang2046/aggbug/16499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-23 22:01 <a href="http://www.blogjava.net/weidagang2046/articles/16499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Smart Pointers - What, Why, Which?</title><link>http://www.blogjava.net/weidagang2046/articles/16489.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 23 Oct 2005 10:47:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/16489.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/16489.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/16489.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/16489.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/16489.html</trackback:ping><description><![CDATA[<I><A href="http://ootips.org/yonat/">Yonat Sharon</A></I> 
<HR>

<UL>
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#What">What are they?</A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#Why">Why would I use them?</A> 
<UL>
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhyBugs"><I>Less bugs</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhyExceptions"><I>Exception Safety</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhyGC"><I>Garbage collection</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhyEfficiency"><I>Efficiency</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhySTL"><I>STL containers</I></A> </LI></UL>
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#Which">Which one should I use?</A> 
<UL>
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichLocals"><I>Local variables</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichMembers"><I>Class members</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichSTL"><I>STL containers</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichOwnership"><I>Explicit ownership transfer</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichBig"><I>Big objects</I></A> 
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#WhichSummary"><I>Summary</I></A> </LI></UL>
<LI><A href="http://ootips.org/yonat/4dev/smart-pointers.html#Conclusion">Conclusion</A> </LI></UL>
<HR>

<H2><A name=What>What are they?</A></H2>Smart pointers are objects that look and feel like pointers, but are smarter. What does this mean? 
<P>To look and feel like pointers, smart pointers need to have the same interface that pointers do: they need to support pointer operations like dereferencing (operator *) and indirection (operator -&gt;). An object that looks and feels like something else is called a proxy object, or just proxy. <A href="http://rampages.onramp.net/~huston/proxy.html">The proxy pattern</A> and its many uses are described in the books <A href="http://www.amazon.com/exec/obidos/ASIN/0201633612/ref=nosim/ootips">Design Patterns</A> and <A href="http://www.amazon.com/exec/obidos/ASIN/0471958697/ref=nosim/ootips">Pattern Oriented Software Architecture</A>. 
<P>To be smarter than regular pointers, smart pointers need to do things that regular pointers don't. What could these things be? Probably the most common bugs in C++ (and C) are related to pointers and memory management: dangling pointers, memory leaks, allocation failures and other joys. Having a smart pointer take care of these things can save a lot of aspirin... 
<P>The simplest example of a smart pointer is auto_ptr, which is included in the standard C++ library. You can find it in the header &lt;memory&gt;, or take a look at <A href="http://cseng.aw.com/book/related/0,3833,020163371X+80,00.html">Scott Meyers' auto_ptr implementation</A>. Here is part of auto_ptr's implementation, to illustrate what it does: 
<BLOCKQUOTE><PRE><FONT color=navy>template</FONT> &lt;<FONT color=navy>class</FONT> T&gt; <FONT color=navy>class</FONT> <B>auto_ptr</B>
{
    T* ptr;
<FONT color=navy>public</FONT>:
    <FONT color=navy>explicit</FONT> <B>auto_ptr</B>(T* p = 0) : ptr(p) {}
    <B>~auto_ptr</B>()                 {<FONT color=navy>delete</FONT> ptr;}
    T&amp; <B>operator*</B>()              {<FONT color=navy>return</FONT> *ptr;}
    T* <B>operator-&gt;</B>()             {<FONT color=navy>return</FONT> ptr;}
    <FONT color=green>// ...
</FONT>};</PRE></BLOCKQUOTE>As you can see, auto_ptr is a simple wrapper around a regular pointer. It forwards all meaningful operations to this pointer (dereferencing and indirection). Its smartness in the destructor: the destructor takes care of deleting the pointer. 
<P>For the user of auto_ptr, this means that instead of writing: 
<BLOCKQUOTE><PRE>void <B>foo</B>()
{
    <B>MyClass</B>* p(<FONT color=navy>new</FONT> <B>MyClass</B>);
    p-&gt;<B>DoSomething</B>();
    <FONT color=navy>delete</FONT> p;
}</PRE></BLOCKQUOTE>You can write: 
<BLOCKQUOTE><PRE>void <B>foo</B>()
{
    <B>auto_ptr</B>&lt;<B>MyClass</B>&gt; p(<FONT color=navy>new</FONT> <B>MyClass</B>);
    p-&gt;<B>DoSomething</B>();
}</PRE></BLOCKQUOTE>And trust p to cleanup after itself. 
<P>What does this buy you? See the next section. 
<H2><A name=Why>Why would I use them?</A></H2>Obviously, different smart pointers offer different reasons for use. Here are some common reasons for using smart pointers in C++. 
<H3><A name=WhyBugs>Why: <I>Less bugs</I></A></H3><B>Automatic cleanup.</B> As the code above illustrates, using smart pointers that clean after themselves can save a few lines of code. The importance here is not so much in the keystrokes saved, but in reducing the probability for bugs: you don't need to remember to free the pointer, and so there is no chance you will forget about it. 
<P><B>Automatic initialization.</B> Another nice thing is that you don't need to initialize the auto_ptr to NULL, since the default constructor does that for you. This is one less thing for the programmer to forget. 
<P><B>Dangling pointers.</B> A common pitfall of regular pointers is the dangling pointer: a pointer that points to an object that is already deleted. The following code illustrates this situation: 
<BLOCKQUOTE><PRE><B>MyClass</B>* p(<FONT color=navy>new</FONT> <B>MyClass</B>);
<B>MyClass</B>* q = p;
<FONT color=navy>delete</FONT> p;
p-&gt;<B>DoSomething</B>();   <FONT color=green>// Watch out! p is now dangling!</FONT>
p = NULL;<B>           </B><FONT color=green>// p is no longer dangling</FONT>
q-&gt;<B>DoSomething</B>();   <FONT color=green>// Ouch! q is still dangling!</FONT></PRE></BLOCKQUOTE>For auto_ptr, this is solved by setting its pointer to NULL when it is copied: 
<BLOCKQUOTE><PRE><FONT color=navy>template</FONT> &lt;<FONT color=navy>class</FONT> T&gt;
<B>auto_ptr</B>&lt;T&gt;&amp; <B>auto_ptr</B>&lt;T&gt;::<B>operator=</B>(<B>auto_ptr</B>&lt;T&gt;&amp; rhs)
{
    if (<FONT color=navy>this</FONT> != &amp;rhs) {
        <FONT color=navy>delete</FONT> ptr;
        ptr = rhs.ptr;
        rhs.ptr = NULL;
    }
    <FONT color=navy>return</FONT> *<FONT color=navy>this</FONT>;
}</PRE></BLOCKQUOTE>Other smart pointers may do other things when they are copied. Here are some possible strategies for handling the statement q&nbsp;=&nbsp;p, where p and q are smart pointers: 
<UL>
<LI><B>Create a new copy</B> of the object pointed by p, and have q point to this copy. This strategy is implemented in <A href="http://ootips.org/yonat/4dev/copied_ptr.h">copied_ptr.h</A>. 
<LI><B>Ownership transfer</B>: Let both p and q point to the same object, but transfer the responsibility for cleaning up ("ownership") from p to q. This strategy is implemented in <A href="http://ootips.org/yonat/4dev/owned_ptr.h">owned_ptr.h</A>. 
<LI><B>Reference counting</B>: Maintain a count of the smart pointers that point to the same object, and delete the object when this count becomes zero. So the statement q&nbsp;=&nbsp;p causes the count of the object pointed by p to increase by one. This strategy is implemented in <A href="http://ootips.org/yonat/4dev/counted_ptr.h">counted_ptr.h</A>. Scott Meyers offers <A href="http://www.aristeia.com/BookErrata/M29Source_frames.html">another reference counting implementation</A> in his book <A href="http://www.amazon.com/exec/obidos/ASIN/020163371X/ref=nosim/ootips">More Effective C++</A>. 
<LI><B>Reference linking</B>: The same as reference counting, only instead of a count, maintain a circular doubly linked list of all smart pointers that point to the same object. This strategy is implemented in <A href="http://ootips.org/yonat/4dev/linked_ptr.h">linked_ptr.h</A>. 
<LI><B>Copy on write</B>: Use reference counting or linking as long as the pointed object is not modified. When it is about to be modified, copy it and modify the copy. This strategy is implemented in <A href="http://ootips.org/yonat/4dev/cow_ptr.h">cow_ptr.h</A>. </LI></UL>All these techniques help in the battle against dangling pointers. Each has each own benefits and liabilities. The <A href="http://ootips.org/yonat/4dev/smart-pointers.html#Which"><I>Which</I></A> section of this article discusses the suitability of different smart pointers for various situations. 
<H3><A name=WhyExceptions>Why: <I>Exception Safety</I></A></H3>Let's take another look at this simple example: 
<BLOCKQUOTE><PRE><FONT color=navy>void</FONT> <B>foo</B>()
{
    <B>MyClass</B>* p(<FONT color=navy>new</FONT> <B>MyClass</B>);
    p-&gt;<B>DoSomething</B>();
    <FONT color=navy>delete</FONT> p;
}</PRE></BLOCKQUOTE>What happens if DoSomething() throws an exception? All the lines after it will not get executed and p will never get deleted! If we're lucky, this leads only to memory leaks. However, MyClass may free some other resources in its destructor (file handles, threads, transactions, COM references, mutexes) and so not calling it my cause severe resource locks. 
<P>If we use a smart pointer, however, p will be cleaned up whenever it gets out of scope, whether it was during the normal path of execution or during the stack unwinding caused by throwing an exception. 
<P>But isn't it possible to write exception safe code with regular pointers? Sure, but it is so painful that I doubt anyone actually does this when there is an alternative. Here is what you would do in this simple case: 
<BLOCKQUOTE><PRE><FONT color=navy>void</FONT> <B>foo</B>()
{
    <B>MyClass</B>* p;
    <FONT color=navy>try</FONT> {
        p = <FONT color=navy>new</FONT> <B>MyClass</B>;
        p-&gt;<B>DoSomething</B>();
        <FONT color=navy>delete</FONT> p;
    }
    <FONT color=navy>catch</FONT> (...) {
        <FONT color=navy>delete</FONT> p;
        <FONT color=navy>throw</FONT>;
    }
}</PRE></BLOCKQUOTE>Now imagine what would happen if we had some if's and for's in there... 
<H3><A name=WhyGC>Why: <I>Garbage collection</I></A></H3>Since C++ does not provide automatic garbage collection like some other languages, smart pointers can be used for that purpose. The simplest garbage collection scheme is reference counting or reference linking, but it is quite possible to implement more sophisticated garbage collection schemes with smart pointers. For more information see <A href="http://www.iecc.com/gclist/GC-faq.html">the garbage collection FAQ</A>. 
<H3><A name=WhyEfficiency>Why: <I>Efficiency</I></A></H3>Smart pointers can be used to make more efficient use of available memory and to shorten allocation and deallocation time. 
<P>A common strategy for using memory more efficiently is copy on write (COW). This means that the same object is shared by many COW pointers as long as it is only read and not modified. When some part of the program tries to modify the object ("write"), the COW pointer creates a new copy of the object and modifies this copy instead of the original object. The standard string class is commonly implemented using COW semantics (see the &lt;string&gt; header). 
<BLOCKQUOTE><PRE><B>string</B> s(<FONT color=teal>"Hello"</FONT>);

<B>string</B> t = s;       <FONT color=green>// t and s point to the same buffer of characters</FONT>

t<B> </B>+= <FONT color=teal>" there!"</FONT>;<B>     </B><FONT color=green>// a new buffer is allocated for t before
<B>      </B>              // appending " there!", so s is unchanged.</FONT></PRE></BLOCKQUOTE>
<P>Optimized allocation schemes are possible when you can make some assumptions about the objects to be allocated or the operating environment. For example, you may know that all the objects will have the same size, or that they will all live in a single thread. Although it is possible to implement optimized allocation schemes using class-specific new and delete operators, smart pointers give you the freedom to choose whether to use the optimized scheme for each object, instead of having the scheme set for all objects of a class. It is therefore possible to match the allocation scheme to different operating environments and applications, without modifying the code for the entire class. 
<H3><A name=WhySTL>Why: <I>STL containers</I></A></H3>The C++ standard library includes a set of containers and algorithms known as the standard template library (STL). <A href="http://www.cs.rpi.edu/projects/ADS/htdocs/Algor.html">STL is designed</A> to be <I>generic</I> (can be used with any kind of object) and <I>efficient</I> (does not incur time overhead compared to alternatives). To achieve these two design goals, STL containers store their objects by value. This means that if you have an STL container that stores objects of class Base, it cannot store of objects of classes derived from Base. 
<BLOCKQUOTE><PRE><FONT color=navy>class</FONT> <B>Base</B> { <FONT color=green>/*...*/ </FONT>};
<FONT color=navy>class</FONT> <B>Derived</B> : <FONT color=navy>public</FONT> <B>Base</B> { <FONT color=green>/*...*/ </FONT>};

<B>Base</B> b;
<B>Derived</B> d;
<B>vector</B>&lt;<B>Base</B>&gt; v;

v.<B>push_back</B>(b); <FONT color=green>// OK
</FONT>v.<B>push_back</B>(d); <FONT color=green>// error</FONT></PRE></BLOCKQUOTE>What can you do if you need a collection of objects from different classes? The simplest solution is to have a collection of pointers: 
<BLOCKQUOTE><PRE><B>vector</B>&lt;<B>Base</B>*&gt; v;

v.<B>push_back</B>(<FONT color=navy>new</FONT> <B>Base</B>);      <FONT color=green>// OK
</FONT>v.<B>push_back</B>(<FONT color=navy>new</FONT> <B>Derived</B>);   <FONT color=green>// OK too
</FONT>
<FONT color=green>// cleanup:
</FONT><FONT color=navy>for</FONT> (<B>vector</B>&lt;<B>Base</B>*&gt;::<B>iterator</B> i = v.<B>begin</B>(); i != v.<B>end</B>(); ++i)
    <FONT color=navy>delete</FONT> *i;</PRE></BLOCKQUOTE>The problem with this solution is that after you're done with the container, you need to manually cleanup the objects stored in it. This is both error prone and not exception safe. 
<P>Smart pointers are a possible solution, as illustrated below. (An alternative solution is a smart container, like the one implemented in <A href="http://ootips.org/yonat/4dev/pointainer.h">pointainer.h</A>.) 
<BLOCKQUOTE><PRE><B>vector</B>&lt; <B>linked_ptr</B>&lt;<B>Base</B>&gt; &gt; v;
v.<B>push_back</B>(<FONT color=navy>new</FONT> <B>Base</B>);      <FONT color=green>// OK
</FONT>v.<B>push_back</B>(<FONT color=navy>new</FONT> <B>Derived</B>);   <FONT color=green>// OK too

</FONT><FONT color=green>// cleanup is automatic</FONT></PRE></BLOCKQUOTE>Since the smart pointer automatically cleans up after itself, there is no need to manually delete the pointed objects. 
<P>Note: STL containers may copy and delete their elements behind the scenes (for example, when they resize themselves). Therefore, all copies of an element must be equivalent, or the wrong copy may be the one to survive all this copying and deleting. This means that some smart pointers cannot be used within STL containers, specifically the standard auto_ptr and any ownership-transferring pointer. For more info about this issue, see <A href="http://www.gotw.ca/gotw/025.htm">C++ Guru of the Week #25</A>. 
<H2><A name=Which>Which one should I use?</A></H2>Are you confused enough? Well, this summary should help. 
<H3><A name=WhichLocals>Which: <I>Local variables</I></A></H3>The standard auto_ptr is the simplest smart pointer, and it is also, well, standard. If there are no special requirements, you should use it. For local variables, it is usually the right choice. 
<H3><A name=WhichMembers>Which: <I>Class members</I></A></H3>Although you can use auto_ptr as a class member (and save yourself the trouble of freeing objects in the destructor), copying one object to another will nullify the pointer, as illustrated Below. 
<BLOCKQUOTE><PRE><FONT color=navy>class</FONT> <B>MyClass
</B>{
    <B>auto_ptr</B>&lt;<FONT color=navy>int</FONT>&gt; p;
    <FONT color=green>// ...
</FONT>};

<B>MyClass</B> x;
<FONT color=green>// do some meaningful things with x
</FONT><B>MyClass</B> y = x; <FONT color=green>// x.p now has a NULL pointer</FONT></PRE></BLOCKQUOTE>Using a copied pointer instead of auto_ptr solves this problem: the copied object (y) gets a new copy of the member. 
<P>Note that using a reference counted or reference linked pointer means that if y changes the member, this change will also affect x! Therefore, if you want to save memory, you should use a COW pointer and not a simple reference counted/linked pointer. 
<H3><A name=WhichSTL>Which: <I>STL containers</I></A></H3>As explained above, using garbage-collected pointers with STL containers lets you store objects from different classes in the same container. 
<P>It is important to consider the characteristics of the specific garbage collection scheme used. Specifically, reference counting/linking can leak in the case of circular references (i.e., when the pointed object itself contains a counted pointer, which points to an object that contains the original counted pointer). Its advantage over other schemes is that it is both simple to implement and deterministic. The deterministic behavior may be important in some real time systems, where you cannot allow the system to suddenly wait while the garbage collector performs its housekeeping duties. 
<P>Generally speaking, there are two ways to implement reference counting: intrusive and non-intrusive. Intrusive means that the pointed object itself contains the count. Therefore, you cannot use intrusive reference counting with 3-rd party classes that do not already have this feature. You can, however, derive a new class from the 3-rd party class and add the count to it. Non-intrusive reference counting requires an allocation of a count for each counted object. The <A href="http://ootips.org/yonat/4dev/counted_ptr.h">counted_ptr.h</A> is an example of non-intrusive reference counting. 
<P>
<TABLE cols=2 width="100%">
<TBODY>
<TR vAlign=top>
<TD><IMG height=172 alt="Intrusive reference counting" src="http://ootips.org/yonat/4dev/intrusive.gif" width=263></TD>
<TD><IMG height=183 alt="Non-intrusive reference counting" src="http://ootips.org/yonat/4dev/non-intrusive.gif" width=402></TD></TR>
<TR>
<TD width=300>Reference linking does not require any changes to be made to the pointed objects, nor does it require any additional allocations. A reference linked pointer takes a little more space than a reference counted pointer - just enough to store one or two more pointers.</TD>
<TD><IMG height=223 alt="Reference linking" src="http://ootips.org/yonat/4dev/linked.gif" width=291></TD></TR></TBODY></TABLE>
<P>Both reference counting and reference linking require using locks if the pointers are used by more than one thread of execution. 
<H3><A name=WhichOwnership>Which: <I>Explicit ownership transfer</I></A></H3>Sometimes, you want to receive a pointer as a function argument, but keep the ownership of this pointer (i.e. the control over its lifetime) to yourself. One way to do this is to use consistent naming-conventions for such cases. <A href="http://pcroot.cern.ch/TaligentDocs/TaligentOnline/DocumentRoot/1.0/Docs/books/WM/WM_3.html">Taligent's Guide to Designing Programs</A> recommends using "adopt" to mark that a function adopts ownership of a pointer. 
<P>Using an owned pointer as the function argument is an explicit statement that the function is taking ownership of the pointer. 
<H3><A name=WhichBig>Which: <I>Big objects</I></A></H3>If you have objects that take a lot of space, you can save some of this space by using COW pointers. This way, an object will be copied only when necessary, and shared otherwise. The sharing is implemented using some garbage collection scheme, like reference counting or linking. 
<H3><A name=WhichSummary>Which: <I>Summary</I></A></H3>
<TABLE cellPadding=5>
<TBODY>
<TR>
<TD><B>For this:</B></TD>
<TD><B>Use that:</B></TD></TR>
<TR>
<TD><I>Local variables</I></TD>
<TD>auto_ptr</TD></TR>
<TR>
<TD><I>Class members</I></TD>
<TD>Copied pointer</TD></TR>
<TR>
<TD><I>STL Containers</I></TD>
<TD>Garbage collected pointer (e.g. reference counting/linking)</TD></TR>
<TR>
<TD><I>Explicit ownership transfer</I></TD>
<TD>Owned pointer</TD></TR>
<TR>
<TD><I>Big objects</I></TD>
<TD>Copy on write</TD></TR></TBODY></TABLE>
<H2><A name=Conclusion>Conclusion</A></H2>Smart pointers are useful tools for writing safe and efficient code in C++. Like any tool, they should be used with appropriate care, thought and knowledge. For a comprehensive and in depth analysis of the issues concerning smart pointers, I recommend reading Andrei Alexandrescu's <A href="http://www.informit.com/content/index.asp?product_id={7CBDD5B1-129D-427A-9C36-9C506D3DFABA}">chapter about smart pointers</A> in his book <A href="http://www.amazon.com/exec/obidos/ASIN/0201704315/ref=nosim/ootips">Modern C++ Design</A>. 
<P>Feel free to use <A href="http://ootips.org/yonat/4dev/">my own smart pointers</A> in your code, and <A href="mailto:yonat@ootips.org">do tell me</A> if you are having any problems with them.<BR>The <A href="http://www.boost.org/">Boost C++</A> libraries include some smart pointers, which are more rigorously tested and actively maintained. Do try them first, if they are appropriate for your needs. <BR><BR>from: <A href="http://ootips.org/yonat/4dev/smart-pointers.html">http://ootips.org/yonat/4dev/smart-pointers.html</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/16489.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-23 18:47 <a href="http://www.blogjava.net/weidagang2046/articles/16489.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to interpret complex C/C++ declarations</title><link>http://www.blogjava.net/weidagang2046/articles/15707.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 17 Oct 2005 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/15707.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/15707.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/15707.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/15707.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/15707.html</trackback:ping><description><![CDATA[<H2><A name=contents>Contents</A></H2>
<UL>
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#intro">Introduction</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#basics">The basics</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#const">The const modifier</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#typedef">The subtleties of typedef</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#funct_ptrs">Function pointers</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#right_left_rule">The right-left rule</A> [Important] 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#examples">Further examples</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#reading">Suggested reading</A> 
<LI><A href="http://www.codeproject.com/cpp/complex_declarations.asp#credits">Credits</A> </LI></UL>
<H2><A name=intro>Introduction</A></H2>
<P>Ever came across a declaration like <CODE><SPAN class=cpp-keyword>int</SPAN> * (* (*fp1) (<SPAN class=cpp-keyword>int</SPAN>) ) [<SPAN class=cpp-literal>10</SPAN>];</CODE> or something similar that you couldn't fathom? This article will teach you to interpret C/C++ declarations, starting from mundane ones (<I>please</I> bear with me here) and moving on to very complex ones. We shall see examples of declarations that we come across in everyday life, then move on to the troublesome <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> modifier and <CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE>, conquer function pointers, and finally see the right-left rule, which will allow you to interpret any C/C++ declaration accurately. I would like to emphasize that it is <U>not</U> considered good practice to write messy code like this; I'm merely teaching you how to understand such declarations. <B>Note</B>: This article is best viewed with a minimum resolution of 1024x768, in order to ensure the comments don't run off into the next line. Code blocks look weird if viewed in a non-maximized window in Mozilla and Opera- sorry, I can't help it.</P>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=basics>The basics</A></H2>
<P>Let me start with a very simple example. Consider the declaration:</P><PRE><SPAN class=cpp-keyword>int</SPAN> n;</PRE>
<P>This should be interpreted as "declare <CODE>n</CODE> as an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>".</P>
<P>Coming to the declaration of a pointer variable, it would be declared as something like:</P><PRE><SPAN class=cpp-keyword>int</SPAN> *p;</PRE>
<P>This is to be interpreted as "declare <CODE>p</CODE> as an <CODE><SPAN class=cpp-keyword>int</SPAN> *</CODE> i.e., as a pointer to an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>". I'll need to make a small note here - it is always better to write a pointer (or reference) declaration with the <CODE>*</CODE> (or <CODE>&amp;</CODE>) preceding the variable rather than following the base type. This is to ensure there are no slip-ups when making declarations like:</P><PRE><SPAN class=cpp-keyword>int</SPAN>* p,q;</PRE>
<P>At first sight, it looks like <CODE>p</CODE> and <CODE>q</CODE> have been declared to be of type <CODE><SPAN class=cpp-keyword>int</SPAN> *</CODE>, but actually, it is only <CODE>p</CODE> that is a pointer, <CODE>q</CODE> is a simple <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>.</P>
<P>We can have a pointer to a pointer, which can be declared as:</P><PRE><SPAN class=cpp-keyword>char</SPAN> **argv;</PRE>
<P>In principle, there is no limit to this, which means you can have a pointer to a pointer to a pointer to a pointer to a <CODE><SPAN class=cpp-keyword>float</SPAN></CODE>, and so on.</P>
<P>Consider the declarations:</P><PRE><SPAN class=cpp-keyword>int</SPAN> RollNum[<SPAN class=cpp-literal>30</SPAN>][<SPAN class=cpp-literal>4</SPAN>];
<SPAN class=cpp-keyword>int</SPAN> (*p)[<SPAN class=cpp-literal>4</SPAN>]=RollNum;
<SPAN class=cpp-keyword>int</SPAN> *q[<SPAN class=cpp-literal>5</SPAN>];</PRE>
<P>Here, <CODE>p</CODE> is declared as a pointer to an array of 4 <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>s, while <CODE>q</CODE> is declared as an array of 5 pointers to integers.</P>
<P>We can have a mixed bag of <CODE>*</CODE>s and <CODE>&amp;</CODE>s in a single declaration, as explained below:</P><PRE><SPAN class=cpp-keyword>int</SPAN> **p1;  <SPAN class=cpp-comment>//  p1 is a pointer   to a pointer   to an int.</SPAN>
<SPAN class=cpp-keyword>int</SPAN> *&amp;p2;  <SPAN class=cpp-comment>//  p2 is a reference to a pointer   to an int.</SPAN>
<SPAN class=cpp-keyword>int</SPAN> &amp;*p3;  <SPAN class=cpp-comment>//  ERROR: Pointer    to a reference is illegal.</SPAN>
<SPAN class=cpp-keyword>int</SPAN> &amp;&amp;p4;  <SPAN class=cpp-comment>//  ERROR: Reference  to a reference is illegal.</SPAN></PRE>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=const>The const modifier</A></H2>
<P>The <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> keyword is used when you want to prevent a variable (oops, that's an oxymoron) from being modified. When you declare a <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> variable, you need to initialize it, because you can't give it a value at any other time.</P><PRE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN> n=<SPAN class=cpp-literal>5</SPAN>;
<SPAN class=cpp-keyword>int</SPAN> <SPAN class=cpp-keyword>const</SPAN> m=<SPAN class=cpp-literal>10</SPAN>;</PRE>
<P>The two variables <CODE>n</CODE> and <CODE>m</CODE> above are both of the same type - constant integers. This is because the C++ standard states that the <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> keyword can be placed before the type or the variable name. Personally, I prefer using the former style, since it makes the <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> modifier stand out more clearly.</P>
<P><CODE><SPAN class=cpp-keyword>const</SPAN></CODE> is a bit more confusing when it comes to dealing with pointers. For instance, consider the two variables <CODE>p</CODE> and <CODE>q</CODE> in the declaration below:</P><PRE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN> *p;
<SPAN class=cpp-keyword>int</SPAN> <SPAN class=cpp-keyword>const</SPAN> *q;</PRE>
<P>Which of them is a pointer to a <CODE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN></CODE>, and which is a <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> pointer to an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>? Actually, they're both pointers to <CODE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN></CODE>s. A <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> pointer to an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE> would be declared as:</P><PRE><SPAN class=cpp-keyword>int</SPAN> * <SPAN class=cpp-keyword>const</SPAN> r= &amp;n; <SPAN class=cpp-comment>// n has been declared as an int</SPAN></PRE>
<P>Here, <CODE>p</CODE> and <CODE>q</CODE> are pointers to a <CODE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN></CODE>, which means that you can't change the value of <CODE>*p</CODE>. <CODE>r</CODE> is a <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> pointer, which means that once declared as above, an assignment like <CODE>r=&amp;m;</CODE> would be illegal (where <CODE>m</CODE> is another <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>) but the value of <CODE>*r</CODE> can be changed.</P>
<P>To combine these two declarations to declare a <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> pointer to a <CODE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN></CODE>, you would have to declare it as:</P><PRE><SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>int</SPAN> * <SPAN class=cpp-keyword>const</SPAN> p=&amp;n <SPAN class=cpp-comment>// n has been declared as const int</SPAN></PRE>
<P>The following declarations should clear up any doubts over how <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> is to be interpreted. Please note that some of the declarations will NOT compile as such unless they are assigned values during declaration itself. I have omitted them for clarity, and besides, adding that will require another two lines of code for each example.</P><PRE><SPAN class=cpp-keyword>char</SPAN> ** p1;                    <SPAN class=cpp-comment>//        pointer to       pointer to       char</SPAN>
<SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>char</SPAN> **p2;               <SPAN class=cpp-comment>//        pointer to       pointer to const char</SPAN>
<SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN> * p3;             <SPAN class=cpp-comment>//        pointer to const pointer to       char</SPAN>
<SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN> * p4;       <SPAN class=cpp-comment>//        pointer to const pointer to const char</SPAN>
<SPAN class=cpp-keyword>char</SPAN> ** <SPAN class=cpp-keyword>const</SPAN> p5;              <SPAN class=cpp-comment>//  const pointer to       pointer to       char</SPAN>
<SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>char</SPAN> ** <SPAN class=cpp-keyword>const</SPAN> p6;        <SPAN class=cpp-comment>//  const pointer to       pointer to const char</SPAN>
<SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN> * <SPAN class=cpp-keyword>const</SPAN> p7;       <SPAN class=cpp-comment>//  const pointer to const pointer to       char</SPAN>
<SPAN class=cpp-keyword>const</SPAN> <SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN> * <SPAN class=cpp-keyword>const</SPAN> p8; <SPAN class=cpp-comment>//  const pointer to const pointer to const char</SPAN></PRE>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=typedef>The subtleties of typedef</A></H2>
<P><CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE> allows you a way to overcome the *-applies-to-variable-not-type rule. If you use a <CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE> like:</P><PRE><SPAN class=cpp-keyword>typedef</SPAN> <SPAN class=cpp-keyword>char</SPAN> * PCHAR;
PCHAR p,q;</PRE>
<P>both <CODE>p</CODE> and <CODE>q</CODE> become pointers. If the <CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE> had not been used, <CODE>q</CODE> would be a <CODE><SPAN class=cpp-keyword>char</SPAN></CODE>, which is counter-intuitive.</P>
<P>Here are a few declarations made using <CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE>, along with the explanation:</P><PRE><SPAN class=cpp-keyword>typedef</SPAN> <SPAN class=cpp-keyword>char</SPAN> * a;  <SPAN class=cpp-comment>// a is a pointer to a char</SPAN>

<SPAN class=cpp-keyword>typedef</SPAN> a b();     <SPAN class=cpp-comment>// b is a function that returns</SPAN>
                   <SPAN class=cpp-comment>// a pointer to a char</SPAN>

<SPAN class=cpp-keyword>typedef</SPAN> b *c;      <SPAN class=cpp-comment>// c is a pointer to a function</SPAN>
                   <SPAN class=cpp-comment>// that returns a pointer to a char</SPAN>

<SPAN class=cpp-keyword>typedef</SPAN> c d();     <SPAN class=cpp-comment>// d is a function returning</SPAN>
                   <SPAN class=cpp-comment>// a pointer to a function</SPAN>
                   <SPAN class=cpp-comment>// that returns a pointer to a char</SPAN>

<SPAN class=cpp-keyword>typedef</SPAN> d *e;      <SPAN class=cpp-comment>// e is a pointer to a function </SPAN>
                   <SPAN class=cpp-comment>// returning  a pointer to a </SPAN>
                   <SPAN class=cpp-comment>// function that returns a </SPAN>
                   <SPAN class=cpp-comment>// pointer to a char</SPAN>

e var[<SPAN class=cpp-literal>10</SPAN>];         <SPAN class=cpp-comment>// var is an array of 10 pointers to </SPAN>
                   <SPAN class=cpp-comment>// functions returning pointers to </SPAN>
                   <SPAN class=cpp-comment>// functions returning pointers to chars.</SPAN></PRE>
<P><CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE>s are usually used with structure declarations as shown below. The following structure declaration allows you to omit the <CODE><SPAN class=cpp-keyword>struct</SPAN></CODE> keyword when you create structure variables even in C, as is normally done in C++.</P><PRE><SPAN class=cpp-keyword>typedef</SPAN> <SPAN class=cpp-keyword>struct</SPAN> tagPOINT
{
    <SPAN class=cpp-keyword>int</SPAN> x;
    <SPAN class=cpp-keyword>int</SPAN> y;
}POINT;

POINT p; <SPAN class=cpp-comment>/* Valid C code */</SPAN></PRE>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=funct_ptrs>Function pointers</A></H2>
<P>Function pointers are probably the greatest source of confusion when it comes to interpreting declarations. Function pointers were used in the old DOS days for writing TSRs; in the Win32 world and X-Windows, they are used in callback functions. There are lots of other places where function pointers are used: virtual function tables, some templates in STL, and Win NT/2K/XP system services. Let's see a simple example of a function pointer:</P><PRE><SPAN class=cpp-keyword>int</SPAN> (*p)(<SPAN class=cpp-keyword>char</SPAN>);</PRE>
<P>This declares <CODE>p</CODE> as a pointer to a function that takes a <CODE><SPAN class=cpp-keyword>char</SPAN></CODE> argument and returns an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>.</P>
<P>A pointer to a function that takes two <CODE><SPAN class=cpp-keyword>float</SPAN></CODE>s and returns a pointer to a pointer to a <CODE><SPAN class=cpp-keyword>char</SPAN></CODE> would be declared as:</P><PRE><SPAN class=cpp-keyword>char</SPAN> ** (*p)(<SPAN class=cpp-keyword>float</SPAN>, <SPAN class=cpp-keyword>float</SPAN>);</PRE>
<P>How about an array of 5 pointers to functions that receive two <CODE><SPAN class=cpp-keyword>const</SPAN></CODE> pointers to <CODE><SPAN class=cpp-keyword>char</SPAN></CODE>s and return a <CODE><SPAN class=cpp-keyword>void</SPAN></CODE> pointer?</P><PRE><SPAN class=cpp-keyword>void</SPAN> * (*a[<SPAN class=cpp-literal>5</SPAN>])(<SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN>, <SPAN class=cpp-keyword>char</SPAN> * <SPAN class=cpp-keyword>const</SPAN>);</PRE>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=right_left_rule>The right-left rule</A> [Important]</H2>
<P>This is a simple rule that allows you to interpret any declaration. It runs as follows:</P>
<P>Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.<BR><BR>One small change to the right-left rule: When you start reading the declaration for the first time, you have to start from the identifier, and not the innermost parentheses.</P>
<P>Take the example given in the introduction:</P><PRE><SPAN class=cpp-keyword>int</SPAN> * (* (*fp1) (<SPAN class=cpp-keyword>int</SPAN>) ) [<SPAN class=cpp-literal>10</SPAN>];</PRE>
<P>This can be interpreted as follows:</P>
<OL>
<LI>Start from the variable name -------------------------- <CODE>fp1</CODE> 
<LI>Nothing to right but <CODE>)</CODE> so go left to find <CODE>*</CODE> -------------- is a pointer 
<LI>Jump out of parentheses and encounter (<CODE><SPAN class=cpp-keyword>int</SPAN></CODE>) --------- to a function that takes an <CODE><SPAN class=cpp-keyword>int</SPAN></CODE> as argument 
<LI>Go left, find <CODE>*</CODE> ---------------------------------------- and returns a pointer 
<LI>Jump put of parentheses, go right and hit <CODE>[<SPAN class=cpp-literal>10</SPAN>]</CODE> -------- to an array of 10 
<LI>Go left find <CODE>*</CODE> ----------------------------------------- pointers to 
<LI>Go left again, find <CODE><SPAN class=cpp-keyword>int</SPAN></CODE> -------------------------------- <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>s. </LI></OL>
<P>Here's another example:</P><PRE><SPAN class=cpp-keyword>int</SPAN> *( *( *arr[<SPAN class=cpp-literal>5</SPAN>])())();</PRE>
<OL>
<LI>Start from the variable name --------------------- <CODE>arr</CODE> 
<LI>Go right, find array subscript --------------------- is an array of 5 
<LI>Go left, find <CODE>*</CODE> ----------------------------------- pointers 
<LI>Jump out of parentheses, go right to find <CODE>()</CODE> ------ to functions 
<LI>Go left, encounter <CODE>*</CODE> ----------------------------- that return pointers 
<LI>Jump out, go right, find <CODE>()</CODE> ----------------------- to functions 
<LI>Go left, find <CODE>*</CODE> ----------------------------------- that return pointers 
<LI>Continue left, find <CODE>*</CODE> ----------------------------- to <CODE><SPAN class=cpp-keyword>int</SPAN></CODE>s. </LI></OL>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=examples>Further examples</A></H2>
<P>The following examples should make it clear:</P><PRE><SPAN class=cpp-keyword>float</SPAN> ( * ( *b()) [] )();              <SPAN class=cpp-comment>// b is a function that returns a </SPAN>
                                       <SPAN class=cpp-comment>// pointer to an array of pointers</SPAN>
                                       <SPAN class=cpp-comment>// to functions returning floats.</SPAN>

<SPAN class=cpp-keyword>void</SPAN> * ( *c) ( <SPAN class=cpp-keyword>char</SPAN>, <SPAN class=cpp-keyword>int</SPAN> (*)());       <SPAN class=cpp-comment>// c is a pointer to a function that takes</SPAN>
                                       <SPAN class=cpp-comment>// two parameters:</SPAN>
                                       <SPAN class=cpp-comment>//     a char and a pointer to a</SPAN>
                                       <SPAN class=cpp-comment>//     function that takes no</SPAN>
                                       <SPAN class=cpp-comment>//     parameters and returns</SPAN>
                                       <SPAN class=cpp-comment>//     an int</SPAN>
                                       <SPAN class=cpp-comment>// and returns a pointer to void.</SPAN>

<SPAN class=cpp-keyword>void</SPAN> ** (*d) (<SPAN class=cpp-keyword>int</SPAN> &amp;, 
  <SPAN class=cpp-keyword>char</SPAN> **(*)(<SPAN class=cpp-keyword>char</SPAN> *, <SPAN class=cpp-keyword>char</SPAN> **));        <SPAN class=cpp-comment>// d is a pointer to a function that takes</SPAN>
                                       <SPAN class=cpp-comment>// two parameters:</SPAN>
                                       <SPAN class=cpp-comment>//     a reference to an int and a pointer</SPAN>
                                       <SPAN class=cpp-comment>//     to a function that takes two parameters:</SPAN>
                                       <SPAN class=cpp-comment>//        a pointer to a char and a pointer</SPAN>
                                       <SPAN class=cpp-comment>//        to a pointer to a char</SPAN>
                                       <SPAN class=cpp-comment>//     and returns a pointer to a pointer </SPAN>
                                       <SPAN class=cpp-comment>//     to a char</SPAN>
                                       <SPAN class=cpp-comment>// and returns a pointer to a pointer to void</SPAN>

<SPAN class=cpp-keyword>float</SPAN> ( * ( * e[<SPAN class=cpp-literal>10</SPAN>]) 
    (<SPAN class=cpp-keyword>int</SPAN> &amp;) ) [<SPAN class=cpp-literal>5</SPAN>];                    <SPAN class=cpp-comment>// e is an array of 10 pointers to </SPAN>
                                       <SPAN class=cpp-comment>// functions that take a single</SPAN>
                                       <SPAN class=cpp-comment>// reference to an int as an argument </SPAN>
                                       <SPAN class=cpp-comment>// and return pointers to</SPAN>
                                       <SPAN class=cpp-comment>// an array of 5 floats.</SPAN></PRE>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=reading>Suggested reading</A></H2>
<UL>
<LI><A href="http://www.codeproject.com/cpp/pointerprelude.asp">A Prelude to pointers</A> by Nitron. 
<LI><CODE>cdecl</CODE> is an excellent utility that explains variable declarations and does much more. You can download the Windows port of cdecl from <A href="http://www.simtel.net/product.php?url_fb_product_page=41564" target=_blank>here</A>. </LI></UL>
<P><A href="http://www.codeproject.com/cpp/complex_declarations.asp#contents">[Back to contents]</A></P>
<H2><A name=credits>Credits</A></H2>
<P>I got the idea for this article after reading a thread posted by Jörgen Sigvardsson about a pointer declaration that he got in a mail, which has been reproduced in the introduction. Some of the examples were taken from the book "Test your C skills" by Yashvant Kanetkar. Some examples of function pointers were given by my cousin Madhukar M Rao. The idea of adding examples with mixed <CODE>*</CODE>s and <CODE>&amp;</CODE>s and <CODE><SPAN class=cpp-keyword>typedef</SPAN></CODE> with <CODE><SPAN class=cpp-keyword>struct</SPAN></CODE>s was given by my cousin Rajesh Ramachandran. Chris Hills came up with modifications to the right-left rule and the way in which some examples were interpreted.</P>
<P>from: <A href="http://www.codeproject.com/cpp/complex_declarations.asp">http://www.codeproject.com/cpp/complex_declarations.asp</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/15707.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-17 14:16 <a href="http://www.blogjava.net/weidagang2046/articles/15707.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>strtok用法</title><link>http://www.blogjava.net/weidagang2046/articles/15680.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 16 Oct 2005 14:17:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/15680.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/15680.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/15680.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/15680.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/15680.html</trackback:ping><description><![CDATA[<P><FONT color=#008000><FONT face=Arial><STRONG>char * &nbsp;strtok ( char * </STRONG><I>string</I><B>, const char * </B><I>delimiters</I><B> );</B> </FONT></FONT></P>
<P><B>Sequentially truncate string if delimiter is found.</B><BR>&nbsp; If <I>string</I> is not <TT>NULL</TT>, the function scans <I>string</I> for the first occurrence of any character included in <I>delimiters</I>. If it is found, the function overwrites the delimiter in <I>string</I> by a null-character and returns a pointer to the token, i.e. the part of the scanned string previous to the delimiter.<BR>&nbsp; After a first call to <TT><B>strtok</B></TT>, the function may be called with <TT>NULL</TT> as <I>string</I> parameter, and it will follow by where the last call to <TT><B>strtok</B></TT> found a delimiter.<BR>&nbsp; <I>delimiters</I> may vary from a call to another.<BR>
<P><B>Parameters.</B> 
<DL>
<DT><I>string</I> 
<DD>Null-terminated string to scan. 
<DT><I>separator</I> 
<DD>Null-terminated string containing the separators. </DD></DL>
<P><B>Return Value.</B><BR>&nbsp; A pointer to the last token found in <I>string</I>. &nbsp; <TT>NULL</TT> is returned when there are no more tokens to be found. 
<P><B>Portability.</B><BR>&nbsp; Defined in ANSI-C.<BR>
<P><B>Example.</B><BR><FONT color=#000080><TT><PRE>/* strtok example */
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

int main ()
{
  char str[] ="This is a sample string,just testing.";
  char * pch;
  printf ("Splitting string \"%s\" in tokens:\n",str);
  pch = strtok (str," ");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.");
  }
  return 0;
}
</PRE>
<P></TT></FONT><FONT face="宋体, MS Song">Output:<BR></FONT><TT>Splitting string "This is a sample string,just testing." in tokens:<BR>This<BR>is<BR>a<BR>sample<BR>string<BR>just<BR>testing </TT><BR><BR>下面是linux下的strtok manual:<BR>---------------------------------------------------------------<BR>STRTOK(3)&nbsp;&nbsp;&nbsp;&nbsp; Linux Programmer's Manual&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STRTOK(3)</P>
<P>&nbsp;</P>
<P>NAME<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtok, strtok_r - extract tokens from strings</P>
<P>SYNOPSIS<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;string.h&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *strtok(char *s, const char *delim);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *strtok_r(char *s, const char *delim, char **ptrptr);</P>
<P>DESCRIPTION<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A&nbsp; `token'&nbsp; is&nbsp; a&nbsp; nonempty&nbsp; string&nbsp; of characters not occurring in the<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string delim, followed by \0 or by a character occurring in delim.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The strtok() function can be used to parse the string&nbsp; s&nbsp; into&nbsp; tokens.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp; first call to strtok() should have s as its first argument. Subse-<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; quent calls should have the first&nbsp; argument&nbsp; set&nbsp; to&nbsp; NULL.&nbsp; Each&nbsp; call<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; returns&nbsp;a&nbsp; pointer&nbsp; to the next token, or NULL when no more tokens are<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; found.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; If a token ends with a delimiter, this delimiting&nbsp; character&nbsp; is&nbsp; over-<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; written&nbsp;with a \0 and a pointer to the next character is saved for the<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; next call to strtok().&nbsp; The delimiter string delim may be different for<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; each call.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp; strtok_r()&nbsp;function&nbsp; is a reentrant version of the strtok() func-<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tion, which instead of using its own static buffer, requires a&nbsp; pointer<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; to&nbsp; a user allocated char*. This pointer, the ptrptr parameter, must be<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; the same while parsing the same string.</P>
<P>BUGS<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Never use these functions. If you do, note that:</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; These functions modify their first argument.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; These functions cannot be used on constant strings.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The identity of the delimiting character is lost.</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The strtok() function uses a static&nbsp; buffer&nbsp; while&nbsp; parsing,&nbsp; so<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; it's not thread safe. Use strtok_r() if this matters to you.</P>
<P>RETURN VALUE<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The&nbsp; strtok()&nbsp; function returns a pointer to the next token, or NULL if<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; there are no more tokens.</P>
<P>CONFORMING TO<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtok()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SVID 3, POSIX, BSD 4.3, ISO 9899</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strtok_r()<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; POSIX.1c</P>
<P>SEE ALSO<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; index(3), memchr(3), rindex(3), strchr(3), strpbrk(3), strsep(3),&nbsp; str-<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spn(3), strstr(3)</P>
<P>&nbsp;</P>
<P>GNU&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2000-02-13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STRTOK(3)</P><img src ="http://www.blogjava.net/weidagang2046/aggbug/15680.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-16 22:17 <a href="http://www.blogjava.net/weidagang2046/articles/15680.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Learn a new trick with the offsetof() macro</title><link>http://www.blogjava.net/weidagang2046/articles/15670.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 16 Oct 2005 12:12:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/15670.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/15670.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/15670.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/15670.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/15670.html</trackback:ping><description><![CDATA[<P><FONT face=Verdana size=2><FONT face=Verdana color=#003366 size=2><B>Almost never used, the <B>offsetof()</B> macro can actually be a helpful addition to your bag of tricks. Here are a couple of places in embedded systems where the macro is indispensable—packing data structures and describing how EEPROM data are stored.</B></FONT> 
<P>If you browse through an ANSI C compiler's header files, you'll come across a very strange looking macro in <B>stddef.h</B>. The macro, <B>offsetof()</B>, has a horrid declaration. Furthermore, if you consult the compiler manuals, you'll find an unhelpful explanation that reads something like this: 
<P>"<I>The</I> <B>offsetof()</B> <I>macro returns the offset of the element name within the struct or union composite. This provides a portable method to determine the offset.</I>" 
<P>At this point, your eyes start to glaze over, and you move on to something that's more understandable and useful. Indeed, this was my position until about a year ago when the macro's usefulness finally dawned on me. I now kick myself for not realizing the benefits earlier—the macro could have saved me a lot of grief over the years. However, I console myself by realizing that I wasn't alone, since I'd never seen this macro used in any embedded code. Offline and online searches confirmed that <B>offsetof()</B> is essentially not used. I even found compilers that had not bothered to define it. How the macro works 
<P>Before delving into the three areas where I've found the macro useful, it's necessary to discuss what the macro does, and how it does it. 
<P>The <B>offsetof()</B> macro is an ANSI-required macro that should be found in <B>stddef.h</B>. Simply put, the <B>offsetof()</B> macro returns the number of bytes of offset before a particular element of a struct or union. 
<P>The declaration of the macro varies from vendor to vendor and depends upon the processor architecture. Browsing through the compilers on my computer, I found the example declarations shown in Listing 1. As you can see, the definition of the macro can get complicated. </P>
<P><B>Listing 1: A representative set of offsetof() macro definitions 
<P>// Keil 8051 compiler<BR>#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m) 
<P>// Microsoft x86 compiler (version 7)<BR>#define offsetof(s,m) (size_t)(unsigned long)&amp;(((s *)0)-&gt;m) 
<P>// Diab Coldfire compiler<BR>#define offsetof(s,memb) ((size_t)((char *)&amp;((s *)0)-&gt;memb-(char *)0)) </B>
<P>Regardless of the implementation, the <B>offsetof()</B> macro takes two parameters. The first parameter is the structure name; the second, the name of the structure element. (I apologize for using a term as vague as "structure name." I'll refine this shortly.) A straightforward use of the macro is shown in Listing 2. 
<P><B>Listing 2: A straightforward use of offsetof() 
<P>typedef struct<BR>{<BR>
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR></TBODY></TABLE>} SFOO;<BR>
<P>void main(void)<BR>{<BR>&nbsp;&nbsp;printf("Offset of 'f' is %u", offsetof(SFOO, f));<BR>}</B> 
<P>To better understand the magic of the <B>offsetof()</B> macro, consider the details of Keil's definition. The various operators within the macro are evaluated in an order such that the following steps are performed: 
<P>
<OL>
<LI><B>((s *)0)</B> takes the integer zero and casts it as a pointer to <I>s</I>. 
<LI><B>((s *)0)-&gt;m</B> dereferences that pointer to point to structure member <I>m</I>. 
<LI><B>&amp;(((s *)0)-&gt;m)</B> computes the address of <I>m</I>. 
<LI><B>(size_t)&amp;(((s *)0)-&gt;m)</B> casts the result to an appropriate data type. </LI></OL>
<P>By definition, the structure itself resides at address 0. It follows that the address of the field pointed to (Step 3 above) must be the offset, in bytes, from the start of the structure. At this point, we can make several observations: 
<P>
<UL>
<LI>We can be a bit more specific about the term "structure name." In a nutshell, if the structure name you use, call it <I>s</I>, results in a valid C expression when written as <B>(s *)0-&gt;m</B>, you can use <I>s</I> in the <B>offsetof()</B> macro. The examples shown in Listings 3 and 4 will help clarify that point. 
<LI>The member expression, <I>m</I>, can be of arbitrary complexity. Indeed, if you have nested structures, then the member field can be an expression that resolves to a parameter deeply nested within a structure. 
<LI>It's easy enough to see why this macro also works with unions. 
<LI>The macro won't work with bitfields. You simply can't take the address of a bitfield member of a structure or union. </LI></UL>
<P>Listings 3 and 4 contain simple variations on the usage of this macro. These should help you get you comfortable with the <B>offsetof()</B> basics. 
<P><B>Listing 3: Without a typedef 
<P>struct sfoo { 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR></TBODY></TABLE>}; 
<P>void main(void)<BR>{<BR>&nbsp;&nbsp;printf("Offset of 'f' is %u", offsetof(struct sfoo, f));<BR>} 
<P>Listing 4: Nested structs 
<P>typedef struct<BR>{ 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>long</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>l;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>short</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>s;</B></FONT> </TD></TR></TBODY></TABLE>} SBAR; 
<P>typedef struct<BR>{ 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>SBAR</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>b;</B></FONT> </TD></TR></TBODY></TABLE>} SFOO; 
<P>void main(void)<BR>{<BR>&nbsp;&nbsp;printf("Offset of 'l' is %u", &nbsp;&nbsp;&nbsp;&nbsp;offsetof(SFOO, b.l));<BR>}</B> 
<P>Now that you understand the semantics of the macro, it's time to take a look at how to use it. 
<P><FONT face=Verdana color=#003366 size=3><B>Use 1: pad bytes</B></FONT><BR>Most 16-bit and larger processors require that data structures in memory be aligned on a multibyte (for example, 16-bit or 32-bit) boundary. Sometimes the requirement is absolute, and sometimes it's merely recommended for optimal bus throughput. In the latter case, the flexibility is offered because the designers recognized that you may wish to trade off memory access time with other competing issues such as memory size and the ability to transfer (perhaps via a communications link or direct memory access) the memory contents directly to another processor that has a different alignment requirement. 
<P>For cases such as these, it's often necessary to resort to compiler directives to achieve the required level of packing. As the C structure declarations can be quite complex, working out how to achieve this can be daunting. Furthermore, after poring over the compiler manuals, I'm always left with a slight sense of unease about whether I've really achieved what I set out to do. 
<P>The most straightforward solution to this problem is to write a small piece of test code. For instance, consider the moderately complex declaration given in Listing 5. 
<P><B>Listing 5: A union containing a struct 
<P>typedef union<BR>{ 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR></TBODY></TABLE>struct { 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>g;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>double</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>h;</B></FONT> </TD></TR></TBODY></TABLE>&nbsp;&nbsp;} b;<BR>} UFOO; 
<P>void main(void)<BR>{<BR>&nbsp;&nbsp;printf("Offset of 'h' is %u", &nbsp;&nbsp;&nbsp;&nbsp;offsetof(UFOO, b.h)); }</B> 
<P>If you need to know where b.h resides in the structure, then the simplest way to find out is to write some test code such as that shown in Listing 5. This is all well and good, but what about portability? Writing code that relies on offsets into structures can be risky—particularly if the code gets ported to a new target at a later date. Adding a comment is of course a good idea—but what one really needs is a means of forcing the compiler to tell you if the critical members of a structure are in the wrong place. Fortunately, one can do this using the <B>offsetof</B> macro and the technique in Listing 6, courtesy of Michael Barr. 
<P><B>Listing 6: Anonymous union that checks structure offsets 
<P>typedef union<BR>{ 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>struct</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>&nbsp;&nbsp;&nbsp;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>{</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>&nbsp;&nbsp;&nbsp;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>&nbsp;&nbsp;&nbsp;float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>g;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>&nbsp;&nbsp;&nbsp;double</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>h;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>} b;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>&nbsp;&nbsp;&nbsp;</B></FONT> </TD></TR></TBODY></TABLE><BR>} UFOO; 
<P>static union<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char wrong_offset_i[offsetof(UFOO,i) == 0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char wrong_offset_f[offsetof(UFOO,f) == 0]; 
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char wrong_offset_h[offsetof(UFOO, b.h) == 2]; //Error generated<BR>};</B> 
<P>The technique works by attempting to declare an array. If the test evaluates to FALSE, then the array will be of zero size, and a compiler error will result. In Listing 6, the compiler generates an error "Invalid dimension size [0]" for the parameter b.h. 
<P>Thus the <B>offsetof()</B> macro allows you to both determine and check the packing of elements within structures. 
<P><FONT face=Verdana color=#003366 size=3><B>Use 2: nonvolatile memory</B></FONT><BR>Many embedded systems contain some form of nonvolatile memory, which holds configuration parameters and other device-specific information. One of the most common types of nonvolatile memory is serial EEPROM. Normally, such memories are byte addressable. The result is often a serial EEPROM driver that provides an API that includes a read function that looks like this: 
<P><B>ee_rd(uint16_t offset, uint16_t nBytes, uint8_t * dest)</B> 
<P>In other words, <B>read nBytes</B> from offset <B>offset</B> in the EEPROM and store them at <B>dest</B>. The problem is knowing what offset in EEPROM to read from and how many bytes to read (in other words, the underlying size of the variable being read). The most common solution to this problem is to declare a data structure that represents the EEPROM and then declare a pointer to that struct and assign it to address 0x0000000. This technique is shown in Listing 7. 
<P><B>Listing 7: Accessing parameters in serial EEPROM via a pointer 
<P>typedef struct<BR>{</B> 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR></TBODY></TABLE><BR><B>} EEPROM; 
<P>EEPROM * const pEE = 0x0000000; 
<P>ee_rd(&amp;(pEE-&gt;f), sizeof(pEE-&gt;f), dest); </B>
<P>This technique has been in use for years. However, I dislike it precisely because it does create an actual pointer to a variable that supposedly resides at address 0. In my experience, this can create a number of problems including: 
<P>
<OL>
<LI>Someone maintaining the code can get confused into thinking that the EEPROM data structure really does exist. 
<LI>You can write perfectly legal code (for example, <B>pEE-&gt;f = 3.2</B>) and get no compiler warnings that what you're doing is disastrous. 
<LI>The code doesn't describe the underlying hardware well. </LI></OL>
<P>A far better approach is to use the <B>offsetof()</B> macro. An example is shown in Listing 8 
<P><B>Listing 8: Using offsetof()to access parameters in serial EEPROM 
<P>typedef struct<BR>{</B><BR>
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>int</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>i;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>f;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>c;</B></FONT> </TD></TR></TBODY></TABLE><B>} EEPROM; 
<P>ee_rd(offsetof(EEPROM,f), 4, dest);</B> 
<P>However, there's still a bit of a problem. The size of the parameter has been entered manually (in this case "4"). It would be a lot better if we could have the compiler work out the size of the parameter as well. No problem, you say, just use the <B>sizeof()</B> operator. However, the <B>sizeof()</B> operator doesn't work the way we would like it to. That is, we cannot write <B>sizeof(EEPROM.f)</B> because <B>EEPROM</B> is a data type and not a variable. 
<P>Now normally, one always has at least one instance of a data type so that this is not a problem. In our case, the data type <B>EEPROM</B> is nothing more than a template for describing how data are stored in the serial EEPROM. So, how can we use the <B>sizeof()</B> operator in this case? Well, we can simply use the same technique used to define the <B>offsetof()</B> macro. Consider the definition: 
<P><B>#define SIZEOF(s,m) ((size_t) sizeof(((s *)0)-&gt;m))</B> 
<P>This looks a lot like the <B>offsetof()</B> definitions in Listing 1. We take the value 0 and cast it to "pointer to <I>s</I>." This gives us a variable to point to. We then point to the member we want and apply the regular <B>sizeof()</B> operator. The net result is that we can get the size of any member of a typedef without having to actually declare a variable of that data type. 
<P>Thus, we can now refine our read from the serial EEPROM as follows: 
<P><B>ee_rd(offsetof(EEPROM, f), SIZEOF(EEPROM, f), &amp;dest);</B> 
<P>At this point, we're using two macros in the function call, with both macros taking the same two parameters. This leads to an obvious refinement that cuts down on typing and errors: 
<P><B>#define EE_RD(M,D)&nbsp;&nbsp;&nbsp;ee_rd(offsetof(EEPROM,M), SIZEOF(EEPROM,M), D)</B> 
<P>Now our call to the EEPROM driver becomes much more intuitive: 
<P><B>EE_RD(f, &amp;dest);</B> 
<P>That is, read <B>f</B> from the EEPROM and store its contents at location <B>dest</B>. The location and size of the parameter is handled automatically by the compiler, resulting in a clean, robust interface. 
<P><FONT face=Verdana color=#003366 size=3><B>Use 3: protecting nonvolatile memory</B></FONT><BR>The last example contains elements from the first two examples. Many embedded systems contain directly addressable nonvolatile memory, such as battery-backed SRAM. It's usually important to detect if the contents of this memory have been corrupted. I usually group the data into a structure, compute a CRC (cyclic redundancy code) over that structure, and append it to the data structure. Thus, I often end up with something like this: 
<P><B>struct nv<BR>{</B> 
<TABLE cellPadding=2 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>short</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_1;</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_2;</B></FONT> </TD></TR>
<P>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_3;</B></FONT> </TD></TR>
<P>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>uint16_t</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>crc;</B></FONT> </TD></TR>
<P></P></TBODY></TABLE><B>} nvram;</B> 
<P>The intent of the CRC is that it be the CRC of "all the parameters in the data structure with the exception of itself." This seems reasonable enough. Thus, the question is, how does one compute the CRC? If we assume we have a function, crc16( ), that computes the CRC-16 over an array of bytes, then we might be tempted to use the following: 
<P><B>nvram.crc = crc16((char *) &amp;nvram, sizeof(nvram)-sizeof(nvram.crc));</B> 
<P>This code will only definitely work with compilers that pack all data on byte boundaries. For compilers that don't do this, the code will almost certainly fail. To see that this is the case, let's look at this example structure for a compiler that aligns everything on a 32-bit boundary. The effective structure could look like that in Listing 9. 
<P><B>Listing 9: An example structure for a compiler that aligns everything on a 32-bit boundary 
<P>struct nv<BR>{<BR></B>
<TABLE cellPadding=4 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>short</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_1;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 0</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>pad1[2];</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Two byte pad</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_2;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 4</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_3;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 8</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>pad2[3];</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Three byte pad</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>uint16_t </B></FONT></TD>
<TD><FONT face=Verdana size=2><B>crc;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 12</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>pad3[2];</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Two byte pad</B></FONT> </TD></TR></TBODY></TABLE><BR><B>} nvram;</B> 
<P>The first two pads are expected. However, why is the compiler adding two bytes onto the end of the structure? It does this because it has to handle the case when you declare an array of such structures. Arrays are required to be contiguous in memory, too. So to meet this requirement and to maintain alignment, the compiler pads the structure out as shown. 
<P>On this basis, we can see that the <B>sizeof(nvram)</B> is 16 bytes. Now our nave code in Listing 9 computes the CRC over <B>sizeof(nvram) - sizeof(nvram.crc)</B> bytes = 16 - 2 = 14 bytes. Thus the stored CRC now includes its previous value in its computation! We certainly haven't achieved what we set out to do. 
<P><B>Listing 10: Nested data structures 
<P>struct nv<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;struct data<BR>&nbsp;&nbsp;&nbsp;&nbsp;{<BR></B>
<TABLE cellPadding=4 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>short</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_1;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 0</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>float</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_2;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 4</B></FONT> </TD></TR>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>char</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>param_3;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 8</B></FONT> </TD></TR></TBODY></TABLE><BR><B>&nbsp;&nbsp;&nbsp;&nbsp;} data;</B><BR>
<TABLE cellPadding=4 bgColor=#ffffff border=0>
<TBODY>
<TR vAlign=top>
<TD>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
<TD><FONT face=Verdana size=2><B>uint 16_t</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>crc;</B></FONT> </TD>
<TD><FONT face=Verdana size=2><B>//Offset = 12</B></FONT> </TD></TR></TBODY></TABLE><B>} nvram;</B> 
<P>The most common solution to this problem is to nest data structures as shown in Listing 10. Now we can compute the CRC using: 
<P><B>nvram.crc = crc16((uint8_t *) &amp;nvram.data, sizeof(nvram.data));</B> 
<P>This works well and is indeed the technique I've used over the years. However, it introduces an extra level of nesting within the structure—purely to overcome an artifact of the compiler. Another alternative is to place the CRC at the top of the structure. This overcomes most of the problems but feels unnatural to many people. On the basis that structures should always reflect the underlying system, a technique that doesn't rely on artifice is preferable—and that technique is to use the <B>offsetof()</B> macro. 
<P>Using the <B>offsetof()</B> macro, one can simply use the following (assuming the original structure definition): 
<P><B>nvram.crc = crc16((uint8_t *) &amp;nvram, offsetof(struct nv, crc));</B> 
<P><FONT face=Verdana size=3><B>Keep looking</B></FONT><BR>I've provided a few examples where the <B>offsetof()</B> macro can improve your code. I'm sure that I'll find further uses over the next few years. If you've found additional uses for the macro I would be interested to hear about them. 
<P><B>Nigel Jones</B> is a consultant living in Maryland, where he slaves away on a wide range of embedded projects. He enjoys hearing from readers and can be reached at <A href="mailto:najones@rmbconsulting.us">najones@rmbconsulting.us</A>. </P></FONT><img src ="http://www.blogjava.net/weidagang2046/aggbug/15670.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-16 20:12 <a href="http://www.blogjava.net/weidagang2046/articles/15670.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Multiple inheritance and the this pointer</title><link>http://www.blogjava.net/weidagang2046/articles/15651.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 16 Oct 2005 08:41:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/15651.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/15651.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/15651.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/15651.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/15651.html</trackback:ping><description><![CDATA[<DIV class=toc>
<DIV class=heading>Table of contents</DIV>
<DIV>1. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#single_classes">Single classes first</A></DIV>
<DIV>2. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#simple_inheritance">Then simple inheritance</A></DIV>
<DIV>3. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#multiple_inheritance">Multiple inheritance</A></DIV>
<DIV>3.1. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#offsetof_and_multiple_inheritance">A note regarding <CODE>offsetof</CODE> and multiple inheritance</A></DIV>
<DIV>3.2. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#which_class_is_at_zero">Which class is at zero?</A></DIV>
<DIV>3.3. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#unsafe_casts">The danger of unsafe casts</A></DIV>
<DIV>4. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#downcasting">Downcasting</A></DIV>
<DIV>4.1. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#reinterpret_cast_cant_downcast">Why <CODE>reinterpret_cast</CODE>s can't downcast</A></DIV>
<DIV>4.2. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#static_cast_cant_downcast">Why <CODE>static_cast</CODE>s can't <EM>safely</EM> downcast</A></DIV>
<DIV>4.3. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#polymorphic_types_and_dynamic_cast">Polymorphic types: Why <CODE>dynamic_cast</CODE>s <EM>can</EM> downcast</A></DIV>
<DIV>5. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#conclusion">Conclusion</A></DIV>
<DIV>6. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#example_code">Example code</A></DIV>
<DIV>7. <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#postscript">Postscript</A></DIV></DIV>
<H2><A name=single_classes>Single classes first</A></H2>
<P>To understand how the <CODE>this</CODE> pointer works in class heirachies that use multiple inheritance, we start by examining how the compiler uses the instance pointer to access the class's members by dissecting a simpler example.</P>
<P>Say we've defined the following: </P><PRE class=example>class Foo
{
public:
    int a;
    int b;    
};

void modifyFoo(Foo* foo)
{
    foo-&gt;a = 1;
    foo-&gt;b = 2;
}

int main(int argc, char* argv[])
{
    Foo* foo = new Foo();
    modifyFoo(foo);
    delete foo;
}
</PRE>When you create an instance of <CODE>Foo</CODE>, the <CODE>new</CODE> operator allocates a chunk of memory big enough to hold the class's members. Since <CODE>Foo</CODE> has no virtual methods and no ancestor classes, the class needs to be simply as large as the data members themselves are (possibly plus padding - adjustable with most compilers, usually will only be applied if you're using types smaller than words). 
<P></P>
<P>You can easily check this; <CODE>sizeof(Foo)</CODE> in the above example will return <CODE>sizeof(foo-&gt;a)+sizeof(foo-&gt;b)</CODE>, which is <CODE>2*sizeof(int)</CODE> - 8 bytes on a 32-bit platform and 16 on a 64-bit platform (we try this out <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#sizeexample">below</A>).</P>
<P>So for this example, our <CODE>foo</CODE> pointer in <CODE>main()</CODE> is really a pointer to an 8-byte or 16-byte chunk of memory containing the data members of Foo; at compile time, the compiler tracks the fact that this is a <CODE>Foo*</CODE> pointer, but that won't show up in the compiled code. If you have a look at the disassembly for <CODE>modifyFoo()</CODE>, what you'll find is that the expressions like <CODE>foo-&gt;a</CODE> get translated to '*(foo + the offset of Foo::a vs. the instance pointer, in bytes)' - so for our example, <CODE>&amp;foo-&gt;a</CODE> will be the same pointer as <CODE>foo</CODE>, and <CODE>&amp;foo-&gt;b</CODE> will be <CODE>foo+sizeof(foo-&gt;a)</CODE>, ie. <CODE>foo+4</CODE> on 32-bit platforms, <CODE>foo+8</CODE> on 64-bit platforms.</P>
<P>It's worth pausing to check that this actually works. <A name=sizeexample>Adding and running the following method:</A> </P><PRE class=example>void Foo::dump()
{
    cout 
        &lt;&lt; "Foo::dump():" &lt;&lt; endl
        &lt;&lt; "sizeof(Foo) = " &lt;&lt; sizeof(Foo) &lt;&lt; endl
        &lt;&lt; "sizeof(*this) = " &lt;&lt; sizeof(*this) &lt;&lt; endl
        &lt;&lt; "sizeof(a) + sizeof(b) = "
        &lt;&lt;  sizeof(a) + sizeof(b) &lt;&lt; endl
        &lt;&lt; "offsetof(Foo, a) = " &lt;&lt; offsetof(Foo, a) &lt;&lt; endl
        &lt;&lt; "offsetof(Foo, b) = " &lt;&lt; offsetof(Foo, b) &lt;&lt; endl
        &lt;&lt; "this = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) this &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;a = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;a &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;b = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;b &lt;&lt; endl
        &lt;&lt; dec &lt;&lt; endl;
}
</PRE>will, on a 32-bit system with normal 32-bit packing, produce something like: <PRE class=exampleoutput>Foo::dump():
sizeof(Foo) = 8
sizeof(*this) = 8
sizeof(a) + sizeof(b) = 8
offsetof(Foo, a) = 0
offsetof(Foo, b) = 4
this = 0x80518a8
&amp;this-&gt;a = 0x80518a8
&amp;this-&gt;b = 0x80518ac
</PRE>as we expected. (Obviously, the actual memory addresses are meaningless and will change.) 
<P></P>
<P>We'll see <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#polymorphic_types_and_dynamic_cast">later</A> that data members are not the only thing that contribute to the size of an object; polymorphic classes also need a vtable pointer, which is discussed in the section on <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#polymorphic_types_and_dynamic_cast">polymorphic types and the <CODE>dynamic_cast</CODE> operator</A>.</P>
<H2><A name=simple_inheritance>Then simple inheritance</A></H2>
<P>Next, we look at what happens to the <CODE>this</CODE> pointer if we descend from our base class, <CODE>Foo</CODE>, like this: </P><PRE class=example>class Desc:
    public Foo
{
public:
    void dump();

public:
    int z;
};
</PRE>When you create an instance of <CODE>Desc</CODE>, the memory allocated will now be just big enough to hold both <CODE>Foo</CODE>'s members and <CODE>Desc</CODE>'s members; in fact, the compiler will just put <CODE>Desc</CODE>'s members straight after <CODE>Foo</CODE>'s - this: <PRE class=example>void Desc::dump()
{
    Foo::dump();

    cout 
        &lt;&lt; "Desc::dump():" &lt;&lt; endl
        &lt;&lt; "sizeof(Desc) = " &lt;&lt; sizeof(Desc) &lt;&lt; endl
        &lt;&lt; "sizeof(*this) = " &lt;&lt; sizeof(*this) &lt;&lt; endl
        &lt;&lt; "sizeof(a) + sizeof(b) + sizeof(z) = "
        &lt;&lt;  sizeof(a) + sizeof(b) + sizeof(z) &lt;&lt; endl
        &lt;&lt; "offsetof(Desc, a) = " &lt;&lt; offsetof(Desc, a) &lt;&lt; endl
        &lt;&lt; "offsetof(Desc, b) = " &lt;&lt; offsetof(Desc, b) &lt;&lt; endl
        &lt;&lt; "offsetof(Desc, z) = " &lt;&lt; offsetof(Desc, z) &lt;&lt; endl
        &lt;&lt; "this = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) this &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;a = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;a &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;b = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;b &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;z = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;z &lt;&lt; endl
        &lt;&lt; dec &lt;&lt; endl;
}
</PRE>will, in the same conditions as above, print something like: <PRE class=exampleoutput>Foo::dump():
sizeof(Foo) = 8
sizeof(*this) = 8
sizeof(a) + sizeof(b) = 8
offsetof(Foo, a) = 0
offsetof(Foo, b) = 4
this = 0x80518a8
&amp;this-&gt;a = 0x80518a8
&amp;this-&gt;b = 0x80518ac

Desc::dump():
sizeof(Desc) = 12
sizeof(*this) = 12
sizeof(a) + sizeof(b) + sizeof(z) = 12
offsetof(Desc, z) = 8
this = 0x80518a8
&amp;this-&gt;z = 0x80518b0
</PRE>So no suprises there - <CODE>Desc</CODE> instances are <CODE>sizeof(z)</CODE> bytes bigger than <CODE>Foo</CODE> instances, and the <CODE>z</CODE> member is just placed <CODE>sizeof(b)</CODE> bytes past the <CODE>b</CODE> member. 
<P></P>
<P>One interesting thing to note is that even though the instance is a <CODE>Desc</CODE>, <CODE>Foo::dump()</CODE> still only saw <CODE>sizeof(*this) == 8</CODE> (<CODE>sizeof</CODE> is evaluated at compile time, purely in the context of the class itself, so does not include the subclass data).</P>
<H2><A name=multiple_inheritance>Multiple inheritance</A></H2>
<P>But we're about to get a nasty surprise. Let's define another simple class, <CODE>Bar</CODE>, and then make a class, <CODE>Multi</CODE>, that descends from both <CODE>Foo</CODE> and <CODE>Bar</CODE>: </P><PRE class=example>class Bar
{
public:
    void dump();

public:
    int c;
};

void Bar::dump()
{
    cout 
        &lt;&lt; "Bar::dump():" &lt;&lt; endl
        &lt;&lt; "sizeof(Bar) = " &lt;&lt; sizeof(Bar) &lt;&lt; endl
        &lt;&lt; "sizeof(*this) = " &lt;&lt; sizeof(*this) &lt;&lt; endl
        &lt;&lt; "sizeof(c) = " &lt;&lt;  sizeof(c) &lt;&lt; endl
        &lt;&lt; "offsetof(Bar, c) = " &lt;&lt; offsetof(Bar, c) &lt;&lt; endl
        &lt;&lt; "this = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) this &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;c = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;c &lt;&lt; endl
        &lt;&lt; dec &lt;&lt; endl;
}


class Multi:
	public Foo,
	public Bar
{
public:
	void dump();

public:
	int y;
};

void Multi::dump()
{
    Foo::dump();
    Bar::dump();

    cout 
        &lt;&lt; "Multi::dump():" &lt;&lt; endl
        &lt;&lt; "sizeof(Multi) = " &lt;&lt; sizeof(Multi) &lt;&lt; endl
        &lt;&lt; "sizeof(*this) = " &lt;&lt; sizeof(*this) &lt;&lt; endl
        &lt;&lt; "sizeof(a) + sizeof(b) + sizeof(c) + sizeof(y) = "
        &lt;&lt;  sizeof(a) + sizeof(b) + sizeof(c) + sizeof(y) &lt;&lt; endl
        &lt;&lt; "offsetof(Multi, y) = " &lt;&lt; offsetof(Multi, y) &lt;&lt; endl
        &lt;&lt; "this = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) this &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;a = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;a &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;b = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;b &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;c = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;c &lt;&lt; endl
        &lt;&lt; "&amp;this-&gt;y = 0x" &lt;&lt; hex &lt;&lt; (intptr_t) &amp;this-&gt;y &lt;&lt; endl
        &lt;&lt; dec &lt;&lt; endl;
}
</PRE>this produces something like: <PRE class=exampleoutput>Foo::dump():
sizeof(Foo) = 8
sizeof(*this) = 8
sizeof(a) + sizeof(b) = 8
offsetof(Foo, a) = 0
offsetof(Foo, b) = 4
this = 0x80518e8
&amp;this-&gt;a = 0x80518e8
&amp;this-&gt;b = 0x80518ec

Bar::dump():
sizeof(Bar) = 4
sizeof(*this) = 4
sizeof(c) = 4
offsetof(Bar, c) = 0
this = 0x80518f0
&amp;this-&gt;c = 0x80518f0

Multi::dump():
sizeof(Multi) = 16
sizeof(*this) = 16
sizeof(a) + sizeof(b) + sizeof(c) + sizeof(y) = 16
offsetof(Multi, y) = 12
this = 0x80518e8
&amp;this-&gt;a = 0x80518e8
&amp;this-&gt;b = 0x80518ec
&amp;this-&gt;c = 0x80518f0
&amp;this-&gt;y = 0x80518f4
</PRE>
<P></P>
<P>Examine this output carefully, and note: 
<UL>
<LI><STRONG>The <CODE>this</CODE> pointer was the same for <CODE>Foo::dump()</CODE> and <CODE>Multi::dump()</CODE>, but different for <CODE>Bar::dump()</CODE>.</STRONG> See <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#offsets_clue">below</A> for discussion. 
<LI>Despite that (in fact, because of it), all the <CODE>dump()</CODE> methods agreed on the actual memory locations of the fields (<CODE>a</CODE>, <CODE>b</CODE>, <CODE>c</CODE>, and <CODE>y</CODE>). 
<LI>All the <CODE>sizeof</CODE> values agreed with what we'd expect - the size of any base classes plus the size of the fields. </LI></UL>
<P></P>
<P><A name=offsets_clue></A>The clue to what's going on here is that in <CODE>Bar::dump()</CODE>, the offset of <CODE>c</CODE> is 0. The <CODE>Bar</CODE> class doesn't know that you're going to multiple-inherit from it (remember that you could use <CODE>Bar</CODE> on its own as well, so it has to be compiled independently this way, just the same way that <CODE>Foo</CODE> was), so it expects <CODE>this</CODE> to point to the start of its memory.</P>
<P><STRONG>When you use multiple inheritance and make a call to <CODE>Bar::dump()</CODE>, the compiler passes a <CODE>this</CODE> that's been adjusted to point to the start of the <CODE>Bar</CODE> instance inside our <CODE>Multi</CODE></STRONG>. It does this automatically, and normally you don't need to worry about it.</P>
<H3><A name=offsetof_and_multiple_inheritance>A note regarding <CODE>offsetof</CODE> and multiple inheritance</A></H3>
<P>Before we move on to the implications of that, you may be wondering why I didn't output the following in <CODE>Multi::dump()</CODE>: </P><PRE class=example>        &lt;&lt; "offsetof(Multi, a) = " &lt;&lt; offsetof(Multi, a) &lt;&lt; endl
        &lt;&lt; "offsetof(Multi, b) = " &lt;&lt; offsetof(Multi, b) &lt;&lt; endl
        &lt;&lt; "offsetof(Multi, c) = " &lt;&lt; offsetof(Multi, c) &lt;&lt; endl
</PRE>The answer is that it doesn't compile with some compilers! And justifiably too, in my opinion. <A href="http://gcc.gnu.org/">gcc</A>, for example, would output: <PRE class=exampleoutput>invalid reference to NULL ptr, use ptr-to-member instead
</PRE>To understand this error you have to look at the <CODE>stddef</CODE> definition of <CODE>offsetof</CODE> (it's a macro, not an operator like <CODE>sizeof</CODE>): <PRE class=example>#define offsetof(TYPE, MEMBER) ((size_t) &amp;((TYPE *)0)-&gt;MEMBER)
</PRE>- which basically says, 'hypothetically, if I had a TYPE object at address 0, what address would the MEMBER member of it have' (0 being a convenient choice because then we don't have to subtract anything off to get the offset of MEMBER vs. that object instance address). 
<P></P>
<P>gcc recognises that the only useful way to work out what the offsets are when you have multiple inheritance is to do the special casts discussed below, and since these can't possibly be done on the NULL pointer - you can never have an object instance at address 0 - it gives an error.</P>
<P>Some compilers, Borland's for example, are quite happy with the operation and will compile it and Borland's does indeed return the desired result in this case. But you really, really don't want to be addressing members using offsets with multiple inheritance, because you would have to see a different <CODE>offsetof</CODE> in the different classes - <CODE>Multi::dump</CODE> would have to see an <CODE>offsetof(Multi, c)</CODE> of 12, even though <CODE>offsetof(Bar, c)</CODE> is 0... Which would be an accident waiting to happen; IMHO it's not a bad thing if the compiler prevents this inconsistency from surfacing.</P>
<P>Obviously, you shouldn't normally be using <CODE>offsetof</CODE> to access members anyway, but it's definitely an even worse idea with MI. Regardless, doing so is not compatible with some common compilers, and should be avoided.</P>
<H3><A name=which_class_is_at_zero>Which class is at zero?</A></H3>
<P>One final note before we move on: why was it <CODE>Foo</CODE> that shared the same <CODE>this</CODE> pointer value as <CODE>Multi</CODE>, and <CODE>Bar</CODE> that had an offset <CODE>this</CODE>? The answer is simply that <CODE>Foo</CODE> was the first class that we listed as a superclass.</P>
<P>In C++ when you use multiple inheritance, the order in which you list the superclasses does matter: the superclasses will be constructed in that order (and therefore destroyed in the reverse order), and the members will be laid out in that order too, meaning that the first superclass will normally have 0 offset from the instance pointer (I say 'normally' because there is an exception if the subclass is polymorphic but the first superclass wasn't - we'll see why <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#polymorphic_types_and_dynamic_cast">later</A>).</P>
<H3><A name=unsafe_casts>The danger of unsafe casts</A></H3>
<P>From the above discussion, one thing that's not clear is <EM>why you should care</EM>. The compiler correctly adjusts everything so that the base classes find their own members correctly, and the subclass has no problem accessing them; you don't need to do anything special when accessing the class from outside either. So why did I feel the need to write up a page explaining all this?</P>
<P>The answer is: <STRONG>unsafe casts will not work correctly with multiple inheritance</STRONG>.</P>
<P>C++ introduced a number of new casting operators: <CODE>static_cast</CODE>, <CODE>dynamic_cast</CODE>, <CODE>reinterpret_cast</CODE>, and <CODE>const_cast</CODE> (they look more like templates than operators, but that's what they're called). Each of these is different to C-style casts, and so we actually have 5 different cast operators.</P>
<P>A full discussion of what each of these casts does is beyond the scope of this document (if I get enough requests, I'll write up a seperate article), so we will confine ourselves to considering what happens when we apply these cast operators to instances of objects with multiple inheritance. <CODE>const_cast</CODE> is therefore irrelevant to our discussion (it just adds or removes <CODE>const</CODE> and/or <CODE>volatile</CODE> qualification), leaving us four.</P>
<P>Let's start by trying them. Given our <CODE>multi</CODE> instance in the program above, we get these pointers out of our casts: 
<TABLE class=nicetable>
<TBODY>
<TR>
<TH>Original <CODE>multi</CODE><BR>pointer: <CODE>0x80518e8</CODE></TH>
<TH>Cast to<BR><CODE>Multi*</CODE></TH>
<TH>Cast to<BR><CODE>Foo*</CODE></TH>
<TH>Cast to<BR><CODE>Bar*</CODE></TH></TR>
<TR>
<TH>C-style cast</TH>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518f0</CODE></TD></TR>
<TR>
<TH>C-style cast to <CODE>void*</CODE>,<BR>then C-style cast to type</TH>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD class=error><CODE>0x<WBR>80518e8</CODE></TD></TR>
<TR>
<TH><CODE>reinterpret_<WBR>cast</CODE></TH>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD class=error><CODE>0x<WBR>80518e8</CODE></TD></TR>
<TR>
<TH><CODE>static_cast</CODE></TH>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518f0</CODE></TD></TR>
<TR>
<TH><CODE>dynamic_cast</CODE></TH>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518e8</CODE></TD>
<TD><CODE>0x<WBR>80518f0</CODE></TD></TR></TBODY></TABLE>(As usual, actual values are irrelevant - just look at which values are <EM>different</EM>. To try these out yourself, download the <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#example_code">example code</A> at the end of the article.)</P>
<P>The first surprise in the above results is that if you use the old, C-style casts (for example, '<CODE>(Bar*) multi</CODE>', the compiler will adjust the pointer value, as it does for <CODE>static_cast</CODE> and <CODE>dynamic_cast</CODE>. In other words, in C++, C-style casts do not just do a plain copy-the-appropriate-number-of-bits as they did in C; it may actually involve adjustment of the pointer value (I certainly didn't expect that!).</P>
<P>But the other bit of important news here is that if we do a C-style cast to <CODE>void*</CODE> and then cast the result of that to our second type, the compiler cannot perform its address-adjustment magic, because it has no way of knowing that the <CODE>void*</CODE> is actually a <CODE>Multi*</CODE>; and worse, the same problem occurs if you <CODE>static_cast</CODE> to <CODE>void*</CODE> and then <CODE>static_cast</CODE> to our desired type (eg '<CODE>static_cast&lt;Bar*&gt;(static_cast&lt;void*&gt;(multi))</CODE>') - that cast to <CODE>Bar*</CODE> returns the wrong result!.</P>
<P><CODE>reinterpret_cast</CODE>, we can see, doesn't do any adjustment, it just reinterprets the literal bits of our <CODE>multi</CODE> pointer as another type of pointer completely. That will return the wrong value when casting classes with multiple inheritance, as we'd expect from the description of <CODE>reinterpret_cast</CODE></P>
<P>These results give us the most important conclusions of this article: <STRONG>Never use C-style casts to convert pointers or references between object types</STRONG>, and secondly, <STRONG>Avoid using <CODE>static_cast</CODE> to downcast, and never use it with multiple inheritance</STRONG>. Instead: 
<UL>
<LI>If you want to convert the pointer value literally, without adjustment and without real type checks, use <CODE>reinterpret_cast</CODE>. This will not come up very often. 
<LI>If you want to cast an instance pointer to a superclass, use <CODE>static_cast</CODE>; <CODE>dynamic_cast</CODE> will also work, but is unnecessary. <CODE>static_cast</CODE> checks the type relationships at compile-time, and has no unnecessary runtime overhead; <CODE>dynamic_cast</CODE> has runtime overhead and also imposes the extra requirement discussed below - but you might still prefer to use it for consistency sometimes. 
<LI><STRONG>If you want to convert a superclass pointer down to a descendant type, always use <CODE>dynamic_cast</CODE></STRONG>; it will check at runtime that the pointer is in fact an instance of the descendant type, and will adjust it if necessary, making this the only option that works with downcasting objects with multiple inheritance. See the section on <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#downcasting">downcasting</A> below, which explains <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php#polymorphic_types_and_dynamic_cast">one change</A> you may have to make to make this compile. </LI></UL>
<P></P>
<P>(Note that this summary discusses only casting object instance pointers between related types - there are many other things you might do with them, outside the scope of this document.)</P>
<H2><A name=downcasting>Downcasting</A></H2>
<P>'Downcasting' is the term used to describe casting a pointer or reference to a class 'down' the class heirachy - to one of its subclasses.</P>
<H3><A name=reinterpret_cast_cant_downcast>Why <CODE>reinterpret_cast</CODE>s can't downcast</A></H3>
<P>When you <CODE>reinterpret_cast</CODE> an instance pointer, the operator simply makes a pointer of the requested type with exactly the same address as the original. Therefore while <CODE>reinterpet_cast</CODE> will work fine if you are downcasting from a superclass to a subclass with no multiple inheritance anywhere in it's ancestry, it won't work in the general case - it'll compile and run, but will produce the wrong pointer.</P>
<P>For the example above, that pointer will be right for casting a <CODE>void*</CODE> down to a <CODE>Foo*</CODE>, or a <CODE>Foo*</CODE> down to a <CODE>Multi*</CODE>, but that's only because it happens that both <CODE>Foo</CODE> and <CODE>Multi</CODE> start at offset 0 (and even that can't be relied upon - if <CODE>Multi</CODE> is later made polymorphic, it will no longer be true); it won't work if you try to downcast a <CODE>void*</CODE> that actually points to a <CODE>Multi*</CODE> to a <CODE>Bar*</CODE>, because while <CODE>Bar</CODE> starts at offset 0 on its own, when it's a part of a <CODE>Multi</CODE>, it starts at offset 8 (or whatever).</P>
<P>(Note that this document makes no attempt to provide a general explanation of the utility or otherwise of <CODE>reinterpret_cast</CODE>, discussing only what's relevant to the matter of multiple inheritance.)</P>
<H3><A name=static_cast_cant_downcast>Why <CODE>static_cast</CODE>s can't <EM>safely</EM> downcast</A></H3>
<P>One might at first hope that <CODE>static_cast</CODE> could do the job. Sadly however, when starting with a pointer to a <CODE>Foo</CODE> or a <CODE>Bar</CODE>, there's no way to know, at compile time, from that type alone (which is all <CODE>static_cast</CODE> inspects) that it's actually a pointer to an object that's not only a <CODE>Foo</CODE> or a <CODE>Bar</CODE> but is in fact a <CODE>Multi</CODE>.</P>
<P>So the compiler will essentially do the same as it did for <CODE>reinterpret_cast</CODE>; if you cast our <CODE>multi</CODE> instance to a <CODE>void*</CODE> and then try to <CODE>static_cast</CODE> it back down to a <CODE>Bar</CODE>, it'll compile, but return the wrong result, because it doesn't know that this is not really a <CODE>Bar</CODE> - it's a <CODE>Multi</CODE>, which puts the <CODE>Bar</CODE> superclass instance at a nonzero offset. The compiler can't even check that the pointer you're casting is composed of a <CODE>Bar</CODE> instance at all, let alone know where the <CODE>Bar</CODE> is placed inside; so it just unsafely does the cast (I wish it gave an error - after all you can get that behaviour with other operators, if you really want it).</P>
<P>Again, note that this document makes no attempt to provide a general explanation of the use and limitations of <CODE>static_cast</CODE>; there's a lot more to know about this operator not directly relevant here.</P>
<H3><A name=polymorphic_types_and_dynamic_cast>Polymorphic types: Why <CODE>dynamic_cast</CODE>s <EM>can</EM> downcast</A></H3>
<P>The only operator that can successfully downcast is <CODE>dynamic_cast</CODE>. However, there is one catch: in order for downcasts to be possible, the base type you are casting from must be <EM>polymorphic</EM>. 
<P>Polymorphic is a general OOP term with which I assume readers are familiar. However, in the context of the C++ language, it has a specific and tangible meaning: <EM>polymorphic classes</EM> are those that have at least one <CODE>virtual</CODE> method (including destructors, and also including pure virtual methods - so you can just define a dummy private pure virtual method if you want to force a base class to be polymorphic but have no other <CODE>virtual</CODE>s).</P>
<P>The implication of a C++ class being polymorphic is that it has a <EM>vtable</EM>. A vtable ('virtual(s) table') is simply a static data structure (one per class, not per instance - all <CODE>Foo</CODE> instances share the same vtable, all <CODE>Bar</CODE> instances share another) that lists a number of things, including the table of all the virtual methods, and some metadata regarding the class itself and its ancestors, the latter being the part that is of use to us here.</P>
<P>vtables are created by the compiler and generally remain hidden to the application; you shouldn't ever need to access them directly. A fully detailed discussion of the contents of vtables falls outside the scope of this document, but thankfully, you don't need to know exactly what's in them for the problem at hand; we'll just look at how they're placed and how they help with downcasts. <!--If you like, skip the next five paragraphs - they're provided just to help explain how vtables are placed.--></P>
<P>If we make, say, our <CODE>Foo</CODE> class polymorphic, which we could do by (for example) adding a <CODE>virtual</CODE> tag to our definition of <CODE>dump()</CODE>, we notice that the <CODE>sizeof(Foo)</CODE> increases by the size of a pointer (4 bytes on my 32-bit PC), and that <CODE>offsetof(Foo, a)</CODE> and <CODE>offsetof(Foo, b)</CODE> will both increase by the size of a pointer too.</P>
<P>This is not a coincidence: <STRONG>when the compiler compiles the polymorphic class, it creates a vtable for it, and it now stores a pointer to this vtable at the very start of every instance</STRONG> (storing that pointer is one of the many jobs that is performed automatically by the constructor).</P>
<P>When the compiler compiles a normal subclass of a polymorphic class, it will make a new vtable for that subclass, but the instances of this subclass don't have to have pointers to both the superclass vtables and the subclass vtables separately, because the vtable for the subclass includes everything that the vtable for the superclass contained. So the size overhead of being polymorphic for a normal class is just one pointer per instance, regardless of how deep in the ancestry it is (again, the vtable itself is just one per class).</P>
<P>But this isn't quite enough if we're using multiple inheritance. Making any of the superclasses polymorphic is enough to make <CODE>Multi</CODE> polymorphic. But, if we made <CODE>Bar</CODE> polymorphic too, then it too would always have a vtable pointer at the start of its instance data. And since all <CODE>Multi</CODE> instances have what is effectively a standalone <CODE>Bar</CODE> instance embedded somewhere inside them (remember that you can cast the subclass instance to that superclass, so it must be able to work just like a real standalone <CODE>Bar</CODE> would), <CODE>Multi</CODE> will now have to have two vtable pointers - at the very start of our <CODE>Multi</CODE> instance will be the pointer to the combined <CODE>Foo</CODE>+<CODE>Multi</CODE> vtable, and at the offset of the <CODE>Bar</CODE> inside the <CODE>Multi</CODE> will be the pointer to the combined <CODE>Bar</CODE>+<CODE>Multi</CODE> vtable.</P>
<P>You don't need to know that, but if you're interested in how vtables work, convince yourself that this is so. You can test this theory out by noting that adding virtual methods to <CODE>Multi</CODE> adds no more instance size overhead once the <CODE>Foo</CODE> superclass is polymorphic, but that making <CODE>Bar</CODE> polymorphic as well <EM>does</EM> increase the size of <CODE>Multi</CODE> instances by one pointer.</P>
<P>Let's get back on track. We've said that all instances of polymorphic classes have a vtable pointer at the start of the instance. This is great because now, <STRONG>if we have a pointer to <EM>any</EM> polymorphic class, we can inspect the vtable and determine what the 'actual' type of that object is</STRONG>. So, even if we were given a <CODE>Foo*</CODE> or a <CODE>Bar*</CODE>, if we inspected the vtable, we'd be able to see if this is actually a <CODE>Multi</CODE> instance, not just a plain <CODE>Foo</CODE> or <CODE>Bar</CODE> instance.</P>
<P>And this is just what the <CODE>dynamic_cast</CODE> operator does for us: <STRONG><CODE>dynamic_cast</CODE> inspects the vtable of the polymorphic class instance you pass it to check that is actually an instance of whatever type you are trying to convert to - and if so, then it calculates if an offset is required to do the conversion and does it</STRONG>.</P>
<P>The offset for converting a <CODE>Foo*</CODE> to a <CODE>Multi*</CODE> would in the simplest case be 0 (and likewise for going back the other way). But to convert a <CODE>Bar*</CODE> to a <CODE>Multi*</CODE>, <CODE>dynamic_cast</CODE> would find from the vtable that the <CODE>Bar</CODE> instance is embedded some distance into the <CODE>Multi</CODE> (12 bytes when I run my test code), and it therefore subtracts that many bytes to find the correct address of the enclosing <CODE>Multi</CODE> from the <CODE>Bar*</CODE> pointer it started with.</P>
<P>So there you have it: <STRONG><CODE>dynamic_cast</CODE> is the solution to the downcasting problem; the unavoidable cost is that your classes must be polymorphic</STRONG>. That constraint may annoy you sometimes because it means that you can't directly downcast from types like <CODE>void*</CODE>, but in practice you should find that even if you're stuffing your instance pointers into <CODE>void*</CODE>s for a callback or whatever from a C library you're using, you will at least know what the base class is, so you can statically cast to that, and then <CODE>dynamic_cast</CODE> to perform the downcast; if not, you'll just have to declare one. That one issue aside, the overhead is generally small enough to not be an issue.</P>
<H2><A name=conclusion>Conclusion</A></H2>
<P>Whether you are using multiple inheritance or not, remember: 
<UL>
<LI>Don't rely on the <CODE>this</CODE> pointer having the same value everywhere if you're using multiple inheritance - cast it properly if necessary to get a 'canonical' pointer. 
<LI>Never hardcode the offset of members relative to the instance pointer, and avoid using <CODE>offsetof</CODE> unless strictly necessary (rare). 
<LI>Don't use C-style casts to convert pointers or references between object types - use <CODE>reinterpret_cast</CODE> if you want an unsafe, unadjusted conversion, or <CODE>static_cast</CODE> or <CODE>dynamic_cast</CODE> as below. 
<LI>If you want to cast an instance pointer to a superclass, use <CODE>static_cast</CODE> or <CODE>dynamic_cast</CODE>; the former is more efficient. 
<LI>If you want to convert a superclass pointer down to a descendant type, always use <CODE>dynamic_cast</CODE>. </LI></UL>
<P></P>
<H2><A name=example_code>Example code</A></H2>
<P>I encourage readers to download <A href="http://carcino.gen.nz/tech/cpp/test_offsets.cpp">the example code for this article</A> and try out the variations and effects for themselves.<BR><BR>from: <A href="http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php">http://carcino.gen.nz/tech/cpp/multiple_inheritance_this.php</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/15651.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-10-16 16:41 <a href="http://www.blogjava.net/weidagang2046/articles/15651.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>领悟设计模式--Template Method / Visitor</title><link>http://www.blogjava.net/weidagang2046/articles/12575.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 09 Sep 2005 08:05:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/12575.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/12575.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/12575.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/12575.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/12575.html</trackback:ping><description><![CDATA[<P><FONT color=#ff0000>[译者按]</FONT> 本文根据发表在CUJ Expert Forum上的两篇文章编译而成。C/C++ User's Journal是目前最出色的C/C++语言专业杂志，特别是在C++ Report闭刊之後，CUJ的地位更加突出。CUJ Expert Forum是CUJ主办的网上技术专栏，汇集2000年10月以来C++社群中顶尖专家的技术短文，并免费公开发布，精彩纷呈，是每一个C/C++学习者不可错过的资料。由Jim Hyslop和Herb Sutter主持的Conversation系列，是CUJ Expert Forum每期必备的精品专栏，以风趣幽默的对话形式讲解C++高级技术，在C++社群内得到广泛赞誉。译者特别挑选两篇设计模式方面的文章，介绍给大家。<FONT color=#8080ff>设计模式方面的经典着作是GoF的Design Patterns。但是那本书有一个缺点，不好懂。从风格上讲，该书与其说是为学习者而写作的教程范本，还不如说是给学术界人士看的学术报告，严谨有馀，生动不足。</FONT>这一点包括该书作者和象Bjarne Stroustrup这样的大师都从不讳言。实际上Design Pattern并非一定是晦涩难懂的，通过生动的例子，一个中等水平的C++学习者完全可以掌握基本用法，在自己的编程实践中使用，得到立竿见影的功效。这两篇文章就是很好的例证。本文翻译在保证技术完整性的前提下作了不少删节和修改，以便使文章显得更紧凑。</P>
<P>----------------------------------------------------------</P>
<P>人物介绍：</P>
<P>我 --- 一个追求上进的C++程序员，尚在试用期，聪明但是经验不足。</P>
<P>Wendy --- 公司里的技术大拿，就坐在我旁边的隔间里，C++大虾，最了不起的是，她是个女的 她什麽都好，就是有点刻薄，</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我对她真是又崇拜又嫉妒。</P>
<P>----------------------------------------------------------</P>
<P>I. Virtually Yours?-- Template Method模式</P>
<P>我在研究Wendy写的一个类。那是她为这个项目写的一个抽象基类，而我的工作就是从中派生出一个具象类(concrete class)。这个类的public部份是这样的：</P><PRE>class Mountie {
public:
    void read( std::istream &amp; );
    void write( std::ostream &amp; ) const;
    virtual ~Mountie();</PRE>
<P>很正常，virtual destructor表明这个类打算被继承。那麽再看看其protected部份：</P><PRE>protected:
    virtual void do_read( std::istream &amp; );
    virtual void do_write( std::ostream &amp; ) const;
</PRE>
<P>也不过就是一会儿的功夫，我识破了Wendy的把戏：她在使用template method模式。public成员函数read和write是非虚拟的，它们肯定是调用protected部份do_read/do_write虚拟成员函数来完成实际的工作。啊，我简直为自己的进步而飘飘然了 哈，Wendy，这回你可难不住我，还有什麽招数？尽管放马过来... 突然，笑容在我脸上凝固，因为我看到了其private部份：</P><PRE>private:
    virtual std::string classID() const = 0;
</PRE>
<P>这是什麽？一个private纯<FONT color=#ff00ff>序</FONT>函数，能工作麽？我站了起来， </P>
<P>“Wendy，你的Mountie类好像不能工作耶，它有一个private virtual function。”</P>
<P>“你试过了？”她连头都不抬。</P>
<P>“嗯，那倒是没有啦，可是想想也不行啊？我的派生类怎麽能override你的private函数呢？” 我嘟囔 。</P>
<P>“ ，你倒是很确定啊 ”Wendy的声音很轻柔，“你怎麽老是这也不行，那也不行的，这几个月跟 我你就没学到什麽东西吗？小菜鸟。”</P>
<P>真是可恶啊...</P>
<P>“小菜鸟，你全都忘了，<FONT color=#ff00ff>访问控制级别跟一个函数是不是虚拟的根本没关系</FONT>。<FONT color=#ff00ff>判断一个函数是动态绑定还是静态绑定是函数调用解析的最後一个步骤</FONT>。好好读读<FONT color=#ff00ff>标准</FONT>的3.4和5.2.2节吧。”</P>
<P>我完全处於下风，只好采取干扰战术。“好吧，就算你说的不错，我也还是不明白，何必把它设为private？”</P>
<P>“我且问你，倘若你不想让一个类中的成员函数被其他的类调用，应当如何处理？”</P>
<P>“当然是把它设置为private<FONT color=#ff00ff>的</FONT>，” 我回答道。</P>
<P>“那麽你去看看我的<FONT color=#ff00ff>Mountie类实现</FONT>，特别是write()函数的实现。”</P>
<P>我正巴不得逃开Wendy那刺人的目光，便转过头去在我的屏幕上搜索，很快，我找到了：</P><PRE>void Mountie::write(std::ostream &amp;Dudley) const
{
    Dudley &lt;&lt; classID() &lt;&lt; std::endl;
    do_write(Dudley);
}</PRE>
<P>嗨，最近卡通片真是看得太多了，居然犯这样的低级失误。还是<FONT color=#ff00ff>老是</FONT>承认吧：“好了，我明白了。classID()是一个实现细节，用来在保存对象时指示具象类的类型，派生类必须覆盖它，所以必须是纯虚的。但是既然是实现细节，就应该设为private的。”</P>
<P>“这还差不多，小菜鸟。”大虾点了点头，“现在给我解释一下为什麽do_read()和do_write()是protected的？”</P>
<P>这个问题并不难，我组织了一下就回答：“因为派生类对象需要调用这两个函数的实现来读写其中的基类对象。”</P>
<P>“很好很好，”大虾差不多满意了，“不过，你再解释解释为什麽我不把它们设为public的？”</P>
<P>现在我感觉好多了：“因为调用它们的时候必须以一种特定的方式进行。比如do_write()函数，必须先把类型信息写入，再把对象信息写入，这样读取的时候，负责生成对象的模块首先能够知道要读出来的对象是什麽类型的，然後才能正确地从流中读取对象信息。”</P>
<P>“聪明啊，我的小菜鸟 ”Wendy停顿了一下，“就跟学习外国口语一样，<FONT color=#8080ff>学习C++也不光是掌握语法而已，还必须要掌握大量的惯用法</FONT>。”</P>
<P>“是啊是啊，我正打算读Coplien的书...”</P>
<P>[<FONT color=#ff0000>译者注</FONT>：就是James Coplien 1992年的经典着作Advanced C++ Programming Style and Idioms]</P>
<P>大虾挥了挥她的手，“冷静，小菜鸟，我不是指<FONT color=#8080ff>先知Coplien</FONT>的那本书，我是指某种结构背後隐含的惯用法。比如<FONT color=#8080ff>一个类有virtual destructor，相当于告诉你说：‘嗨，我是一个多态基类，来继承我吧 ＇ 而如果一个类的destructor不是虚拟的，则相当於是在说：‘我不能作为多态基类，看在老天的份上，别继承我。</FONT>＇”</P>
<P>“同样的，<FONT color=#8080ff>virtual函数的访问控制级别也具有隐含的意义。一个protected virtual function告诉你：‘你写的派生类应该，哦，可是说是必须调用我的实现。＇而一个private virtual function是在说：‘派生类可以覆盖，也可以不覆盖我，随你的便。但是你不可以调用我的实现</FONT>。＇”</P>
<P>我点点头，告诉她我懂了，然後追问道：“那麽public virtual function呢？”</P>
<P>“<FONT color=#8080ff>尽可能不要使用public virtual function</FONT>。”她拿起一支笔写下了以下代码：</P><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">class HardToExtend </PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">{</PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">public:</PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">	 virtual void f();</PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">};</PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%"> void HardToExtend::f() </PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">{ </PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">	// Perform a specific action </PRE><PRE style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%">}</PRE>
<P style="MARGIN-TOP: -1px; MARGIN-BOTTOM: -1px; LINE-HEIGHT: 100%"></P>
<P>“假设你发布了这个类。在写第二版时，需求有所变化，你必须改用Template Method。可是这根本不可能，你知道为什麽？”</P>
<P>“呃，这个...，不知道。”</P>
<P>“<FONT color=#ff00ff>由</FONT>两种可能的办法。其一，将f()的实现代码转移到一个新的函数中，然後将f()本身设为non-virtual<FONT color=#ff00ff>的</FONT>：</P><PRE>class HardToExtend
{
// possibly protected
    virtual void do_f();
public:
    void f();
};
void HardToExtend::f()
{
    // pre-processing
    do_f();
    // post-processing
}
void HardToExtend::do_f()
{
    // Perform a specific action
}
</PRE>
<P>然而你原来写的派生类都是企图override函数f()而不是do_f()<FONT color=#ff00ff>的</FONT>，你必须改变所有的<FONT color=#ff00ff>派生类实现</FONT>，只要你错过了一个类，你的类层次就会染上<FONT color=#8080ff>先知Meyers</FONT>所说的‘精神分裂的行径＇。”[<FONT color=#ff0000>译者注</FONT>：叁见Scott Meyers，Effective C++, Item 37，绝对不要重新定义继承而来的非虚拟函数]</P>
<P>“另一种办法是将f()移到private区域，引入一个新的non-virtual函数：”</P><PRE>class HardToExtend
{
// possibly protected
    virtual void f();
public:
    void call_f();
};</PRE>
<P>“这会导致无数令人头痛的问题。首先，所有的客户都企图调用f()而不是call_f()，现在它们的代码都不能编译了。更有甚者，大部份派生类都<FONT color=#ff00ff>回</FONT>把f()放在public区域中，这样直接使用派生类的用户可以访问到你本来想保护的细节。”</P>
<P>“对待虚函数要<FONT color=#ff00ff>象</FONT>对待数据成员一样，把它们设为private<FONT color=#ff00ff>的</FONT>，直到设计上要求使用更宽松的访问控制再来调整。要知道<FONT color=#8080ff>由private入public易，由public入private难啊</FONT> ”</P>
<P>[<FONT color=#ff0000>译者注</FONT>：这篇文章所表达的思想具有一定的颠覆性，因为我们太容易在基类中设置public virtual function了，Java中甚至专门为这种做法建立了interface机制，现在竟然说这不好 一时间真是接受不了。但是仔细体会作者的意思，他并不是<FONT color=#ff00ff>一般地</FONT>反对public virtual function，只是在template method大背景下给出上述原则。虽然这个原则在一般的设计中也是值得考虑的，但是主要的应用领域还是在template method模式中。当然，template method是一种非常有用和常用的模式，因此也决定了本文提出的原则具有广泛的意义。]</P>
<P>----------------------------------------------------------------</P>
<P>II. Visitor模式</P>
<P>我正在为一个设计问题苦恼。试用期快结束了，我希望自己解决这个问题，来证明自己的进步。每个人都记得自己的第一份工作吧，也都应该知道在这个时候把活儿做好是多麽的重要 我亲眼看到其他的新雇员没有过完试用期就被炒了鱿鱼，就是因为他们不懂得如何对付那个大虾...，别误会，我不是说她不好，她是我见过最棒的程序员，可就是有点刻薄古怪...。现在我拜她为师，不为别的，就是因为我十分希望能<FONT color=#8080ff>达到她那个高度</FONT>。</P>
<P>我想在一个<FONT color=#ff00ff>类层次(class hierarchy)</FONT>中增加一个新的虚函数，但是这个类层次是由另外一帮人维护的，其他人碰都不能碰：</P><PRE>class Personnel
{
public:
  virtual void Pay ( /*...*/ ) = 0;
  virtual void Promote( /*...*/ ) = 0;
  virtual void Accept ( PersonnelV&amp; ) = 0;
  // ... other functions ...
};

class Officer : public Personnel { /* override virtuals */ };
class Captain : public Officer { /* override virtuals */ };
class First : public Officer { /* override virtuals */ };</PRE>
<P>我想要一个函数，如果对象是船长(Captain)就这麽做，如果是大副(First Officer)就那麽做。Virtual function正是解决之道，在Personnel或者Officer中声明它，而在Captain和First<FONT color=#8080ff>覆盖(override)</FONT>它。</P>
<P>糟糕的是，我不能增加这麽一个虚函数。我知道可以用RTTI给出一个解决方案：</P><PRE>void f( Officer &amp;o )
{
  if( dynamic_cast&lt;Captain*&gt;(&amp;o) )
    /* do one thing */
  else if( dynamic_cast&lt;First*&gt;(&amp;o) )
    /* do another thing */
}

int main()
{
  Captain k;
  First s;
  f( k );
  f( s );
}</PRE>
<P>但是我知道使用RTTI是<FONT color=#ff00ff>公司编码标准</FONT>所排斥的行为，我对自己说：“是的，虽然我以前不喜欢RTTI，但是这回我得改变对它的看法了。很显然，除了使用RTTI，别无它法。”</P>
<P><FONT color=#8080ff><BIG><BIG>“<STRONG>任何问题都可以通过增加间接层次的方法解决。”</STRONG></BIG></BIG></FONT></P>
<P>我噌地一下跳起来，那是大虾的声音，她不知道什麽时候跑到我背後，“啊哟，您吓了我一跳...您刚才说什麽？”</P>
<P>“任何问...”</P>
<P>“是的，我听清楚了，”我也不知道哪来的勇气，居然敢打断她，“我只是不知道您从哪冒出来的。”其实这话只不过是掩饰我内心的慌张。</P>
<P>“哈，算了吧，小菜鸟，”大虾斜 眼看 我，“你以为我不知道你心里想什麽 ”她把声音提高了八度，直盯 我，“那些可怜的C语言门徒才会使用switch语句处理不同的对象类型。你看：”</P><PRE>/* A not-atypical C program */
void f(struct someStruct *s)
{
  switch(s-&gt;type) {
  case APPLE:
    /* do one thing */
    break;
  case ORANGE:
    /* do another thing */
    break;
  /* ... etc. ... */
  }
}</PRE>
<P>“这些人学习<FONT color=#8080ff>Stroustrup教主</FONT>的C++语言时，最重要的事情就是学习<FONT color=#ff00ff>如何设计好的类层次</FONT>。”</P>
<P>“没错，”我又一次打断她，迫不及待地想让Wendy明白，我还是有两下子的，“他们应该设计一个Fruit基类，派生出Apple和Orange，用virtual function来作具体的事情。</P>
<P>“很好，小菜鸟。<FONT color=#8080ff>C语言门徒</FONT>通常老习惯改不掉。但是，你应该知道，通过使用virtual function，你增加了一个间接层次。”她放下笔，“你所需要的不就是一个新的虚函数吗？”</P>
<P>“是的。可是我没有权力这麽干。”</P>
<P>“因为你无权修改类层次，对吧 ”</P>
<P>“您终於了解了情况，我们没法动它。也不知道这个该死的类层次是哪个家伙设计的...” 我嘀嘀咕咕 。</P>
<P>“是我设计的。”</P>
<P>“啊...，真的？ 这个，嘿嘿...”，我极为尴尬。</P>
<P>“这个类层次必须非常稳定，因为有跨平台的问题。但是它的设计允许你增加新的virtual function，而不必烦劳RTTI。你可以通过增加一个间接层次的办法解决这个问题。请问，Personnel::Accept是什麽?”</P>
<P>”嗯，这个...”</P>
<P>“这个类实现了一个模式，可惜这个模式的名字起得不太好，是个PNP，叫Visitor模式。”</P>
<P>[<FONT color=#ff0000>译者注</FONT>：PNP，Poor-Named Pattern, 没起好名字的模式]</P>
<P>“啊，我刚刚读过Visitor模式。但是那<FONT color=#ff00ff>只</FONT>不过是允许若干对象之间相互迭代访问的模式，不是吗？”</P>
<P>她叹了一口气，“这是<FONT color=#ff00ff>流行的</FONT>错误理解。那个V，我觉得毋宁说是Visitor，还不如说是Virtual更好。这个PNP最重要的用途是允许在不改变类层次的前提下，向已经存在的类层次中增加新的虚函数。首先来看看Personnel及其派生类的Accept实现细节。”她拿起笔写下：</P><PRE>void Personnel::Accept( PersonnelV&amp; v )
  { v.Visit( *this ); }

void Officer::Accept ( PersonnelV&amp; v )
  { v.Visit( *this ); }

void Captain::Accept ( PersonnelV&amp; v )
  { v.Visit( *this ); }

void First::Accept ( PersonnelV&amp; v )
  { v.Visit( *this ); }</PRE>
<P>“Visitor的基类如下：”</P><PRE>class PersonnelV/*isitor*/
{
public:
  virtual void Visit( Personnel&amp; ) = 0;
  virtual void Visit( Officer&amp; ) = 0;
  virtual void Visit( Captain&amp; ) = 0;
  virtual void Visit( First&amp; ) = 0;
};</PRE>
<P>“啊，我记起来了。当我要利用Personnel类层次的多态性时，我只要调用Personnel::Accept(myVisitorObject)。由於Accept是虚函数，我的myVisitorObject.Visit()会针对正确的对象类型调用，根据重载法则，编译器会挑选最贴切的那个Visit来调用。这不相当于增加了一个新的虚拟函数了吗？”</P>
<P>“没错，小菜鸟。只要类层次支持Accept，我们就可以在不改动类层次的情况下增加新的虚函数了。”</P>
<P>“好了，我现在知道该怎麽办了”，我写道：</P><PRE>class DoSomething : public PersonnelV
{
public:
  virtual void Visit( Personnel&amp; );
  virtual void Visit( Officer&amp; );
  virtual void Visit( Captain&amp; );
  virtual void Visit( First&amp; );
};

void DoSomething::Visit( Captain&amp; c )
{
  if( femaleGuestStarIsPresent )
    c.TurnOnCharm();
  else
    c.StartFight();
}

void DoSomething::Visit( First&amp; f )
{
  f.RaiseEyebrowAtCaptainsBehavior();
}</PRE><PRE>void f( Personnel&amp; p )
{
  p.Accept( DoSomething() ); // 相当于 p.DoSomething()
}

int main()
{
  Captain k;
  First s;

  f( k );
  f( s );
}</PRE>
<P>大虾满意地笑了，“也许这个模式换一个名字会更好理解，可惜世事往往不遂人意...”。</P>
<P>[<FONT color=#ff0000>译者注</FONT>：这篇文章我作了一定的删节，原文中有稍微多一些的论述，而且提供了两篇技术文章的link。]</P>
<P>from: <A href="http://jjhou.csdn.net/myan-design-patterns-big5.htm">http://jjhou.csdn.net/myan-design-patterns-big5.htm</A></P><img src ="http://www.blogjava.net/weidagang2046/aggbug/12575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-09-09 16:05 <a href="http://www.blogjava.net/weidagang2046/articles/12575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>异步消息的传递－回调机制</title><link>http://www.blogjava.net/weidagang2046/articles/11934.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 04 Sep 2005 06:10:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/11934.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/11934.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/11934.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/11934.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/11934.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR vAlign=top align=left>
<TD>
<P>级别: 中级</P></TD></TR></TBODY></TABLE>
<P><A href="http://www-128.ibm.com/developerworks/cn/linux/l-callback/?ca=dwcn-newsletter-linux#author1"><NAME>陈家朋</NAME></A><BR>系统架构师和技术顾问, 杭州迈可行通信技术有限公司<BR>2003 年 3 月 </P>
<BLOCKQUOTE>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。</BLOCKQUOTE>
<P><A name=1><SPAN class=atitle2>1 什么是回调</SPAN></A><BR>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。 </P>
<P><A name=N10050><B></B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-callback/images/image001.gif" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>对于不同类型的语言（如结构化语言和对象语言）、平台（Win32、JDK）或构架（CORBA、DCOM、WebService），客户和服务的交互除了同步方式以外，都需要具备一定的异步通知机制，让服务方（或接口提供方）在某些情况下能够主动通知客户，而回调是实现异步的一个最简捷的途径。 </P>
<P>对于一般的结构化语言，可以通过回调函数来实现回调。回调函数也是一个函数或过程，不过它是一个由调用方自己实现，供被调用方使用的特殊函数。 </P>
<P>在面向对象的语言中，回调则是通过接口或抽象类来实现的，我们把实现这种接口的类成为回调类，回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言，不仅提供了回调对象、回调方法等特性，也能兼容过程语言的回调函数机制。 </P>
<P>Windows平台的消息机制也可以看作是回调的一种应用，我们通过系统提供的接口注册消息处理函数（即回调函数），从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的，我们可以认为它也是回调函数的一个特例。 </P>
<P>对于分布式组件代理体系CORBA，异步处理有多种方式，如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务，他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程，我们可以通过回调机制来实现。 </P>
<P>下面我们集中比较具有代表性的语言（C、Object Pascal）和架构（CORBA）来分析回调的实现方式、具体作用等。 </P>
<P><A name=2><SPAN class=atitle2>2 过程语言中的回调（C）</SPAN></A><BR></P>
<P><A name=N10072><SPAN class=atitle3>2.1 函数指针</SPAN></A><BR>回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此，要实现回调，必须首先定义函数指针，请看下面的例子： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void Func(char *s)；// 函数原型
void (*pFunc) (char *);//函数指针
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>可以看出，函数的定义和函数指针的定义非常类似。 </P>
<P>一般的化，为了简化函数指针类型的变量定义，提高程序的可读性，我们需要把函数指针类型自定义一下。 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
typedef void(*pcb)(char *);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>回调函数可以象普通函数一样被程序调用，但是只有它被当作参数传递给被调函数时才能称作回调函数。 </P>
<P>被调函数的例子：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void GetCallBack(pcb callback)
{
/*do something*/
}
用户在调用上面的函数时，需要自己实现一个pcb类型的回调函数：
void fCallback(char *s) 
{
/* do something */
} 
然后，就可以直接把fCallback当作一个变量传递给GetCallBack,
GetCallBack（fCallback）;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果赋了不同的值给该参数，那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。 </P>
<P><A name=2.2><SPAN class=atitle2>2.2 参数传递规则</SPAN></A><BR>到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。 </P>
<P>将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
// 被调用函数是以int为参数，以int为返回值
__stdcall int callee(int); 

// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int)); 

// 在p中企图存储被调用函数地址的非法操作
__cdecl int(*p)(int) = callee; // 出错
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列 </P>
<P><A name=N100A9><SPAN class=atitle3>2.3 应用举例</SPAN></A><BR>C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。 </P>
<P>快速排序函数原型：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
二分搜索函数原型：
void *bsearch(const void *key, const void *base, size_t nelem,
				 size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>其中fcmp就是一个回调函数的变量。 </P>
<P>下面给出一个具体的例子： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };

int main(void)
{
   int  x;

   qsort((void *)list, 5, sizeof(list[0]), sort_function);
   for (x = 0; x &lt; 5; x++)
      printf("%i\n", list[x]);
   return 0;
}

int sort_function( const void *a, const void *b)
{
   return *(int*)a-*(int*)b;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>2.4 面向对象语言中的回调（Delphi） </P>
<P>Dephi与C++一样，为了保持与过程语言Pascal的兼容性，它在引入面向对象机制的同时，保留了以前的结构化特性。因此，对回调的实现，也有两种截然不同的模式，一种是结构化的函数回调模式，一种是面向对象的接口模式。 </P>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.4.1 回调函数</B> </P>
<P>回调函数类型定义： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type
   TCalcFunc=function (a:integer;b:integer):integer;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>按照回调函数的格式自定义函数的实现，如</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
function Add(a:integer;b:integer):integer
begin
  result:=a+b;
end;
function Sub(a:integer;b:integer):integer
begin
  result:=a-b;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>回调的使用</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
function Calc(calc:TcalcFunc;a:integer;b:integer):integer
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>下面，我们就可以在我们的程序里按照需要调用这两个函数了</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
c:=calc(add,a,b);//c=a+b
c:=calc(sub,a,b);//c=a-b
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.4.2 回调对象</B> </P>
<P>什么叫回调对象呢，它具体用在哪些场合？首先，让我们把它与回调函数对比一下，回调函数是一个定义了函数的原型，函数体则交由第三方来实现的一种动态应用模式。要实现一个回调函数，我们必须明确知道几点：该函数需要那些参数，返回什么类型的值。同样，一个回调对象也是一个定义了对象接口，但是没有具体实现的抽象类（即接口）。要实现一个回调对象，我们必须知道：它需要实现哪些方法，每个方法中有哪些参数，该方法需要放回什么值。 </P>
<P>因此，在回调对象这种应用模式中，我们会用到接口。接口可以理解成一个定义好了但是没有实现的类，它只能通过继承的方式被别的类实现。Delphi中的接口和COM接口类似，所有的接口都继承与IInterface（等同于IUnknow），并且要实现三个基本的方法QueryInterface, _AddRef, 和_Release。 </P>
<UL xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LI>定义一个接口 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type IShape=interface(IInterface)
	procedure Draw;
end
</CODE></PRE></TD></TR></TBODY></TABLE>
<LI>实现回调类 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TRect=class(TObject,IShape)
	protected
      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
      function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
    public
	  procedure Draw;
end;

type TRound=class(TObject,IShape)
	protected
      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
      function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
    public
	  procedure Draw;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<LI>使用回调对象 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
procedure MyDraw(shape:IShape);
var 
shape:IShape;
begin
shape.Draw; 
end;
</CODE></PRE></TD></TR></TBODY></TABLE></LI></UL>
<P>如果传入的对象为TRect，那么画矩形；如果为TRound，那么就为圆形。用户也可以按照自己的意图来实现IShape接口，画出自己的图形： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
MyDraw(Trect.Create);
MyDraw(Tround.Create);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">2.4.3 回调方法</B> </P>
<P>回调方法(Callback Method)可以看作是回调对象的一部分，Delphi对windows消息的封装就采用了回调方法这个概念。在有些场合，我们不需要按照给定的要求实现整个对象，而只要实现其中的一个方法就可以了，这是我们就会用到回调方法。 </P>
<P>回调方法的定义如下：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
TNotifyEvent = procedure(Sender: TObject) of object; 
TMyEvent=procedure(Sender:Tobject;EventId:Integer) of object;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>TNotifyEvent 是Delphi中最常用的回调方法，窗体、控件的很多事件，如单击事件、关闭事件等都是采用了TnotifyEvent。回调方法的变量一般通过事件属性的方式来定义，如TCustomForm的创建事件的定义： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsForm;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们通过给事件属性变量赋值就可以定制事件处理器。</P>
<P>用户定义对象（包含回调方法的对象）：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TCallback=Class
    procedure ClickFunc(sender:TObject);
end;
procedure Tcallback.ClickFunc(sender:TObject);
begin
  showmessage('the caller is clicked!');
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>窗体对象：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TCustomFrm=class(TForm)
  public
	procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object);
end;

procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent);
begin
  self.OnClick=cb;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>使用方法：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
var
  frm:TcustomFrm;
begin
  frm:=TcustomFrm.Create(Application);
  frm.RegisterClickFunc(Tcallback.Create().ClickFunc);
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=3><SPAN class=atitle2>3 回调在分布式计算中的应用（CORBA）</SPAN></A><BR></P>
<P><A name=N10154><SPAN class=atitle3>3.1 回调接口模型</SPAN></A><BR>CORBA的消息传递机制有很多种，比如回调接口、事件服务和通知服务等。回调接口的原理很简单，CORBA客户和服务器都具有双重角色，即充当服务器也是客户客户。 </P>
<P>回调接口的反向调用与正向调用往往是同时进行的，如果服务端多次调用该回调接口，那么这个回调接口就变成异步接口了。因此，回调接口在CORBA中常常充当事件注册的用途，客户端调用该注册函数时，客户函数就是回调函数，在此后的调用中，由于不需要客户端的主动参与，该函数就是实现了一种异步机制。 </P>
<P>从CORBA规范我们知道，一个CORBA接口在服务端和客户端有不同的表现形式，在客户端一般使用桩（Stub）文件，服务端则用到框架（Skeleton）文件，接口的规格采用IDL来定义。而回调函数的引入，使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型： </P>
<P><A name=N10165><B>3.1.1 范例</B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-callback/images/image002.gif" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> </P>
<P>下面给出了一个使用回调的接口文件，服务端需要实现Server接口的框架，客户端需要实现CallBack的框架：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
module cb
{
	interface CallBack;
	interface Server;

interface CallBack 
{
    	void OnEvent(in long Source,in long msg);
};
  	interface Server 
{
    	long RegisterCB(in CallBack cb);
		void UnRegisterCB(in long hCb);
};
};
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>客户端首先通过同步方式调用服务端的接口RegistCB，用来注册回调接口CallBack。服务端收到该请求以后，就会保留该接口引用，如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数，以便对方及时处理。 </P>
<P>本文源码 <A href="http://www-128.ibm.com/developerworks/cn/linux/l-callback/samplecode.rar" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">下载</A>。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><A name=author1></A><SPAN class=atitle2>关于作者</SPAN><BR>陈家朋，系统架构师和技术顾问，目前担任杭州迈可行通信技术有限公司MPS2000业务交换平台的首席设计人员。擅长架构设计、提供技术解决方案等工作，熟知计算机和通信领域的各种前沿技术。可以通过 <A href="mailto:japen@vip.sina.com" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">japen@vip.sina.com</A>跟他联系。 <BR><BR>from: <A href="http://www-128.ibm.com/developerworks/cn/linux/l-callback/?ca=dwcn-newsletter-linux">http://www-128.ibm.com/developerworks/cn/linux/l-callback/?ca=dwcn-newsletter-linux</A></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/weidagang2046/aggbug/11934.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-09-04 14:10 <a href="http://www.blogjava.net/weidagang2046/articles/11934.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>内联函数与普通函数的区别是什么</title><link>http://www.blogjava.net/weidagang2046/articles/11927.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 04 Sep 2005 00:42:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/11927.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/11927.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/11927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/11927.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/11927.html</trackback:ping><description><![CDATA[内联函数是代码被插入到调用者代码处的函数。如同 #define 宏，内联函数通过避免被调用的开销来提高执行效率，尤其是它能够通过调用（“过程化集成”）被编译器优化。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;内联函数和宏很类似，而区别在于，宏是由预处理器对宏进行替代，而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数，只是在需要用到的时候，内联函数像宏一样的展开，所以取消了函数的参数压栈，减少了调用的开销。你可以象调用函数一样来调用内联函数，而不必担心会产生于处理宏的一些问题。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;声明内联函数看上去和普通函数非常相似： <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT face="Courier New" size=2><FONT color=#ffffff> </FONT><FONT color=#0000ff><B>void </B></FONT>f(<FONT color=#0000ff><B>int </B></FONT>i, <FONT color=#0000ff><B>char </B></FONT>c); <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;当你定义一个内联函数时，在函数定义前加上 inline 关键字，并且将定义放入头文件： <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT face="Courier New" size=2><FONT color=#ffffff> </FONT><FONT color=#0000ff><B>inline</B></FONT><FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp; </FONT><FONT color=#0000ff><B>void </B></FONT>f(<FONT color=#0000ff><B>int </B></FONT>i, <FONT color=#0000ff><B>char </B></FONT>c)<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp; </FONT>{<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp; </FONT><FONT color=#008000>// ...</FONT><FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp; </FONT>} <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;任何在类的说明部分定义的函数都会被自动的认为是内联函数。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;内联函数必须是和函数体申明在一起，才有效。像这样的申明Inline Tablefunction(int I)是没有效果的，编译器只是把函数作为普通的函数申明，我们必须定义函数体。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT face="Courier New" size=2><FONT color=#000000>Inline tablefunction(<FONT color=#0000ff><B>int </B></FONT>I) {<FONT color=#0000ff><B>return </B></FONT>I*I}; <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;这样我们才算定义了一个内联函数。我们可以把它作为一般的函数一样调用。但是执行速度确比一般函数的执行速度要快。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;我们也可以将定义在类的外部的函数定义为内联函数，比如： <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT face="Courier New" size=2><FONT color=#000000>Class TableClass{<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　Private:<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Int I,j;<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　Public: <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Int add() { <FONT color=#0000ff><B>return </B></FONT>I+j;};<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Inline <FONT color=#0000ff><B>int </B></FONT>dec() { <FONT color=#0000ff><B>return </B></FONT>I-j;}<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Int GetNum();<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>}<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT color=#0000ff><B>inline int </B></FONT>tableclass::GetNum(){<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT><FONT color=#0000ff><B>return </B></FONT>I;<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>} <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;上面申明的三个函数都是内联函数。在C++中，在类的内部定义了函数体的函数，被默认为是内联函数。而不管你是否有inline关键字。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;内联函数在C++类中，应用最广的，应该是用来定义存取函数。我们定义的类中一般会把数据成员定义成私有的或者保护的，这样，外界就不能直接读写我们类成员的数据了。对于私有或者保护成员的读写就必须使用成员接口函数来进行。如果我们把这些读写成员函数定义成内联函数的话，将会获得比较好的效率。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;<FONT face="Courier New" size=2><FONT color=#000000>Class sample{<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　Private:<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Int nTest;<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　Public:<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　　Int readtest(){ <FONT color=#0000ff><B>return </B></FONT>nTest;}<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>　Void settest(<FONT color=#0000ff><B>int </B></FONT>I) {nTest=I;}<FONT color=#ffffff> <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT>} <FONT color=#ffffff><BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;</FONT></FONT> <BR>&nbsp;&nbsp;&nbsp;&nbsp;当然，内联函数也有一定的局限性。就是函数中的执行代码不能太多了，如果，内联函数的函数体过大，一般的编译器会放弃内联方式，而采用普通的方式调用函数。这样，内联函数就和普通函数执行效率一样了。 <BR><BR>from: <A href="http://www.china-askpro.com/msg49/qa00.shtml">http://www.china-askpro.com/msg49/qa00.shtml</A></FONT></FONT></FONT><img src ="http://www.blogjava.net/weidagang2046/aggbug/11927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2005-09-04 08:42 <a href="http://www.blogjava.net/weidagang2046/articles/11927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>