﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-More than C++-随笔分类-C++</title><link>http://www.cppblog.com/Lee/category/7130.html</link><description>Beyond  C++</description><language>zh-cn</language><lastBuildDate>Sun, 03 May 2009 15:17:31 GMT</lastBuildDate><pubDate>Sun, 03 May 2009 15:17:31 GMT</pubDate><ttl>60</ttl><item><title>C++ 注册表操作总结</title><link>http://www.cppblog.com/Lee/archive/2009/05/03/81775.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sun, 03 May 2009 09:30:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/05/03/81775.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/81775.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/05/03/81775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/81775.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/81775.html</trackback:ping><description><![CDATA[
		<div class="cnt" id="blog_text">
				<div>
						<strong>1、RegCloseKey()</strong>
						<br />　　原型：RegCloseKey(HKEY hKey)<br /><br />　　解释：关闭指定的注册表键，释放句柄。当对一个或多个键或值操作完成以后，需要关闭其键来进行保存操作结果，关闭一个键后，句柄变为非法，此时应释放句柄。</div>
				<div>
				</div>
				<div>
						<strong>2、RegCreateKeyEx()</strong>
						<br />　　原型：LONG RegCreateKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved,<br />LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes,<br />PHKEY phkResult, LPDWORD lpdwDisposition );<br /><br />　　解释：打开指定的键或子键。如果要打开的键不存在的话，本函数会试图建立它。提供该函数是为了向后兼容。所有的WIN32应用程序应使用函数RegCreateKeyEx（）。　各参数及返回值的含义如下：</div>
				<div>
						<br />·hKey为主键值，可以取下面的一些数值：HKEY_CLASSES_ROOT、HKEY_CURRENT_CONFIG、　　HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE、HKEY_USER、HKEY_PERFORMANCE_DATA(WINNT操作系统)、HKEY_DYN_DATA（WIN9X操作系统）；</div>
				<div>·参数lpSubKey为一个指向以零结尾的字符串的指针，其中包含将要创建或打开的子键的名称。子键不可以用反斜线（\）开始。该参数可以为NULL；<br /><br />·参数Reserved为保留值，必须设置为0；<br /><br />·参数lpClass为一个指向包含键类型的字符串。如果该键已经存在，则忽略该参数；<br /><br />·参数dwOptions为新创建的键设置一定的属性。可以取下面的一些数值：　　REG_OPTION_NON_VOLATILE ，表示新创建的键为一个非短暂性的键（数据信息保存在文件中，当系统重新启动时，数据信息恢复）；REG_OPTION_VOLATILE，表示新创建的键为一个短暂性的键（数据信息保存在内存中），Windows95忽略该数值；REG_OPTION_BACKUP_RESTORE 仅在WINNT中支持，可以提供优先级支持；<br /><br />　　·参数samDesired用来设置对键访问的权限，可以取下面的一些数值：KEY_CREATE_LINK，表示准许生成符号键；KEY_CREATE_SUB_KEY 表示准许生成子键；KEY_ENUMERATE_SUB_KEYS 表示准许生成枚举子键；KEY_EXECUTE 表示准许进行读操作；KEY_NOTIFY表示准许更换通告；　　KEY_QUERY_VALUE 表示准许查询子键；KEY_ALL_ACCESS 提供完全访问，是上面数值的组合；<br /><br />　　KEY_READ 是下面数值的组合：KEY_QUERY_VALUE、KEY_ENUMERATE_SUB_KEYS、KEY_NOTIFY；　　KEY_SET_VALUE 表示准许设置子键的数值；KEY_WRITE 是下面数值的组合：KEY_SET_VALUE、KEY_CREATE_SUB_KEY；<br />·参数lpSecurityAttributes为一个指向SECURITY_ATTRIBUTES结构的指针，确定返回的句柄是否被子处理过程继承。如果该参数为NULL，则句柄不可以被继承。在WINNT中，该参数可以为新创建的键增加安全的描述；<br /><br />　　·参数phkResult为一个指向新创建或打开的键的句柄的指针；<br /><br />　　·参数lpdwDispition指明键是被创建还是被打开的，可以是下面的一些数值：　　REG_CREATE_NEW_KEY 表示键先前不存在，现在被创建；REG_OPENED_EXISTING_KEY 表示键先前已存在，现在被打开。<br /><br />　　如果该函数调用成功，则返回ERROR_SUCCESS。否则，返回值为文件WINERROR.h中定义的一个非零的错误代码，可以通过设置FORMAT_MESSAGE_FROM_SYSTEM标识调用FormatMessage（）函数来获取一个对错误的总体描述。</div>
				<div>
						<strong>3、RegOpenKeyEx（）</strong>
						<br />　　原型：LONG RegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions,<br />REGSAM samDesired, PHKEY phkResult );<br /><br />　　解释：打开一个指定的键，并返回打开键的句柄。<br /><br />各参数及返回值的含义如下：<br />·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />·参数lpSubKey为一个指向以零结尾的字符串的指针，其中包含子键的名称，可以利用反斜线（\）分隔不同的子键名。如果字符串为空，则根据hKey参数创建一个新的句柄。在这种情况下，并不关闭先前打开的句柄；</div>
				<div>·参数ulOption保留，通常必须设置为0；<br /><br />·参数samDesired的含义同RegCreateKeyEx函数中的samDesired参数；<br /><br />·参数phkResult为一个指针，用来指向打开的键的句柄。可以通过RegCloseKey函数关闭这个句柄；<br /><br />·函数的返回值同RegCreateKeyEx函数的返回值。</div>
				<div>
						<strong>4、查询某一个键值：RegQueryValueEx（）<br /></strong>
						<br />　　原型：LONG RegQueryValueEx(HKEY hKey, LPCTSTR lpValueName, LPDWORD pReserved, LPDWORD lpType,<br />LPBYTE lpData, LPDWORD lpcbData );<br /><br />　　解释：根据要查询的键的句柄，要返回的查询的数据。<br /><br />　　各个参数及返回值的含义如下：<br /><br />　　·参数hKey为当前的一个打开的键的句柄，具体数值同RegCreateKeyEx函数的hKey参数；<br /><br />　　·参数lpVauleName为一个指向非空的包含查询值的名称的字符串指针；<br /><br />　　·参数lpReserved保留，必须为NULL；<br /><br />　　·参数lpType为一个指向数据类型的指针，数据类型为下列类型之一：REG_BINARY 二进制数据、REG_DWORD 32位整数、REG_DWORD_LITTLE_ENDIAN little－endian格式的数据，例如0X12345678以（0X78 0X56 0X34 0X12）方式保存、REG_DWORD_BIG_ENDIAN big－endian格式的数据，例如0X12345678以（0X12 0X34 0X56 0X78）方式保存、REG_EXPAND_SZ 一个包含未扩展环境变量的字符串、REG_LINK 一个Unicode类型的链接、REG_MULIT_SZ 以两个零结尾的字符串、REG_NONE 无类型数值、REG_RESOURCE_LIST 设备驱动资源列表、REG_SZ 一个以零结尾的字符串根据函数使用的字符集类型的不同而设置为Unicode或ANSI类型的字符串；<br /><br />　　·参数lpData为一个指向保存返回值的变量的指针。如果不需要返回值，该参数可以为NULL；<br /><br />　　·参数lpcbData为一个指向保存返回值长度的变量的指针。其中长度以字节为单位。如果数据类型为REG_SZ、REG_MULTI_SZ或REG_EXPAND_SZ，那么长度也包括结尾的零字符，只有在参数lpData为NULL时，参数lpcbData才可以为NULL；返回值同RegCreateKeyEx函数的返回值；</div>
				<div>
						<strong>　5、RegSetValueEx（）</strong>
						<br />
						<br />　　原型：LONG RegSetValueEx(HKEY hKey, LPCTSTR lpValueName, LPDWORD lpReserved, DWORD dwType,<br />const BYTE *lpData, DWORD cbData);<br /><br />　　解释：设置注册表中的一个键值。<br /><br />　　各个参数及返回值的含义如下：<br /><br />　　·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />　　·参数lpValueName为一个指向包含值名的字符串指针；Reserved保留，通常必须设置为0；<br /><br />　　·参数dwType确定了设置的值的类型同RegQueryValueKeyEx的lyType参数；<br /><br />　　·参数lpData为一个指向包含数据的缓冲区的指针；<br /><br />　　·参数cbData以字节为单位，指定数据的长度；<br /><br />　　返回值同RegCreateKeyEx函数的返回值。</div>
				<div>
						<strong>6、RegDeketeKey（）</strong>
						<br />
						<br />　　原型：LONG RegDeleteKey（HKEY hKey，LPCTSTR lpSubKEY）；<br /><br />　　解释：函数RegDeketeKey删除一个键及所有的子键。<br /><br />　　各个参数及返回值的含义如下：<br /><br />　　·参数hKey的含义同RegCreateKeyEx函数中的hKey参数；<br /><br />　　·参数lpSubKey的含义同RegCreateKeyEx函数中的lpSubKey参数。</div>
		</div>
<img src ="http://www.cppblog.com/Lee/aggbug/81775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-05-03 17:30 <a href="http://www.cppblog.com/Lee/archive/2009/05/03/81775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>局部变量、全局变量、静态变量</title><link>http://www.cppblog.com/Lee/archive/2009/03/19/77103.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Wed, 18 Mar 2009 23:28:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/03/19/77103.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/77103.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/03/19/77103.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/77103.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/77103.html</trackback:ping><description><![CDATA[
		<p>静态变量的类型说明符是static。 静态变量当然是属于静态存储方式，但是属于静态存储方式的量不一定就是静态变量，例如外部变量虽属于静态存储方式，但不一定是静态变量，必须由 static加以定义后才能成为静态外部变量，或称静态全局变量。对于自动变量，它属于动态存储方式。 但是也可以用static定义它为静态自动变量，或称静态局部变量，从而成为静态存储方式。</p>
		<p>　　由此看来， 一个变量可由static进行再说明，并改变其原有的存储方式。</p>
		<p>　　1. 静态局部变量</p>
		<p>　　在局部变量的说明前再加上static说明符就构成静态局部变量。</p>
		<p>　　例如：</p>
		<p>　　static int a,b;</p>
		<p>　　static float array[5]={1,2,3,4,5}； </p>
		<p>　　静态局部变量属于静态存储方式，它具有以下特点：</p>
		<p>　　(1)静态局部变量在函数内定义，但不象自动变量那样，当调用时就存在，退出函数时就消失。静态局部变量始终存在着，也就是说它的生存期为整个源程序。</p>
		<p>　　(2)静态局部变量的生存期虽然为整个源程序，但是其作用域仍与自动变量相同，即只能在定义该变量的函数内使用该变量。退出该函数后，尽管该变量还继续存在，但不能使用它。</p>
		<p>　　(3)允许对构造类静态局部量赋初值。若未赋以初值，则由系统自动赋以0值。</p>
		<p>　　(4)对基本类型的静态局部变量若在说明时未赋以初值，则系统自动赋予0值。而对自动变量不赋初值，则其值是不定的。 根据静态局部变量的特点， 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用，但如再次调用定义它的函数时，它又可继续使用，而且保存了前次被调用后留下的值。 因此，当多次调用一个函数且要求在调用之间保留某些变量的值时，可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的，但全局变量有时会造成意外的副作用，因此仍以采用局部静态变量为宜</p>
		<p>　　2.静态全局变量</p>
		<p>　　全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式， 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序，当一个源程序由多个源文件组成时，非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域， 即只在定义该变量的源文件内有效， 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内，只能为该源文件内的函数公用，因此可以避免在其它源文件中引起错误。从以上分析可以看出， 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域，限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。</p>
		<p>　　静态变量</p>
		<p>　　除范围之外，变量还有存活期，在这一期间变量能够保持它们的值。在应用程序的存活期内一直保持模块级变量和公用变量的值。但是，对于 Dim 声明的局部变量以及声明局部变量的过程，仅当过程在执行时这些局部变量才存在。通常，当一个过程执行完毕，它的局部变量的值就已经不存在，而且变量所占据的内存也被释放。当下一次执行该过程时，它的所有局部变量将重新初始化。</p>
		<p>　　但可将局部变量定义成静态的，从而保留变量的值。在过程内部用 Static 关键字声明一个或多个变量，其用法和 Dim 语句完全一样：</p>
		<p>　　Static Depth</p>
		<p>　　例如，下面的函数将存储在静态变量 Accumulate 中的以前的运营总值与一个新值相加，以计算运营总值。</p>
		<p>　　Function RunningTotal (num)</p>
		<p>　　Static ApplesSold</p>
		<p>　　ApplesSold = ApplesSold + num</p>
		<p>　　RunningTotal = ApplesSold</p>
		<p>　　End Function</p>
		<p>　　如果用 Dim 而不用 Static 声明 ApplesSold，则以前的累计值不会通过调用函数保留下来，函数只会简单地返回调用它的那个相同值。</p>
		<p>　　在模块的声明段声明 ApplesSold，并使它成为模块级变量，由此也会收到同样效果。但是，这种方法一旦改变变量的范围，过程就不再对变量排他性存取。由于其它过程也可以访问和改变变量的值，所以运营总值也许不可靠，代码将更难于维护。</p>
		<p>　　声明所有的局部变量为静态变量</p>
		<p>　　为了使过程中所有的局部变量为静态变量，可在过程头的起始处加上 Static 关键字。例如：</p>
		<p>　　Static Function RunningTotal (num)</p>
		<p>　　这就使过程中的所有局部变量都变为静态，无论它们是用 Static、Dim 或 Private 声明的还是隐式声明的。可以将 Static 放在任何 Sub 或 Funtion 过程头的前面，包括事件过程和声明为 Private 的过程。</p>
		<p>
				<br />文章出处：DIY部落(<a href="http://www.diybl.com/course/3_program/vc/vc_js/20090215/155523.html">http://www.diybl.com/course/3_program/vc/vc_js/20090215/155523.html</a>)</p>
<img src ="http://www.cppblog.com/Lee/aggbug/77103.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-03-19 07:28 <a href="http://www.cppblog.com/Lee/archive/2009/03/19/77103.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++中static的作用</title><link>http://www.cppblog.com/Lee/archive/2009/03/19/77101.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Wed, 18 Mar 2009 22:36:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/03/19/77101.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/77101.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/03/19/77101.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/77101.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/77101.html</trackback:ping><description><![CDATA[
		<p>   1、什么是static?<br />       static 是C++中很常用的修饰符，它被用来控制变量的存储方式和可见性。</p>
		<p>    2、为什么要引入static?<br />       函数内部定义的变量，在程序执行到它的定义处时，编译器为它在栈上分配空间，大家知道，函数在栈上分配的空间在此函数执行结束时会释放掉，这样就产生了一 个问题: 如果想将函数中此变量的值保存至下一次调用时，如何实现？ 最容易想到的方法是定义一个全局的变量，但定义为一个全局变量有许多缺点，最明显的缺点是破坏了此变量的访问范围（使得在此函数中定义的变量，不仅仅受此 函数控制）。</p>
		<p>    3、什么时候用static?<br />       需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部，对外不可见。</p>
		<p>    4、static的内部机制：<br />       静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用，所以静态数据成员不能在任何函数内分配空间和初始化。<br />       这样，它的空间分配有三个可能的地方，一是作为类的外部接口的头文件，那里有类声明；二是类定义的内部实现，那里有类的成员函数定义；三是应用程序的main（）函数前的全局数据声明和定义处。<br />      静态数据成员要实际地分配空间，故不能在类的声明中定义（只能声明数据成员）。类声明只声明一个类的“尺寸和规格”，并不进行实际的内存分配，所以在类声 明中写成定义是错误的。它也不能在头文件中类声明的外部定义，因为那会造成在多个使用该类的源文件中，对其重复定义。<br />      static被引入以告知编译器，将变量存储在程序的静态存储区而非栈上空间，静态<br />数据成员按定义出现的先后顺序依次初始化，注意静态成员嵌套时，要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。</p>
		<p>    5、static的优势：<br />       可以节省内存，因为它是所有对象所公有的，因此，对多个对象来说，静态数据成员只存储一处，供所有对象共用。静态数据成员的值对每个对象都是一样，但它的 值是可以更新的。只要对静态数据成员的值更新一次，保证所有对象存取更新后的相同的值，这样可以提高时间效率。</p>
		<p>    6、引用静态数据成员时，采用如下格式：<br />         &lt;类名&gt;::&lt;静态成员名&gt;<br />    如果静态数据成员的访问权限允许的话(即public的成员)，可在程序中，按上述格式<br />来引用静态数据成员。</p>
		<p>    7、注意事项：<br />      (1)类的静态成员函数是属于整个类而非类的对象，所以它没有this指针，这就导致<br />了它仅能访问类的静态数据和静态成员函数。<br />      (2)不能将静态成员函数定义为虚函数。<br />      (3)由于静态成员声明于类中，操作于其外，所以对其取地址操作，就多少有些特殊<br />，变量地址是指向其数据类型的指针 ，函数地址类型是一个“nonmember函数指针”。</p>
		<p>      (4)由于静态成员函数没有this指针，所以就差不多等同于nonmember函数，结果就<br />产生了一个意想不到的好处：成为一个callback函数，使得我们得以将C++和C-based X W<br />indow系统结合，同时也成功的应用于线程函数身上。<br />      (5)static并没有增加程序的时空开销，相反她还缩短了子类对父类静态成员的访问<br />时间，节省了子类的内存空间。<br />      (6)静态数据成员在&lt;定义或说明&gt;时前面加关键字static。<br />      (7)静态数据成员是静态存储的，所以必须对它进行初始化。<br />      (8)静态成员初始化与一般数据成员初始化不同:<br />       初始化在类体外进行，而前面不加static，以免与一般静态变量或对象相混淆；<br />       初始化时不加该成员的访问权限控制符private，public等；<br />           初始化时使用作用域运算符来标明它所属类；<br />           所以我们得出静态数据成员初始化的格式：<br />         &lt;数据类型&gt;&lt;类名&gt;::&lt;静态数据成员名&gt;=&lt;值&gt;<br />      (9)为了防止父类的影响，可以在子类定义一个与父类相同的静态变量，以屏蔽父类的影响。这里有一点需要注意：我们说静态成员为父类和子类共享，但我们有 重复定义了静态成员，这会不会引起错误呢？不会，我们的编译器采用了一种绝妙的手法：name-mangling 用以生成唯一的标志。</p>
		<p>
		</p>
		<p>
				<strong>静态数据成员<br /><br /></strong>　　在类中，静态成员可以实现多个对象之间的数据共享，并且使用静态数据成员还不会破坏隐藏的原则，即保证了安全性。因此，静态成员是类的所有对象中共享的成员，而不是某个对象的成员。<br /><br />使用静态数据成员可以节省内存，因为它是所有对象所公有的，因此，对多个对象来说，静态数据成员只存储一处，供所有对象共用。静态数据成员的值对每个 对象都是一样，但它的值是可以更新的。只要对静态数据成员的值更新一次，保证所有对象存取更新后的相同的值，这样可以提高时间效率。<br /><br />静态数据成员的使用方法和注意事项如下：<br /><br />1、静态数据成员在定义或说明时前面加关键字static。<br /><br />2、静态成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式如下：<br /><br />&lt;数据类型&gt;&lt;类名&gt;::&lt;静态数据成员名&gt;=&lt;值&gt;<br /><br />这表明：<br /><br />      　　(1) 初始化在类体外进行，而前面不加static，以免与一般静态变量或对象相混淆。<br /><br />(2) 初始化时不加该成员的访问权限控制符private，public等。<br /><br />(3) 初始化时使用作用域运算符来标明它所属类，因此，静态数据成员是类的成员，而不是对象的成员。<br /><br />3、静态数据成员是静态存储的，它是静态生存期，必须对它进行初始化。<br /><br />4、引用静态数据成员时，采用如下格式：<br /><br />&lt;类名&gt;::&lt;静态成员名&gt;<br /><br />如果静态数据成员的访问权限允许的话(即public的成员)，可在程序中，按上述格式来引用静态数据成员。</p>
		<p>
				<strong>静态成员函数<br /><br /></strong>　　静态成员函数和静态数据成员一样，它们都属于类的静态成员，它们都不是对象成员。因此，对静态成员的引用不需要用对象名。<br /><br />在静态成员函数的实现中不能直接引用类中说明的非静态成员，可以引用类中说明的静态成员。如果静态成员函数中要引用非静态成员时，可通过对象来引用。</p>
		<p>
				<br />下面看一个例子：<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{<br />}<br />static void init()<br />{  <br />} <br />};<br />void main( void )<br />{<br />Point pt;<br />pt.init();<br />pt.output(); <br />}<br />这样编译是不会有任何错误的。<br />下面这样看<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{  <br />}<br />static void init()<br />{  <br />} <br />};<br />void main( void )<br />{<br />Point::output();<br />}<br />这样编译会处错，错误信息：<a name="baidusnap0"></a><strong style="COLOR: black; BACKGROUND-COLOR: rgb(255,255,102)">illegal</strong><a name="baidusnap1"></a><strong style="COLOR: black; BACKGROUND-COLOR: rgb(160,255,255)">call</strong><a name="baidusnap2"></a><strong style="COLOR: black; BACKGROUND-COLOR: rgb(153,255,153)">of</strong><a name="baidusnap3"></a><strong style="COLOR: black; BACKGROUND-COLOR: rgb(255,153,153)">non-static</strong><a name="baidusnap6"></a><strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong><a name="baidusnap7"></a><strong style="COLOR: white; BACKGROUND-COLOR: rgb(136,104,0)">function</strong>，为什么？<br />因为在没有实例化一个类的具体对象时，类是没有被分配内存空间的。<br />好的再看看下面的例子:<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{  <br />}<br />static void init()<br />{  <br />} <br />};<br />void main( void )<br />{<br />Point::init();<br />}<br />这时编译就不会有错误，因为在类的定义时，它静态数据和成员函数就有了它的内存区，它不属于类的任何一个具体对象。<br />好的再看看下面的例子:<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{  <br />}<br />static void init()<br />{ <br />   x = 0;<br />   y = 0;<br />}<br />private:<br />int x;<br />int y;<br />};<br />void main( void )<br />{<br />Point::init();<br />}<br />编译出错：<br /><strong style="COLOR: black; BACKGROUND-COLOR: rgb(255,255,102)">illegal</strong> reference to data <strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong> 'Point::x' in a static <strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong><strong style="COLOR: white; BACKGROUND-COLOR: rgb(136,104,0)">function</strong><br /><strong style="COLOR: black; BACKGROUND-COLOR: rgb(255,255,102)">illegal</strong> reference to data <strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong> 'Point::y' in a static <strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong><strong style="COLOR: white; BACKGROUND-COLOR: rgb(136,104,0)">function</strong><br />在一个静态成员函数里错误的引用了数据成员，<br />还是那个问题，静态成员（函数），不属于任何一个具体的对象，那么在类的具体对象声明之前就已经有了内存区，<br />而现在非静态数据成员还没有分配内存空间，那么这里调用就错误了，就好像没有声明一个变量却提前使用它一样。<br />也就是说在静态成员函数中不能引用非静态的成员变量。<br />好的再看看下面的例子:<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{<br />   x = 0;<br />   y = 0;<br />   init();  <br />}<br />static void init()<br />{</p>
		<p>}<br />private:<br />int x;<br />int y;<br />};<br />void main( void )<br />{<br />Point::init();<br />}<br />好的，这样就不会有任何错误。这最终还是一个内存模型的问题，<br />任何变量在内存中有了自己的空间后，在其他地方才能被调用，否则就会出错。<br />好的再看看下面的例子:<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{ <br />}<br />static void init()<br />{ <br />   x = 0;<br />   y = 0;<br />}<br />private:<br />static int x;<br />static int y;<br />};<br />void main( void )<br />{<br />Point::init();<br />}<br />编译：<br />Linking...<br />test.obj : error LNK2001: unresolved external symbol "private: static int Point::y" <br />test.obj : error LNK2001: unresolved external symbol "private: static int Point::x" <br />Debug/Test.exe : fatal error LNK1120: 2 unresolved externals<br />执行 link.exe 时出错.<br />可以看到编译没有错误，连接错误，这又是为什么呢？<br />这是因为静态的成员变量要进行初始化，可以这样：<br />#include &lt;iostream.h&gt;////////////////////////(这段代码还有疑问，init()中的x, y 和static 的x ,y 有联系吗？)<br />class Point<br />{<br />public:<br />void output()<br />{ <br />}<br />static void init()<br />{ <br />   x = 0;<br />   y = 0;<br />}<br />private:<br />static int x;<br />static int y;<br />};<br />int Point::x = 0;<br />int Point::y = 0;<br />void main( void )<br />{<br />Point::init();<br />}<br />在静态成员数据变量初始化之后就不会出现编译错误了。<br />再看看下面的代码：<br />#include &lt;iostream.h&gt;<br />class Point<br />{<br />public:<br />void output()<br />{ <br />}<br />static void init()<br />{ <br />   x = 0;<br />   y = 0;<br />}<br />private:<br />static int x;<br />static int y;<br />};<br />void main( void )<br />{<br />}<br />编译没有错误，为什么？<br />即使他们没有初始化，因为我们没有访问x，y，所以编译不会出错。  </p>
		<p>
		</p>
		<p>
				<font size="2">C++会区分两种类型的成员函数：静态成员函数和非静态成员函数。这两者之间的一个重大区别是，静态成员函数不接受隐含的<em>this</em>自变量。所以，它就无法访问自己类的非静态成员。</font>
		</p>
		<p>
				<font size="2">在某些条件下，比如说在使用诸如pthread（它不支持类）此类的多线程库时，就必须使用静态的成员函数，因为其地址同C语言函数的地址兼容。这种铜限制就迫使程序员要利用各种解决办法才能够从静态成员函数访问到非静态数据成员。</font>
		</p>
		<p>
				<font size="2">第一个解决办法是声明类的所有数据成员都是静态的。运用这种方式的话，静态的成员函数就能够直接地访问它们，例如：</font>
		</p>
		<p>
				<font size="2">
						<font color="#0066ff">class Singleton<br />{<br />public:<br />   static Singleton * instance();<br />private:<br />   Singleton * p;<br />   static Lock lock;<br />};</font>
						<br />
						<br />
				</font>
				<font color="#0066ff" size="2">Singleton * Singleton::instance()<br />{<br />lock.getlock(); <font color="#006600">// fine, lock is static</font><br />if (!p)<br />   p=new Singleton;<br />lock.unlock();<br />return p;<br />} </font>
		</p>
		<p>
				<font size="2">这种解决方法不适用于需要使用非静态数据成员的类。</font>
		</p>
		<p>
				<font size="2">访问非静态数据成员</font>
		</p>
		<p>
				<font size="2">将参照传递给需要考量的对象能够让静态的成员函数访问到对象的非静态数据：</font>
		</p>
		<p>
				<font color="#0066ff" size="2">class A<br />{<br />public:<br />   static void func(A &amp; obj);<br />   intgetval() const; <font color="#006600">//<strong style="COLOR: black; BACKGROUND-COLOR: rgb(255,153,153)">non-static</strong><strong style="COLOR: white; BACKGROUND-COLOR: rgb(0,170,0)">member</strong><strong style="COLOR: white; BACKGROUND-COLOR: rgb(136,104,0)">function</strong></font><br />private:<br />intval;<br />}; </font>
		</p>
		<p>
				<font size="2">静态成员函数func()会使用参照<em>obj</em>来访问非静态成员<em>val</em>。</font>
		</p>
		<p>
				<font color="#0066ff" size="2">voidA::func(A &amp; obj)<br />{<br />   int n = obj.getval();<br />} </font>
		</p>
		<p>
				<font size="2">将一个参照或者指针作为静态成员函数的自变量传递，就是在模仿自动传递非静态成员函数里<em>this</em>自变量这一行为。</font>
		</p>
<img src ="http://www.cppblog.com/Lee/aggbug/77101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-03-19 06:36 <a href="http://www.cppblog.com/Lee/archive/2009/03/19/77101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>默认生成的函数和屏蔽它们的方法</title><link>http://www.cppblog.com/Lee/archive/2009/03/19/77100.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Wed, 18 Mar 2009 21:17:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/03/19/77100.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/77100.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/03/19/77100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/77100.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/77100.html</trackback:ping><description><![CDATA[如果你只是声明一个空类，不做任何事情的话，编译器会自动为你生成一个默认构造函数、一个拷贝默认构造函数、一个默认拷贝赋值操作符和一个默认析构函数。<font color="#008080"><strong>这些函数只有在第一次被调用时，才会别编译器创建。所有这些函数都是inline和public的。</strong></font><br /><br /><font color="#008080"><strong>默认的析构函数是非虚函数</strong></font>（除非基类有自己声明的虚析构函数）。<font color="#008080"><strong>而拷贝默认构造函数和默认拷贝赋值操作符知识是单纯将来源对象的每一个非静态成员拷贝到对象目标中（bitwise copy）。</strong></font><br /><br />其中的默认拷贝赋值操作符只有在生成的代码合法并且有机会证明它有意义存在时才会生成。这就说明，<font color="#008080"><strong>如果你打算在一个“内含引用成员”或者“内含const成员”的类内支持赋值操作，就必须定义自己的默认拷贝赋值操作符。</strong></font>因为C++本身不允许引用改指不同的对象，也不允许更改const成员。<br /><br />最后一种情况，<font color="#008080"><strong>当基类将自己的默认拷贝赋值操作符声明为private时，子类就不会产生自己的的默认拷贝赋值操作符。</strong></font>因为假如产生了这样的默认拷贝赋值操作符，它会试着去调用基类的默认拷贝赋值操作符去处理基类的部分，不幸的是，它没有权利。<br /><br />你可以将拷贝构造函数或默认拷贝赋值操作符声明为private。这样明确声明一个成员函数，就阻止了编译器暗自创建的默认版本，而这些函数为private，使得可以成功阻止人们调用它。<br /><br />上面的做法有一个隐患，因为类自身的member和friend还是可以调用这些private函数。有一个很刁钻的方法，“<font color="#008080"><strong>将成员函数声明为private而且故意不实现它们</strong></font>”，这样既阻止了默认函数的生成，而且如果你试着调用这些函数，就会得到一个链接错误。只声明，不定义，链接器报错。甚至在声明的时候，你连参数也不用写。<br /><br />而试着将上述的链接器错误提前到编译器也是可以的。我们专门设计一个类Unconpyable。<br />--------------------------------------------------------------------<br />class Uncopybale {<br />protected:<br />    Uncopyable() {}<br />    ~Uncopyable() {}<br />private:<br />    Ucopyable(const Uncopyable&amp;)<br />    Uncopyable&amp; operator=(const Uncopyable&amp;)<br />};<br />--------------------------------------------------------------------<br />为了阻止对象被拷贝，<font color="#008080"><strong>我们唯一需要做的就是继承Uncopyable</strong></font>。这些函数的默认生成版本会尝试调用其基类的对应版本，那些调用会被编译器拒绝，因为它基类的拷贝函数是private。<br /><br />Boost提供的noncopyable类也有类似的功能。<br /><br /><font style="BACKGROUND-COLOR: rgb(0,128,128)" color="#ffffff"><strong>忠告：<br /><br />为了驳回编译器自动提供的技能，可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的基类也是一种做法。</strong></font><img src ="http://www.cppblog.com/Lee/aggbug/77100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-03-19 05:17 <a href="http://www.cppblog.com/Lee/archive/2009/03/19/77100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++ 标准编程 之 类型转换</title><link>http://www.cppblog.com/Lee/archive/2009/03/09/76028.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Mon, 09 Mar 2009 13:34:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/03/09/76028.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/76028.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/03/09/76028.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/76028.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/76028.html</trackback:ping><description><![CDATA[
		<span class="pg" id="xydwtext"> 
<p><font size="3"><strong>一、自动类型转换</strong></font></p><p>    一般来说，同一句语句或表达式应该使用同一种类型的变量和常量。如果使用了多种类型的变量和常量（类型混用），C 会自动把它们转换成同一种类型。自动类型转换为我们写程序提供了方便，却也带来了危机。因粗心大意而造成的类型混用也许会导致程序运行出错。以下是自动类型转换的基本规则：</p><p>    <font color="#ff0000"><strong>1.</strong></font> 在表达式中，char 和 short 类型的值，无论有符号还是无符号，都会自动转换成 int 或者 unsigned int（如果 short 的大小和 int 一样，unsigned short 可表示的最大值就大于 int，在这种情况下，unsigned short 被转换成 unsigned int）。因为它们被转换成表示范围更大的类型，故而我们把这种转换称之为“<strong>升级</strong>（<font color="#990000">promotion</font>）”。</p><p>    <font color="#ff0000"><strong>2.</strong></font> 按照从高到低的顺序给各种数据类型分等级，依次为：long double, double, float, unsigned long long, long long, unsigned long, long, unsigned int 和 int。这里有一个小小的例外，如果 long 和 int 大小相同，则 unsigned int 的等级应位于 long 之上。char 和 short 并没有出现于这个等级列表，是因为它们参与运算时就应该已经被升级成了 int 或者 unsigned int。</p><p>    <font color="#ff0000"><strong>3.</strong></font> 在任何涉及两种数据类型的操作中，它们之间等级较低的类型会被转换成等级较高的类型。</p><p>    <font color="#ff0000"><strong>4.</strong></font> 在赋值语句中，= 右边的值在赋予 = 左边的变量之前，首先要将右边的值的数据类型转换成左边变量的类型。也就是说，左边变量是什么数据类型，右边的值就要转换成什么数据类型的值。这个过程可能导致右边的值的类型升级，也可能导致其类型<strong>降级</strong>（<font color="#990000">demotion</font>）。所谓“降级”，是指等级较高的类型被转换成等级较低的类型。</p><p>    <font color="#ff0000"><strong>5.</strong></font> 作为参数传递给函数时，char 和 short 会被转换成 int，float 会被转换成 double。使用函数原型可以避免这种自动升级。这点我以后会详细讲解。</p><p>    类型升级通常不会有什么问题，但是类型降级却会带来不少问题。例如：</p><p>        char ch = 1222;</p><p>1222 降级为 char，但 char 无法表示 1222。再如：</p><p>        int j = 22.2;</p><p>22.2 降级为 int，小数部分被截断。</p><p><br /><font size="3"><strong>二、类型转换运算符</strong></font></p><p>    使用类型转换运算符可以指定我们想要进行的类型转换。类型转换运算符由括号和类型名组成：</p><p>        (type)</p><p>其中，我们应该用我们想转换成的类型替换掉 type，例如：(long), (unsigned long)。</p><p>    类型转换运算符应该放在值的前面：</p><p>        int i = (int)1.1 + (int)2.2;</p><p>1.1 在加法运算前就因为类型转换运算符而降级为 int，其值变为 1。类似地，2.2 也降级为 int，其值变为 2。又如：</p><p>        int j = 20;<br />        double k = (double)j;</p><p>j 的值被转换成 double，然后用于初始化 k。</p><p><br /><font size="3"><strong>三、小例子</strong></font></p><p>    #include <b>&lt;</b>stdio.h<b>&gt;</b></p><p>    int main(void)<br />    {<br />        char ch;<br />        int i;<br />        float fl;<br />        double db;</p><p>        db = fl = i = ch = 'C';                 /* 10 */<br />        printf("ch=%c, i=%d, fl=%2.2f, db=%2.2f\n", ch, i, fl, db);</p><p>        ch = ch + 1;                            /* 13 */<br />        i = db + fl + 2 * ch;                   /* 14 */<br />        fl = 2.0 * ch + i;                      /* 15 */<br />        db = 22 * i + ch - fl;                  /* 16 */<br />        printf("ch=%c, i=%d, fl=%2.2f, db=%2.2f\n", ch, i, fl, db);</p><p>        i = 666.6 + 777.7;                      /* 19 */<br />        printf("Now i = %d\n", i);<br />        i = (int)666.6 + (int)777.7;            /* 21 */<br />        printf("Now i = %d\n", i);</p><p>        return 0;<br />    }</p><p>我的系统中，char 是 8 位的，int 是 32 位的。输出为：</p><p>    ch=C, i=67, fl=67.00, db=67.00<br />    ch=D, i=270, fl=406.00, db=5602.00<br />    Now i = 1444<br />    Now i = 1443</p><p>第 10 行，'C' 的值被转换为 char，然后赋值给 ch。ch 的值被转换成 int，然后赋值给 i。以此类推。</p><p>第 13 行，= 右边的 ch 的值先被转换成 int，然后和 1 相加，得到 32 位的 int 类型整数 68。最后，68 降级为 char，然后赋值给 ch。在 ASCII 中，D 的编码是 68。</p><p>第 14 行，ch 的值被转换成 int，然后和 2 相乘得 136。fl 的值被转换成 double，然后和 db 相加，其和再与转换成 double 的 136 相加得 270.0 。最后，270.0 被转换成 int，然后赋值给 i。</p><p>第 15 行，ch 的值被转换成 double，然后和 2.0 相乘，i 的值被转换成 double，然后和前面得到的乘积相加，相加的得数被转换成 float，然后赋值给 fl。</p><p>第 16 行，22 和 i 相乘得 5940，ch 的值被转换成 int，然后和 5940 相加得 6008，然后，6008 被转换成 float，再和 fl 相减得 5602.0f，最后 5602.0f 被转换成 double，再赋值给 db。</p><p>第 19 行，666.6 和 777.7 相加得 1444.3，然后 1444.3 被降级成 int，再赋值给 i。</p><p>第 21 行，类型转换运算符把 666.6 和 777.7 转换为 int，得 666 和 777，它们相加得 1443，最后，1443 被赋值给 i。</p><p>注意，<font color="#ff0000">类型转换改变的是值的类型，而不是对象的类型，对象的类型自始至终都是不变的</font>。例如：</p><p>    i = fl + ch;</p><p>ch 的值被转换成 float，而 ch 本身仍然是 char 类型的变量。</p><p><strong>参考资料</strong>：C Primer 5th Edition<br />          The C Programming Language 2nd Edition<br />          C99 标准</p><!----></span>
<img src ="http://www.cppblog.com/Lee/aggbug/76028.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-03-09 21:34 <a href="http://www.cppblog.com/Lee/archive/2009/03/09/76028.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>new/delete 和malloc/free 的区别（综合转帖）</title><link>http://www.cppblog.com/Lee/archive/2009/03/09/75990.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Mon, 09 Mar 2009 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/03/09/75990.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/75990.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/03/09/75990.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/75990.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/75990.html</trackback:ping><description><![CDATA[
		<p>
				<strong>相同点</strong>：都可用于申请动态内存和释放内存</p>
		<p>
				<strong>不同点</strong>：<br />(<strong>1)操作对象有所不同</strong>。<br />malloc与free是C++/C 语言的标准库函数，new/delete 是C++的运算符。对于非内部数据类的对象而言，光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数， 对象消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符，不在编译器控制权限之内，不能够把执行构造函数和析构函数的任务强加malloc/free。</p>
		<p>
				<strong>(2)在用法上也有所不同</strong>。<br />函数malloc 的原型如下：<br /><strong>void * malloc(size_t size);</strong><br />用malloc 申请一块长度为length 的整数类型的内存，程序如下：<br />int *p = (int *) malloc(sizeof(int) * length);<br />我们应当把注意力集中在两个要素上：“类型转换”和“sizeof”。<br />􀂋 malloc 返回值的类型是void *，所以在调用malloc 时要显式地进行类型转换，将void * 转换成所需要的指针类型。<br />􀂋 malloc 函数本身并不识别要申请的内存是什么类型，它只关心内存的总字节数。</p>
		<p>函数free 的原型如下：<br /><strong>void free( void * memblock );</strong><br />为什么free 函数不象malloc 函数那样复杂呢？这是因为指针p 的类型以及它所指的内存的容量事先都是知道的，语句free(p)能正确地释放内存。如果p 是NULL 指针，那么free </p>
		<p>对p 无论操作多少次都不会出问题。如果p 不是NULL 指针，那么free 对p连续操作两次就会导致程序运行错误。</p>
		<p>
				<strong>new/delete 的使用要点<br /></strong>运算符new 使用起来要比函数malloc 简单得多，例如：<br />int *p1 = (int *)malloc(sizeof(int) * length);<br />int *p2 = new int[length];<br />这是因为new 内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言，new 在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数，那么new 的语句也可以有多种形式。</p>
		<p>如果用new 创建对象数组，那么只能使用对象的无参数构造函数。例如<br />Obj *objects = new Obj[100]; // 创建100 个动态对象<br />不能写成<br />Obj *objects = new Obj[100](1);// 创建100 个动态对象的同时赋初值1<br />在用delete 释放对象数组时，留意不要丢了符号‘[]’。例如<br />delete []objects; // 正确的用法<br />delete objects; // 错误的用法<br />后者相当于delete objects[0]，漏掉了另外99 个对象。<br />/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////<br /><br /><br />1  new自动计算需要分配的空间，而malloc需要手工计算字节数<br />2  new是类型安全的，而malloc不是，比如：<br />int* p = new float[2]; // 编译时指出错误<br />int* p = malloc(2*sizeof(float)); // 编译时无法指出错误<br />new operator 由两步构成，分别是 operator new 和 construct<br />3  operator new对应于malloc，但operator new可以重载，可以自定义内存分配策略，甚至不做内存分配，甚至分配到非内存设备上。而malloc无能为力<br />4  new将调用constructor，而malloc不能；delete将调用destructor，而free不能。<br />5  malloc/free要库文件支持，new/delete则不要。 <br />/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////<br /><br />1. 本质区别<br />malloc/free是C/C++语言的标准库函数，new/delete是C++的运算符。<br />对于用户自定义的对象而言，用maloc/free无法满足动态管理对象的要求。对象在创建的同时要自动执行构造函数，对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符，不在编译器控制权限之内，不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++需要一个能完成动态内存分配和初始化工作的运算符new，以及一个能完成清理与释放内存工作的运算符delete。<br /><br /></p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<span style="COLOR: #0000ff">class</span>
				<span style="COLOR: #000000"> Obj<br />{<br /></span>
				<span style="COLOR: #0000ff">public</span>
				<span style="COLOR: #000000"> :<br />      Obj(</span>
				<span style="COLOR: #000000">) { cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> “Initialization” </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl; }<br />      </span>
				<span style="COLOR: #000000">~</span>
				<span style="COLOR: #000000">Obj(</span>
				<span style="COLOR: #000000">) { cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> “Destroy” </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl; }<br />      </span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> Initialize(</span>
				<span style="COLOR: #000000">) { cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> “Initialization” </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl; }<br />      </span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> Destroy(</span>
				<span style="COLOR: #000000">) { cout </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> “Destroy” </span>
				<span style="COLOR: #000000">&lt;&lt;</span>
				<span style="COLOR: #000000"> endl; }<br />};<br /><br /></span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> UseMallocFree(</span>
				<span style="COLOR: #000000">)<br />{<br />      Obj  </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">a </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> (obj </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">) malloc( </span>
				<span style="COLOR: #0000ff">sizeof</span>
				<span style="COLOR: #000000">( obj ) );     </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000"> allocate memory</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">      a</span>
				<span style="COLOR: #000000">-&gt;</span>
				<span style="COLOR: #000000">Initialize();                                                </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000"> initialization<br />      </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">…</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">      a</span>
				<span style="COLOR: #000000">-&gt;</span>
				<span style="COLOR: #000000">Destroy();   </span>
				<span style="COLOR: #008000">                                             // deconstruction</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">      free(a);                                                           <font color="#008000">// release memory<br /></font></span>
				<span style="COLOR: #000000">}<br /><br /></span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> UseNewDelete(</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000">)<br />{<br />    Obj  </span>
				<span style="COLOR: #000000">*</span>
				<span style="COLOR: #000000">a </span>
				<span style="COLOR: #000000">=</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #0000ff">new</span>
				<span style="COLOR: #000000"> Obj;  </span>
				<span style="COLOR: #008000">                                       </span>
				<span style="COLOR: #008000">
						<br />    </span>
				<span style="COLOR: #008000">//</span>
				<span style="COLOR: #008000">…</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">    delete a;</span>
				<span style="COLOR: #008000">
						<br />
				</span>
				<span style="COLOR: #000000">}</span>
		</div>
		<p>
				<br />类Obj的函数Initialize实现了构造函数的功能，函数Destroy实现了析构函数的功能。函数UseMallocFree中，由于malloc/free不能执行构造函数与析构函数，必须调用成员函数Initialize和Destroy来完成“构造”与“析构”。所以我们不要用malloc/free来完成动态对象的内存管理，应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程，对它们而言malloc/free和new/delete是等价的。<br /><br />2. 联系<br />既然new/delete的功能完全覆盖了malloc/free，为什么C++还保留malloc/free呢？因为C++程序经常要调用C函数，而C程序只能用malloc/free管理动态内存。如果用free释放“new创建的动态对象”，那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”，理论上讲程序不会出错，但是该程序的可读性很差。所以new/delete，malloc/free必须配对使用。<br />///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////<br /><br /><br /></p>
<img src ="http://www.cppblog.com/Lee/aggbug/75990.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-03-09 16:18 <a href="http://www.cppblog.com/Lee/archive/2009/03/09/75990.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]C++资源之不完全导引（完整版）</title><link>http://www.cppblog.com/Lee/archive/2009/02/25/74811.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Tue, 24 Feb 2009 16:26:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/02/25/74811.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/74811.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/02/25/74811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/74811.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/74811.html</trackback:ping><description><![CDATA[
		<p>来源：<a href="http://www.csdn.net/">www.csdn.net</a><br />撰文：曾毅、陶文<br />声明：本文2004年5月首发于《CSDN开发高手》，版权归该杂志与《程序员》杂志社<br />所有。<br />------------------------------------------------------------------------<br />　　1，前言</p>
		<p>　　无数次听到“我要开始学习C++!”的呐喊，无数次听到“C++太复杂了，我真的<br />学不会”的无奈。Stan Lippman先生曾在《C++ Primer》一书中指出“C++是最为难<br />学的高级程序设计语言之一”，人们常将“之一”去掉以表达自己对C++的敬畏。诚<br />然，C++程序设计语言对于学习者的确有很多难以逾越的鸿沟，体系结构的庞大，应<br />接不暇并不断扩充的特性……除此之外，参考资料之多与冗杂使它的学习者望而却<br />步，欲求深入者苦不堪言。希望这一份不完全导引能够成为您C++学习之路上的引路<br />灯。</p>
		<p>　　撰写本文的初衷并不打算带领大家体验古老的C++历史，如果你想了解C++的历<br />史与其前期发展中诸多技术的演变，你应当去参考Bjarne的《The Design and Evo<br />lution of C++》。当然也不打算给大家一个无所不包的宝典（并非不想：其一是因<br />水平有限，其二无奈C++之博大精深），所给出的仅仅是一些我们认为对于想学习C<br />++的广大读者来说最重要并且触手可及的开发与学习资源。</p>
		<p>　　本文介绍并分析了一些编译器，开发环境，库，少量的书籍以及参考网站，并<br />且尽可能尝试着给出一个利用这些资源的导引，望对如同我们一样的初学者能够有<br />所裨益。</p>
		<p>-----------------------------------------------------------------------</p>
		<p>　　2，编译器</p>
		<p>　　在C++之外的任何语言中，编译器都从来没有受到过如此之重视。因为C++是一<br />门相当复杂的语言，所以编译器也难于构建。直到最近我们才开始能够使用上完全<br />符合C++标准的编译器（哦，你可能会责怪那些编译器厂商不能尽早的提供符合标准<br />的编译器，这只能怪他们各自维系着自身的一套别人不愿接受的标准）。什么？你<br />说这无关紧要？哦，不，你所需要的是和标准化C++高度兼容的编译环境。长远来看<br />，只有这样的编译器对C++开发人员来说才是最有意义的工具，尤其是对于程序设计<br />语言的学习者。一至性让代码具备可移植性，并让一门语言及其库的应用更为广泛<br />。嗯，是的，我们这里只打算介绍一些公认的优秀编译器。</p>
		<p>　　2.1 Borland C++</p>
		<p>　　这个是Borland C++ Builder和Borland C++ Builder X这两种开发环境的后台<br />编译器。（哦，我之所以将之分为两种开发环境你应当能明白为什么，正如Delphi<br />7到Delphi8的转变，是革命性的两代。）Borland C++由老牌开发工具厂商Borland<br />倾力打造。该公司的编译器素以速度快，空间效率高著称，Borland C++ 系列编译<br />器秉承了这个传统，属于非常优质的编译器。标准化方面早在5.5版本的编译器中对<br />标准化C++的兼容就达到了92.73%。目前最新版本是Borland C++ Builder X中的6.<br />0版本，官方称100%符合ANSI/ISO的C++标准以及C99标准。嗯…这正是我前面所指的<br />“完全符合C++标准的编译器”。</p>
		<p>　　2.2 Visual C++</p>
		<p>　　这个正是我们熟知的Visual Studio 和 Visual Studio.net 2002, 2003以及2<br />005 Whidbey中带的C++编译器。由Microsoft公司研制。在Visual Studio 6.0中，<br />因为编译器有太多地方不能与后来出现的C++标准相吻合而饱受批评（想想你在使用<br />STL的时候编译时报出的那些令人厌恶的error和warning吧）。VC++6.0对标准化C+<br />+的兼容只有83.43%。但是随着C++编译器设计大师Stanley Lippman以及诸多C++社<br />群达人的加盟，在Visual Studio.NET 2003中，Visual C++编译器已经成为一个非<br />常成熟可靠的C++编译器了。Dr.Dobb's Journal的评测显示Visual C++7.1对标准C<br />++的兼容性高达98.22%，一度成为CBX之前兼容性最好的编译器。结合强大的Visua<br />l Studio.NET开发环境，是一个非常不错的选择。至于Whidbey时代的Visual C++,<br />似乎微软所最关注的是C++/CLI……我们不想评论微软下一代的C++编译器对标准化<br />兼容如何，但他确实越来越适合.NET (其实你和我的感觉可能是一样的，微软不应<br />当把标准C++这块肥肉丢给Borland,然而微软可能并不这样认为)。</p>
		<p>　　2.3 GNU C++</p>
		<p>　　著名的开源C++编译器。是类Unix操作系统下编写C++程序的首选。特点是有非<br />常好的移植性，你可以在非常广泛的平台上使用它，同时也是编写跨平台，嵌入式<br />程序很好的选择。另外在符合标准这个方面一直都非常好，GCC3.3大概能够达到96<br />.15%。但是由于其跨平台的特性，在代码尺寸速度等优化上略微差一点。</p>
		<p>　　基于GNU C++的编译器有很多，比如：</p>
		<p>　　(1) Mingw<br />　　<a href="http://www.mingw.org/">http://www.mingw.org/</a><br />　　GCC的一个Windows的移植版本（Dev-C++的后台）</p>
		<p>　　(2) Cygwin<br />　　<a href="http://sources.redhat.com/cygwin/">http://sources.redhat.com/cygwin/</a><br />　　GCC的另外一个Windows移植版本是Cygwin的一部分，Cygwin是Windows下的一个<br />Unix仿真环境。严格的说是模拟GNU的环境，这也就是"Gnu's Not Unix"要表达的意<br />思，噢，扯远了，这并不是我们在这里关心的实质内容。</p>
		<p>　　(3) Djgpp<br />　　<a href="http://www.delorie.com/djgpp/">http://www.delorie.com/djgpp/</a><br />　　这是GCC的DOS移植版本。</p>
		<p>　　(4) RSXNT<br />　　<a href="http://www.mathematik.uni-bielefeld.de/~rainer/">http://www.mathematik.uni-bielefeld.de/~rainer/</a><br />　　这是GCC的DOS和Windows移植版本。</p>
		<p>　　(5) Intel C++<br />　　著名CPU制造厂商Intel出品的编译器，Special Design for Intel x86！对于<br />Intel x86结构的CPU经过特别的优化。在有些应用情况下，特别是数值计算等高性<br />能应用，仅仅采用Intel的编译器编译就能大幅度的提高性能。</p>
		<p>　　(6) Digital Mars C++<br />　　网络上提供免费下载，Zortech/Symantec C++的继承者，其前身在当年惨烈的<br />C++四国战中也是主角之一。</p>
		<p>　　3，开发环境</p>
		<p>　　开发环境对于程序员的作用不言而喻。选择自己朝夕相处的环境也不是容易的<br />事情，特别是在IDE如此丰富的情况下。下面就是我们推荐的一些常见的C++开发环<br />境，并没有包括一些小型的，罕见的IDE。其中任何一款都是功能丰富，可以用作日<br />常开发使用的。对于不同层面的开发者，请参见内文关于适用对象的描述。</p>
		<p>　　3.1 Visual Studio 6.0</p>
		<p>　　这个虽然是Microsoft公司的老版本的开发环境，但是鉴于其后继版本Visual<br />Studio.NET的庞大身躯，以及初学者并不那么高的功能要求，所以推荐这个开发环<br />境给C++的初学者，供其学习C++的最基本的部分，比如C的那部分子集，当然你别指<br />望他能够支持最新的C99标准。在日常的开发中，仍然有很多公司使用这个经典稳定<br />的环境，比如笔者就看曾亲见有些公司将其编译器替换为GCC做手机开发之用。</p>
		<p>　　3.2 Visual Studio.NET 2003</p>
		<p>　　作为Microsoft公司官方正式发布的最新版本开发环境，其中有太多激动人心的<br />功能。结合其最新的C++编译器。对于机器配置比较好的开发人员来说，使用这个开<br />发环境将能满足其大部分的要求。这里不打算单独说Visual Studio Whidbey,虽然<br />Visual Studio .NET 2005 - Whidbey社区预览版已经推出，但暂不是很稳定，读者<br />可以亲身去体验。</p>
		<p>　　3.3 Borland C++ Builder 6</p>
		<p>　　这个并不是Borland的C++开发环境的最新版本。选择它的原因是它不是用Java<br />写的IDE，速度比较快。它有一个很完善的GUI窗体设计器，和Delphi共用一个VCL。<br />由于这些特点，比较适合初学者上手。但是由于其GUI的中心位置，可能不利于对于<br />C++语言的学习。而且其为了支持VCL这个Object Pascal写的库也对C++进行了一些<br />私有的扩充。使得人们有一个不得不接受的事实：“Borland C++ Builder 6的高手<br />几乎都是Delphi高手”。</p>
		<p>　　3.4 Borland C++ Builder X</p>
		<p>　　正如前文所述，虽然版本号上和前面那个IDE非常相象，但是其实它们是完全不<br />同的两个集成开发环境。C++Builder更多的是一个和Delphi同步的C++版本的开发环<br />境，C++BuilderX则是完全从C++的角度思考得出的一个功能丰富的IDE。其最大的特<br />点是跨平台，跨编译器，多种Framework的集成，并且有一个WxWindows为基础的GU<br />I设计器。尤其是采用了纯C++来重写了整个Framework,摒弃了以前令人无奈的版本<br />。对于C++的开发来说，从编译器，到库，到功能集成都是非常理想的。可以预见，<br />Borland C++ Builder X 2.0很值得C++爱好者期待。唯一令人难堪之处是作为一个<br />C++的开发工具，其IDE是用Java写的，在配置不够理想的机器上请慎重考虑再安装<br />。</p>
		<p>　　3.5 Emacs + GCC</p>
		<p>　　前面讲的大部分是Windows环境下的集成开发环境。Linux上的开发者更倾向于<br />使用Emacs来编辑C++的文件，用Makefile来命令GCC做编译。虽然看上去比较松散，<br />但是这些东西综合起来还是一个开发环境。如果你能够娴熟的使用这样的环境写程<br />序，你的水平应该足够指导我们来写这篇陋文了。</p>
		<p>　　3.6 Dev C++</p>
		<p>　　GCC是一个很好的编译器。在Windows上的C++编译器一直和标准有着一段距离的<br />时候，GCC就是一个让Windows下开发者流口水的编译器。Dev-C++就是能够让GCC跑<br />在Windows下的工具，作为集成开发环境，还提供了同专业IDE相媲美的语法高亮，<br />代码提示，调试等功能。由于使用Delphi开发，占用内存少，速度很快，比较适合<br />轻量级的学习和使用。</p>
		<p>　　3.7 Eclipse + CDT</p>
		<p>　　Eclipse可是近来大名鼎鼎的开发工具。最新一期的Jolt大奖就颁给了这个杰出<br />的神物。说其神奇是因为，它本身是用Java写的，但是拥有比一般Java写的程序快<br />得多的速度。而且因为其基于插件组装一切的原则，使得能够有CDT这样的插件把E<br />clipse变成一个C/C++的开发环境。如果你一直用Eclipse写Java的程序，不妨用它<br />体验一下C++开发的乐趣。</p>
		<p>　　4，工具</p>
		<p>　　C++的辅助工具繁多，我们分门别类的为大家作介绍：<br />　　4.1 文档类<br />　　(1) Doxygen<br />　　参考站点：<a href="http://www.doxygen.org/">http://www.doxygen.org</a><br />　　Doxygen是一种适合C风格语言（如C++、C、IDL、Java甚至包括C#和PHP）的、<br />开放源码的、基于命令行的文档产生器。</p>
		<p>　　(2) C++2HTML<br />　　参考站点：<a href="http://www.bedaux.net/cpp2html/">http://www.bedaux.net/cpp2html/</a><br />　　把C++代码变成语法高亮的HTML</p>
		<p>　　(3) CodeColorizer<br />　　参考站点：<a href="http://www.chami.com/colorizer/">http://www.chami.com/colorizer/</a><br />　　它能把好几种语言的源代码着色为HTML</p>
		<p>　　(4) Doc-O-Matic<br />　　参考站点：<a href="http://www.doc-o-matic.com/">http://www.doc-o-matic.com/</a><br />　　Doc-O_Matic为你的C/C++，C++.net，Delphi/Pascal, VB.NET，C#和Java程序<br />或者组件产生准确的文档。Doc-O-Matic使用源代码中的符号和注释以及外部的文档<br />文件创建与流行的文档样式一致的文档。</p>
		<p>　　(5) DocVizor<br />　　参考站点：<a href="http://www.ucancode.net/Products/DocBuilder/Features.htm">http://www.ucancode.net/Products/DocBuilder/Features.htm</a><br />　　DocVizor满足了面向对象软件开发者的基本要求——它让我们能够看到C++工程<br />中的类层次结构。DocVizor快速地产生完整可供打印的类层次结构图，包括从第三<br />方库中来的那些类，除此之外DocVizor还能从类信息中产生HTML文件。</p>
		<p>　　(6) SourcePublisher C++<br />　　参考站点：<a href="http://www.scitools.com/sourcepublisher_c.html">http://www.scitools.com/sourcepublisher_c.html</a><br />　　给源代码产生提供快速直观的HTML报表，包括代码，类层次结构，调用和被调<br />用树，包含和被包含树。支持多种操作系统。</p>
		<p>　　(7) Understand<br />　　参考站点：<a href="http://www.scitools.com/ucpp.html">http://www.scitools.com/ucpp.html</a><br />　　分析任何规模的C或者C++工程，帮助我们更好的理解以及编写文档。</p>
		<p>　　4.2 代码类<br />　　(1) CC-Rider<br />　　参考站点：<a href="http://www.cc-rider.com/">http://www.cc-rider.com</a><br />　　CC-Rider是用于C/C++程序强大的代码可视化工具，通过交互式浏览、编辑及自<br />动文件来促进程序的维持和发展。</p>
		<p>　　(2) CodeInspect<br />　　参考站点：<a href="http://www.yokasoft.com/">http://www.yokasoft.com/</a><br />　　一种新的C/C++代码分析工具。它检查我们的源代码找出非标准的，可能的，以<br />及普通的错误代码。</p>
		<p>　　(3) CodeWizard<br />　　参考站点：<a href="http://www.parasoft.com/">http://www.parasoft.com</a><br />　　先进的C/C++源代码分析工具，使用超过500个编码规范自动化地标明危险的，<br />但是编译器不能检查到的代码结构。</p>
		<p>　　(4) C++ Validation Test Suites<br />　　参考站点：<a href="http://www.plumhall.com/suites.html">http://www.plumhall.com/suites.html</a><br />　　一组用于测试编译器和库对于标准吻合程度的代码库。</p>
		<p>　　(5) CppRefactory<br />　　参考站点：<a href="http://cpptool.sourceforge.net/">http://cpptool.sourceforge.net/</a><br />　　CPPRefactory是一个使得开发者能够重构他们的C++代码的程序。目的是使得C<br />++代码的重构能够尽可能的有效率和简单。</p>
		<p>　　(6) Lzz<br />　　参考站点：<a href="http://www.lazycplusplus.com/">http://www.lazycplusplus.com/</a><br />　　Lzz是一个自动化许多C++编程中的体力活的工具。它能够节省我们许多事件并<br />且使得编码更加有乐趣。给出一系列的声明，Lzz会给我们创建头文件和源文件。</p>
		<p>　　(7) QA C++ Generation 2000<br />　　参考站点：<a href="http://www.programmingresearch.com/solutions/qacpp.htm">http://www.programmingresearch.com/solutions/qacpp.htm</a><br />　　它关注面向对象的C++源代码，对有关于设计，效率，可靠性，可维护性的部分<br />提出警告信息。</p>
		<p>　　(8) s-mail project - Java to C++DOL<br />　　参考站点：<a href="http://sadlocha.strefa.pl/s-mail/ja2dol.html">http://sadlocha.strefa.pl/s-mail/ja2dol.html</a><br />　　把Java源代码翻译为相应的C++源代码的命令行工具。</p>
		<p>　　(9) SNIP from Cleanscape Software International<br />　　参考站点：<a href="http://www.cleanscape.net/stdprod/snip/index.html">http://www.cleanscape.net/stdprod/snip/index.html</a><br />　　一个填平编码和设计之间沟壑的易于使用的C++开发工具，节省大量编辑和调试<br />的事件，它还使得开发者能够指定设计模式作为对象模型，自动从对象模型中产生<br />C++的类。</p>
		<p>　　(10) SourceStyler C++<br />　　参考站点：<a href="http://www.ochresoftware.com/">http://www.ochresoftware.com/</a><br />　　对C/C++源代码提供完整的格式化和排版控制的工具。提供多于75个的格式化选<br />项以及完全支持ANSI C++。</p>
		<p>　　4.3 编译类<br />　　(1) Compilercache<br />　　参考站点：<a href="http://www.erikyyy.de/compilercache/">http://www.erikyyy.de/compilercache/</a><br />　　Compilercache是一个对你的C和C++编译器的封装脚本。每次我们进行编译，封<br />装脚本，把编译的结果放入缓存，一旦编译相同的东西，结果将从缓存中取出而不<br />是再次编译。</p>
		<p>　　(2) Ccache<br />　　参考站点：<a href="http://ccache.samba.org/">http://ccache.samba.org/</a><br />　　Ccache是一个编译器缓存。它使用起来就像C/C++编译器的缓存预处理器，编译<br />速度通常能提高普通编译过程的5~10倍。</p>
		<p>　　(3) Cmm (C++ with MultiMethods)<br />　　参考站点：<a href="http://www.op59.net/cmm/cmm-0.28/users.html">http://www.op59.net/cmm/cmm-0.28/users.html</a><br />　　这是一种C++语言的扩展。读入Cmm源代码输出C++的源代码，功能是对C++语言<br />添加了对multimethod的支持。</p>
		<p>　　(4) The Frost Project<br />　　参考站点：<a href="http://frost.flewid.de/">http://frost.flewid.de/</a><br />　　Forst使得你能够在C++程序中像原生的C++特性一样使用multimethod以及虚函<br />数参数。它是一个编译器的外壳。</p>
		<p>　　4.4 测试和调试类<br />　　(1) CPPUnit<br />　　CppUnit 是个基于 LGPL 的开源项目，最初版本移植自 JUnit，是一个非常优<br />秀的开源测试框架。CppUnit 和 JUnit 一样主要思想来源于极限编程。主要功能就<br />是对单元测试进行管理，并可进行自动化测试。</p>
		<p>　　(2) C++Test<br />　　参考站点：<a href="http://www.parasoft.com/">http://www.parasoft.com/</a><br />　　C++ Test是一个单元测试工具，它自动化了C和C++类，函数或者组件的测试。</p>
		<p>　　(3) Cantata++<br />　　参考站点：<a href="http://www.iplbath.com/products/tools/pt400.shtml">http://www.iplbath.com/products/tools/pt400.shtml</a><br />　　设计的目的是为了满足在合理的经济开销下使用这个工具可以让开发工程师开<br />展单元测试和集成测试的需求.</p>
		<p>　　(4) Purify<br />　　参考站点：<a href="http://www-900.ibm.com/cn/software/rational/products/purif">http://www-900.ibm.com/cn/software/rational/products/purif</a><br />yplus/index.shtml<br />　　IBM Rational PurifyPlus是一套完整的运行时分析工具，旨在提高应用程序的<br />可靠性和性能。PurifyPlus将内存错误和泄漏检测、应用程序性能描述、代码覆盖<br />分析等功能组合在一个单一、完整的工具包中。</p>
		<p>　　(5) BoundsChecker<br />　　BoundsChecker是一个C++运行时错误检测和调试工具。它通过在Visual Studi<br />o内自动化调试过程加速开发并且缩短上市的周期。BoundsChecker提供清楚，详细<br />的程序错误分析，许多是对C++独有的并且在static，stack和heap内存中检测和诊<br />断错误，以及发现内存和资源的泄漏。　　<br />        <br />        (6) Insure++<br />　　参考站点：<a href="http://www.parasoft.com/">http://www.parasoft.com/</a><br />　　一个自动化的运行时程序测试工具，检查难以察觉的错误,如内存覆盖，内存泄<br />漏，内存分配错误，变量初始化错误，变量定义冲突，指针错误，库错误，逻辑错<br />误和算法错误等。</p>
		<p>　　(7) GlowCode<br />　　参考站点：<a href="http://www.glowcode.com/">http://www.glowcode.com/</a><br />　　GlowCode包括内存泄漏检查，code profiler，函数调用跟踪等功能。给C++开<br />发者提供完整的错误诊断，和运行时性能分析工具包。</p>
		<p>　　(8) Stack Spy<br />　　参考站点：<a href="http://www.imperioustech.com/">http://www.imperioustech.com/</a><br />　　它能捕捉stack corruption, stack over run, stack overflow等有关栈的错<br />误。</p>
		<p>　　5，库</p>
		<p>　　在C++中，库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了<br />设计库来扩充功能要好过设计更多的语法的言论。现实中，C++的库门类繁多，解决<br />的问题也是极其广泛，库从轻量级到重量级的都有。不少都是让人眼界大开，亦或<br />是望而生叹的思维杰作。由于库的数量非常庞大，而且限于笔者水平，其中很多并<br />不了解。所以文中所提的一些库都是比较著名的大型库。</p>
		<p>　　5.1 标准库</p>
		<p>　　标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年<br />，直到标准的出台才正式定型，但是在标准库的实现上却很令人欣慰得看到多种实<br />现，并且已被实践证明为有工业级别强度的佳作。</p>
		<p>　　(1) Dinkumware C++ Library<br />　　参考站点：<a href="http://www.dinkumware.com/">http://www.dinkumware.com/</a><br />　　P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb's程序设<br />计杰出奖的获得者。其编写的库长期被Microsoft采用，并且最近Borland也取得了<br />其OEM的license，在其C/C++的产品中采用Dinkumware的库。</p>
		<p>　　(2) RogueWave Standard C++ Library<br />　　参考站点：<a href="http://www.roguewave.com/">http://www.roguewave.com/</a><br />　　这个库在Borland C++ Builder的早期版本中曾经被采用，后来被其他的库给替<br />换了。笔者不推荐使用。</p>
		<p>　　(3) SGI STL<br />　　参考站点：<a href="http://www.roguewave.com/">http://www.roguewave.com/</a><br />　　SGI公司的C++标准模版库。</p>
		<p>　　(4) STLport<br />　　参考站点：<a href="http://www.stlport.org/">http://www.stlport.org/</a><br />　　SGI STL库的跨平台可移植版本。</p>
		<p>　　5.2 “准”标准库 - Boost<br />　　参考站点：<a href="http://www.boost.org/">http://www.boost.org</a><br />　　国内镜像：<a href="http://www.c-view.org/tech/lib/boost/index.htm">http://www.c-view.org/tech/lib/boost/index.htm</a><br />　　Boost库是一个经过千锤百炼、可移植、提供源代码的C++库，作为标准库的后<br />备，是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起<br />，在C++社区中影响甚大，其成员已近2000人。 Boost库为我们带来了最新、最酷、<br />最实用的技术，是不折不扣的“准”标准库。<br />　　Boost中比较有名气的有这么几个库：</p>
		<p>　　Regex<br />　　正则表达式库<br />　　Spirit<br />　　LL parser framework，用C++代码直接表达EBNF</p>
		<p>　　Graph<br />　　图组件和算法</p>
		<p>　　Lambda<br />　　在调用的地方定义短小匿名的函数对象，很实用的functional功能</p>
		<p>　　concept check<br />　　检查泛型编程中的concept</p>
		<p>　　Mpl<br />　　用模板实现的元编程框架</p>
		<p>　　Thread<br />　　可移植的C++多线程库</p>
		<p>　　Python<br />　　把C++类和函数映射到Python之中</p>
		<p>　　Pool<br />　　内存池管理</p>
		<p>　　smart_ptr<br />　　5个智能指针，学习智能指针必读，一份不错的参考是来自CUJ的文章：</p>
		<p>　　Smart Pointers in Boost，哦，这篇文章可以查到，CUJ是提供在线浏览的。<br />中文版见笔者在《Dr. Dobb's Journal软件研发杂志》第7辑上的译文。</p>
		<p>　　Boost总体来说是实用价值很高，质量很高的库。并且由于其对跨平台的强调，<br />对标准C++的强调，是编写平台无关，现代C++的开发者必备的工具。但是Boost中也<br />有很多是实验性质的东西，在实际的开发中实用需要谨慎。并且很多Boost中的库功<br />能堪称对语言功能的扩展，其构造用尽精巧的手法，不要贸然的花费时间研读。Bo<br />ost另外一面，比如Graph这样的库则是具有工业强度，结构良好，非常值得研读的<br />精品代码，并且也可以放心的在产品代码中多多利用。</p>
		<p>5.3 GUI</p>
		<p>　　在众多C++的库中，GUI部分的库算是比较繁荣，也比较引人注目的。在实际开<br />发中，GUI库的选择也是非常重要的一件事情，下面我们综述一下可选择的GUI库，<br />各自的特点以及相关工具的支持。</p>
		<p>　　(1) MFC</p>
		<p>　　大名鼎鼎的微软基础类库（Microsoft Foundation Class）。大凡学过VC++的<br />人都应该知道这个库。虽然从技术角度讲，MFC是不大漂亮的，但是它构建于Windo<br />ws API 之上，能够使程序员的工作更容易,编程效率高，减少了大量在建立 Windo<br />ws 程序时必须编写的代码，同时它还提供了所有一般 C++ 编程的优点，例如继承<br />和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的，例如，在<br />Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但<br />是在最近发展以及官方支持上日渐势微。</p>
		<p>　　(2) QT</p>
		<p>　　参考网站：<a href="http://www.trolltech.com/">http://www.trolltech.com/</a><br />　　Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给<br />应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的<br />很容易扩展，并且允许真正地组件编程。自从1996年早些时候，Qt进入商业领域，<br />它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环<br />境KDE 的基础，同时它还支持Windows、Macintosh、Unix/X11等多种平台。</p>
		<p>　　(3) WxWindows</p>
		<p>　　参考网站：<a href="http://www.wxwindows.org/">http://www.wxwindows.org/</a><br />　　跨平台的GUI库。因为其类层次极像MFC，所以有文章介绍从MFC到WxWindows的<br />代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库，支持同<br />样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计<br />器就是基于这个库的。</p>
		<p>　　(4) Fox</p>
		<p>　　参考网站：<a href="http://www.fox-toolkit.org/">http://www.fox-toolkit.org/</a><br />　　开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应<br />该是什么样子的感受出发，从而开始了对这个库的开发。有兴趣的可以尝试一下。</p>
		<p>
				<br />　　(5) WTL<br />　　基于ATL的一个库。因为使用了大量ATL的轻量级手法，模板等技术，在代码尺<br />寸，以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络<br />下载的可视化控件的开发者。</p>
		<p>　　(6) GTK</p>
		<p>　　参考网站：<a href="http://gtkmm.sourceforge.net/">http://gtkmm.sourceforge.net/</a><br />　　GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。<br />而GTK就是这个库的C++封装版本。</p>
		<p>　　5.4 网络通信</p>
		<p>　　(1) ACE</p>
		<p>　　参考网站：<a href="http://www.cs.wustl.edu/~schmidt/ACE.html">http://www.cs.wustl.edu/~schmidt/ACE.html</a><br />　　C++库的代表，超重量级的网络通信开发框架。ACE自适配通信环境（Adaptive<br />Communication Environment）是可以自由使用、开放源代码的面向对象框架，在<br />其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包<br />装外观（Wrapper Facade）和框架组件，可跨越多种平台完成通用的通信软件任务<br />，其中包括：事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通<br />信、共享内存管理、消息路由、分布式服务动态（重）配置、并发执行和同步，等<br />等。</p>
		<p>　　(2) StreamModule</p>
		<p>　　参考网站：<a href="http://www.omnifarious.org/StrMod/">http://www.omnifarious.org/StrMod/</a><br />　　设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容<br />易，而不是用同步的外壳包起异步的本质。</p>
		<p>　　(3) SimpleSocket</p>
		<p>　　参考网站：<a href="http://home.hetnet.nl/~lcbokkers/simsock.htm">http://home.hetnet.nl/~lcbokkers/simsock.htm</a><br />　　这个类库让编写基于socket的客户/服务器程序更加容易。</p>
		<p>　　(4) A Stream Socket API for C++<br />　　参考网站：<a href="http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.h">http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.h</a><br />tml</p>
		<p>　　又一个对Socket的封装库。</p>
		<p>　　5.5 XML</p>
		<p>　　(1) Xerces</p>
		<p>　　参考网站：<a href="http://xml.apache.org/xerces-c/">http://xml.apache.org/xerces-c/</a><br />　　Xerces-C++ 是一个非常健壮的XML解析器，它提供了验证，以及SAX和DOM API<br />。XML验证在文档类型定义(Document Type Definition，DTD)方面有很好的支持，<br />并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。</p>
		<p>　　(2) XMLBooster</p>
		<p>　　参考网站：<a href="http://www.xmlbooster.com/">http://www.xmlbooster.com/</a><br />　　这个库通过产生特制的parser的办法极大的提高了XML解析的速度，并且能够产<br />生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了<br />另外一个可行的解决方案。</p>
		<p>　　(3) Pull Parser</p>
		<p>　　参考网站：<a href="http://www.extreme.indiana.edu/xgws/xsoap/xpp/">http://www.extreme.indiana.edu/xgws/xsoap/xpp/</a><br />　　这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parse<br />r，这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。</p>
		<p>
				<br />　　(4) Xalan</p>
		<p>　　参考网站：<a href="http://xml.apache.org/xalan-c/">http://xml.apache.org/xalan-c/</a><br />　　Xalan是一个用于把XML文档转换为HTML，纯文本或者其他XML类型文档的XSLT处<br />理器。</p>
		<p>　　(5) CMarkup</p>
		<p>　　参考网站：<a href="http://www.firstobject.com/xml.htm">http://www.firstobject.com/xml.htm</a><br />　　这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在D<br />OM和SAX之外寻求一点灵感。</p>
		<p>　　(6) libxml++</p>
		<p>　　<a href="http://libxmlplusplus.sourceforge.net/">http://libxmlplusplus.sourceforge.net/</a><br />　　libxml++是对著名的libxml XML解析器的C++封装版本</p>
		<p>　　5.6 科学计算</p>
		<p>　　(1) Blitz++</p>
		<p>　　参考网站：<a href="http://www.oonumerics.org/blitz/">http://www.oonumerics.org/blitz/</a><br />　　Blitz++ 是一个高效率的数值计算函数库，它的设计目的是希望建立一套既具<br />像C++ 一样方便，同时又比Fortran速度更快的数值计算环境。通常，用C++所写出<br />的数值程序，比 Fortran慢20%左右，因此Blitz++正是要改掉这个缺点。方法是利<br />用C++的template技术，程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中<br />，对于常见的SVD，FFTs，QMRES等常见的线性代数方法并不提供，不过使用者可以<br />很容易地利用Blitz++所提供的函数来构建。</p>
		<p>　　(2) POOMA</p>
		<p>　　参考网站：<a href="http://www.codesourcery.com/pooma/pooma">http://www.codesourcery.com/pooma/pooma</a><br />　　POOMA是一个免费的高性能的C++库，用于处理并行式科学计算。POOMA的面向对<br />象设计方便了快速的程序开发，对并行机器进行了优化以达到最高的效率，方便在<br />工业和研究环境中使用。</p>
		<p>　　(3) MTL</p>
		<p>　　参考网站：<a href="http://www.osl.iu.edu/research/mtl/">http://www.osl.iu.edu/research/mtl/</a><br />　　Matrix Template Library(MTL)是一个高性能的泛型组件库，提供了各种格式<br />矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下，比如In<br />tel的编译器，从产生的汇编代码可以看出其与手写几乎没有两样的效能。</p>
		<p>　　(4) CGAL</p>
		<p>　　参考网站：<a href="http://www.cgal.org/">www.cgal.org</a></p>
		<p>　　Computational Geometry Algorithms Library的目的是把在计算几何方面的大<br />部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。</p>
		<p>　　5.7 游戏开发</p>
		<p>　　(1) Audio/Video 3D C++ Programming Library</p>
		<p>　　参考网站：<a href="http://www.galacticasoftware.com/products/av/">http://www.galacticasoftware.com/products/av/</a><br />　　AV3D是一个跨平台，高性能的C++库。主要的特性是提供3D图形，声效支持（S<br />B,以及S3M），控制接口（键盘，鼠标和遥感），XMS。</p>
		<p>　　(2) KlayGE</p>
		<p>　　参考网站：<a href="http://home.g365.net/enginedev/">http://home.g365.net/enginedev/</a><br />　　国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平<br />台的游戏引擎，并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏<br />先生为中国游戏开发事业所做出的贡献。</p>
		<p>　　(3) OGRE</p>
		<p>　　参考网站：<a href="http://www.ogre3d.org/">http://www.ogre3d.org</a><br />　　OGRE（面向对象的图形渲染引擎）是用C++开发的，使用灵活的面向对象3D引擎<br />。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。<br />引擎中的类库对更底层的系统库（如：Direct3D和OpenGL）的全部使用细节进行了<br />抽象，并提供了基于现实世界对象的接口和其它类。</p>
		<p>　　5.8 线程</p>
		<p>　　(1) C++ Threads</p>
		<p>　　参考网站：<a href="http://threads.sourceforge.net/">http://threads.sourceforge.net/</a><br />　　这个库的目标是给程序员提供易于使用的类，这些类被继承以提供在Linux环境<br />中很难看到的大量的线程方面的功能。</p>
		<p>　　(2) ZThreads</p>
		<p>　　参考网站：<a href="http://zthread.sourceforge.net/">http://zthread.sourceforge.net/</a><br />　　一个先进的面向对象，跨平台的C++线程和同步库。</p>
		<p>　　5.9 序列化</p>
		<p>　　(1) s11n</p>
		<p>　　参考网站：<a href="http://s11n.net/">http://s11n.net/</a><br />　　一个基于STL的C++库，用于序列化POD，STL容器以及用户定义的类型。</p>
		<p>　　(2) Simple XML Persistence Library</p>
		<p>　　参考网站：<a href="http://sxp.sourceforge.net/">http://sxp.sourceforge.net/</a><br />　　这是一个把对象序列化为XML的轻量级的C++库。</p>
		<p>　　5.10 字符串</p>
		<p>　　(1) C++ Str Library</p>
		<p>　　参考网站：<a href="http://www.utilitycode.com/str/">http://www.utilitycode.com/str/</a><br />　　操作字符串和字符的库，支持Windows和支持gcc的多种平台。提供高度优化的<br />代码，并且支持多线程环境和Unicode，同时还有正则表达式的支持。</p>
		<p>　　(2) Common Text Transformation Library</p>
		<p>　　参考网站：<a href="http://cttl.sourceforge.net/">http://cttl.sourceforge.net/</a><br />　　这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较，插入，<br />替换以及用EBNF的语法进行解析。</p>
		<p>　　(3) GRETA</p>
		<p>　　参考网站：<a href="http://research.microsoft.com/projects/greta/">http://research.microsoft.com/projects/greta/</a><br />　　这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况<br />下有非常优秀的表现。</p>
		<p>　　5.11 综合</p>
		<p>　　(1) P::Classes</p>
		<p>　　参考网站：<a href="http://pclasses.com/">http://pclasses.com/</a><br />　　一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot<br />机制，i/o系统包括基于插件的网络协议透明的i/o架构，基于插件的应用程序消息<br />日志框架，访问sql数据库的类等等。</p>
		<p>　　(2) ACDK - Artefaktur Component Development Kit</p>
		<p>　　参考网站：<a href="http://acdk.sourceforge.net/">http://acdk.sourceforge.net/</a><br />　　这是一个平台无关的C++组件框架，类似于Java或者.NET中的框架（反射机制，<br />线程，Unicode，废料收集，I/O，网络，实用工具，XML，等等），以及对Java, P<br />erl, Python, TCL, Lisp, COM 和 CORBA的集成。</p>
		<p>　　(3) dlib C++ library</p>
		<p>　　参考网站：<a href="http://www.cis.ohio-state.edu/~kingd/dlib/">http://www.cis.ohio-state.edu/~kingd/dlib/</a><br />　　各种各样的类的一个综合。大整数，Socket，线程，GUI，容器类,以及浏览目<br />录的API等等。</p>
		<p>　　(4) Chilkat C++ Libraries</p>
		<p>　　参考网站：<a href="http://www.chilkatsoft.com/cpp_libraries.asp">http://www.chilkatsoft.com/cpp_libraries.asp</a><br />　　这是提供zip，e-mail，编码，S/MIME，XML等方面的库。</p>
		<p>　　(5) C++ Portable Types Library (PTypes)</p>
		<p>　　参考网站：<a href="http://www.melikyan.com/ptypes/">http://www.melikyan.com/ptypes/</a><br />　　这是STL的比较简单的替代品，以及可移植的多线程和网络库。</p>
		<p>　　(6) LFC</p>
		<p>　　参考网站：<a href="http://lfc.sourceforge.net/">http://lfc.sourceforge.net/</a><br />　　哦，这又是一个尝试提供一切的C++库</p>
		<p>　　5.12 其他库</p>
		<p>　　(1) Loki</p>
		<p>　　参考网站：<a href="http://www.moderncppdesign.com/">http://www.moderncppdesign.com/</a><br />　　哦，你可能抱怨我早该和Boost一起介绍它，一个实验性质的库。作者在loki中<br />把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过<br />库来提供。同时还提供了智能指针这样比较实用的功能。</p>
		<p>　　(2) ATL</p>
		<p>　　ATL(Active Template Library)<br />　　是一组小巧、高效、灵活的类，这些类为创建可互操作的COM组件提供了基本的<br />设施。</p>
		<p>　　(3) FC++: The Functional C++ Library<br />　　这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表<br />作。如果想要在OOP之外寻找另一分的乐趣，可以去看看函数式程序设计的世界。大<br />师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函<br />数式语言列为至少应当学习的6类编程语言之一。</p>
		<p>　　(4) FACT!</p>
		<p>　　参考网站：<a href="http://www.kfa-juelich.de/zam/FACT/start/index.html">http://www.kfa-juelich.de/zam/FACT/start/index.html</a><br />　　另外一个实现函数式语言特性的库</p>
		<p>　　(5) Crypto++<br />　　提供处理密码，消息验证，单向hash，公匙加密系统等功能的免费库。</p>
		<p>　　还有很多非常激动人心或者是极其实用的C++库，限于我们的水平以及文章的篇<br />幅不能包括进来。在对于这些已经包含近来的库的介绍中，由于并不是每一个我们<br />都使用过，所以难免有偏颇之处，请读者见谅。</p>
		<p>　　6，书籍</p>
		<p>　　以前熊节先生曾撰文评论相对于Java程序设计语言，C++的好书多如牛毛。荣耀<br />先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的<br />经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见，<br />除非你打算以C++作为唯一兴趣或者生存之本，一般读者确实没有足够的时间和必要<br />将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章：《至少应该阅<br />读的九本C++著作》，可以从下面的地址浏览到此文：</p>
		<p>　　<a href="http://www.royaloo.com/articles/articles_2003/9CppBooks.htm">http://www.royaloo.com/articles/articles_2003/9CppBooks.htm</a><br />　　下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的：</p>
		<p>　　(1) 《C++ Primer》</p>
		<p>　　哦，也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者，<br />本书内容更为全面，更为详细易懂，我们称它为“C++的超级宝典”并不过分。配有<br />一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。</p>
		<p>　　(2) 《Essential C++》</p>
		<p>　　如果说《C++ Primer》是C++领域的超级宝典，那么此书作为掌握C++的大局观<br />当之无愧。正如《.NET大局观》一书能够让读者全揽.NET，本书讲述了C++中最核心<br />的全部主题。书虽不厚，内容精炼，不失为《C++ Primer》读者茶余饭后的主题回<br />顾之作。</p>
		<p>　　(3) 《The C++ Programming Language》</p>
		<p>　　Bjarne为你带来的C++教程，真正能够告诉你怎么用才叫真正的C++的唯一一本<br />书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽，入门到精<br />通的感觉，但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程<br />序员，那至少也要反复咀嚼Bjarne先生所强调的若干内容。</p>
		<p>　　(4) 《Effective C++》，《More Effective C++》</p>
		<p>　　是的，正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是<br />C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面，这两<br />本是最贴近语言本质，看后最能够有脱胎换骨感觉的书，读此书你需每日三省汝身<br />。</p>
		<p>　　技术书籍仁者见仁，过多的评论反无太多意义，由读者喜好选择最适合自己的<br />书方为上策。</p>
		<p>
				<br />　　7，资源网站</p>
		<p>　　正如我们可以通过计算机历史上的重要人物了解计算机史的发展，C++相关人物<br />的网站也可以使我们得到最有价值的参考与借鉴，下面的人物我们认为没有介绍的<br />必要，只因下面的人物在C++领域的地位众所周知，我们只将相关的资源进行罗列以<br />供读者学习，他们有的工作于贝尔实验室，有的工作于知名编译器厂商，有的在不<br />断推进语言的标准化，有的为读者撰写了多部千古奇作……<br />　　(1) Bjarne Stroustrup<br />　　<a href="http://www.research.att.com/~bs/">http://www.research.att.com/~bs/</a></p>
		<p>　　(2) Stanley B. Lippman<br />　　<a href="http://blogs.msdn.com/slippman/">http://blogs.msdn.com/slippman/</a><br />　　中文版 <a href="http://www.zengyihome.net/slippman/index.htm">http://www.zengyihome.net/slippman/index.htm</a></p>
		<p>　　(3) Scott Meyers<br />　　<a href="http://www.aristeia.com/">http://www.aristeia.com/</a></p>
		<p>　　(4) David Musser<br />　　<a href="http://www.cs.rpi.edu/~musser/">http://www.cs.rpi.edu/~musser/</a></p>
		<p>　　(5) Bruce Eckel<br />　　<a href="http://www.bruceeckel.com/">http://www.bruceeckel.com</a></p>
		<p>　　(6) Nicolai M. Josuttis<br />　　<a href="http://www.josuttis.com/">http://www.josuttis.com/</a></p>
		<p>　　(7) Herb Sutter<br />　　<a href="http://www.gotw.ca/">http://www.gotw.ca/</a></p>
		<p>　　(8) Andrei Alexandrescu<br />　　<a href="http://www.coderncppdesign.com/">http://www.coderncppdesign.com/</a></p>
		<p>　　(9) 侯捷先生<br />　　<a href="http://www.jjhou.com/">http://www.jjhou.com</a></p>
		<p>　　(10) 孟岩先生<br />　　先生繁忙于工作，痴迷于技术，暂无个人主页，关于先生的作品可以通过CSDN<br />的专栏和侯先生的主页访问到。</p>
		<p>　　(11) 荣耀先生<br />　　<a href="http://www.royaloo.com/">http://www.royaloo.com/</a></p>
		<p>　　(12) 潘爱民先生<br />　　<a href="http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm">http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm</a></p>
		<p>　　除了上述大师的主页外，以下的综合类C++学习参考站点是我们非常愿意向大家<br />推荐的：</p>
		<p>　　(1) CodeProject<br />　　<a href="http://www.codeproject.com/">http://www.codeproject.com</a></p>
		<p>　　(2) CodeGuru<br />　　<a href="http://www.codeguru.com/">http://www.codeguru.com</a></p>
		<p>　　(3) Dr. Dobb's Journal<br />　　<a href="http://www.ddj.com/">http://www.ddj.com</a></p>
		<p>　　(4) C/C++ Users Journal<br />　　<a href="http://www.cuj.com/">http://www.cuj.com</a></p>
		<p>　　(5) C维视点<br />　　<a href="http://www.c-view.org/">http://www.c-view.org</a></p>
		<p>　　(6) allaboutprogram<br />　　<a href="http://www.allaboutprogram.com/">http://www.allaboutprogram.com</a><br />　　其他资料</p>
		<p>　　(1) ISO IEC JTC1/SC22/WG21 - C++：标准C++的权威参考<br />　　<a href="http://anubis.dkuug.dk/jtc1/sc22/wg21/">http://anubis.dkuug.dk/jtc1/sc22/wg21/</a></p>
		<p>　　(2) C++ FAQ LITE — Frequently Asked Questions: 最为全面的C++FAQ<br />　　<a href="http://www.sunistudio.com/cppfaq/index.html">http://www.sunistudio.com/cppfaq/index.html</a><br />　　C/C++ 新闻组：<br />　　你不妨尝试从这里提问和回答问题，很多不错的Q&amp;A资源......</p>
		<p>　　(1) .alt.comp.lang.learn.c-c++<br />　　这个简单些，如果你和我一样是个菜鸟</p>
		<p>　　(2) .comp.lang.c++.moderated<br />嗯，这个显然水平高一些</p>
		<p>　　(3) .comp.std.c++<br />　　如果你需要讨论标准C++相关话题的话</p>
		<p>　　8，不得不写的结束语</p>
		<p>　　结束的时候也是总结现状，展望未来的时候。虽然C++从脱胎于C开始，一路艰<br />难坎坷的走过来，但是无论如何C++已经取得了工业基础的地位。文章列举的大量相关<br />资源就是最好的证明，而业界的大量用C++写成的产品代码以及大量的C++职业工程<br />师则是最直接的证明。同时，我们可以看到各个高校的计算机专业都开设有C++这门<br />课程，网络上对于C++的学习讨论也从来都没有停过。但是，在Java和.NET两大企业<br />开发平台的围攻下，给人的感觉是C++越来越“不行”了。</p>
		<p>　　C++在面向企业的软件开发中，在开发便捷性等方面的确要比Java和C#差很多，<br />其中一个问题是C++语言本身比较复杂，学习曲线比较陡峭，另外一个问题是C++标<br />准化的时间太长，丧失了很多的壮大机会，耗费了很多精力在厂商的之间的斗争上<br />，而C++的标准库离一个完善的程序开发框架还缺少太多太多的内容，各个第三方的<br />类库和框架又在一致性和完整性上没法和随平台提供的框架相提并论。难道C++真的<br />要退出历史舞台了？</p>
		<p>　　从C++目前的活跃程度，以及应用现状来说是完全能够肯定C++仍然是软件工业<br />的基础，也不会退出历史舞台的。另外从Boost，Loki这些库中我们也能够看到C++<br />的发展非常活跃，对于新技术新思维非常激进，C++仍然广泛受到关注。从ACE在高<br />性能通信领域的应用，以及MTL这样的库在数值计算领域的出色表现，我们可以看到<br />C++在高性能应用场合下的不可替代的作用，而嵌入式系统这样的内存受限开发平台<br />，比如Symbian OS上，C++已经发挥着并且将发挥更大的作用。可以预见的是以后的<br />软件无论上层的应用怎么变，它的底层核心都会是由C/C++这样的系统级软件编写的<br />，比如Java虚拟机，.NET Framwork。因为只有这样的系统级软件才能完全彻底的发<br />挥机器的功能。</p>
		<p>　　需要看到的是两个趋势，一个趋势是C++变得更加复杂，更加学院派，通过模板<br />等有潜力的语法因素构造越来越精巧的库成为了现代C++的热点，虽然在利用库实现<br />新的编程范式，乃至设计模式等方面很有开创意义，也确实产生了一些能够便捷开<br />发的工具，但是更多的是把C++变得更加强大，更加复杂，也更加难懂，似乎也更加<br />学院派，不得不说它正在向边缘化道路发展。另一个趋势是C++在主流的企业应用开<br />发中已经逐渐退出了，ERP这样的企业软件开发中基本上不会考虑C++，除非需要考<br />虑性能或者和遗留代码的集成这些因素。C++退守到系统级别语言，成为软件工业的<br />基础是大势所趋。然而反思一下，真的是退守么？自从STL出现，无数的人风起云涌<br />的开始支持C++,他们狂呼“我看到深夜消失了，目标软件工程的出现。我看到了可<br />维护的代码。”是的，STL在可维护性下做得如此出色。但是又怎样呢？STL为C++铺<br />平了现代软件工程的道路，而在上层应用程序软件开发领域这块场地早不单独属于<br />C++,很多程序设计语言都做得很出色，疯狂的支持者会毫不犹豫地说我们应当支持<br />C++,因为它是世界上最棒的语言。而坦率地说，你的腰杆真的那么硬么？也许只是<br />在逃避一些事实。C++是优秀的，这不可否认，STL的出现让C++一度走上了最辉煌的<br />时刻，然而现在看来……我的一位恩师曾言：真正能够将STL应用得淋漓尽致的人很<br />保守地说国内也不超过200人，或许不加入STL能够使C++向着它应当发展的方向发展<br />的更好，而现在看来，C++也应当回首到真正属于他的那一片圣地上……</p>
		<p>参考资料</p>
		<p>本文成文时参考了以下资源：</p>
		<p>1、《程序员》2004年2月，3月，“C++ 程序设计之四书五经” 荣耀<br />2、水'木清华BBS C++版精华区<br />3、<a href="http://jjhou.csdn.net/">http://jjhou.csdn.net</a><br />4、<a href="http://www.royaloo.com/">http://www.royaloo.com</a><br />5、<a href="http://www.zengyihome.net/">http://www.zengyihome.net</a><br />6、C/C++ 开发人员：充实您的 XML 工具箱 </p>
<img src ="http://www.cppblog.com/Lee/aggbug/74811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-02-25 00:26 <a href="http://www.cppblog.com/Lee/archive/2009/02/25/74811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>二进制、八进制、十进制、十六进制之间转换（转）</title><link>http://www.cppblog.com/Lee/archive/2009/02/13/73736.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Fri, 13 Feb 2009 10:15:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/02/13/73736.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/73736.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/02/13/73736.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/73736.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/73736.html</trackback:ping><description><![CDATA[
		<h1 class="block_title">
				<font style="BACKGROUND-COLOR: #ffffff" color="#000000">一、 十进制与二进制之间的转换 <br />（1） 十进制转换为二进制，分为整数部分和小数部分 <br />① 整数部分 <br />方法：除2取余法，即每次将整数部分除以2，余数为该位权上的数，而商继续除以2，余数又为上一个位权上的数，这个步骤一直持续下去，直到商为0为止，最后读数时候，从最后一个余数读起，一直到最前面的一个余数。下面举例： <br />例：将十进制的168转换为二进制 <br /><br />得出结果 将十进制的168转换为二进制，（10101000）2 <br />分析:第一步，将168除以2,商84,余数为0。 <br />第二步，将商84除以2，商42余数为0。 <br />第三步，将商42除以2，商21余数为0。 <br />第四步，将商21除以2，商10余数为1。 <br />第五步，将商10除以2，商5余数为0。 <br />第六步，将商5除以2，商2余数为1。 <br />第七步，将商2除以2，商1余数为0。 <br />第八步，将商1除以2，商0余数为1。 <br />第九步，读数，因为最后一位是经过多次除以2才得到的，因此它是最高位，读数字从最后的余数向前读，即10101000 <br /><br />（2） 小数部分 <br />方法：乘2取整法，即将小数部分乘以2，然后取整数部分，剩下的小数部分继续乘以2，然后取整数部分，剩下的小数部分又乘以2，一直取到小数部分 <br />为零为止。如果永远不能为零，就同十进制数的四舍五入一样，按照要求保留多少位小数时，就根据后面一位是0还是1，取舍，如果是零，舍掉，如果是1，向入一位。换句话说就是0舍1入。读数要从前面的整数读到后面的整数，下面举例： <br />例1：将0.125换算为二进制 <br /><br />得出结果：将0.125换算为二进制（0.001）2 <br />分析：第一步，将0.125乘以2，得0.25,则整数部分为0,小数部分为0.25; <br />第二步, 将小数部分0.25乘以2,得0.5,则整数部分为0,小数部分为0.5; <br />第三步, 将小数部分0.5乘以2,得1.0,则整数部分为1,小数部分为0.0; <br />第四步,读数,从第一位读起,读到最后一位,即为0.001。 <br /><br /><br />例2,将0.45转换为二进制（保留到小数点第四位） <br /><br /><br />大家从上面步骤可以看出，当第五次做乘法时候，得到的结果是0.4，那么小数部分继续乘以2，得0.8，0.8又乘以2的，到1.6这样一直乘下去，最后不可能得到小数部分为零，因此，这个时候只好学习十进制的方法进行四舍五入了，但是二进制只有0和1两个，于是就出现0舍1入。这个也是计算机在转换中会产生误差，但是由于保留位数很多，精度很高，所以可以忽略不计。 <br />那么，我们可以得出结果将0.45转换为二进制约等于0.0111 <br />上面介绍的方法是十进制转换为为二进制的方法，需要大家注意的是： <br />1） 十进制转换为二进制，需要分成整数和小数两个部分分别转换 <br />2） 当转换整数时，用的除2取余法，而转换小数时候，用的是乘2取整法 <br />3） 注意他们的读数方向 <br />因此，我们从上面的方法，我们可以得出十进制数168.125转换为二进制为10101000.001,或者十进制数转换为二进制数约等于10101000.0111。 <br /><br />（3） 二进制转换为十进制 不分整数和小数部分 <br />方法：按权相加法，即将二进制每位上的数乘以权，然后相加之和即是十进制数。例 <br />将二进制数101.101转换为十进制数。 <br /><br />得出结果：（101.101）2=(5.625)10 <br />大家在做二进制转换成十进制需要注意的是 <br />1） 要知道二进制每位的权值 <br />2） 要能求出每位的值 <br /><br /><br />二、 二进制与八进制之间的转换 <br />首先，我们需要了解一个数学关系，即23=8，24=16，而八进制和十六进制是用这 <br />关系衍生而来的，即用三位二进制表示一位八进制，用四位二进制表示一位十六进制数。 <br />接着，记住4个数字8、4、2、1（23=8、22=4、21=2、20=1）。现在我们来练习二进制与八进制之间的转换。 <br />（1） 二进制转换为八进制 <br />方法：取三合一法，即从二进制的小数点为分界点，向左（向右）每三位取成一位，接着将这三位二进制按权相加，得到的数就是一位八位二进制数，然后，按顺序进行排列，小数点的位置不变，得到的数字就是我们所求的八进制数。如果向左（向右）取三位后，取到最高（最低）位时候，如果无法凑足三位，可以在小数点最左边（最右边），即整数的最高位（最低位）添0，凑足三位。例 <br />①将二进制数101110.101转换为八进制 <br /><br />得到结果：将101110.101转换为八进制为56.5 <br /><br />② 将二进制数1101.1转换为八进制 <br /><br />得到结果：将1101.1转换为八进制为15.4 <br /><br />（2） 将八进制转换为二进制 <br />方法：取一分三法，即将一位八进制数分解成三位二进制数，用三位二进制按权相加去凑这位八进制数，小数点位置照旧。例： <br />① 将八进制数67.54转换为二进制 <br /><br />因此，将八进制数67.54转换为二进制数为110111.101100，即110111.1011 <br />大家从上面这道题可以看出，计算八进制转换为二进制 <br />首先，将八进制按照从左到右，每位展开为三位，小数点位置不变 <br />然后，按每位展开为22，21，20（即4、2、1）三位去做凑数，即a×22+ b×21 +c×20=该位上的数（a=1或者a=0，b=1或者b=0，c=1或者c=0）,将abc排列就是该位的二进制数 <br />接着，将每位上转换成二进制数按顺序排列 <br />最后，就得到了八进制转换成二进制的数字。 <br />以上的方法就是二进制与八进制的互换，大家在做题的时候需要注意的是 <br />1） 他们之间的互换是以一位与三位转换，这个有别于二进制与十进制转换 <br />2） 大家在做添0和去0的时候要注意，是在小数点最左边或者小数点的最右边（即整数的最高位和小数的最低位）才能添0或者去0，否则将产生错误 <br /><br />三、 二进制与十六进制的转换 <br />方法：与二进制与八进制转换相似，只不过是一位（十六）与四位（二进制）的转换，下面具体讲解 <br />（1） 二进制转换为十六进制 <br />方法：取四合一法，即从二进制的小数点为分界点，向左（向右）每四位取成一位，接着将这四位二进制按权相加，得到的数就是一位十六位二进制数，然后，按顺序进行排列，小数点的位置不变，得到的数字就是我们所求的十六进制数。如果向左（向右）取四位后，取到最高（最低）位时候，如果无法凑足四位，可以在小数点最左边（最右边），即整数的最高位（最低位）添0，凑足四位。 <br />①例：将二进制11101001.1011转换为十六进制 <br /><br />得到结果：将二进制11101001.1011转换为十六进制为E9.B <br /><br /><br />② 例：将101011.101转换为十六进制 <br /><br />因此得到结果：将二进制101011.101转换为十六进制为2B.A <br /><br /><br /><br />(2)将十六进制转换为二进制 <br />方法：取一分四法，即将一位十六进制数分解成四位二进制数，用四位二进制按权相加去凑这位十六进制数，小数点位置照旧。 <br />①将十六进制6E.2转换为二进制数 <br /><br />因此得到结果：将十六进制6E.2转换为二进制为01101110.0010即110110.001 <br /><br />四、八进制与十六进制的转换 <br />方法：一般不能互相直接转换，一般是将八进制（或十六进制）转换为二进制，然后再将二进制转换为十六进制（或八进制），小数点位置不变。那么相应的转换请参照上面二进制与八进制的转换和二进制与十六进制的转 <br /><br /><br />五、八进制与十进制的转换 <br />（1）八进制转换为十进制 <br />方法：按权相加法，即将八进制每位上的数乘以位权，然后相加之和即是十进制数。 <br />例：①将八进制数67.35转换为十进制 <br /><br />（2）十进制转换为八进制 <br />十进制转换成八进制有两种方法： <br />1）间接法：先将十进制转换成二进制，然后将二进制又转换成八进制 <br />2）直接法：前面我们讲过，八进制是由二进制衍生而来的，因此我们可以采用与十进制转换为二进制相类似的方法，还是整数部分的转换和小数部分的转换，下面来具体讲解一下： <br />①整数部分 <br />方法：除8取余法，即每次将整数部分除以8，余数为该位权上的数，而商继续除以8，余数又为上一个位权上的数，这个步骤一直持续下去，直到商为0为止，最后读数时候，从最后一个余数起，一直到最前面的一个余数。 <br />②小数部分 <br />方法：乘8取整法，即将小数部分乘以8，然后取整数部分，剩下的小数部分继续乘以8，然后取整数部分，剩下的小数部分又乘以8，一直取到小数部分为零为止。如果永远不能为零，就同十进制数的四舍五入一样，暂取个名字叫3舍4入。 <br />例：将十进制数796.703125转换为八进制数 <br />解：先将这个数字分为整数部分796和小数部分0.703125 <br />整数部分 <br /><br />小数部分 <br /><br />因此，得到结果十进制796.703125转换八进制为1434.55 <br />上面的方法大家可以验证一下，你可以先将十进制转换，然后在转换为八进制，这样看得到的结果是否一样 <br /><br />六、十六进制与十进制的转换 <br />十六进制与八进制有很多相似之处，大家可以参照上面八进制与十进制的转换自己试试这两个进制之间的转换。 <br />通过上面对各种进制之间的转换，我们可以将前面的转换图重新完善一下： <br /><br /><br />本文介绍了二进制、十进制、八进制、十六进制四种进制之间相互的转换，大家在转换的时候要注意转换的方法，以及步骤，特别是十进制转换为期于三种进制之间，要分为整数部分和小数部分，最后就是小数点的位置。但是要保证考试中不出现错误还是需要大家经常练习，这样才能熟能生巧。 </font>
		</h1>
<img src ="http://www.cppblog.com/Lee/aggbug/73736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-02-13 18:15 <a href="http://www.cppblog.com/Lee/archive/2009/02/13/73736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GDI &amp; GDI+ </title><link>http://www.cppblog.com/Lee/archive/2009/02/07/73170.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sat, 07 Feb 2009 11:37:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2009/02/07/73170.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/73170.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2009/02/07/73170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/73170.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/73170.html</trackback:ping><description><![CDATA[
		<strong>1.基本概念<br /><br /></strong>　　GDI在Windows中定义为Graphics Device Interface，即图形设备接口，是Windows API(Application Programming Interface)的一个重要组成部分。它是Windows图形显示程序与实际物理设备之间的桥梁，GDI使得用户无需关心具体设备的细节，而只需在一个虚拟的环境(即逻辑设备)中进行操作。它的桥梁作用体现在：<br /><br />　　(1)用户通过调用GDI函数将逻辑空间的操作转化为具体针对设备驱动程序的调用。<br /><br />　　为实现图形设备无关性，Windows 的绘图操作在一个设备描述表上进行。用户拥有自己的"逻辑坐标"系统，它独立于实际的物理设备，与"设备坐标"相对应。开发Windows应用程序时，程序员关心的是逻辑坐标，我们在逻辑坐标系上绘图，利用GDI将逻辑窗口映射到物理设备上。<br /><br />　　(2)GDI能检测具体设备的能力，并依据具体的设备以最优方式驱动这些设备，完成真实的显示。<br /><br />　　GDI函数大致可分类为：设备上下文函数(如GetDC、CreateDC、DeleteDC)、 画线函数(如LineTo、Polyline、Arc)、填充画图函数(如Ellipse、FillRect、Pie)、画图属性函数(如SetBkColor、SetBkMode、SetTextColor)、文本、字体函数(如TextOut、GetFontData)、位图函数(如SetPixel、BitBlt、StretchBlt)、坐标函数(如DPtoLP、LPtoDP、ScreenToClient、ClientToScreen)、映射函数(如SetMapMode、SetWindowExtEx、SetViewportExtEx)、元文件函数(如PlayMetaFile、SetWinMetaFileBits)、区域函数(如FillRgn、FrameRgn、InvertRgn)、路径函数(如BeginPath、EndPath、StrokeAndFillPath)、裁剪函数(如SelectClipRgn、SelectClipPath)等。<br /><br />　　GDI虽然使程序员得到了一定程度的解脱，但是其编程方式仍很麻烦。譬如，显示一张位图，程序员需要进行"装入位图―读取位图文件头信息―启用设备场景―调色板变换"等一连串操作。而有了GDI+，这些问题便迎刃而解了。<br /><br />　　顾名思义，GDI+是GDI的增强版。它是微软在Windows 2000以后操作系统中提供的新接口，其通过一套部署为托管代码的类来展现，这套类被称为GDI+的"托管类接口"。GDI+主要提供了以下三类服务:<br /><br />　　(1) 二维矢量图形：GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类；<br /><br />　　(2) 图像处理：大多数图片都难以划定为直线和曲线的集合，无法使用二维矢量图形方式进行处理。因此，GDI+为我们提供了Bitmap、Image等类,它们可用于显示、操作和保存BMP、JPG、GIF等图像格式。<br /><br />　　(3) 文字显示：GDI+支持使用各种字体、字号和样式来显示文本。<br /><br />　　GDI接口是基于函数的，而GDI+是基于C++类的对象化的应用程序编程接口，因此使用起来比GDI要方便。<br /><br />　　<strong>2.例程简述</strong><br /><br />　　单击此处下载本文例程源代码。<br /><br />　　本文后续的讲解都基于这样的一个例子工程(例程的开发环境为Visual C++6.0，操作系统为Windows XP)，它是一个基于对话框的MFC应用程序，包括2个父菜单：<br /><br />　　(1) GDI<br /><br />　　GDI父菜单下包括一个子菜单：<br /><br />ID：IDM_GDI_DRAW_LINE caption：画线<br /><br />　　单击事件：在窗口绘制正旋曲线<br /><br />　　(2) GDI+<br /><br />　　DIB位图父菜单下包括两个子菜单：<br /><br />a. ID：IDM_GDIP_DRAW_LINE caption：画线<br /><br />　　单击事件：在窗口绘制正旋曲线<br /><br />b. caption：新增功能，其下又包括下列子菜单：<br /><br />　　(ⅰ)ID：IDM_Gradient_Brush caption：渐变画刷<br /><br />　　单击事件：在窗口演示GDI+的渐变画刷功能<br /><br />　　(ⅱ)ID：IDM_Cardinal_Spline caption：基数样条<br /><br />　　单击事件：在窗口演示GDI+的基数样条函数功能<br /><br />　　(ⅲ)ID：IDM_Transformation_Matrix caption：变形和矩阵对象<br /><br />　　单击事件：在窗口演示GDI+的变形和矩阵对象功能<br /><br />　　(ⅳ)ID：IDM_Scalable_Region caption：可伸缩区域<br /><br />　　单击事件：在窗口演示GDI+的可伸缩区域功能<br /><br />　　(ⅴ)ID：IDM_IMAGE caption：图像<br /><br />　　单击事件：在窗口演示GDI+的多种图像格式支持功能<br /><br />　　(ⅵ)ID：IDM_Alpha_Blend caption：Alpha混合<br /><br />　　单击事件：在窗口演示GDI+的Alpha混合功能<br /><br />　　(ⅶ)ID：IDM_TEXT caption：文本<br /><br />　　单击事件：在窗口演示GDI+的强大文本输出能力<br /><br />　　后续篇章将集中在对上述菜单单击事件消息处理函数的讲解，下面的代码是整个对话框类CGdiexampleDlg的消息映射：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>BEGIN_MESSAGE_MAP(CGdiexampleDlg, CDialog)<br />//{{AFX_MSG_MAP(CGdiexampleDlg)<br />ON_WM_SYSCOMMAND()<br />ON_WM_PAINT()<br />ON_WM_QUERYDRAGICON()<br />ON_COMMAND(IDM_GDI_DRAW_LINE, OnGdiDrawLine)<br />ON_COMMAND(IDM_GDIP_DRAW_LINE, OnGdipDrawLine)<br />ON_COMMAND(IDM_Gradient_Brush, OnGradientBrush)<br />ON_COMMAND(IDM_Cardinal_Spline, OnCardinalSpline)<br />ON_COMMAND(IDM_Transformation_Matrix, OnTransformationMatrix)<br />ON_COMMAND(IDM_Scalable_Region, OnScalableRegion)<br />ON_COMMAND(IDM_IMAGE, OnImage)<br />ON_COMMAND(IDM_Alpha_Blend, OnAlphaBlend)<br />ON_COMMAND(IDM_TEXT, OnText)<br />//}}AFX_MSG_MAP<br />END_MESSAGE_MAP()</td></tr></tbody></table><p><strong>3.GDI编程<br /><br /></strong>　　"GDI"菜单下的"画线"子菜单单击事件消息处理函数的代码如下：<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnGdiDrawLine()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//逻辑坐标与设备坐标变换<br />　CRect rect;<br />　GetClientRect(&amp;rect);<br />　dc.SetMapMode(MM_ANISOTROPIC);<br />　dc.SetWindowOrg(0, 0);<br />　dc.SetWindowExt(rect.right, rect.bottom);<br />　dc.SetViewportOrg(0, rect.bottom / 2);<br />　dc.SetViewportExt(rect.right, - rect.bottom);<br />　//创建绘制正旋曲线的pen并将其选入设备上下文<br />　CPen pen(PS_SOLID, 1, RGB(255, 0, 0));<br />　HGDIOBJ oldObject = dc.SelectObject(pen.GetSafeHandle());<br />　//绘制正旋曲线<br />　dc.MoveTo(0, 0);<br />　for (int i = 0; i &lt; rect.right; i++)<br />　{<br />　　dc.LineTo(i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI));<br />　}<br />　//创建绘制x轴的pen并将其选入设备上下文<br />　CPen penx(PS_SOLID, 1, RGB(0, 0, 255));<br />　dc.SelectObject(penx.GetSafeHandle());<br />　//绘制X轴<br />　dc.MoveTo(0, 0);<br />　dc.LineTo(rect.right, 0);<br />　//恢复原先的pen<br />　dc.SelectObject(oldObject);<br />}</td></tr></tbody></table><br />　　单击这个按钮，会出现如图1所示的效果，我们来对此进行解读。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img alt="WindowsGDI和GDI+编程实例全剖析(2)" src="http://image2.sina.com.cn/IT/cr/2005/1111/2929087046.jpg" border="0" /><br />图1 绘制正旋曲线</div></td></tr></tbody></table><br />　　前文提到，GDI编程需进行设备坐标和逻辑坐标的转化。而屏幕上的设备坐标通常会按客户坐标给出，客户坐标依赖于窗口的客户区域，其起始位置位于客户区域的左上角。为示区别，图2给出了设备坐标和用户逻辑坐标的示例。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 398px; HEIGHT: 206px" alt="WindowsGDI和GDI+编程实例全剖析(2)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/1867704990.jpg" border="1" /><br />图2 设备坐标与逻辑坐标</div></td></tr></tbody></table><br />　　设备坐标与逻辑坐标的转换关系如下：<br /><br /><p align="center"><img style="WIDTH: 292px; HEIGHT: 25px" alt="WindowsGDI和GDI+编程实例全剖析(2)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/3687346646.gif" border="1" /><br /><img style="WIDTH: 257px; HEIGHT: 25px" alt="WindowsGDI和GDI+编程实例全剖析(2)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/3445173466.gif" border="1" /></p><p>　　公式中的&lt;Xvorg, Yvorg&gt;是设备空间中视口的原点，而&lt; Xworg, Yworg &gt;是逻辑空间中窗口的原点。 Xwext/Xvext和Ywext/Yvext分别是窗口与视口水平和垂直范围的比例。<br /><br />　　因此，经过程序中的dc.SetWindowOrg (0，0) 和dc.SetViewportOrg (0，rect.bottom/2)语句我们设置了视口和窗口的原点；而经过程序中的dc.SetWindowExt (rect.right，rect.bottom) 和dc.SetViewportExt (rect.right，-rect.bottom) 语句我们设置了视口和窗口的范围。由于视口和窗口的纵坐标方向相反，设置视口的垂直范围为负值。这样我们得到了一个逻辑坐标原点为客户区水平方向最左边和垂直方向居中的坐标系，我们在这个坐标系上直接绘制正旋曲线，不需要再理睬Windows对话框客户区坐标了。<br /><br />　　void CGdiexampleDlg::OnGdiDrawLine()函数中未指定逻辑设备和物理设备的映射模式，则为缺省的MM_TEXT。在这种模式下，一个逻辑单位对应于一个像素点。映射模式是GDI中的一个重要概念，其它的映射模式还有MM_LOENGLlSH、MM_HIENGLISH、MM_LOMETRIC和MM_HIMETRIC等。我们可以通过如下语句指定映射模式为MM_TEXT：<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>dc.SetMapMode(MM_TEXT);</td></tr></tbody></table><br />　　值得一提的是，从上述代码可以看出：在GDI编程中，几乎所有的操作都围绕设备上下文dc展开。的确，这正是GDI编程的特点！设备上下文是Windows 使用的一种结构，所有GDI操作前都需取得特定设备的上下文，函数中的CClientDC dc (this) 语句完成这一功能。<br /><br />　　归纳可得，利用GDI进行图形、图像处理的一般操作步骤为：<br /><br />　　1. 取得指定窗口的DC；<br /><br />　　2. 确定使用的坐标系及映射方式； <br /><br />　　3. 进行图形、图像或文字处理；<br /><br />　　4. 释放所使用的DC。<br /><br />　　4.GDI+编程<br /><br />　　"GDI+"菜单下的"画线"子菜单单击事件消息处理函数的代码如下： <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnGdipDrawLine()<br />{<br />　// TODO: Add your command handler code here <br />　CClientDC dc(this); <br />　//逻辑坐标与设备坐标变换<br />　CRect rect;<br />　GetClientRect(&amp;rect);<br />　dc.SetMapMode(MM_ANISOTROPIC);<br />　dc.SetWindowOrg(0, 0);<br />　dc.SetWindowExt(rect.right, rect.bottom);<br />　dc.SetViewportOrg(0, rect.bottom / 2);<br />　dc.SetViewportExt(rect.right, - rect.bottom);<br /><br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　//创建pen<br />　Pen myPen(Color::Red);<br />　myPen.SetWidth(1);<br />　//画正旋曲线<br />　for (int i = 0; i &lt; rect.right; i++)<br />　{<br />　　graphics.DrawLine(&amp;myPen, i, 100 *sin(2 *(i / (rect.right / 5.0)) *PI), i +<br />1, 100 *sin(2 *((i + 1) / (rect.right / 5.0)) *PI));<br />　}<br />　//画X轴<br />　myPen.SetColor(Color::Blue);<br />　graphics.DrawLine(&amp;myPen, 0, 0, rect.right, 0);<br />}</td></tr></tbody></table><br />　　由于我们使用的是Visual C++6.0而非VS.Net，我们需要下载微软的GDIPLUS支持包。在微软官方网站下载时需认证Windows为正版，我们可从这个地址下载：http://www.codeguru.com/code/legacy/gdi/GDIPlus.zip。一个完整的GDI+支持包至少包括如下文件：<br /><br />　　(1)头文件：gdiplus.h<br /><br />　　(2)动态链接库的.lib文件：gdiplus.lib<br /><br />　　(3)动态链接库的.dll文件：gdiplus.dll<br /><br />　　少了(1)、(2)程序不能编译，少了(3)程序能以共享DLL的方式编译但是不能运行，运行时找不到.dll文件。<br /><br />　　为使得Visual C++6.0支持GDI+，我们需要在使用GDI+对象的文件的开头添加如下代码：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>#define UNICODE<br />#ifndef ULONG_PTR<br />#define ULONG_PTR unsigned long*<br />#endif<br />#include "c:\gdiplus\includes\gdiplus.h"<br />using namespace Gdiplus;<br />#pragma comment(lib, "c:\gdiplus\lib\gdiplus.lib")</td></tr></tbody></table><br />　　在Visual C++中使用GDI+必须先进行GDI+的初始化，我们在CWinApp派生类的InitInstance函数中进行此项工作是最好的：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><p>///////////////////////////////////////<br />CGdiexampleApp initialization<br /><br />BOOL CGdiexampleApp::InitInstance()<br />{<br />　AfxEnableControlContainer();<br /><br />　// Standard initialization<br /><br />　#ifdef _AFXDLL<br />　　Enable3dControls(); // Call this when using MFC in a shared DLL<br />　#else<br />　　Enable3dControlsStatic(); // </p><p>Call this when linking to MFC statically<br />　#endif<br /><br />　//初始化gdiplus的环境<br />　GdiplusStartupInput gdiplusStartupInput;<br />　ULONG_PTR gdiplusToken;<br />　// 初始化GDI+.<br />　GdiplusStartup(&amp;gdiplusToken, &amp;gdiplusStartupInput, NULL);<br /><br />　CGdiexampleDlg dlg;<br />　m_pMainWnd = &amp;dlg;<br />　int nResponse = dlg.DoModal();<br />　if (nResponse == IDOK){}<br />　else if (nResponse == IDCANCEL){}<br /><br />　//关闭gdiplus的环境<br />　GdiplusShutdown(gdiplusToken);<br /><br />　return FALSE;<br />}</p></td></tr></tbody></table><br />　　单击"GDI+"菜单下的"画线"子菜单，也会出现如图1所示的效果。观察void CGdiexampleDlg::OnGdipDrawLine() 函数，我们发现用GDI+进行图形、图像操作的步骤为：<br /><br />　　(1)创建 Graphics 对象：Graphics 对象表示GDI+绘图表面，是用于创建图形图像的对象；<br /><br />　　(2)使用 Graphics 对象绘制线条和形状、呈现文本或显示与操作图像。<br /><br />　　Graphics 对象是GDI+的核心，GDI中设备上下文dc和Graphics 对象的作用相似，但在GDI中使用的是基于句柄的编程模式，而GDI+中使用的则是基于对象的编程模式。Graphics封装了GDI+ 绘图面，而且此类无法被继承，它的所有成员函数都不是虚函数。<br /><br />　　下面，我们来逐个用实际代码实现GDI+的新增功能，这些新增功能包括：渐变的画刷(Gradient Brushes)、基数样条函数(Cardinal Splines)、持久的路径对象(Persistent Path Objects)、变形和矩阵对象(Transformations &amp;Matrix Object)、可伸缩区域(Scalable Regions)、Alpha混合(Alpha Blending)和丰富的图像格式支持等。<p><strong>渐变的画刷<br /><br /></strong>　　GDI+提供了用于填充图形、路径和区域的线性渐变画刷和路径渐变画刷。<br /><br />　　线性渐变画刷使用渐变颜色来填充图形。<br /><br />　　当用路径渐变画刷填充图形时，可指定从图形的一部分移至另一部分时画刷颜色的变化方式。例如，我们可以只指定图形的中心颜色和边缘颜色，当画刷从图形中间向外边缘移动时，画刷会逐渐从中心颜色变化到边缘颜色。 <br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnGradientBrush()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　CRect rect;<br />　GetClientRect(&amp;rect);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　//创建渐变画刷<br />　LinearGradientBrush lgb(Point(0, 0), Point(rect.right, rect.bottom), Color::Blue, Color::Green);<br />　//填充<br />　graphics.FillRectangle(&amp;lgb, 0, 0, rect.right, rect.bottom);<br />}</td></tr></tbody></table><br />　　本程序使用线性渐变画刷，当画刷从客户区左上角移向客户区右下角的过程中，颜色逐渐由蓝色转变为绿色。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 491px; HEIGHT: 344px" alt="WindowsGDI和GDI+编程实例全剖析(3)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/3410161706.jpg" border="1" /><br />图3 GDI+渐变画刷</div></td></tr></tbody></table><br />　　<strong>基数样条函数</strong><br /><br />　　GDI+支持基数样条，基数样条指的是一连串单独的曲线，这些曲线连接起来形成一条较大的曲线。样条由点(Point结构体)的数组指定，并通过该数组中的每一个点。基数样条平滑地穿过数组中的每一个点(不出现尖角)，因此比用直线连接创建的路径精确。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnCardinalSpline()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　Point points[] =<br />　{<br />　　Point(0, 0), Point(100, 200), Point(200, 0), Point(300, 200), Point(400, 00)<br />　};<br />　//直接画线<br />　for (int i = 0; i &lt; 4; i++)<br />　{<br />　　graphics.DrawLine(&amp;Pen(Color::Blue, 3), points[i], points[i + 1]);<br />　}<br />　//利用基数样条画线<br />　graphics.DrawCurve(&amp;Pen(Color::Red, 3), points, 5);<br />}</td></tr></tbody></table><br />　　图4演示了直接连线和经过基数样条平滑拟合后的线条的对比，后者的曲线(Curve)没有尖角。这个工作我们在中学的数学课上把离散的点连接成曲线时做过。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 493px; HEIGHT: 344px" alt="WindowsGDI和GDI+编程实例全剖析(3)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/2697862847.jpg" border="1" /><br />图4 GDI+基数样条</div></td></tr></tbody></table><p> </p><p><strong>持久的路径对象<br /><br /></strong>　　在GDI中，路径隶属于一个设备上下文，一旦设备环境指针超过它的生存期，路径也会被删除。利用GDI+，可以创建并维护与Graphics对象分开的GraphicsPath 对象，它不依赖于Graphics对象的生存期。<br /><br />　　变形和矩阵对象<br /><br />　　GDI+提供了Matrix对象，它是一种可以使变形(旋转、平移、缩放等) 简易灵活的强大工具，Matrix对象需与要被变形的对象联合使用。对于GraphicsPath类，我们可以使用其成员函数Transform接收Matrix参数用于变形。<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnTransformationMatrix()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　GraphicsPath path;<br />　path.AddRectangle(Rect(250, 20, 70, 70));<br />　graphics.DrawPath(&amp;Pen(Color::Black, 1), &amp;path); // 在应用变形矩阵之前绘制矩形<br />　// 路径变形<br />　Matrix matrix1, matrix2;<br /><br />　matrix1.Rotate(45.0f); //旋转顺时针45度<br />　path.Transform(&amp;matrix1); //应用变形<br />　graphics.DrawPath(&amp;Pen(Color::Red, 3), &amp;path);<br /><br />　matrix2.Scale(1.0f, 0.5f); //转化成为平行四边形法则<br />　path.Transform(&amp;matrix2); //应用变形<br />　graphics.DrawPath(&amp;Pen(Color::Blue, 3), &amp;path);<br />} </td></tr></tbody></table><br />　　图5演示了正方形经过旋转和拉伸之后的效果：黑色的为原始图形，红色的为旋转45度之后的图形，蓝色的为经过拉伸为平行四边形后的图形。 <br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 493px; HEIGHT: 344px" alt="WindowsGDI和GDI+编程实例全剖析(4)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/2682685663.jpg" border="1" /><br />图5 GDI+变形和矩阵对象</div></td></tr></tbody></table><br />　　<strong>可伸缩区域<br /></strong><br />　　GDI+通过对区域(Region)的支持极大地扩展了GDI。在GDI 中，区域存储在设备坐标中，可应用于区域的唯一变形是平移。但是在GDI +中，区域存储在全局坐标(世界坐标)中，可对区域利用变形矩阵进行变形(旋转、平移、缩放等)。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnScalableRegion()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　//创建GraphicsPath<br />　GraphicsPath path;<br />　path.AddLine(100, 100, 150, 150);<br />　path.AddLine(50, 150, 150, 150);<br />　path.AddLine(50, 150, 100, 100);<br />　//创建Region<br />　Region region(&amp;path);<br />　//填充区域 <br />　graphics.FillRegion(&amp;SolidBrush(Color::Blue), &amp;region);<br />　//区域变形<br />　Matrix matrix;<br />　matrix.Rotate(10.0f); //旋转顺时针20度<br />　matrix.Scale(1.0f, 0.3f); //拉伸<br />　region.Transform(&amp;matrix); //应用变形<br />　//填充变形后的区域 <br />　graphics.FillRegion(&amp;SolidBrush(Color::Green), &amp;region);<br />}</td></tr></tbody></table><br />　　上述程序中以蓝色填充一个三角形区域，接着将此区域旋转和拉伸，再次显示，其效果如图6。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 495px; HEIGHT: 345px" alt="WindowsGDI和GDI+编程实例全剖析(4)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/3493949473.jpg" border="1" /><br />图6 GDI+区域变形</div></td></tr></tbody></table><p><strong>丰富的图像格式支持<br /><br /></strong>　　GDI +提供了Image、Bitmap 和Metafile 类，方便用户进行图像格式的加载、操作和保存。GDI+支持的图像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、EMF等，几乎涵盖了所有的常用图像格式。<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnImage()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　Image image(L "d:\1.jpg");<br />　//在矩形区域内显示jpg图像<br />　Point destPoints1[3] =<br />　{<br />　　Point(10, 10), Point(220, 10), Point(10, 290)<br />　};<br />　graphics.DrawImage(&amp;image, destPoints1, 3);<br />　//在平行四边形区域内显示jpg图像<br />　Point destPoints2[3] =<br />　{<br />　　Point(230, 10), Point(440, 10), Point(270, 290)<br />　};<br />　graphics.DrawImage(&amp;image, destPoints2, 3);<br />}</td></tr></tbody></table><br />　　上述程序将D盘根目录下文件名为"1.jpg"的jpg图像以矩阵和平行四边形两种方式显示，效果如图7。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 493px; HEIGHT: 344px" alt="WindowsGDI和GDI+编程实例全剖析(5)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/3247894246.jpg" border="1" /><br />图7 GDI+多种图像格式支持</div></td></tr></tbody></table><br />　　由此我们可以看出，GDI+在图像显示和操作方面的确比GDI简单许多。回忆我们在《Visual C++中DDB与DIB位图编程全攻略》一文中所介绍的用GDI显示位图的方式，其与GDI+图像处理的难易程度真是有天壤之别。<p><strong>Alpha混合<br /><br /></strong>　　Alpha允许将两个物体混合起来显示，在3D气氛和场景渲染等方面有广泛应用。它能"雾化"图像，使得一个图像着色在另一个半透明的图像上，呈现一种朦胧美。我们知道，一个像素可用R，G，B三个维度来表示，我们可以再加上第4个即：Alpha维度(channel)，表征透明程度。<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnAlphaBlend()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　//创建ColorMatrix<br />　ColorMatrix ClrMatrix =<br />　{<br />　　1.0f, 0.0f, 0.0f, 0.0f, 0.0f,<br />　　0.0f, 1.0f, 0.0f, 0.0f, 0.0f,<br />　　0.0f, 0.0f, 1.0f, 0.0f, 0.0f,<br />　　0.0f, 0.0f, 0.0f, 0.5f, 0.0f,<br />　　0.0f, 0.0f, 0.0f, 0.0f, 1.0f<br />　};<br />　//将ColorMatrix赋给ImageAttributes<br />　ImageAttributes ImgAttr;<br />　ImgAttr.SetColorMatrix(&amp;ClrMatrix, ColorMatrixFlagsDefault,ColorAdjustTypeBitmap);<br />　//在矩形区域内显示jpg图像<br />　Image img1(L "d:\1.jpg");<br />　Point destPoints1[3] =<br />　{<br />　　Point(10, 10), Point(220, 10), Point(10, 290)<br />　};<br />　graphics.DrawImage(&amp;img1, destPoints1, 3);<br />　//Alpha混合<br />　Image img2(L "d:\2.jpg");<br />　int width, height;<br />　width = img2.GetWidth();<br />　height = img2.GetHeight();<br />　graphics.DrawImage(&amp;img2, RectF(10, 10, 210, 280), 0, 0, width, height,UnitPixel, &amp;ImgAttr);<br />　//在平行四边形区域内显示jpg图像<br />　Point destPoints2[3] =<br />　{<br />　　Point(230, 10), Point(440, 10), Point(270, 290)<br />　};<br />　graphics.DrawImage(&amp;img1, destPoints2, 3);<br />　//Alpha混合<br />　graphics.DrawImage(&amp;img2, destPoints2, 3, 0, 0, width, height, UnitPixel,&amp;ImgAttr);<br />}</td></tr></tbody></table><br />　　上述程序中将D盘根目录下文件名为"1.jpg"的图像以矩阵和平行四边形两种方式显示，然后将文件名为为"2.jpg"的图像与之进行混合，其效果如图8。<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 492px; HEIGHT: 344px" alt="WindowsGDI和GDI+编程实例全剖析(6)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/1131810837.jpg" border="1" /><br />图8 GDI+ Alpha混合</div></td></tr></tbody></table><br />　　为了能进行Alpha混合，我们需要使用ImageAttributes类和ColorMatrix矩阵，ImageAttributes可以进行颜色、灰度等调整从而达到控制图像着色方式的目的。ColorMatrix是ImageAttributes类大多数函数的参数，它包含了Alpha、Red、Green、Blue维度的值，以及另一维w，顺序为RGBaw。<br /><br />　　CGdiexampleDlg::OnAlphaBlend()函数中ColorMatrix的实例ClrMatrix中元素(4,4)的值为0.5，表示Alpha度的值为0.5(即半透明)。在ColorMatrix中，元素(5,5)的值恒定为1.0。我们把ClrMatrix的元素(0,0)修改为0.0，即使得图像2.jpg的红色维度全不显示，再看效果，为图9。列位读者，我们以前在豪杰超级解霸中调整R，G，B值从而控制图像输出颜色的时候，调的就是这个东东！图9的效果很像破旧彩色电视机，红色电子枪"嗝"了。刚大学毕业时，俺那个叫穷啊，就买了这么个电视机，还看得很爽，真是往事不堪回首！<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="WIDTH: 493px; HEIGHT: 341px" alt="WindowsGDI和GDI+编程实例全剖析(6)" hspace="0" src="http://image2.sina.com.cn/IT/cr/2005/1111/1780812232.jpg" border="1" /><br />图9 GDI+中的ColorMatrix</div></td></tr></tbody></table><p><strong>强大的文字输出<br /><br /></strong>　　GDI+拥有极其强大的文字输出处理能力，输出文字的颜色、字体、填充方式都可以直接作为Graphics类DrawString成员函数的参数进行设置，其功能远胜过GDI设备上下文的TextOut函数。<br /><br /></p><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>void CGdiexampleDlg::OnText()<br />{<br />　// TODO: Add your command handler code here<br />　CClientDC dc(this);<br />　//创建Graphics对象<br />　Graphics graphics(dc);<br />　//创建20号"楷体"字体<br />　FontFamily fontFamily1(L "楷体_GB2312"); // 定义"楷体"字样<br />　Font font1(&amp;fontFamily1, 20, FontStyleRegular, UnitPoint);<br />　//定义输出UNICODE字符串<br />　WCHAR string[256];<br />　wcscpy(string, L "天极网的读者朋友，您好！");<br />　//以蓝色画刷和20号"楷体"显示字符串<br />　graphics.DrawString(string, (INT)wcslen(string), &amp;font1, PointF(30, 10),&amp;SolidBrush(Color::Blue));<br />　//定义字符串显示画刷<br />　LinearGradientBrush linGrBrush(Point(30, 50), Point(100, 50), Color(255, 255,0, 0), Color(255, 0, 0, 255));<br />　//以线性渐变画刷和创建的20号"楷体"显示字符串<br />　graphics.DrawString(string, (INT)wcslen(string), &amp;font1, PointF(30, 50),&amp;linGrBrush);<br />　//创建20号"华文行楷"字体<br />　FontFamily fontFamily2(L "华文行楷"); // 定义"楷体"字样<br />　Font font2(&amp;fontFamily2, 20, FontStyleRegular, UnitPoint);<br />　//以线性渐变画刷和20号"华文行楷"显示字符串<br />　graphics.DrawString(string, (INT)wcslen(string), &amp;font2, PointF(30, 90),&amp;linGrBrush);<br />　//以图像创建画刷<br />　Image image(L "d:\3.jpg");<br />　TextureBrush tBrush(&amp;image);<br />　//以图像画刷和20号"华文行楷"显示字符串<br />　graphics.DrawString(string, (INT)wcslen(string), &amp;font2, PointF(30, 130),&amp;tBrush);<br />　//创建25号"华文中宋"字体<br />　FontFamily fontFamily3(L "华文中宋"); // 定义"楷体"字样<br />　Font font3(&amp;fontFamily2, 25, FontStyleRegular, UnitPoint);<br />　//以图像画刷和20号"华文行楷"显示字符串<br />　graphics.DrawString(string, (INT)wcslen(string), &amp;font3, PointF(30, 170),&amp;tBrush);<br />}</td></tr></tbody></table><br />　　上述代码的执行效果如图10所示，字体、颜色和填充都很丰富！<br /><img src ="http://www.cppblog.com/Lee/aggbug/73170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2009-02-07 19:37 <a href="http://www.cppblog.com/Lee/archive/2009/02/07/73170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>错误 警告 死刑宣判大会</title><link>http://www.cppblog.com/Lee/archive/2008/12/19/69828.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Fri, 19 Dec 2008 05:52:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/12/19/69828.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/69828.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/12/19/69828.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/69828.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/69828.html</trackback:ping><description><![CDATA[
		<strong>
				<font size="4">1</font>
		</strong>.Compiler  Warning (level 1)  C4717<br />warning C4717: 'CTestView::OnCreate' : recursive on all control paths, function will cause runtime stack overflow//本人遇到<br />下为网络转载：<br /><font color="#000000">Error Message <br />'<b style="COLOR: white; BACKGROUND-COLOR: #00aa00">function</b>' : <b style="COLOR: black; BACKGROUND-COLOR: #99ff99">recursive</b> on <b style="COLOR: black; BACKGROUND-COLOR: #ff9999">all</b><b style="COLOR: black; BACKGROUND-COLOR: #ff66ff">control</b><b style="COLOR: white; BACKGROUND-COLOR: #880000">paths</b>, <b style="COLOR: white; BACKGROUND-COLOR: #00aa00">function</b><b style="COLOR: white; BACKGROUND-COLOR: #886800">will</b><b style="COLOR: white; BACKGROUND-COLOR: #004699">cause</b><b style="COLOR: white; BACKGROUND-COLOR: #990099">runtime</b><b style="COLOR: black; BACKGROUND-COLOR: #ffff66">stack</b><b style="COLOR: black; BACKGROUND-COLOR: #a0ffff">overflow</b><br /><br />如下面代码：<br />#include "wingdi.h"<br /><br />HPEN Graphics::CreatePen( int penStyles /*= PS_SOLID*/, COLORREF penCol /*= RGB(255, 255, 255)*/, int thickness /*= 1*/ )<br />{<br />HPEN ahPen = CreatePen(penStyles, thickness, penCol);<br />return ahPen;<br />}<br /><br />这样在编译的时候，编译器会提示<b style="COLOR: black; BACKGROUND-COLOR: #ffff66">warning</b><b style="COLOR: black; BACKGROUND-COLOR: #a0ffff">C4717</b>: 'GDIEngine::Graphics::CreatePen' : <b style="COLOR: black; BACKGROUND-COLOR: #99ff99">recursive</b> on <b style="COLOR: black; BACKGROUND-COLOR: #ff9999">all</b><b style="COLOR: black; BACKGROUND-COLOR: #ff66ff">control</b><b style="COLOR: white; BACKGROUND-COLOR: #880000">paths</b>, <b style="COLOR: white; BACKGROUND-COLOR: #00aa00">function</b><b style="COLOR: white; BACKGROUND-COLOR: #886800">will</b><b style="COLOR: white; BACKGROUND-COLOR: #004699">cause</b><b style="COLOR: white; BACKGROUND-COLOR: #990099">runtime</b><b style="COLOR: black; BACKGROUND-COLOR: #ffff66">stack</b><b style="COLOR: black; BACKGROUND-COLOR: #a0ffff">overflow</b><br /><br />导致这样问题的原因是：编译器不知道HPEN ahPen = CreatePen(penStyles, thickness, penCol);调用的CreatePen是哪一个。<br /><br />解决问题：<br />HPEN Graphics::CreatePen( int penStyles /*= PS_SOLID*/, COLORREF penCol /*= RGB(255, 255, 255)*/, int thickness /*= 1*/ )<br />{<br />HPEN ahPen = ::CreatePen(penStyles, thickness, penCol);<br />return ahPen;<br />}</font> <br />      <img src ="http://www.cppblog.com/Lee/aggbug/69828.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-12-19 13:52 <a href="http://www.cppblog.com/Lee/archive/2008/12/19/69828.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>#define WINAPI __stdcall是什么意思（转）</title><link>http://www.cppblog.com/Lee/archive/2008/12/09/68966.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Tue, 09 Dec 2008 10:43:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/12/09/68966.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/68966.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/12/09/68966.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/68966.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/68966.html</trackback:ping><description><![CDATA[ 调用约定(Calling   convention)：决定函数参数传送时入栈和出栈的顺序，由调用者还是被调用者把参数弹出栈，以及编译器用来识别函数名字的修饰约定。   <br />  函数调用约定有多种，这里简单说一下：   <br />        1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持（实际上它已被定义为__stdcall。除了__pascal外，__fortran和__syscall也不被支持），取而代之的是__stdcall调用约定。两者实质上是一致的，即函数的参数自右向左通过栈传递，被调用的函数在返回前清理传送参数的内存栈，但不同的是函数名的修饰部分（关于函数名的修饰部分在后面将详细说明）。   <br />          _stdcall是Pascal程序的缺省调用方式，通常用于Win32   Api中，函数采用从右到左的压栈方式，自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀，在函数名后加上"@"和参数的字节数。   <br />          2、C调用约定（即用__cdecl关键字说明）按从右至左的顺序压参数入栈，由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的（正因为如此，实现可变参数的函数只能使用该调用约定）。另外，在函数名修饰约定方面也有所不同。   <br />          _cdecl是C和C＋＋程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。   <br />          3、__fastcall调用约定是“人”如其名，它的主要特点就是快，因为它是通过寄存器来传送参数的（实际上，它用ECX和EDX传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈），在函数名修饰约定方面，它和前两者均不同。   <br />          _fastcall方式的函数采用寄存器传递参数，VC将函数编译后会在函数名前面加上"@"前缀，在函数名后加上"@"和参数的字节数。           <br />          4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器，参数从右到左压。thiscall不是关键词，因此不能被程序员指定。   <br />          5、naked   call采用1-4的调用约定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这样的代码。naked   call不是类型修饰符，故必须和_declspec共同使用。   <br />          关键字   __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前，也可以在编译环境的Setting...\C/C++   \Code   Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时，直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd，即__cdecl。   <br />          要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定，至于函数名修饰约定，可以通过其它方法模仿。还有一个值得一提的是WINAPI宏，Windows.h支持该宏，它可以将出函数翻译成适当的调用约定，在WIN32中，它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。 <img src ="http://www.cppblog.com/Lee/aggbug/68966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-12-09 18:43 <a href="http://www.cppblog.com/Lee/archive/2008/12/09/68966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序、进程和线程</title><link>http://www.cppblog.com/Lee/archive/2008/11/19/67278.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Wed, 19 Nov 2008 06:11:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/11/19/67278.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/67278.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/11/19/67278.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/67278.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/67278.html</trackback:ping><description><![CDATA[
		<font style="BACKGROUND-COLOR: #ffffff" color="#9acd32" size="4">
				<strong>
						<font color="#9acd32" size="5">程序</font>
				</strong>
		</font>：是计算机指令的集合，它以文件的形式存储在磁盘上。<br /><br /><font size="5"><font color="#ff1493"><strong>进程</strong></font>：</font>通常被定义为一个正在运行的程序的实例，是一个程序在其自身的地址空间中的一次执行活动。<br /><br />进程是资源申请、调度和独立运行的单位，因此，它试用系统中的运行资源；<br />而程序不能申请系统资源，不能被系统调度，也不能作为独立运行的单位，因此它不占用系统资源。<br /><br />进程由2部分组成：<br />1.内核对象          操作系统用来管理进程的内核对象。   内核对象也就是系统用来存放有关进程的统计信息的地方。<br />2.地址空间          地址空间包含所有可执行模块或DLL模块的代码和数据，还包含动态内存分配的空间。 <br />                              比如：线程堆栈和堆分配空间<br /><br />进程从来不执行任何东西，它只是线程的容器。<br />若要使它完成某项操作，它必须拥有一个在它环境里运行的线程，此线程负责执行包含在进程的地址空间中的代码。<br /><br />单个进程可能包含若干线程，这些线程都是“同时”执行进程地址空间中的代码。<br /><br />每个进程至少拥有一个线程，来执行地址空间中的代码。<br />当创建一个进程时，系统会自动创建这个进程的第一个线程，此线程称为主线程。<br />此后，主线程再创建其它线程。<br /><br /><font color="#ee82ee" size="4"><strong>进程地址空间：<br /><br /></strong><font color="#000000" size="3">系统赋予每个进程独立的虚拟地址空间。<br />对于32位进程来说，这个地址空间就是4GB。<br /><br />每个进程都有它的私有地址空间。<br />进程A可能有一个存放在A的地址空间中的数据结构，地址是0x12345678；<br />进程B有一个完全不同的数据结构存放在B的地址空间，地址是0x12345678;<br />当进程A的线程访问地址为0x12345678的内存时，这些线程访问的是进程A的数据结构；<br />当进程B的线程访问地址为0x12345678的内存时，这些线程访问的是进程B的数据结构；<br />进程A的线程不能访问进程B的地址空间中的数据结构，反之亦然。<br /><br />4GB是虚拟内存地址，只是内存地址的一个范围。<br />在你能成功访问数据而不会出现非法访问之前，必须赋予物理寄存器、或者将物理存储器映射到各个部分的地址空间。<br /><br />4GB虚拟空间地址中，2GB是内核方式分区，供内核代码、设备驱动程序、设备I/O高速缓冲、非页面内存池的分配和进程页面表等使用；<br />而用户方式分区使用的地址空间约为2GB，这个分区是进程的私有地址空间所在的地方。<br />一个进程不能读取、写入、或者以任何方式访问驻留在该分区的另一个进程的数据。<br />对于所有应用程序来说，该分区是维护进程的大部分数据的地方。<br /><br /><font color="#0000ff" size="4"><font size="5">线程：<br /></font><font color="#000000" size="3">线程由2部分组成：<br />1.线程的内核对象：<br />操作系统用它来对线程实施管理。<br />内核对象也是系统用来存放线程统计信息的地方。<br />2.线程堆栈：<br />它用来维护线程在执行代码时需要的所有参数和局部变量。<br /><br />当创建线程时，系统创建一个线程内核对象。<br />该线程内核对象不是线程本身，而是操作系统用来管理线程的较小的数据结果。<br />可以将线程内核对象视为由关于线程的统计信息组成的一个小型数据结构。<br /><br />线程总是在某个进程环境中创建。系统从进程的地址空间中分配内存，供线程的堆栈使用。<br />新线程运行的进程环境与创建线程的环境相同。因此，新线程可以访问进程的内核对象的所有句柄、<br />进程中的所有内存和在这个相同进程中的所有其他线程的堆栈。<br />这使得单个进程的多个线程能够非常容易的相互通信。<br /><br />线程只有一个内核对象和堆栈，保留的记录很少，所以所需的内存也很小。<br /><br />因为线程需要的开销比进程少，因此编程中经常用多线程来解决问题，避免开启新的线程。<br /><br /><br /><font color="#800080" size="4"><font color="#0000ff">线程运行：<br /><font size="3"><font color="#000000">操作系统为每个运行线程安排一定的CPU时间——时间片。<br />系统通过一种循环的方式为线程提供时间片，每个线程在自己的时间内运行，因时间片相当短，<br /></font><font color="#000000">所以给用户的感觉就好像多线程在同时运行一样。<br /></font></font><br /><font color="#000000" size="3">如果一台计算机有多个CPU，线程就能真正意义上的同时运行了，也就是真正的多线程了。</font><br /></font><br /></font><font color="#ff1493" size="5">互斥对象：<br /></font>互斥对象属于内核对象，它能够确保线程拥有对单个资源的互斥访问权。<br /><br />互斥对象包含一个使用数量，一个线程ID和一个计数器。<br /><br />ID用于标示系统中的哪个线程当前拥有互斥对象，计数器用于指明该线程拥有互斥对象的次数。<br /></font></font></font></font><img src ="http://www.cppblog.com/Lee/aggbug/67278.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-11-19 14:11 <a href="http://www.cppblog.com/Lee/archive/2008/11/19/67278.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅析C++里面的宏(转帖)</title><link>http://www.cppblog.com/Lee/archive/2008/11/11/66589.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Tue, 11 Nov 2008 03:31:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/11/11/66589.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/66589.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/11/11/66589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/66589.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/66589.html</trackback:ping><description><![CDATA[
		<div id="content">　　说到宏，恐怕大家都能说出点东西来：一种预处理，没有分号（真的吗？）。然后呢？<br />　　嗯.......茫然中......<br />　　好吧，我们就从这开始说起。<br />　　最常见的宏恐怕是#include了，其次就是#define还有.......<br />　　还是从宏的用途分类吧：<br />　　1、#include 主要用于包含引用文件，至今其地位无人能替代；<br />　　2、注释掉代码。例如：<br />　　 #if 0<br />　　 .......<br />　　 #endif;<br />　　 这种机制是目前注释掉代码的最佳选择，为摩托罗拉公司员工所普遍采用；<br />　　3、代码版本管理。例如：<br />　　 #ifdef DEBUG<br />　　 <a class="bbs" href="file://调/" target="_blank" rel="nofollow">file://调</a>试版本<br />　　 #else<br />　　 <a class="bbs" href="file://非/" target="_blank" rel="nofollow">file://非</a>调试版本<br />　　 #endif;<br />　　4、声明宏。例如：<br />　　 #define DECLARE_MESSAGE(x) x();~x() <a class="bbs" href="file://有/" target="_blank" rel="nofollow">file://有</a>没有分号？哈哈<br />　　 //........<br />　　 class A<br />　　 {<br />　　 public:<br />　　 DECLARE_MESSAGE(A);<br />　　 ..............<br />　　 }<br />　　 想起什么了，呵呵：）对,VC里面有好多这样的东东，有空我会写《我的VC历程》，到<br />　　时候会把VC里的各种宏详细的解释一下，那可是一个庞大的工程：）<br />　　5、符号常量。例如：<br />　　 #define PI 3.14159<br />　　6、内联函数。例如：<br />　　 #define CLEAR(x) ((x)=0)<br />　　7、泛型函数。例如:<br />　　 #define ABS(x) ((x)&gt;0? (x):-(x))<br />　　 x=3 没问题！ x=1.3 也没问题！<br />　　 如果是这样呢:<br />　　 #include &lt;iostream.h&gt;<br />　　 #define A(x) ((x)&gt;0? (x):-(x))<br />　　 void main()<br />　　 {<br />　　int i=-1;<br />　　cout&lt;&lt;A(1)&lt;&lt;endl;<br />　　cout&lt;&lt;A(++i)&lt;&lt;endl;<br />　　 }<br />　　 有问题了，不过以后再说，大概讲const or inline 时会说的：）<br />　　8、泛型类型。例如：<br />　　 #define Stack(T) Stack__ ##T<br />　　 #define Stackdeclare(T) class Stack(T) {.....}<br />　　 Stackdeclare(int);<br />　　 Stackdeclare(char);<br />　　 .......<br />　　 Stack(int) s1;<br />　　 Stack(char) s2;<br />　　9、语法扩展。例如：<br />　　 Set&lt;int&gt; s;//假设Set为一个描述集合的类<br />　　 int i;<br />　　 FORALL(i,s);<br />　　 .......<br />　　 宏最大的问题便是易引起冲突，例如：<br />　　 libA.h:<br />　　 #define MACRO stuff<br />　　 同时：<br />　　 libB.h:<br />　　 #define MACRO stuff<br />　　 下面我们对他们进行引用：<br />　　 user.cpp:<br />　　 #include "libA.h" <br />　　 #include "libB.h" <br />　　 .............<br />　　 糟糕，出现了重定义！<br />　　 还有一种冲突的可能：<br />　　 libB.h:（没有定义宏MACRO)<br />　　 class x { void MACRO(); ...........};<br />　　 那么程序运行期间，libA.h中的宏讲会改变libB.h中的成员函数的名字，导致不可预料<br />　　的结果。<br />　　 宏的另一个问题，便是如7中出现的问题，如果你把7中的x设为'a'，程序也不会给出任<br />　　何警告，所以他是不安全的。<br />　　 针对以上的问题，我们说：<br />　　 1、尽可能的少用公用宏,能替换掉就替换掉；<br />　　 2、对那些不能替换的宏，使用命名约定；<br />　　 1、符号常量预处理程序我们可以用const or enum 来代替：<br />　　 const int TABLESIZE=1024;<br />　　 enum { TABLESIZE=1024 }；<br />　　 2、非泛型内联函数的预处理程序可以使用真正的内联函数来代替：<br />　　 inline void clear(int&amp; x) {x=0;}<br />　　 奥，对了，还有这样一种情况：<br />　　 #define CONTROL(c) ((c)-64)<br />　　 ..........<br />　　 switch(c)<br />　　 {<br />　　 case CONTROL('a') : ......<br />　　 case CONTROL('b') : ......<br />　　 case CONTROL('c') : ......<br />　　 case CONTROL('d') : ......<br />　　 ..........<br />　　 }<br />　　 这时候就不能单独使用内联函数来取代了，因为case标签禁止函数调用，我们只好<br />　　做如下转换：<br />　　 inline char control(char c) { return c+64; }<br />　　 ...........<br />　　 switch(control(c))<br />　　 {<br />　　 case 'a':..... <br />　　 case 'b':..... <br />　　 case 'c':..... <br />　　 case 'd':..... <br />　　 ........<br />　　 }<br />　　 当然这样做是以牺牲时间作为代价的（你想想为什么:)）<br />　　 3、对于泛型预处理程序，我们可以用函数模板或类默板来代替：<br />　　 template&lt;class T&gt;<br />　　 T ABS(const T&amp; t) { return t&gt;0 ? t : -t; }<br />　　 template&lt;class T&gt;<br />　　 Class Stack { ............ };<br />　　 4、最后对于语法扩展程序几乎都可以用一个或多个C++类代替：<br />　　 Set&lt;int&gt; s;<br />　　 int i;<br />　　 Set_iter&lt;int&gt; iter(s);<br />　　 while(iter.next(i))<br />　　 ...........<br />　　 与使用宏相比，我们只是牺牲了一点程序的简洁性而已。<br />　　 当然并不是所有的宏都能替换（我们也并不主张替换掉所有的宏！），对于不能替换的<br />　　宏，我们应该对他们实行命名约定，例如：<br />　　 #define COMPANY_XYZ_LIBABC_MACRO stuff<br />　　 同时我们也要采取一定的方法，进行预防：<br />　　 #ifndef COMPANY_XYZ_LIBABC_MACRO<br />　　 #define COMPANY_XYZ_LIBABC_MACRO stuff<br />　　 #endif<br />　　 当然，在程序库实现内部定义的宏没有这个约束：<br />　　 my.cpp:<br />　　 #define MACRO stuff<br />　　 ........<br />　　 我们给出几个常见的宏： <br />　　 #define A(x) T_##x<br />　　 #define Bx) #@x<br />　　 #define Cx) #x<br />　　 我们假设：x=1，则有：<br />　　 A(1)=======T_1<br />　　 B(1)======'1'<br />　　 C(1)======"1"<br />　　 还有一个比较常见的宏：_T<br />　　 TCHAR tStr[] = _T("t code");<br />　　 _T宏的作用就是转换成TCHAR。</div>
<img src ="http://www.cppblog.com/Lee/aggbug/66589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-11-11 11:31 <a href="http://www.cppblog.com/Lee/archive/2008/11/11/66589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Structure and Union有什么区别？(转)</title><link>http://www.cppblog.com/Lee/archive/2008/10/20/64491.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Mon, 20 Oct 2008 06:39:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/10/20/64491.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/64491.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/10/20/64491.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/64491.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/64491.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 共用体 构造数据类型,也叫联合体 用途：使几个不同类型的变量共占一段内存(相互覆盖) 结构体是一种构造数据类型 用途：把不同类型的数据组合成一个整体-------自定义数据类型 --------------------------------------------------------------- 结构体变量所占内存长度是各成员占的内存长度的总和。 共同体变量所占内存长度是各最长的成员占的内...&nbsp;&nbsp;<a href='http://www.cppblog.com/Lee/archive/2008/10/20/64491.html'>阅读全文</a><img src ="http://www.cppblog.com/Lee/aggbug/64491.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-10-20 14:39 <a href="http://www.cppblog.com/Lee/archive/2008/10/20/64491.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算机语言——（资料性质）</title><link>http://www.cppblog.com/Lee/archive/2008/10/20/64450.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sun, 19 Oct 2008 18:13:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/10/20/64450.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/64450.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/10/20/64450.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/64450.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/64450.html</trackback:ping><description><![CDATA[
		<span style="FONT-SIZE: 9pt">
				<font size="4">　　计算机语言：计算机语言通常是一个能完整、准确和规则地表达人们的意图<span lang="EN-US">,</span>并用以指挥或控制计算机工作的“符号系统”。<span lang="EN-US"><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span></font>
		</span>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　<span style="COLOR: red">计算机语言通常分为三类：即机器语言，汇编语言和高级语言。</span>　（了解内容一）</font>
						<span lang="EN-US">
								<br />
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">1. </span>机器语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　机器语言是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。它是计算机的设计者通过计算机的硬件结构赋予计算机的操作功能。机器语言具有灵活、直接执行和速度快等特点。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　用机器语言编写程序<span lang="EN-US">,</span>编程人员要首先熟记所用计算机的全部指令代码和代码的涵义。手编程序时<span lang="EN-US">,</span>程序员得自己处理每条指令和每一数据的存储分配和输入输出<span lang="EN-US">,</span>还得记住编程过程中每步所使用的工作单元处在何种状态。这是一件十分繁琐的工作<span lang="EN-US">,</span>编写程序花费的时间往往是实际运行时间的几十倍或几百倍。而且<span lang="EN-US">,</span>编出的程序全是些<span lang="EN-US">0</span>和<span lang="EN-US">1</span>的指令代码<span lang="EN-US">,</span>直观性差<span lang="EN-US">,</span>还容易出错。现在<span lang="EN-US">,</span>除了计算机生产厂家的专业人员外<span lang="EN-US">,</span>绝大多数程序员已经不再去学习机器语言了。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　<span lang="EN-US">2.</span>汇编语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　为了克服机器语言难读、难编、难记和易出错的缺点<span lang="EN-US">,</span>人们就用与代码指令实际含义相近的英文缩写词、字母和数字等符号来取代指令代码<span lang="EN-US">(</span>如用<span lang="EN-US">ADD</span>表示运算符号“＋”的机器代码<span lang="EN-US">),</span>于是就产生了汇编语言。所以说<span lang="EN-US">,</span>汇编语言是一种用助记符表示的仍然面向机器的计算机语言。汇编语言亦称符号语言。汇编语言由　　　于是采用了助记符号来编写程序<span lang="EN-US">,</span>比用机器语言的二进制代码编程要方便些<span lang="EN-US">,</span>在一定程度上简化了编程过程。汇编语言的特点是用符号代替了机器指令代码<span lang="EN-US">,</span>而且助记符与指令代码一一对应<span lang="EN-US">,</span>基本保留了机器语言的灵活性。使用汇编语言能面向机器并较好地发挥机器的特性<span lang="EN-US">,</span>得到质量较高的程序。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　汇编语言中由于使用了助记符号<span lang="EN-US">,</span>用汇编语言编制的程序送入计算机<span lang="EN-US">,</span>计算机不能象用机器语言编写的程序一样直接识别和执行<span lang="EN-US">,</span>必须通过预先放入计算机的“汇编程序“的加工和翻译<span lang="EN-US">,</span>才能变成能够被计算机识别和处理的二进制代码程序。用汇编语言等非机器语言书写好的符号程序称源程序<span lang="EN-US">,</span>运行时汇编程序要将源程序翻译成目标程序。目标程序是机器语言程序<span lang="EN-US">,</span>它一经被安置在内存的预定位置上<span lang="EN-US">,</span>就能被计算机的<span lang="EN-US">CPU</span>处理和执行。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　汇编语言像机器指令一样<span lang="EN-US">,</span>是硬件操作的控制信息<span lang="EN-US">,</span>因而仍然是面向机器的语言<span lang="EN-US">,</span>使用起来还是比较繁琐费时<span lang="EN-US">,</span>通用性也差。汇编语言是低级语言。但是<span lang="EN-US">,</span>汇编语言用来编制系统软件和过程控制软件<span lang="EN-US">,</span>其目标程序占用内存空间少<span lang="EN-US">,</span>运行速度快<span lang="EN-US">,</span>有着高级语言不可替代的用途。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　<span lang="EN-US">3.</span>高级语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　　不论是机器语言还是汇编语言都是面向硬件的具体操作的，语言对机器的过分依赖，要求使用者必须对硬件结构及其工作原理都十分熟悉，这对非计算机专业人员是难以做到的<span lang="EN-US">,</span>对于计算机的推广应用是不利的。计算机事业的发展<span lang="EN-US">,</span>促使人们去寻求一些与人类自然语言相接近且能为计算机所接受的语意确定、规则明确、自然直观和通用易学的计算机语言。这种与自然语言相近并为计算机所接受和执行的计算机语言称高级语言。高级语言是面向用户的语言。无论何种机型的计算机<span lang="EN-US">,</span>只要配备上相应的高级语言的编译或解释程序<span lang="EN-US">,</span>则用该高级语言编写的程序就可以通用。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<font size="4">
						<span style="FONT-SIZE: 9pt; COLOR: red">　　目前被广泛使用的高级语言有<span lang="EN-US">BASIC</span>、<span lang="EN-US">PASCAL</span>、<span lang="EN-US">C</span>、<span lang="EN-US">COBOL</span>、<span lang="EN-US">FORTRAN</span>、<span lang="EN-US">LOGO</span>以及<span lang="EN-US">VC</span>、<span lang="EN-US">VB</span>等。这些语言都是属于系统软件。　</span>
						<span style="FONT-SIZE: 9pt">（了解内容二）<span lang="EN-US"><o:p></o:p></span></span>
				</font>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　计算机并不能直接地接受和执行用高级语言编写的源程序<span lang="EN-US">,</span>源程序在输入计算机时<span lang="EN-US">,</span>通过“翻译程序”翻译成机器语言形式的目标程序<span lang="EN-US">,</span>计算机才能识别和执行。这种“翻译”通常有两种方式<span lang="EN-US">,</span>即编译方式和解释方式。编译方式是：事先编好一个称为编译程序的机器语言程序<span lang="EN-US">,</span>作为系统软件存放在计算机内<span lang="EN-US">,</span>当用户由高级语言编写的源程序输入计算机后<span lang="EN-US">,</span>编译程序便把源程序整个地翻译成用机器语言表示的与之等价的目标程序<span lang="EN-US">,</span>然后计算机再执行该目标程序<span lang="EN-US">,</span>以完成源程序要处理的运算并取得结果。解释方式是：源程序进入计算机时<span lang="EN-US">,</span>解释程序边扫描边解释作逐句输入逐句翻译<span lang="EN-US">,</span>计算机一句句执行<span lang="EN-US">,</span>并不产生目标程序。<span lang="EN-US">PASCAL</span>、<span lang="EN-US">FORTRAN</span>、<span lang="EN-US">COBOL</span>等高级语言执行编译方式<span lang="EN-US">;BASIC</span>语言则以执行解释方式为主<span lang="EN-US">;</span>而<span lang="EN-US">PASCAL</span>、<span lang="EN-US">C</span>语言是能书写编译程序的高级程序设计语言。 每一种高级<span lang="EN-US">(</span>程序设计<span lang="EN-US">)</span>语言<span lang="EN-US">,</span>都有自己人为规定的专用符号、英文单词、语法规则和语句结构<span lang="EN-US">(</span>书写格式<span lang="EN-US">)</span>。高级语言与自然语言<span lang="EN-US">(</span>英语<span lang="EN-US">)</span>更接近<span lang="EN-US">,</span>而与硬件功能相分离<span lang="EN-US">(</span>彻底脱离了具体的指令系统<span lang="EN-US">),</span>便于广大用户掌握和使用。高级语言的通用性强<span lang="EN-US">,</span>兼容性好<span lang="EN-US">,</span>便于移植。下面介绍几种较有代表性的高级程序设计语言：<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　⑴<span lang="EN-US">BASIC</span>语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">BASIC</span>语言全称是<span lang="EN-US">Beginner</span>’<span lang="EN-US">s all Purpose Symbolic Instruction Code,</span>意为“初学者通用符号指令代码“。<span lang="EN-US">1964</span>年由美国达尔摩斯学院的基米尼和科茨完成设计并提出了<span lang="EN-US">BASIC</span>语言的第一个版本<span lang="EN-US">,</span>经过不断丰富和发展<span lang="EN-US">,</span>现已成为一能全种功面的中小型计算机语言。<span lang="EN-US">BASIC</span>易学、易懂、易记、易用<span lang="EN-US">,</span>是初学者的入门语言<span lang="EN-US">,</span>也可以作为学习其他高语级言的基础。<span lang="EN-US">BASIC</span>有解释方式和编译方式两种翻译程序。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　⑵<span lang="EN-US">PASCAL</span>语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">PASCAL</span>是一种结构程序设计语言<span lang="EN-US">,</span>由瑞士苏黎世联邦工业大学的沃斯<span lang="EN-US">(<span class="SpellE">N.Wirth</span>)</span>教授研制<span lang="EN-US">,</span>于<span lang="EN-US">1971</span>年正式发表。是从<span lang="EN-US">ALGOL60</span>衍生的<span lang="EN-US">,</span>但功能更强且容易使用。目前<span lang="EN-US">,</span>作为一个能高效率实现的实用语言和一个极好的教学工具<span lang="EN-US">,PASCAL</span>语言在高校计算机软件教中学一直处于主导地位。<span lang="EN-US">Pascal(<span class="SpellE">B.Pascal</span>)</span>是十七世纪法国著名数学家<span lang="EN-US">,</span>他于<span lang="EN-US">1642</span>年曾发明现代台式计算机的雏型机—加减法计算机。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">PASCAL</span>具有大量的控制结构<span lang="EN-US">,</span>充分反映了结构化程序设计的思想和要求<span lang="EN-US">,</span>直观易懂<span lang="EN-US">,</span>使用灵活<span lang="EN-US">,</span>既可用于科学计算<span lang="EN-US">,</span>又能用来编写系统软件<span lang="EN-US">,</span>应用范日围益广泛。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　⑶通用编程语言</font>
						<font size="4">
								<span lang="EN-US">C<br /></span>　　<span lang="EN-US">C</span>语言是美国<span lang="EN-US">AT&amp;T</span>与电话<span lang="EN-US">) (</span>电报公司为了实现<span lang="EN-US">UNIX</span>系统的设计思想而发展起来的语言工具。<span lang="EN-US">C</span>语言的主要特色是兼顾了高级语言和汇编语言的特点<span lang="EN-US">,</span>简洁、丰富、可移植。相当于其他高级语言子程序的函数是<span lang="EN-US">C</span>语言的补充<span lang="EN-US">,</span>每一个函数解决一个大问题中的小任务<span lang="EN-US">,</span>函数使程序模块化。<span lang="EN-US">C</span>语言提供了结构式编程所需要的各种现代化的控制结构。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">C</span>语言是一种通用编程语言<span lang="EN-US">,</span>正被越来越多的计算机用户所推崇。使用Ｃ语言编写程序<span lang="EN-US">,</span>既感觉到使用高级语言的自然<span lang="EN-US">,</span>也体会到利用计算机硬件指令的直接<span lang="EN-US">,</span>而程序员却无需卷入汇编语言的繁琐。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　⑷<span lang="EN-US">COBOL</span>语言</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">COBOL</span>的全称是<span lang="EN-US">Common Business Oriented Language,</span>意即：通用商业语言。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　在企业管理中<span lang="EN-US">,</span>数值计算并不复杂<span lang="EN-US">,</span>但数据处理信息量却很大。为专门解决经企管理问题<span lang="EN-US">,</span>于<span lang="EN-US">1959</span>年<span lang="EN-US">,</span>由美国的一些计算机用户组织设计了专用于商务处理的计算机语言<span lang="EN-US">COBOL,</span>并于<span lang="EN-US">1961</span>年美国数据系统语言协会公布。经不断修改、丰富完善和标准化<span lang="EN-US">,</span>已发展为多种版本。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">COBOL</span>语言使用了<span lang="EN-US">300</span>多个英语保留字<span lang="EN-US">,</span>大量采用普通英语词汇和句型<span lang="EN-US">,COBOL</span>程序通俗易懂<span lang="EN-US">,</span>素有“英语语言”之称。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　<span lang="EN-US">COBOL</span>语言语法规则严格。用<span lang="EN-US">COBOL</span>语言编写的任一源程序<span lang="EN-US">,</span>依都要次按标识部、环境部、数据部和过程部四部分书写<span lang="EN-US">,COBOL</span>程序结构的“部”内包含“节”<span lang="EN-US">,</span>“节”内包含“段”<span lang="EN-US">,</span>段内包含语句<span lang="EN-US">,</span>语句由字或字符串组成<span lang="EN-US">,</span>整个源程序象一棵由根到干<span lang="EN-US">,</span>由干到枝<span lang="EN-US">,</span>由枝到叶的树<span lang="EN-US">,</span>习惯上称之为树型结构。</font>
						<span lang="EN-US">
								<br />
						</span>
						<font size="4">　　目前<span lang="EN-US">COBOL</span>语言主要应用于情报检索、商业数据处理等管理领域。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　常用的高级程序设计语言<span lang="EN-US">,</span>除了上述的几种之外<span lang="EN-US">,</span>还有很多<span lang="EN-US">,</span>如以英国著名诗人拜伦<span lang="EN-US">(<span class="SpellE">G.N.G.Byron</span>)</span>的独生女艾达·拜伦<span lang="EN-US">(<span class="SpellE">Ada</span> Byron)</span>的名字命名的军用语言<span class="SpellE"><span lang="EN-US">Ada</span></span><span lang="EN-US">,</span>深受中、小学生欢迎的语言<span lang="EN-US">LOGO</span>等等。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
		<p>
				<span style="FONT-SIZE: 9pt">
						<font size="4">　　目前<span lang="EN-US">,</span>程序设计语言及编程环境正向面向对象语言及可视化编程环境方向发展<span lang="EN-US">,</span>出现了许多第四代语言及其开发工具。如：微软公司<span lang="EN-US">(Microsoft)</span>开发的<span lang="EN-US">Visual</span>系列<span lang="EN-US">(VC++</span>、<span lang="EN-US">VB</span>、<span lang="EN-US">FoxPro)</span>编程工</font>
						<span lang="EN-US">
								<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /?>
								<v:shapetype id="_x0000_t75" path=" m@4@5 l@4@11@9@11@9@5 xe" stroked="f" filled="f" o:spt="75" o:preferrelative="t" coordsize="21600,21600">
										<font size="4">
												<v:stroke joinstyle="miter">
												</v:stroke>
												<v:formulas>
														<v:f eqn="if lineDrawn pixelLineWidth 0 ">
														</v:f>
														<v:f eqn="sum @0 1 0 ">
														</v:f>
														<v:f eqn="sum 0 0 @1 ">
														</v:f>
														<v:f eqn="prod @2 1 2 ">
														</v:f>
														<v:f eqn="prod @3 21600 pixelWidth ">
														</v:f>
														<v:f eqn="prod @3 21600 pixelHeight ">
														</v:f>
														<v:f eqn="sum @0 0 1 ">
														</v:f>
														<v:f eqn="prod @6 1 2 ">
														</v:f>
														<v:f eqn="prod @7 21600 pixelWidth ">
														</v:f>
														<v:f eqn="sum @8 21600 0 ">
														</v:f>
														<v:f eqn="prod @7 21600 pixelHeight ">
														</v:f>
														<v:f eqn="sum @10 21600 0 ">
														</v:f>
												</v:formulas>
												<v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f">
												</v:path>
												<o:lock v:ext="edit" aspectratio="t">
												</o:lock>
										</font>
								</v:shapetype>
								<v:shape id="_x0000_i1032" style="WIDTH: 12pt; HEIGHT: 12pt" coordsize="21600,21600" type="#_x0000_t75">
										<v:imagedata src="yuyan.files/image001.png" o:title="">
										</v:imagedata>
								</v:shape>
						</span>
						<font size="4">具及<span lang="EN-US">Power Builder</span>等<span lang="EN-US">,</span>目前已经在国内外得到了广泛的应用。<span lang="EN-US"><o:p></o:p></span></font>
				</span>
		</p>
<img src ="http://www.cppblog.com/Lee/aggbug/64450.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-10-20 02:13 <a href="http://www.cppblog.com/Lee/archive/2008/10/20/64450.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC框架关系图</title><link>http://www.cppblog.com/Lee/archive/2008/09/21/62376.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sat, 20 Sep 2008 16:21:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/21/62376.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/62376.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/21/62376.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/62376.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/62376.html</trackback:ping><description><![CDATA[
		<p>
				<img height="686" src="file:///C:/Documents%20and%20Settings/Administrator/桌面/59_36494_7602cdbb3156f36.jpg" width="539" />
		</p>
<img src ="http://www.cppblog.com/Lee/aggbug/62376.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-21 00:21 <a href="http://www.cppblog.com/Lee/archive/2008/09/21/62376.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>学习VC MFC开发必须了解的常用宏和指令</title><link>http://www.cppblog.com/Lee/archive/2008/09/19/62305.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Fri, 19 Sep 2008 12:56:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/19/62305.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/62305.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/19/62305.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/62305.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/62305.html</trackback:ping><description><![CDATA[
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>1、＃include指令</strong>  <br />包含指定的文件  </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>2、#define指令</strong>   <br />预定义,通常用它来定义常量(包括无参量与带参量)，以及用来实现那些“表面似和善、背后一长串”的宏，它本身并不在编译过程中进行，而是在这之前(预处理过程)就已经完成了 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>3、#typedef指令</strong>
								<br />常用来定义一个标识符及关键字的别名它是语言编译过程的一部分，但它并不实际分配内存空间。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>4、#ifndef   #else   #endif指令</strong>  <br />条件编译。一般情况下，源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译，也就是对一部分内容指定编译的条件，这就是“条件编译”。有时，希望当满足某条件时对一组语句进行编译，而当条件不满足时则编译另一组语句。  <br />条件编译命令最常见的形式为：  <br />#ifdef 标识符  <br />   程序段1 <br />#else <br />   程序段2  <br />#endif  <br />它的作用是：当标识符已经被定义过(一般是用#define命令定义)，则对程序段1进行编译，否则编译程序段2。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>5、#Pragma 指令</strong>  <br />在所有的预处理指令中，#Pragma 指令可能是最复杂的了，它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。其格式一般为  <br />#Pragma Para  <br />其中Para 为参数，下面来看一些常用的参数。 <br />l          message 参数。它能够在编译信息输出窗口中输出相应的信息，这对于源代码信息的控制是非常重要的。其使用方法为：  <br />#Pragma message(“消息文本”)  <br />当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。  <br />当我们在程序中定义了许多宏来控制源代码版本的时候，我们自己有可能都会忘记有没有正确的设置这些宏，此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 <br />#ifdef _X86 <br />         #Pragma message(“_X86 macro activated!”) <br />         #endif   <br />当我们定义了_X86这个宏以后，应用程序在编译时就会在编译输出窗口里显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了  <br />l          另一个使用得比较多的pragma参数是code_seg。格式如： <br />#pragma code_seg( ["section-name"[,"section-class"] ] )  <br />它能够设置程序中函数代码存放的代码段，当我们开发驱动程序的时候就会使用到它。  <br />l          #pragma once  <br />只要在头文件的最开始加入这条指令就能够保证头文件被编译一次，这条指令实际上在VC6中就已经有了，但是考虑到兼容性并没有太多的使用它。  <br />l          #pragma hdrstop  <br />表示预编译头文件到此为止，后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度，但如果所有头文件都进行预编译又可能占太多磁盘空间，所 以使用这个选项排除一些头文件。有时单元之间有依赖关系，比如单元A依赖单元B，所以单元B要先于单元A编译。你可以用#pragma startup指 定编译优先级，如果使用了#pragma package(smart_init) ，BCB就会根据优先级的大小先后编译。  <br />l          #pragma resource "*.dfm"  <br />表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。  <br />l          #pragma warning( disable : 4507 34; once : 4385; error : 164 ) <br />等价于：  <br />#pragma warning(disable:4507 34) // 不显示4507和34号警告信息 <br />         #pragma warning(once:4385) // 4385号警告信息仅报告一次 <br />         #pragma warning(error:164) // 把164号警告信息作为一个错误。 <br />同时这个pragma warning 也支持如下格式： <br />#pragma warning( push [ ,n ] ) <br />#pragma warning( pop )<br />这里n代表一个警告等级(1---4)。 <br />#pragma warning( push )保存所有警告信息的现有的警告状态。 <br />#pragma warning( push, n)保存所有警告信息的现有的警告状态，并且把全局警告 <br />等级设定为n。 <br />#pragma warning( pop )向栈中弹出最后一个警告信息，在入栈和出栈之间所作的 <br />一切改动取消。例如： <br />#pragma warning( push ) <br />#pragma warning( disable : 4705 ) <br />#pragma warning( disable : 4706 ) <br />#pragma warning( disable : 4707 ) <br />//....... <br />#pragma warning( pop ) <br />在这段代码的最后，重新保存所有的警告信息(包括4705，4706和4707)。 <br />l          pragma comment(...) <br />该指令将一个注释记录放入一个对象文件或可执行文件中。 <br />常用的lib关键字，可以帮我们连入一个库文件。 <br />每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如，对循环优化功能： <br />#pragma loop_opt(on) // 激活 <br />#pragma loop_opt(off) // 终止 <br />有时，程序中会有些函数会使编译器发出你熟知而想忽略的警告，如“Parameter xxx is never used in function xxx”，可以这样： <br />#pragma warn —100 // Turn off the warning message for warning #100 <br />int insert_record(REC *r) <br />{ /* function body */ } <br />#pragma warn +100 // Turn the warning message for warning #100 back on <br />函数会产生一条有唯一特征码100的警告信息，如此可暂时终止该警告。 <br />每个编译器对#pragma的实现不同，在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>6、宏：__LINE__   和__FILE__</strong>
								<br />     定义源程序文件名和代码行，这对于调试跟踪代码错误行很有帮助。 <br />__TIME__           ：编译时间 <br />__DATE__       ：编译日期 <br />__TIMESTAMP__ ：文件修改时间 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>7、调试宏：ASSERT()、VERIFY()、TRACE()</strong>
								<br />这三个宏在Debug环境下特别有效，常用于代码的跟踪调试。它们是否起作用取决于是否定义了预定义了宏 _DEBUG <br />l          ASSERT <br />ASSERT(booleanExpression) <br />说明： <br />计算变量的值。如果结构的值为0，那么此宏便打印一个诊断消息并且程序运行失败。如果条件为非0，那么什么也不做。诊断消息的形式为： assertion failed in file in line 其中name是元文件名，num是源文件中运行失败的中断号。 在Release 版中，ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。 <br />这个宏通常原来判断程序中是否出现了明显非法的数据，如果出现了终止程序以免导致严重后果，同时也便于查找错误。 <br />ASSERT_VAILD <br />ASSERT_VAILD(pObject) <br />说明： <br />用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数（把它们作为自己的变量来传递）。在 Release版中ASSERT_VALID什么也不做。在DEBUG版中，他检查指针，以不同于NULL的方式进行检查，并调用对象自己的 AssertValid成员函数。如果这些检测中有任何一个失败的话，那么他会以与ASSERT相同的方法显示一个警告的消息。 <br />l          VERIFY <br />VERIFY(booleanExpression) <br />说明： <br />在MFC的DEBUG版中，VERIFY宏计算它的变量值。如果结果为0，那么宏打印一个诊断消息并中止程序。如果条件不为0，那么什么工作也不作。诊断 有如下形式： assertion failed in file in line 其中name是源文件的名字，num是在源文件中失败的中止行号。在 MFC的Release版中，VERIFY计算表达式值但不打印或中止程序。例如：如果表达式是个函数调用，那么调用成功。 <br />l          TRACE <br />TRACE(exp) <br />说明： <br />把一个格式化字符串送到转储设备，例如，文件或调试监视器，功能上和printf相似，可以说就是调试环境下printf的一个拷贝。TRACE宏是一个 在程序运行时跟踪变量值的方便形式。在DEBUG环境中，TRACE宏输出到afxDump。在Release版中他不做任何工作。另外还有一组可以带参 数的相似的宏：TRACE0、TRACE1、TRACE2和TRACE3。提供格式如： <br />TRACE0(exp) <br />TRACE1(exp,param1) <br />TRACE2(exp,param1,param2) <br />TRACE3(exp,param1,param2,param3) <br />与TRACE相似，但它把跟踪字符串放在代码段中，而不是DGROUP，因此使用少的DGROUP空间。这些宏的用法和printf类似。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>8、消息处理宏：DECLARE_MESSAGE_MAP 、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP <br />DECLARE_MESSAGE_MAP() <br /></strong>说明： <br />用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着，在定义类 成员函数的.CPP文件中，使用BEGIN_MESSAGE_MAP宏，每个用户消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。 <br />注释： <br />如果在DECLARE_MESSAGE_MAP之后定义任何一个成员，那么必须为他们指定一个新存取类型（公共的，私有的，保护的）。 <br />BEGIN_MESSAGE_MAP(the class,baseclass) <br />END_MESSAGE_MAP <br />说明： <br />使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具（.cpp）文件中，以BEGIN_MESSAGE_MAP宏开始消息映射，然后为每个消息处理函数增加宏项，接着以END_MESSAGE_MAP宏完成消息映射。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>9、消息映射宏：ON_COMMAND 、ON_CONTROL、ON_MESSAGE、ON_VBXEVENT、</strong>ON_Update_COMMAND_UI和ON_REGISTERED_MESSAGE <br />ON_COMMAND(id,memberFxn) <br />说明： <br />此宏通过ClassWizard或手工插入一个消息映射。它表明那个函数将从一个命令用户接口（例如一个菜单项或toolbar按钮）处理一个命令消息。 当一个命令对象通过指定的ID接受到一个Windows WM_COMMAND消息时，ON_COMMAND将调用成员函数memberFxn处理此消 息。在用户的消息映射中，对于每个菜单或加速器命令（必须被映射到一个消息处理函数）应该确实有一个ON_COMMAND宏语句。 <br />ON_CONTROL(wNotifyCode,id,memberFxn) <br />说明： <br />表明哪个函数将处理一个常规控制表示消息。控制标识消息是那些从一个控制夫发送到母窗口的消息。 <br />ON_MESSAGE(message,memberFxn) <br />说明： <br />指明哪个函数将处理一用户定义消息。用户定义消息通常定义在WM_USER到0x7FF范围内。用户定义消息是那些不是标准 Windows WM_MESSAGE消息的任何消息。在用户的消息映射中，每个必须被映射到一个消息处理函数。用户定义消息应该有一个 ON_MESSAGE宏语句。 <br />ON_Update_COMMAND_UI(id，memberFxn) <br />说明： <br />此宏通常通过ClassWizard被插入一个消息映射，以指明哪个函数将处理一个用户接口个更改命令消息。在用户的消息映射中，每个用户接口更改命令（比讯被映射到一个消息处理函数）应该有一个ON_Update_COMMAND_UI宏语句。 <br />ON_VBXEVENT(wNotifyCode,memberFxn) <br />说明： <br />此宏通常通过ClassWizard被插入一个消息映射，以指明哪个函数将处理一个来自VBX控制的消息。在用户的消息映射中每个被映射到一消息处理函数的VBX控制消息应该有一个宏语句。 <br />ON_REGISTERED_MESSAGE(nmessageVarible,memberFxn) <br />说明： <br />Windows的RegisterWindowsMesage函数用于定义一个新窗口消息，此消息保证在整个系统中是唯一的。此宏表明哪个函数处理已注册消息。变量nMessageViable应以NEAR修饰符来定义。</font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>10、DEBUG_NEW</strong>
								<br />#define new DEBUG_NEW<br />说明： <br />帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下（但定义了一个DEBUG符号）， DEBUG_NEW为它分配的每个对象记录文件名和行号。然后，在用户使用CMemoryState::DumpAllObjectSince成员函数 时，每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令： #define new DEBUG_NEW 一旦用户插入本指令，预处理程序将在使用new的地方插入DEBUG_NEW，而MFC作其余的工作。但 用户编译自己的程序的一个发行版时，DEBUG_NEW便进行简单的new操作，而且不产生文件名和行号消息。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>11、异常宏：TRY、CATCH 、THROW、AND_CATCH、THROW_LAST和END_CATCH <br /></strong>TRY <br />说明： <br />使用此宏建立一TRY块。一个TRY识别一个可排除异常的代码块。这些异常在随后的CATCH和AND_CATCH块处理。传递是允许的：异常可以传递一个外部TRY块，或者忽略它们或者使用THROW_LAST宏。 <br />CATCH(exception_class,exception_object_pointer_name) <br />说明： <br />使用此用定义一个代码块，此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象，如何合适的话，就会得到关于异常的特殊原因的更多 消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架，如果exception-class是类CExceptioon,那么会获取所有异 常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语 句，每个带一个不同的异常类型。此异常类型的指针由宏定义，用户不必定义。 <br />注释： <br />此CATCH块被定义作一个C++范围（由花括号描述）。如用户在此范围定义变量，那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。 <br />THROW(exception_object_pointer) <br />说明： <br />派出指定的异常。THROW中断程序的运行，把控制传递给用户程序中的相关的CATCH块。如果用户没有提供CATCH块，那么控制被传递到一个MFC模块，他打印出一个错误并终止运行。 <br />AND_CATCH(exception_class,exception _object_point_name) <br />说明： <br />定义一个代码块，它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型，然后使用AND_CATCH宏获得随后的异常处理代 码可以访问异常对象（若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部 异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。 <br />注释： <br />AND_CATCH块被定义成为一个C++作用域（由花括号来描述）。若用户在此作用域定义变量，那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。 <br />THROW_LAST() <br />说明： <br />此宏允许用户派出一个局部建立的异常。如果用户试图排除一个刚发现的异常，那么一般此异常将溢出并被删除。使用THROW_LAST,此异常被直接传送到下一个CATCH处理程序。 <br />END_CATCH <br />说明： <br />标识最后的CATCH或AND_CATCH块的末尾。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>12、DECLARE_DYNAMIC 、IMPLEMENT_DYNAMIC <br /></strong>DECLARE_DYNAMIC(class_name) <br />说明： <br />但从CObject派生一个类时，此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中，然后在全部需要访问 此类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏，那么用户便可使用 RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中，那 么IMPLEMETN_DYNAMIC必须包含在类工具中。  <br />IMPLEMENT_DYNAMIC(class_name,base_class_name)<br />说明： <br />通过运行时在串行结构中为动态CObject派生类访问类名和位置来产生必要的C++代码。在.CPP文件中使用IMPLEMENT_DYNAMIC宏，接着一次链接结果对象代码 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>13、DECLARE_DYNCreate、IMPLEMENT_DYNCreate <br /></strong>DECLARE_DYNCreate(class_name) <br />说明： <br />使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。使用此功能自动建立新对象，例如，但它在串行化过程中从 磁盘读一个对象时，文件及视图和框架窗应该支持动态建立，因为框架需要自动建立它。把DECLARE_DYNCreate宏加入类的.H文件中，然后在全 部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCreate包含在类定义中，那么IMPLEMENT_DYNCreate 必须包含在类工具中。 <br />IMPLEMENT_DYNCreate(class_name,base_class_name) <br />说明： <br />通过DECLARE_DYNCreate宏来使用IMPLEMENT_DYNCreate宏，以允许CObject派生类对象在运行时自动建立。主机使用 此功能自动建立对象，例如，但它在串行化过程中从磁盘读去一个对象时，他在类工具里加入IMPLEMENT_DYNCreate宏。若用户使用 DECLARE_DYNCreate和IMPLEMENT_DYNCreate宏,那么接着使用RUNTIME_CLASS宏和CObject:: IsKindOf成员函数以在运行时确定对象类。若declare_dyncreate包含在定义中，那么IMPLEMENT_DYNCreate必须包 含在类工具中。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>14、DECLARE_SERIAL、IMPLEMENT_SERIAL <br /></strong>DECLARE_SERIAL(class_name) <br />说明： <br />DECLARE_SERIAL为一个可以串行化的CObject派生类产生必要的C++标题代码。串行化是把某个对象的内容从一个文件读出和写入一文件。 在.H文件中使用DECLARE_SERIAL宏，接着在需要访问此类对象的全部.CPP文件中包含此文件。如果DECLARE_SERIAL包含在类定 义中，那么IMPLEMENT_SERIAL必须包含在类工具中。DECLARE_SERIAL宏包含全部DECLARE_DYNAMIC, IMPLEMENT_DYCreate的功能。 <br />IMPLEMENT_SERIAL(class_name,base_class_name,wSchema) <br />说明： <br />通过运行时在串行结构中动态CObject派生类访问类名和位置来建立必要的C++代码。在.CPP文件中使用IMPLEMENT_SERIAL宏，然后一次链接结果对象代码。 </font>
				</span>
		</p>
		<p style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 240%; TEXT-ALIGN: left" align="left">
				<span>
						<font face="宋体" size="3">
								<strong>15、RUNTIME_CLASS <br /></strong>RUNTIME_CLASS(class_name) <br />说明： <br />使用此宏从c++类名中获取运行时类结构。RUNTIME_CLASS为由class_name指定的类返回一个指针到CRuntimeClass结构。 只有以DECLARE_DYNAMIC、DECLARE_DYNCreate或DECLARE_SERIAL定义的CObject派生类才返回到一个 CRuntimeClass结构的指针。</font>
				</span>
		</p>
<img src ="http://www.cppblog.com/Lee/aggbug/62305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-19 20:56 <a href="http://www.cppblog.com/Lee/archive/2008/09/19/62305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC应用程序中指针的使用(转帖)</title><link>http://www.cppblog.com/Lee/archive/2008/09/19/62236.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 18 Sep 2008 17:13:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/19/62236.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/62236.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/19/62236.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/62236.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/62236.html</trackback:ping><description><![CDATA[1) 在View中获得Doc指针<br />2) 在App中获得MainFrame指针<br />3) 在View中获得MainFrame指针<br />4) 获得View（已建立）指针<br />5) 获得当前文档指针<br />6) 获得状态栏与工具栏指针<br />7) 获得状态栏与工具栏变量<br />8) 在Mainframe获得菜单指针<br />9) 在任何类中获得应用程序类<br />10) 从文档类取得视图类的指针(1)<br />11) 在App中获得文档模板指针<br />12) 从文档模板获得文档类指针<br />13) 在文档类中获得文档模板指针<br />14) 从文档类取得视图类的指针(2)<br />15) 从一个视图类取得另一视图类的指针<br /><br />VC中编程对于刚刚开始学习的同学，最大的障碍和问题就是消息机制和指针获取与<br />操作。其实这些内容基本上是每本VC学习工具书上必讲的内容，而且通过MSDN很多<br />问题都能解决。下面文字主要是个人在编程中指针使用的一些体会，说的不当的地<br />方请指正。一般我们使用的框架是VC提供的Wizard生成的MFC App Wizard(exe)框架，<br />无论是多文档还是单文档，都存在指针获取和操作问题。下面这节内容主要是一般<br />的框架，然后再讲多线程中的指针使用。使用到的类需要包含响应的头文件。首先<br />一般获得本类(视，文档，对话框都支持)实例指针this，用this的目的，主要可以通<br />过类中的函数向其他类或者函数中发指针，以便于在非本类中操作和使用本类中的<br />功能。<br /><br /> 1） 在View中获得Doc指针 CYouSDIDoc *pDoc=GetDocument();一个视只能有一个文<br />档。<br /> 2) 在App中获得MainFrame指针<br />CWinApp 中的 m_pMainWnd变量就是MainFrame的指针<br />也可以： CMainFrame *pMain =(CMainFrame *)AfxGetMainWnd();<br /> 3) 在View中获得MainFrame指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-&gt;m_pMainWnd;<br /> 4) 获得View（已建立）指针 CMainFrame *pMain=(CmaimFrame *)AfxGetApp()-&gt;m_pMainWnd;<br />CyouView *pView=(CyouView *)pMain-&gt;GetActiveView();<br /> 5) 获得当前文档指针 CDocument * pCurrentDoc =(CFrameWnd *)m_pMainWnd-&gt;GetActiveDocument();<br /> 6) 获得状态栏与工具栏指针 CStatusBar * pStatusBar＝(CStatusBar *)AfxGetMainWnd()-&gt;GetDescendantWindow(AFX_IDW_STATUS_BAR);<br />CToolBar * pToolBar=(CtoolBar *)AfxGetMainWnd()-&gt;GetDescendantWindow(AFX_IDW_TOOLBAR);<br /><br /> 7) 如果框架中加入工具栏和状态栏变量还可以这样 <br />(CMainFrame *)GetParent()-&gt;m_wndToolBar;<br />(CMainFrame *)GetParent()-&gt;m_wndStatusBar;<br /><br /> 8) 在Mainframe获得菜单指针 CMenu *pMenu=m_pMainWnd-&gt;GetMenu();<br /> 9) 在任何类中获得应用程序类<br />用MFC全局函数AfxGetApp()获得。<br /><br /> 10) 从文档类取得视图类的指针<br />我是从http://download.cqcnc.com/soft/program/article/vc/vc405.html学到的，<br />从文档获得视图类指针目的一般为了控制同一文档的多个视图的定位问题，我的体会<br />特别是文字处理CEditView当产生多个视图类时，这个功能是非常需要的。 <br />CDocument类提供了两个函数用于视图类的定位：<br />GetFirstViewPosition()和GetNextView() <br />virtual POSITION GetFirstViewPosition() const;<br />virtual CView* GetNextView(POSITION&amp; rPosition) const;<br /><br />注意：GetNextView()括号中的参数用的是引用方式，因此执行后值可能改变。<br />GetFirstViewPosition()用于返回第一个视图位置（返回的并非视图类指针，而是一<br />个POSITION类型值），GetNextView()有两个功能：返回下一个视图类的指针以及用<br />引用调用的方式来改变传入的POSITION类型参数的值。很明显，在Test程序中，只有<br />一个视图类，因此只需将这两个函数调用一次即可得到CTestView的指针如下（需定<br />义一个POSITION结构变量来辅助操作）： <br />CTestView* pTestView;<br />POSITION pos=GetFirstViewPosition();<br />pTestView=GetNextView(pos);<br /><br />这样，便可到了CTestView类的指针pTestView.执行完几句后，变量pos=NULL,因为没<br />有下一个视图类，自然也没有下一个视图类的POSITION.但是这几条语句太简单，不<br />具有太强的通用性和安全特征；当象前面说的那样，当要在多个视图为中返回某个指<br />定类的指针时，我们需要遍历所有视图类，直到找到指定类为止。判断一个类指针指<br />向的是否某个类的实例时，可用IsKindOf()成员函数时行检查，如：<br /> pView-&gt;IsKindOf(RUNTIME_CLASS(CTestView));<br />即可检查pView所指是否是CTestView类。<br /><br />有了以上基础，我们已经可以从文档类取得任何类的指针。为了方便，我们将其作<br />为一个文档类的成员函数，它有一个参数，表示要获得哪个类的指针。实现如下： <br />CView* CTestDoc::GetView(CRuntimeClass* pClass)<br />{<br />	CView* pView;<br />	POSITION pos=GetFirstViewPosition();<br /><br />	while(pos!=NULL){<br />		pView=GetNextView(pos);<br />		if(!pView-&gt;IsKindOf(pClass))<br />		break;<br />	}<br /><br />	if(!pView-&gt;IsKindOf(pClass)){<br />		AfxMessageBox("Connt Locate the View.\r\n http://www.VCKBASE.com");<br />		return NULL;<br />	}<br /><br />	return pView;<br />}<br /><br />其中用了两次视图类的成员函数IsKindOf()来判断，是因为退出while循环有三种<br />可能：<br /><br />1.pos为NULL，即已经不存在下一个视图类供操作；<br />2.pView已符合要求。<br /><br />1和2同是满足。这是因为GetNextView()的功能是将当前视图指针改变成一个视图<br />的位置同时返回当前视图指针，因此pos是pView的下一个视图类的POSITION,完全<br />有可能既是pos==NULL又是pView符合需要。当所需的视图是最后一个视图是最后一<br />个视图类时就如引。因此需采用两次判断。<br />使用该函数应遵循如下格式（以取得CTestView指针为例）：<br />CTestView* pTestView=(CTestView*)GetView(RUNTIME_CLASS(CTestView));<br />RUNTIME_CLASS是一个宏，可以简单地理解它的作用：将类的名字转化为<br />CRuntimeClass为指针。至于强制类型转换也是为了安全特性考虑的，因为从同一个<br />基类之间的指针类型是互相兼容的。这种强制类型转换也许并不必要，但能避免一<br />些可能出现的麻烦。<br /><br />3.从一个视图类取得另一视图类的指针 综合1和2，很容易得出视图类之间互相获得<br />指针的方法：就是用文档类作中转，先用1的方法得到文档类的指针，再用2的方法，<br />以文档类的视图定位函数取得另一个视图类。同样，可以实现成一个函数：<br />（假设要从CTestAView中取得指向其它视图类的指针）<br />CView* CTestAView::GetView(CRuntimeClass* pClass)<br />{<br />	CTestDoc* pDoc=(CTestDoc*)GetDocument();<br />	CView* pView;<br />	POSITION pos=pDoc-&gt;GetFirstViewPosition();<br />	while(pos!=NULL){<br />		pView=pDoc-&gt;GetNextView(pos);<br />		if(!pView-&gt;IsKindOf(pClass))<br />		break;<br />	}<br />	if(!pView-&gt;IsKindOf(pClass)){<br />		AfxMessageBox("Connt Locate the View.");<br />		return NULL;<br />	}<br /><br />	return pView;<br />}<br />这个函数和2中的GetView()相比，一是多了第一句以取得文档类指针，二是在<br />GetFirstViewPosition()和GetNextView()前加上了文档类指针，以表示它们是文档<br />类成员函数。有了此函数；当要从CTestAView中取得CTestBView的指针时，只需如<br />下：CTestBView* pTestbView=(CTestView*)GetView(RUNTIME_CLASS(CTestBView));<br /><br /><pre>11）对于单文档中也可以加入多个文档模板，但是一般的开发就使用MDI方式开发
多文档模板，其方法与上述视图的获取方法很接近，这里稍做解释，如果不清楚，
请查阅MSDN，（以下四个内容（11、12、13、14）来源：
http://sanjianxia.myrice.com/vc/vc45.htm）

可以用CWinApp::GetFirstDocTemplatePostion获得应用程序注册的第一个文档模板
的位置；利用该值来调用CWinApp::GetNextDocTemplate函数，获得第一个
CDocTemplate对象指针。 POSITION GetFirstDocTemplate( ) const; 
CDocTemplate *GetNextDocTemplate( POSITION &amp; pos ) const;

第二个函数返回由pos 标识的文档模板。POSITION是MFC定义的一个用于迭代或对象
指针检索的值。通过这两个函数，应用程序可以遍历整个文档模板列表。如果被检索
的文档模板是模板列表中的最后一个，则pos参数被置为NULL。

 12）一个文档模板可以有多个文档，每个文档模板都保留并维护了一个所有对应文
档的指针列表。 
用CDocTemplate::GetFirstDocPosition函数获得与文档模板相关的文档集合中第一
个文档的位置，并用POSITION值作为CDocTemplate::GetNextDoc的参数来重复遍历与
模板相关的文档列表。函数原形为：
viaual POSITION GetFirstDocPosition( ) const = 0; 
visual CDocument *GetNextDoc(POSITION &amp; rPos) const = 0;  

如果列表为空，则rPos被置为NULL. 

 13）在文档中可以调用CDocument::GetDocTemplate获得指向该文档模板的指针。
函数原形如下： CDocTemplate * GetDocTemplate ( ) const; 
如果该文档不属于文档模板管理，则返回值为NULL。 

 14)一个文档可以有多个视。每一个文档都保留并维护一个所有相关视的列表。
CDocument::AddView将一个视连接到文档上，将该视加入到文档相联系的视的列表
中，并将视的文档指针指向该文档。当有File/New、File/Open、Windows/New或
Window/Split的命令而将一个新创建的视的对象连接到文档上时， MFC会自动调用
该函数，框架通过文档/视的结构将文档和视联系起来。当然，程序员也可以根据自
己的需要调用该函数。
Virtual POSITION GetFirstViewPosition( ) const; 
Virtual CView * GetNextView( POSITION &amp;rPosition) cosnt; 

应用程序可以调用CDocument::GetFirstViewPosition返回与调用文档相联系的视的
列表中的第一个视的位置，并调用CDocument::GetNextView返回指定位置的视，并将
rPositon的值置为列表中下一个视的POSITION值。如果找到的视为列表中的最后一个
视，则将rPosition置为NULL. 

 15)从一个视图类取得另一视图类的指针
这个应用在多视的应用程序中很多见，一般如果自己在主程序或者主框架中做好变
量记号，也可以获得，还有比较通用的就是用文档类作中转，以文档类的视图遍历
定位，取得另一个视图类。这个功能从本文第10项中可以得到。
访问应用程序的其它类

获得CWinApp:
	-在CMainFrame,CChildFrame,CDocument,CView中直接调用AfxGetApp()或用theApp
	-在其它类中只能用AfxGetApp()

获得CMainFrame:
	-在CMinApp中用AfxGetMainWnd()或者m_pMainWnd
	-在CChildFrame中可用GetParentFrame()
	-在其它类中用AfxGetMainWnd()

获得CChildFrame:
	-在CView中用GetParentFrame()
	-在CMainFrame中用MDIGetActive()或GetActiveFrame()
	-在其它类中用AfxGetMainWnd()-&gt;MDIGetActive()或AfxGetMainWnd()-&gt;GetActiveFrame()

获得CDocument:
	-在CView中用GetDocument()
	-在CChildFrame中用GetActiveView()-&gt;GetDocument()
	-在CMainFrame中用
		-if SDI:GetActiveView()-&gt;GetDocument()
		-if MDI:MDIGetActive()-&gt;GetActiveView()-&gt;GetDocument()
	-在其它类中
		-if SDI:AfxGetMainWnd()-&gt;GetActiveView()-&gt;GetDocument()
		-if MDI:AfxGetMainWnd()-&gt;MDIGetActive()-&gt;GetActiveView()-&gt;GetDocument()

获得CView:
	-在CDocument中 POSITION pos = GetFirstViewPosition();GetNextView(pos)
	-在CChildFrame中 GetActiveView()
	-在CMainFrame中
		-if SDI:GetActiveView()
		-if MDI:MDIGetActive()-&gt;GetActiveView()
	-在其它类中
		-if SDI:AfxGetMainWnd()-&gt;GetActiveView()
		-if MDI:AfxGetMainWnd()-&gt;MDIGetActive()-&gt;GetActiveView()
访问应用程序的其它类

获得CWinApp:
	-在CMainFrame,CChildFrame,CDocument,CView中直接调用AfxGetApp()或用theApp
	-在其它类中只能用AfxGetApp()

获得CMainFrame:
	-在CMinApp中用AfxGetMainWnd()或者m_pMainWnd
	-在CChildFrame中可用GetParentFrame()
	-在其它类中用AfxGetMainWnd()

获得CChildFrame:
	-在CView中用GetParentFrame()
	-在CMainFrame中用MDIGetActive()或GetActiveFrame()
	-在其它类中用AfxGetMainWnd()-&gt;MDIGetActive()或AfxGetMainWnd()-&gt;GetActiveFrame()

获得CDocument:
	-在CView中用GetDocument()
	-在CChildFrame中用GetActiveView()-&gt;GetDocument()
	-在CMainFrame中用
		-if SDI:GetActiveView()-&gt;GetDocument()
		-if MDI:MDIGetActive()-&gt;GetActiveView()-&gt;GetDocument()
	-在其它类中
		-if SDI:AfxGetMainWnd()-&gt;GetActiveView()-&gt;GetDocument()
		-if MDI:AfxGetMainWnd()-&gt;MDIGetActive()-&gt;GetActiveView()-&gt;GetDocument()

获得CView:
	-在CDocument中 POSITION pos = GetFirstViewPosition();GetNextView(pos)
	-在CChildFrame中 GetActiveView()
	-在CMainFrame中
		-if SDI:GetActiveView()
		-if MDI:MDIGetActive()-&gt;GetActiveView()
	-在其它类中
		-if SDI:AfxGetMainWnd()-&gt;GetActiveView()
		-if MDI:AfxGetMainWnd()-&gt;MDIGetActive()-&gt;GetActiveView()

</pre><img src ="http://www.cppblog.com/Lee/aggbug/62236.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-19 01:13 <a href="http://www.cppblog.com/Lee/archive/2008/09/19/62236.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WTL的安装、使用[转]</title><link>http://www.cppblog.com/Lee/archive/2008/09/18/62228.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 18 Sep 2008 14:24:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/18/62228.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/62228.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/18/62228.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/62228.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/62228.html</trackback:ping><description><![CDATA[
		<div class="tit"> </div>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt" id="blog_text">WTL全称为Window Template Library,一个构架于ATL之上的C++类库,它包装了大多数的窗口控制.从网上看的,也只是个大概的了解.先安装了弄个Hello World试试吧,呵呵<br />1,安装<br />   1.1 下个WTL安装包,其实上个压缩包.现在最新的是8.0版,默认解压目录为C:\WTL80,其中80为版本号,如果下的是7.5则为C:\WTL75<br />   1.2 自动安装<br />   在目录C:\WTL80\AppWiz有四个JS文件,选择一个你机子上安装的VC版本,点击相应的JS脚本进行安装即可,如VS2005的VC版本为8.0,则相应文件为setup80.js<br />   1.3 手动安装<br />   自动安装有时安装不成功,如点击文件后却打开了一个记事本,其实手动安装也挺不错的.<br />   将C:\WTL80\AppWiz\Files目录下的三个WTLAppWiz.*文件考到VC安装目录的vcprojects目录下,如E:\Program Files\Microsoft Visual Studio 8\VC\vcprojects, 打开WTLAppWiz.vsz,原文件为: 
<p><br />   VSWIZARD 7.0<br />   Wizard=VsWizard.VsWizardEngine</p><p>   Param="WIZARD_NAME = WTLAppWiz"<br />   Param="WIZARD_VERSION = 7.0"<br />   Param="ABSOLUTE_PATH = ."<br />   Param="FALLBACK_LCID = 1033"<br />   将其改为<br />   VSWIZARD 7.0<br />   Wizard=VsWizard.VsWizardEngine.8.0</p><p>   Param="WIZARD_NAME = WTLAppWiz"<br />   Param="WIZARD_VERSION = 7.0"<br />   Param="ABSOLUTE_PATH = C:\WTL80\AppWiz\Files"<br />   Param="FALLBACK_LCID = 1033"</p><p>   其中Wizard后面需加上WTL的版本号,否则能在VC里看到WTL项目,但其向导无法工作</p><p>   Param ="ABSOLUTE_PATH后加上WTL解压的路径</p><p>   新将项目时选择WTL项目,接默认选项生成项目，编译时会报'atlapp.h': No such file or directory错误。需要加一个编译路径，工具--&gt;选项--&gt;项目和解决方案--&gt;VC目录，在左边选择包含文件，把WTL解压目录下的C:\WTL80\include路径加上</p><p>   如果编译时报以下两个Bug：</p><p>   错误 1 fatal error CVT1100: 重复的资源。type:MANIFEST, name:1, language:0x0409 CVTRES<br />   错误 2 fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏 sdf<br />    则说明资源文件有问题，具体的原因未知，从网上找我资料看，有以下四种解决方法：</p><p>   1，工程属性-&gt;配置属性-&gt; 清单工具-&gt;输入和输出-&gt;嵌入清单，选择[否]</p><p>    2，打开rc文件，找MANIFEST，所属段或行全去掉</p><p>   3，打开此项目属性页，链接器--&gt;嵌入的 IDL--&gt;类型库资源 ID 设一个从1-65535的值</p><p>   4，项目属性页<font color="#000000">链接器--&gt;清单 文件--&gt;生成清单文件 选项设置为no</font></p></div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/Lee/aggbug/62228.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-18 22:24 <a href="http://www.cppblog.com/Lee/archive/2008/09/18/62228.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 命名规则 (转)</title><link>http://www.cppblog.com/Lee/archive/2008/09/16/62003.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Tue, 16 Sep 2008 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/16/62003.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/62003.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/16/62003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/62003.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/62003.html</trackback:ping><description><![CDATA[ 比较著名的命名规则当推Microsoft公司的“匈牙利”法，该命名规则的主要思想是“在变量和函数名中加入前缀以增进人们对程序的理解”。例如所有的字符变量均以ch为前缀，若是指针变量则追加前缀p。如果一个变量由ppch开头，则表明它是指向字符指针的指针。<br />    “匈牙利”法最大的缺点是烦琐，例如
<p>    int i, j, k;</p><p>    float x, y, z;</p><p>    倘若采用“匈牙利”命名规则，则应当写成</p><p>    int iI, iJ, ik; // 前缀 i表示int类型</p><p>    float fX, fY, fZ; // 前缀 f表示float类型</p><p>    如此烦琐的程序会让绝大多数程序员无法忍受。</p><p>    据考察，没有一种命名规则可以让所有的程序员赞同，程序设计教科书一般都不指定命名规则。命名规则对软件产品而言并不是“成败悠关”的事，我们不要化太多精力试图发明世界上最好的命名规则，而应当制定一种令大多数项目成员满意的命名规则，并在项目中贯彻实施。</p><p>    3.1 共性规则<br />    本节论述的共性规则是被大多数程序员采纳的，我们应当在遵循这些共性规则的前提下，再扩充特定的规则，如3.2节。</p><p><br />    l 【规则3-1-1】标识符应当直观且可以拼读，可望文知意，不必进行“解码”。</p><p>    标识符最好采用英文单词或其组合，便于记忆和阅读。切忌使用汉语拼音来命名。程序中的英文单词一般不会太复杂，用词应当准确。例如不要把CurrentValue写成NowValue。</p><p><br />    l 【规则3-1-2】标识符的长度应当符合“min-length &amp;&amp; max-information”原则。</p><p>    几十年前老ANSI C规定名字不准超过6个字符，现今的<a class="channel_keylink" href="http://c.chinaitlab.com/" target="_blank">C++</a>/C不再有此限制。一般来说，长名字能更好地表达含义，所以函数名、变量名、类名长达十几个字符不足为怪。那么名字是否越长约好？不见得! 例如变量名maxval就比maxValueUntilOverflow好用。单字符的名字也是有用的，常见的如i,j,k,m,n,x,y,z等，它们通常可用作函数内的局部变量。</p><p><br />    l 【规则3-1-3】命名规则尽量与所采用的操作系统或开发工具的风格保持一致。</p><p>    例如<a class="channel_keylink" href="http://windows.chinaitlab.com/" target="_blank">Windows</a>应用程序的标识符通常采用“大小写”混排的方式，如AddChild。而Unix应用程序的标识符通常采用“小写加下划线”的方式，如add_child。别把这两类风格混在一起用。</p><p><br />    l 【规则3-1-4】程序中不要出现仅靠大小写区分的相似的标识符。</p><p>    例如：</p><p>    int x, X; // 变量x 与 X 容易混淆</p><p>    void foo(int x); // 函数foo 与FOO容易混淆</p><p>    void FOO(float x);</p><p><br />    l 【规则3-1-5】程序中不要出现标识符完全相同的局部变量和全局变量，尽管两者的作用域不同而不会发生语法错误，但会使人误解。<br /><br />  l 【规则3-1-6】变量的名字应当使用“名词”或者“形容词＋名词”。 </p><p></p><p>    例如：</p><p>    float value;</p><p>    float oldValue;</p><p>    float newValue;</p><p><br />    l 【规则3-1-7】全局函数的名字应当使用“动词”或者“动词＋名词”（动宾词组）。类的成员函数应当只使用“动词”，被省略掉的名词就是对象本身。</p><p>    例如：</p><p>    DrawBox(); // 全局函数</p><p>    box-&gt;Draw(); // 类的成员函数</p><p><br />    l 【规则3-1-8】用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。</p><p>    例如：</p><p>    int minValue;</p><p>    int maxValue;</p><p><br />    int SetValue(…);</p><p>    int GetValue(…);</p><p><br />    2 【建议3-1-1】尽量避免名字中出现数字编号，如Value1,Value2等，除非逻辑上的确需要编号。这是为了防止程序员偷懒，不肯为命名动脑筋而导致产生无意义的名字（因为用数字编号最省事）。</p><p>    3.2 简单的<a class="channel_keylink" href="http://windows.chinaitlab.com/" target="_blank">Windows</a>应用程序命名规则<br />    作者对“匈牙利”命名规则做了合理的简化，下述的命名规则简单易用，比较适合于Windows应用软件的开发。</p><p><br />    l 【规则3-2-1】类名和函数名用大写字母开头的单词组合而成。</p><p>    例如：</p><p>    class Node; // 类名</p><p>    class LeafNode; // 类名</p><p>    void Draw(void); // 函数<br /></p><img src ="http://www.cppblog.com/Lee/aggbug/62003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-16 20:40 <a href="http://www.cppblog.com/Lee/archive/2008/09/16/62003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针（10）指针的安全问题（转）</title><link>http://www.cppblog.com/Lee/archive/2008/09/16/61927.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Mon, 15 Sep 2008 18:02:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/16/61927.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61927.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/16/61927.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61927.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61927.html</trackback:ping><description><![CDATA[看下面的例子：<br />例十八：<br />char s='a';<br />int *ptr;<br />ptr=(int *)&amp;s;<br />*ptr=1298；<br />指针ptr 是一个int *类型的指针，它指向的类型是int。它指向<br />的地址就是s 的首地址。在32 位程序中，s 占一个字节，int 类型占四<br />个字节。最后一条语句不但改变了s 所占的一个字节，还把和s 相临的<br />高地址方向的三个字节也改变了。这三个字节是干什么的？只有编译程<br />序知道，而写程序的人是不太可能知道的。也许这三个字节里存储了非<br />常重要的数据，也许这三个字节里正好是程序的一条代码，而由于你对<br />指针的马虎应用，这三个字节的值被改变了！这会造成崩溃性的错误。<br />让我们再来看一例：<br />例十九：<br />char a;<br />int *ptr=&amp;a;<br />ptr++;<br />*ptr=115;<br />该例子完全可以通过编译，并能执行。但是看到没有？第3 句对指<br />针ptr 进行自加1 运算后，ptr 指向了和整形变量a 相邻的高地址方向<br />的一块存储区。这块存储区里是什么？我们不知道。有可能它是一个非<br />常重要的数据，甚至可能是一条代码。而第4 句竟然往这片存储区里写<br />入一个数据！这是严重的错误。所以在使用指针时，程序员心里必须非<br />常清楚：我的指针究竟指向了哪里。在用指针访问数组的时候，也要注<br />意不要超出数组的低端和高端界限，否则也会造成类似的错误。<br />在指针的强制类型转换：ptr1=(TYPE *)ptr2 中，如果sizeof(ptr2<br />的类型)大于sizeof(ptr1 的类型)，那么在使用指针ptr1 来访问ptr2<br />所指向的存储区时是安全的。如果sizeof(ptr2 的类型) 小于<br />sizeof(ptr1 的类型)，那么在使用指针ptr1 来访问ptr2 所指向的存<br />储区时是不安全的。至于为什么，读者结合例十八来想一想，应该会明<br />白的。<img src ="http://www.cppblog.com/Lee/aggbug/61927.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-16 02:02 <a href="http://www.cppblog.com/Lee/archive/2008/09/16/61927.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针（9）指针类型转换（转）</title><link>http://www.cppblog.com/Lee/archive/2008/09/15/61887.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Mon, 15 Sep 2008 13:32:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/15/61887.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61887.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/15/61887.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61887.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61887.html</trackback:ping><description><![CDATA[当我们初始化一个指针或给一个指针赋值时，赋值号的左边是一个指<br />针，赋值号的右边是一个指针表达式。在我们前面所举的例子中，绝大<br />多数情况下，指针的类型和指针表达式的类型是一样的，指针所指向的<br />类型和指针表达式所指向的类型是一样的。<br />例十五：<br />float f=12.3;<br />float *fptr=&amp;f;<br />int *p;<br />在上面的例子中，假如我们想让指针p 指向实数f，应该怎么办？<br />是用下面的语句吗？<br />p=&amp;f;<br />不对。因为指针p 的类型是int *，它指向的类型是int。表达式<br />&amp;f 的结果是一个指针，指针的类型是float *,它指向的类型是float。<br />两者不一致，直接赋值的方法是不行的。至少在我的MSVC++6.0 上，对<br />指针的赋值语句要求赋值号两边的类型一致，所指向的类型也一致，其<br />它的编译器上我没试过，大家可以试试。为了实现我们的目的，需要进<br />行"强制类型转换"：<br />p=(int*)&amp;f;<br />如果有一个指针p，我们需要把它的类型和所指向的类型改为<br />TYEP *TYPE， 那么语法格式是： (TYPE *)p；<br />这样强制类型转换的结果是一个新指针，该新指针的类型是<br />TYPE *，它指向的类型是TYPE，它指向的地址就是原指针指向的地址。<br />而原来的指针p 的一切属性都没有被修改。（切记）<br />一个函数如果使用了指针作为形参，那么在函数调用语句的实参和<br />形参的结合过程中，必须保证类型一致，否则需要强制转换<br />例十六：<br />void fun(char*);<br />int a=125,b;<br />fun((char*)&amp;a);<br />void fun(char*s)<br />{<br />charc;<br />c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;<br />c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;<br />}<br />注意这是一个32 位程序，故int 类型占了四个字节，char 类型占一个<br />字节。函数fun 的作用是把一个整数的四个字节的顺序来个颠倒。注意<br />到了吗？在函数调用语句中，实参&amp;a 的结果是一个指针，它的类型是<br />int *，它指向的类型是int。形参这个指针的类型是char *，它指向<br />的类型是char。这样，在实参和形参的结合过程中，我们必须进行一<br />次从int *类型到char *类型的转换。结合这个例子，我们可以这样来<br />想象编译器进行转换的过程：编译器先构造一个临时指针char *temp，<br />然后执行temp=(char *)&amp;a，最后再把temp 的值传递给s。所以最后的<br />结果是：s 的类型是char *,它指向的类型是char，它指向的地址就是<br />a 的首地址。<br />我们已经知道，指针的值就是指针指向的地址，在32 位程序中，<br />指针的值其实是一个32 位整数。那可不可以把一个整数当作指针的值<br />直接赋给指针呢？就象下面的语句：<br />unsigned int a;<br />TYPE *ptr; //TYPE 是int，char 或结构类型等等类型。<br />a=20345686;<br />ptr=20345686; //我们的目的是要使指针ptr 指向地址20345686<br />ptr=a; //我们的目的是要使指针ptr 指向地址20345686<br />编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能<br />达到了吗？不，还有办法：<br />unsigned int a;<br />TYPE *ptr; //TYPE 是int，char 或结构类型等等类型。<br />a=N //N 必须代表一个合法的地址；<br />ptr=(TYPE*)a； //呵呵，这就可以了。<br />严格说来这里的(TYPE *)和指针类型转换中的(TYPE *)还不一样。这里<br />的(TYPE*)的意思是把无符号整数a 的值当作一个地址来看待。上面强<br />调了a 的值必须代表一个合法的地址，否则的话，在你使用ptr 的时候，<br />就会出现非法操作错误。<br />想想能不能反过来，把指针指向的地址即指针的值当作一个整数取<br />出来。完全可以。下面的例子演示了把一个指针的值当作一个整数取出<br />来，然后再把这个整数当作一个地址赋给一个指针：<br />例十七：<br />int a=123,b;<br />int *ptr=&amp;a;<br />char *str;<br />b=(int)ptr; //把指针ptr 的值当作一个整数取出来。<br />str=(char*)b; //把这个整数的值当作一个地址赋给指针str。<br />现在我们已经知道了，可以把指针的值当作一个整数取出来，也可<br />以把一个整数值当作地址赋给一个指针。<img src ="http://www.cppblog.com/Lee/aggbug/61887.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-15 21:32 <a href="http://www.cppblog.com/Lee/archive/2008/09/15/61887.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针（8） 指针和函数的关系（转）</title><link>http://www.cppblog.com/Lee/archive/2008/09/11/61572.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 11 Sep 2008 04:02:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/11/61572.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61572.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/11/61572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61572.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61572.html</trackback:ping><description><![CDATA[可以把一个指针声明成为一个指向函数的指针。<br />int fun1(char *,int);<br />int (*pfun1)(char *,int);<br />pfun1=fun1;<br />int a=(*pfun1)("abcdefg",7); //通过函数指针调用函数。<br />可以把指针作为函数的形参。在函数调用语句中，可以用指针表达式来<br />作为实参。<br />例十四：<br />int fun(char *);<br />inta;<br />char str[]="abcdefghijklmn";<br />a=fun(str);<br />int fun(char *s)<br />{<br />int num=0;<br />for(int i=0;;)<br />{<br />num+=*s;s++;<br />}<br />return num;<br />}<br /><br />这个例子中的函数fun 统计一个字符串中各个字符的ASCII 码值之<br />和。前面说了，数组的名字也是一个指针。在函数调用中，当把str<br />作为实参传递给形参s 后，实际是把str 的值传递给了s，s 所指向的<br />地址就和str 所指向的地址一致，但是str 和s 各自占用各自的存储空<br />间。在函数体内对s 进行自加1 运算，并不意味着同时对str 进行了自<br />加1 运算。<img src ="http://www.cppblog.com/Lee/aggbug/61572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-11 12:02 <a href="http://www.cppblog.com/Lee/archive/2008/09/11/61572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针（7） 指针和结构类型的关系（转）</title><link>http://www.cppblog.com/Lee/archive/2008/09/11/61566.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 11 Sep 2008 02:33:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/11/61566.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61566.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/11/61566.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61566.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61566.html</trackback:ping><description><![CDATA[可以声明一个指向结构类型对象的指针。<br />例十二：<br />struct MyStruct<br />{<br />int a;<br />int b;<br />int c;<br />};<br />struct MyStruct ss={20,30,40};<br />//声明了结构对象ss，并把ss 的成员初始化为20，30 和40。<br />struct MyStruct *ptr=&amp;ss;<br />//声明了一个指向结构对象ss 的指针。它的类型是<br />//MyStruct *,它指向的类型是MyStruct。<br />int *pstr=(int*)&amp;ss;<br />//声明了一个指向结构对象ss 的指针。但是pstr 和<br />//它被指向的类型ptr 是不同的。<br />请问怎样通过指针ptr 来访问ss 的三个成员变量？<br />答案：<br />ptr-&gt;a; //指向运算符，或者可以这们(*ptr).a,建议使用前者<br />ptr-&gt;b;<br />ptr-&gt;c;<br />又请问怎样通过指针pstr 来访问ss 的三个成员变量？<br />答案：<br />*pstr； //访问了ss 的成员a。<br />*(pstr+1); //访问了ss 的成员b。<br />*(pstr+2) //访问了ss 的成员c。<br />虽然我在我的MSVC++6.0 上调式过上述代码，但是要知道，这样使<br />用pstr 来访问结构成员是不正规的，为了说明为什么不正规，让我们<br />看看怎样通过指针来访问数组的各个单元: (将结构体换成数组)<br />例十三：<br />int array[3]={35,56,37};<br />int *pa=array;<br />通过指针pa 访问数组array 的三个单元的方法是：<br />*pa; //访问了第0 号单元<br />*(pa+1); //访问了第1 号单元<br />*(pa+2); //访问了第2 号单元<br />从格式上看倒是与通过指针访问结构成员的不正规方法的格式一<br />样。<br />所有的C/C++编译器在排列数组的单元时，总是把各个数组单元存<br />放在连续的存储区里，单元和单元之间没有空隙。但在存放结构对象的<br />各个成员时，在某种编译环境下，可能会需要字对齐或双字对齐或者是<br />别的什么对齐，需要在相邻两个成员之间加若干个"填充字节"，这就导<br />致各个成员之间可能会有若干个字节的空隙。<br />所以，在例十二中，即使*pstr 访问到了结构对象ss 的第一个成<br />员变量a，也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员<br />a 和成员b 之间可能会有若干填充字节，说不定*(pstr+1)就正好访问<br />到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想<br />看看各个结构成员之间到底有没有填充字节，嘿，这倒是个不错的方法。<br />不过指针访问结构成员的正确方法应该是象例十二中使用指针ptr 的<br />方法。<img src ="http://www.cppblog.com/Lee/aggbug/61566.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-11 10:33 <a href="http://www.cppblog.com/Lee/archive/2008/09/11/61566.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)指针(6)  数组和指针的关系</title><link>http://www.cppblog.com/Lee/archive/2008/09/09/61433.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Tue, 09 Sep 2008 15:48:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/09/61433.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61433.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/09/61433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61433.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61433.html</trackback:ping><description><![CDATA[数组的数组名其实可以看作一个指针。看下例：<br />例九：<br />intarray[10]={0,1,2,3,4,5,6,7,8,9},value;<br />value=array[0]; //也可写成：value=*array;<br />value=array[3]; //也可写成：value=*(array+3);<br />value=array[4]; //也可写成：value=*(array+4);<br />上例中，一般而言数组名array 代表数组本身，类型是int[10]，但如<br />果把array 看做指针的话，它指向数组的第0 个单元，类型是int* ，<br />所指向的类型是数组单元的类型即int。因此*array 等于0 就一点也不<br />奇怪了。同理，array+3 是一个指向数组第3 个单元的指针，所以<br />*(array+3)等于3。其它依此类推。<br />例十：<br />char *str[3]={<br />"Hello,thisisasample!",<br />"Hi,goodmorning.",<br />"Helloworld"<br />};<br />chars[80]；<br />strcpy(s,str[0]); //也可写成strcpy(s,*str);<br />strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));<br />strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));<br />上例中，str 是一个三单元的数组，该数组的每个单元都是一个指针，<br />这些指针各指向一个字符串。把指针数组名str 当作一个指针的话，它<br />指向数组的第0 号单元，它的类型是char **，它指向的类型是char *。<br />*str 也是一个指针，它的类型是char *，它所指向的类型是char，它<br />指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址，即<br />'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储<br />存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果<br />看成指针的话,他即是常量指针,也是指针常量.<br />str+1 也是一个指针，它指向数组的第1 号单元，它的类型是char**，<br />它指向的类型是char*。<br />*(str+1)也是一个指针，它的类型是char*，它所指向的类型是char，<br />它指向"Hi,goodmorning."的第一个字符'H'<br />下面总结一下数组的数组名(数组中储存的也是数组)的问题:<br />声明了一个数组TYPE array[n]，则数组名称array 就有了两重含义：<br />第一，它代表整个数组，它的类型是TYPE[n]；第二，它是一个常量<br />指针，该指针的类型是TYPE*，该指针指向的类型是TYPE，也就是数组<br />单元的类型，该指针指向的内存区就是数组第0 号单元，该指针自己占<br />有单独的内存区，注意它和数组第0 号单元占据的内存区是不同的。该<br />指针的值是不能修改的，即类似array++的表达式是错误的。<br />在不同的表达式中数组名array 可以扮演不同的角色。<br />在表达式sizeof(array)中，数组名array 代表数组本身，故这时<br />sizeof 函数测出的是整个数组的大小。<br />在表达式*array 中，array 扮演的是指针，因此这个表达式的结果就是<br />数组第0 号单元的值。sizeof(*array)测出的是数组单元的大小。<br />表达式array+n（其中n=0，1，2，.....）中，array 扮演的是指<br />针，故array+n 的结果是一个指针，它的类型是TYPE *，它指向的类<br />型是TYPE，它指向数组第n 号单元。故sizeof(array+n)测出的是指针<br />类型的大小。在32 位程序中结果是4<br />例十一:<br />int array[10];<br />int (*ptr)[10];<br />ptr=&amp;array;：<br />上例中ptr 是一个指针，它的类型是int(*)[10]，他指向的类型是<br />int[10] ，我们用整个数组的首地址来初始化它。在语句ptr=&amp;array<br />中，array 代表数组本身。<br />本节中提到了函数sizeof()，那么我来问一问，sizeof(指针名称)<br />测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小？<br />答案是前者。例如：<br />int(*ptr)[10];<br />则在32 位程序中，有：<br />sizeof(int(*)[10])==4<br />sizeof(int[10])==40<br />sizeof(ptr)==4<br />实际上，sizeof(对象)测出的都是对象自身的类型的大小，而不是别的<br />什么类型的大小。<img src ="http://www.cppblog.com/Lee/aggbug/61433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-09 23:48 <a href="http://www.cppblog.com/Lee/archive/2008/09/09/61433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>引用的作用 （转）</title><link>http://www.cppblog.com/Lee/archive/2008/09/06/61093.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sat, 06 Sep 2008 01:51:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/06/61093.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/61093.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/06/61093.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/61093.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/61093.html</trackback:ping><description><![CDATA[
		<div id="BlogArticleDetail" style="FONT-SIZE: 14px">分析以下程序的执行结果 <br />#include&lt;iostream.h&gt; <br />class Sample <br />{ <br />int x,y; <br />public: <br />Sample(){x=y=0;} <br />Sample(int i,int j){x=i;y=j;} <br />void copy(Sample &amp;s); <br />void setxy(int i,int j){x=i;y=j;} <br />void print(){cout&lt;&lt;"x="&lt;&lt;x&lt;&lt;",y="&lt;&lt;y&lt;&lt;endl;} <br />}; <br />void Sample::copy(Sample &amp;s) <br />{ <br />x=s.x;y=s.y; <br />} <br />void func(Sample s1,Sample &amp;s2) <br />{ <br />s1.setxy(10,20); <br />s2.setxy(30,40); <br />} <br />void main() <br />{ <br />Sample p(1,2),q; <br />q.copy(p); <br />func(p,q); <br />p.print(); <br />q.print(); <br />} <br />解： <br />本题说明对象引用作为函数参数的作用。Sample类中的copy()成员函数进行对象拷贝。在main()中先建立对象p和q，p与q对象的x,y值相同,调用func()函数，由于第2个参数为引用类型，故实参发生改变；而第1个参数不是引用类型，实参不发生改变。所以输出为： <br />x=1,y=2 <br />x=30,y=40 <br /><br />===============================================================================================<br /><br />众所周知，引用作为函数参数可以避免参数对象的额外拷贝，对于非内置类型，一般而言可以获得更高的效率，同时比指针更安全，语义也更清晰。<br />   但是除此之外引用有什么特别的作用呢？<br />   在同一个作用域的引用，就像这样：<br />   void f()<br />   {<br />   int i = 0;<br />   int &amp;ri = i; //这里。<br />   //...<br />   }<br />   <br />   事实上，在f的内部，需要操作i的地方，完全可以直接使用i，而不必要使用ri间接操作，使用i在语义上更明确。而混合使用i和ri反倒容易引起逻辑的混乱。<br />   似乎这是一个鸡肋，但是其实不是。
<p>   这里引用乾坤一笑文章中的例子：<br />                例一、用C宏，书写代码更简洁<br />                这段代码写网络程序的朋友都很眼熟，是Net/3中mbuf的实现。<br />                <br />                struct mbuf<br />                {<br />                    struct m_hdr mhdr;<br />                    union {<br />                        struct <br />                        {<br />                            struct pkthdr MH_pkthdr; /* M_PKTHDR set */<br />                            union <br />                            {<br />                                struct m_ext MH_ext; /* M_EXT set */<br />                                char MH_databuf[MHLEN];<br />                            } MH_dat;<br />                        } MH;<br />                        char M_databuf[MLEN];        /* !M_PKTHER, !M_EXT*/<br />                    } M_dat;<br />                };<br />                <br />                上面的代码，假如我想访问最里层的MH_databuf，那么我必须写M_dat.MH.MH_dat.MH_databuf; 这是不是很长，很难写呀？这样的代码阅读起来也不明了。其实，对于MH_pkthdr、MH_ext、MH_databuf来说，虽然不是在一个结构层次上，但是如果我们站在mbuf之外来看，它们都是mbuf的属性，完全可以压扁到一个平面上去看。所以，源码中有这么一组宏：<br />                <br />                #define m_next      m_hdr.mh_next<br />                #define m_len       m_hdr.mh_len<br />                #define m_data      m_hdr.mh_data<br />                ... ...<br />                #define m_pkthdr    M_dat.MH.MH_pkthdr<br />                #define m_pktdat    M_dat.MH.MH_dat.MH_databuf<br />                ... ...<br />                <br />                这样写起代码来，是不是很精练呢！</p><p>    这里用宏很巧妙的解决了访问深层数据的问题，但是宏的固有缺点也被引入了代码中，同时，如果其他地方无意中引用了这个宏定义的头文件，而且恰好使用了名为m_pktdat的数据成员，那这个宏带来的后果可就不是我们想要的了。<br />    事实上用引用也可以达到类似的效果，不过必须是在使用的时候。由于引用不是标准C的组成部分，所以这只是一个C++技巧。<br />    //假如代码是这样的：<br />    mbuf m; //这里的mbuf就是前面的struct mbuf。<br />            //如果要使用MH_ext成员，可以这样：<br />    m_ext &amp;MH_ext = m.M_dat.MH.MH_dat.MH_ext;<br />    //然后你的代码中就可以直接使用MH_ext作为m.M_dat.MH.MH_dat.MH_ext的替代品了。<br />    也许看起来不是很自然，不过这无疑是一种很直接的方法。你还可以通过一个const引用来在逻辑上避免无意的写操作。<br />    实际的“面向对象”的C++代码中，不推荐直接数据成员的访问，取而代之的是使用Get()和Set()方法存取数据，有些人只使用Get，通过返回一个成员的引用来达到读写数据成员的双重目的，这时候，你可以在外部定义一个引用接受函数的返回，从而避免每次都要写(XXX.Get()).Get()这种拖沓的语句来访问一个深层的成员。<br />    <br />    引用的另一个作用，就是“别名”。别名是引用的另一种翻译，很明确的表达了引用的另一个作用。<br />    仅仅是为了代码的可读性：<br />    //下面的代码<br />    int i = 0,j = 0;<br />    //...<br />    for( i = 0; i &lt; 10; i++)<br />        for( j = 0; j &lt; 10; j++ )<br />            a[i][j] = 0;<br />    //你能明白这段代码的含义嘛？有点困难，i和j的含义是不明确的，无法一眼看透。<br />    假如改成这样：<br />    const int width = 10;<br />    const int height = 10;<br />    //...<br />    int i = 0,j = 0;<br />    //...<br />    int &amp;line = i;<br />    int &amp;row = j;<br />    for(line = 0;line &lt; height;line++)<br />        for(row = 0;row &lt; width;row++)<br />            a[line][row] = 0;<br />    //是不是好了一点？<br />    //这并不是一个典型的例子，因为i和j的定义是任意的，某些情况你必须使用别人给定的名称很郁闷的变量，而他们又必须用来表达截然不同的含义，这时候一个引用往往可以让你清爽很多。<br />    <br />    再看下面这个例子：<br />    class CA<br />        {<br />                int m_i;<br />        public:<br />                int &amp;i;<br />                int const &amp;c_i;<br />                CA():i(m_i),c_i(m_i){};<br />        };<br />    这是一个简单的类，与所谓的“面向对象”的方法不同，这里使用引用实现内部数据的公用接口。这个手法用来对应 乾坤一笑 的另一段话：<br />         这就是偶说的PME模型的问题了，delphi、java、c#之类的语言都提供一种叫做属性的语法，大概是这个样子的：</p><p>                class A<br />                {<br />                property int x<br />                {<br />                get {return x;}<br />                set {x = value;}<br />                }<br />                };<br />                <br />                A a;<br />                这样, 就可以这么访问了 a.x = 8; int b = a.x;<br />                <br />                这比用 a.setx(8); b=a.getx()；直观多了。</p><p>    你可以用 CA a; a.i访问CA的私有数据成员，达到像属性方法那样的效果。但是这个方法在VC6下的表现却不尽如人意，因为它存储了一个指针用来取代语法上的引用，这导致类体积不必要的扩张，是我们所不希望看到的。也许在实现上确实存在难度，不过还是希望有更好的编译器能实现真正意义的引用接口。<br />    这个例子其实是上面深层数据成员访问的一个引申。<br />    引用，作为C++的一个特殊手法，也许还有很多不为人知的作用等待我们去发掘呢～<br />    <br />    注：文中的代码并未经过严格测试，有兴趣的读者可以自己测试代码的有效性。<br />    本文引用的  乾坤一笑 的内容均出自：<br />    <a href="http://blog.vckbase.com/smileonce/archive/2005/03/27/4081.html">http://blog.vckbase.com/smileonce/archive/2005/03/27/4081.html</a></p></div>
<img src ="http://www.cppblog.com/Lee/aggbug/61093.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-06 09:51 <a href="http://www.cppblog.com/Lee/archive/2008/09/06/61093.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++ 运算符优先级列表 </title><link>http://www.cppblog.com/Lee/archive/2008/09/04/60962.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 04 Sep 2008 14:15:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/04/60962.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/60962.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/04/60962.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/60962.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/60962.html</trackback:ping><description><![CDATA[
		<table class="code-table" border="1">
				<tbody>
						<tr>
								<th class="code-table-th">Precedence</th>
								<th class="code-table-th">Operator</th>
								<th class="code-table-th">Description</th>
								<th class="code-table-th">Example</th>
								<th class="code-table-th">Associativity</th>
						</tr>
						<tr>
								<td class="code-table-td">1</td>
								<td class="code-table-td">()<br />[]<br />-&gt;<br />.<br />::<br />++<br />--</td>
								<td class="code-table-td">Grouping operator<br />Array access<br />Member access from a pointer<br />Member access from an object<br />Scoping operator<br />Post-increment<br />Post-decrement</td>
								<td class="code-table-td">(a + b) / 4;<br />array[4] = 2;<br />ptr-&gt;age = 34;<br />obj.age = 34;<br />Class::age = 2;<br />for( i = 0; i &lt; 10; i++ ) ...<br />for( i = 10; i &gt; 0; i-- ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">2</td>
								<td class="code-table-td">!<br />~<br />++<br />--<br />-<br />+<br />*<br />&amp;<br />(type)<br /><a href="http://www.cppreference.com/keywords/sizeof.html"><font color="#006600">sizeof</font></a></td>
								<td class="code-table-td">Logical negation<br />Bitwise complement<br />Pre-increment<br />Pre-decrement<br />Unary minus<br />Unary plus<br />Dereference<br />Address of<br />Cast to a given type<br />Return size in bytes</td>
								<td class="code-table-td">if( !done ) ...<br />flags = ~flags;<br />for( i = 0; i &lt; 10; ++i ) ...<br />for( i = 10; i &gt; 0; --i ) ...<br />int i = -1;<br />int i = +1;<br />data = *ptr;<br />address = &amp;obj;<br />int i = (int) floatNum;<br />int size = sizeof(floatNum);</td>
								<td class="code-table-td">right to left</td>
						</tr>
						<tr>
								<td class="code-table-td">3</td>
								<td class="code-table-td">-&gt;*<br />.*</td>
								<td class="code-table-td">Member pointer selector<br />Member pointer selector</td>
								<td class="code-table-td">ptr-&gt;*var = 24;<br />obj.*var = 24;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">4</td>
								<td class="code-table-td">*<br />/<br />%</td>
								<td class="code-table-td">Multiplication<br />Division<br />Modulus</td>
								<td class="code-table-td">int i = 2 * 4;<br />float f = 10 / 3;<br />int rem = 4 % 3;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">5</td>
								<td class="code-table-td">+<br />-</td>
								<td class="code-table-td">Addition<br />Subtraction</td>
								<td class="code-table-td">int i = 2 + 3;<br />int i = 5 - 1;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">6</td>
								<td class="code-table-td">&lt;&lt;<br />&gt;&gt;</td>
								<td class="code-table-td">Bitwise shift left<br />Bitwise shift right</td>
								<td class="code-table-td">int flags = 33 &lt;&lt; 1;<br />int flags = 33 &gt;&gt; 1;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">7</td>
								<td class="code-table-td">&lt;<br />&lt;=<br />&gt;<br />&gt;=</td>
								<td class="code-table-td">Comparison less-than<br />Comparison less-than-or-equal-to<br />Comparison greater-than<br />Comparison geater-than-or-equal-to</td>
								<td class="code-table-td">if( i &lt; 42 ) ...<br />if( i &lt;= 42 ) ...<br />if( i &gt; 42 ) ...<br />if( i &gt;= 42 ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">8</td>
								<td class="code-table-td">==<br />!=</td>
								<td class="code-table-td">Comparison equal-to<br />Comparison not-equal-to</td>
								<td class="code-table-td">if( i == 42 ) ...<br />if( i != 42 ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">9</td>
								<td class="code-table-td">&amp;</td>
								<td class="code-table-td">Bitwise AND</td>
								<td class="code-table-td">flags = flags &amp; 42;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">10</td>
								<td class="code-table-td">^</td>
								<td class="code-table-td">Bitwise exclusive OR</td>
								<td class="code-table-td">flags = flags ^ 42;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">11</td>
								<td class="code-table-td">|</td>
								<td class="code-table-td">Bitwise inclusive (normal) OR</td>
								<td class="code-table-td">flags = flags | 42;</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">12</td>
								<td class="code-table-td">&amp;&amp;</td>
								<td class="code-table-td">Logical AND</td>
								<td class="code-table-td">if( conditionA &amp;&amp; conditionB ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">13</td>
								<td class="code-table-td">||</td>
								<td class="code-table-td">Logical OR</td>
								<td class="code-table-td">if( conditionA || conditionB ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
						<tr>
								<td class="code-table-td">14</td>
								<td class="code-table-td">? :</td>
								<td class="code-table-td">Ternary conditional (if-then-else)</td>
								<td class="code-table-td">int i = (a &gt; b) ? a : b;</td>
								<td class="code-table-td">right to left</td>
						</tr>
						<tr>
								<td class="code-table-td">15</td>
								<td class="code-table-td">=<br />+=<br />-=<br />*=<br />/=<br />%=<br />&amp;=<br />^=<br />|=<br />&lt;&lt;=<br />&gt;&gt;=</td>
								<td class="code-table-td">Assignment operator<br />Increment and assign<br />Decrement and assign<br />Multiply and assign<br />Divide and assign<br />Modulo and assign<br />Bitwise AND and assign<br />Bitwise exclusive OR and assign<br />Bitwise inclusive (normal) OR and assign<br />Bitwise shift left and assign<br />Bitwise shift right and assign</td>
								<td class="code-table-td">int a = b;<br />a += 3;<br />b -= 4;<br />a *= 5;<br />a /= 2;<br />a %= 3;<br />flags &amp;= new_flags;<br />flags ^= new_flags;<br />flags |= new_flags;<br />flags &lt;&lt;= 2;<br />flags &gt;&gt;= 2;</td>
								<td class="code-table-td">right to left</td>
						</tr>
						<tr>
								<td class="code-table-td">16</td>
								<td class="code-table-td">,</td>
								<td class="code-table-td">Sequential evaluation operator</td>
								<td class="code-table-td">for( i = 0, j = 0; i &lt; 10; i++, j++ ) ...</td>
								<td class="code-table-td">left to right</td>
						</tr>
				</tbody>
		</table>
		<a href="http://www.cppreference.com/operator_precedence.html">http://www.cppreference.com/operator_precedence.html</a>
<img src ="http://www.cppblog.com/Lee/aggbug/60962.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-04 22:15 <a href="http://www.cppblog.com/Lee/archive/2008/09/04/60962.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(转)指针（5）指针表达式</title><link>http://www.cppblog.com/Lee/archive/2008/09/04/60895.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Thu, 04 Sep 2008 05:03:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/09/04/60895.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/60895.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/09/04/60895.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/60895.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/60895.html</trackback:ping><description><![CDATA[
		<br />一个表达式的结果如果是一个指针，那么这个表达式就叫指针表式。<br />下面是一些指针表达式的例子：<br />例七：<br />int a,b;<br />int array[10];<br />int *pa;<br />pa=&amp;a; //&amp;a 是一个指针表达式。<br />Int **ptr=&amp;pa; //&amp;pa 也是一个指针表达式。<br />*ptr=&amp;b; //*ptr 和&amp;b 都是指针表达式。<br />pa=array;<br />pa++; //这也是指针表达式。<br />例八：<br />char *arr[20];<br />char **parr=arr; //如果把arr 看作指针的话，arr 也是指针表达式<br />char *str;<br />str=*parr; //*parr 是指针表达式<br />str=*(parr+1); //*(parr+1)是指针表达式<br />str=*(parr+2); //*(parr+2)是指针表达式<br />由于指针表达式的结果是一个指针，所以指针表达式也具有指针所<br />具有的四个要素：指针的类型，指针所指向的类型，指针指向的内存区，<br />指针自身占据的内存。<br />好了，当一个指针表达式的结果指针已经明确地具有了指针自身占<br />据的内存的话，这个指针表达式就是一个左值，否则就不是一个左值。<br />在例七中，&amp;a 不是一个左值，因为它还没有占据明确的内存。*ptr 是<br />一个左值，因为*ptr 这个指针已经占据了内存，其实*ptr 就是指针pa，<br />既然pa 已经在内存中有了自己的位置，那么*ptr 当然也有了自己的位<br />置。<img src ="http://www.cppblog.com/Lee/aggbug/60895.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-09-04 13:03 <a href="http://www.cppblog.com/Lee/archive/2008/09/04/60895.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）  指针（4） 运算符&amp;和*</title><link>http://www.cppblog.com/Lee/archive/2008/08/31/60547.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sun, 31 Aug 2008 14:42:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/08/31/60547.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/60547.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/08/31/60547.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/60547.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/60547.html</trackback:ping><description><![CDATA[这里&amp;是取地址运算符，*是间接运算符。<br />&amp;a 的运算结果是一个指针，指针的类型是a 的类型加个*，指针所<br />指向的类型是a 的类型，指针所指向的地址嘛，那就是a 的地址。<br />*p 的运算结果就五花八门了。总之*p 的结果是p 所指向的东西，<br />这个东西有这些特点：它的类型是p 指向的类型，它所占用的地址是p<br />所指向的地址。<br />例六：<br />int a=12; int b; int *p; int **ptr;<br />p=&amp;a; //&amp;a 的结果是一个指针，类型是int*，指向的类型是<br />//int，指向的地址是a 的地址。<br />*p=24; //*p 的结果，在这里它的类型是int，它所占用的地址是<br />//p 所指向的地址，显然，*p 就是变量a。<br />ptr=&amp;p; //&amp;p 的结果是个指针，该指针的类型是p 的类型加个*，<br />//在这里是int **。该指针所指向的类型是p 的类型，这<br />//里是int*。该指针所指向的地址就是指针p 自己的地址。<br />*ptr=&amp;b; //*ptr 是个指针，&amp;b 的结果也是个指针，且这两个指针<br />//的类型和所指向的类型是一样的，所以用&amp;b 来给*ptr 赋<br />//值就是毫无问题的了。<br />**ptr=34; //*ptr 的结果是ptr 所指向的东西，在这里是一个指针，<br />//对这个指针再做一次*运算，结果是一个int 类型的变量。<img src ="http://www.cppblog.com/Lee/aggbug/60547.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-08-31 22:42 <a href="http://www.cppblog.com/Lee/archive/2008/08/31/60547.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转） 指针（3）指针的算术运算</title><link>http://www.cppblog.com/Lee/archive/2008/08/31/60544.html</link><dc:creator>David Lee</dc:creator><author>David Lee</author><pubDate>Sun, 31 Aug 2008 14:19:00 GMT</pubDate><guid>http://www.cppblog.com/Lee/archive/2008/08/31/60544.html</guid><wfw:comment>http://www.cppblog.com/Lee/comments/60544.html</wfw:comment><comments>http://www.cppblog.com/Lee/archive/2008/08/31/60544.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Lee/comments/commentRss/60544.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Lee/services/trackbacks/60544.html</trackback:ping><description><![CDATA[指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减<br />运算的意义是不一样的，以单元为单位。例如：<br />例二：<br />char a[20];<br />int *ptr=(int *)a; //强制类型转换并不会改变a 的类型<br />ptr++;<br />在上例中，指针ptr 的类型是int*,它指向的类型是int，它被初始化<br />为指向整型变量a。接下来的第3 句中，指针ptr 被加了1，编译器是这样<br />处理的：它把指针ptr 的值加上了sizeof(int)，在32 位程序中，是被加上<br />了4，因为在32 位程序中，int 占4 个字节。由于地址是用字节做单位的，<br />故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。<br />由于char 类型的长度是一个字节，所以，原来ptr 是指向数组a 的第0 号<br />单元开始的四个字节，此时指向了数组a 中从第4 号单元开始的四个字节。<br />我们可以用一个指针和一个循环来遍历一个数组，看例子：<br />例三：<br />int array[20]={0};<br />int *ptr=array;<br />for(i=0;i&lt;20;i++)<br />{<br />(*ptr)++;<br />ptr++；<br />}<br />这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr<br />加1 个单元，所以每次循环都能访问数组的下一个单元。<br />再看例子：<br />例四：<br />char a[20]="You_are_a_girl";<br />int *ptr=(int *)a;<br />ptr+=5;<br />在这个例子中，ptr 被加上了5，编译器是这样处理的：将指针ptr 的<br />值加上5 乘sizeof(int)，在32 位程序中就是加上了5 乘4=20。由于地址<br />的单位是字节，故现在的ptr 所指向的地址比起加5 后的ptr 所指向的地址<br />来说，向高地址方向移动了20 个字节。在这个例子中，没加5 前的ptr 指<br />向数组a 的第0 号单元开始的四个字节，加5 后，ptr 已经指向了数组a 的<br />合法范围之外了。虽然这种情况在应用上会出问题，但在语法上却是可以的。<br />这也体现出了指针的灵活性。<br />如果上例中，ptr 是被减去5，那么处理过程大同小异，只不过ptr 的<br />值是被减去5 乘sizeof(int)，新的ptr 指向的地址将比原来的ptr 所指向<br />的地址向低地址方向移动了20 个字节。<br />下面请允许我再举一个例子:(一个误区)<br />例五:<br />#include&lt;stdio.h&gt;<br />int main()<br />{<br />char a[20]=" You_are_a_girl";<br />char *p=a;<br />char **ptr=&amp;p;<br />//printf("p=%d\n",p);<br />//printf("ptr=%d\n",ptr);<br />//printf("*ptr=%d\n",*ptr);<br />printf("**ptr=%c\n",**ptr);<br />ptr++;<br />//printf("ptr=%d\n",ptr);<br />//printf("*ptr=%d\n",*ptr);<br />printf("**ptr=%c\n",**ptr);<br />}<br />误区一、输出答案为Y 和o<br />误解:ptr 是一个char 的二级指针,当执行ptr++;时,会使指针加一个<br />sizeof(char),所以输出如上结果,这个可能只是少部分人的结果.<br />误区二、输出答案为Y 和a<br />误解:ptr 指向的是一个char *类型,当执行ptr++;时,会使指针加一个<br />sizeof(char *)(有可能会有人认为这个值为1,那就会得到误区一的答<br />案,这个值应该是4,参考前面内容), 即&amp;p+4; 那进行一次取值运算不<br />就指向数组中的第五个元素了吗?那输出的结果不就是数组中第五个元<br />素了吗?答案是否定的.<br />正解: ptr 的类型是char **,指向的类型是一个char *类型,该指向的<br />地址就是p的地址(&amp;p),当执行ptr++;时,会使指针加一个sizeof(char<br />*),即&amp;p+4;那*(&amp;p+4)指向哪呢,这个你去问上帝吧,或者他会告诉你在<br />哪?所以最后的输出会是一个随机的值,或许是一个非法操作.<br />总结一下:<br />一个指针ptrold 加(减)一个整数n 后，结果是一个新的指针ptrnew，<br />ptrnew 的类型和ptrold 的类型相同，ptrnew 所指向的类型和ptrold<br />所指向的类型也相同。ptrnew 的值将比ptrold 的值增加(减少)了n 乘<br />sizeof(ptrold 所指向的类型)个字节。就是说，ptrnew 所指向的内存<br />区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘<br />sizeof(ptrold 所指向的类型)个字节。<br />指针和指针进行加减：<br />两个指针不能进行加法运算，这是非法操作，因为进行加法后，得到的<br />结果指向一个不知所向的地方，而且毫无意义。两个指针可以进行减法<br />操作，但必须类型相同，一般用在数组方面，不多说了。<br /><img src ="http://www.cppblog.com/Lee/aggbug/60544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Lee/" target="_blank">David Lee</a> 2008-08-31 22:19 <a href="http://www.cppblog.com/Lee/archive/2008/08/31/60544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>