您好,欢迎访问三七文档
一夜鱼龙舞--C++编程要点本来想写“谨以此书,纪念C++之父BjarneStroustrup”,可惜他老人家还健在,活的非强逍遥。所谓编程规则,若依赖于文档、口述、自觉、经验,都是非常脆弱的。只用充分利用语言特性构思而成的开发模式,才是真正有效的规则,才是计算机世界中的法的精神。(林灿星)参考文献《EffectiveC++2nd》Copyright©1999byScottMeyers,於春景(lostmouse)译.《MoreEffectiveC++》Copyright©1999byScottMeyers,侯捷()译.《Guruoftheweek》CàC++尽量使用C++风格的注释(//)“/**/”是不能嵌套使用的,”//”则无此顾虑,而且有时要基于注释生成help文件,编译器也只认”//”。当然,某些头文件(如:API头文件)即要在C++编译器上编译,又要在C编译器上编译,那只能使用C风格的注释。尽量使用命名空间(usingnamespace)对于类成员,其名字空间就是类的名字。由于名字空间的概念引入的时间相对较晚,有些编译器可能不支持。即便如此,也没理由污染全局名字空间,因为可以用struct来近似实现namespace。但如果struct中,若运算符被定义为结构的静态成员,它就只能通过函数调用来使用,而不能象常规的运算符所设计的那样,可以通过自然的中缀语法来使用:定义structwidgets{classwidget{...};staticconstwidgetoperator+(constwidget&lhs,constwidget&rhs);使用typedefwidgets::widgetwidget;//建立全局(无修饰符的)名称widgetw1,w2,sum;sum=w1+w2;//错误!本空间没有声明参数为widgets的operator+sum=widgets::operator+(w1,w2);//合法,但不是自然的语法尽量用iostream而不用stdio.hscanf和printf虽然轻巧,却不是类型安全的,且没有扩展性。可以用操作算子和替代。例如:可以coutintiMyClasscendl;//MyClass可以重载,并可以隐式int类型转换intC++标准化委员会没有定义iostream.h,如果编译器同时支持iostream和iostream.h,使用iostream会得到std名字空间下的iostream库的元素;使用iostream.h,得到的是置于全局空间的同样的元素,容易冲突。iostream.h只支持窄字符集,iostream则支持窄/宽字符集。而且,打字时iostream比iostream.h少写两个字母。#include“”和的区别“”先在当前目录中查找,先在LD_PATH中查找尽量用const和inline而不用#define编译器会永远也看不到宏名,如果涉及到宏的代码在编译时报错,就很难定位。常量定义对于常量,可以用const定义代替#define,例如:#definePI3.14àconstdoublePI=3.14;定义指针常量要双const,除了指针所指的类型要定义const外,指针也经常要定义const。例:constchar*constName=“LCX”;定义类(class)中的常量时,要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员:头文件classGamePlayer{staticconstintNUMS=5;//常量声明并赋初值实现代码constintGamePlayer::NUMS;//上述语句只是NUMS的声明,所以还得在实现代码中定义类的静态成员旧的编译器认为类的静态成员在声明时定义初值是非法的,遇到这种情况时,可以在定义时赋初值:头文件classGamePlayer{staticconstintNUMS;实现代码constintGamePlayer::NUMS=5;//可以在定义时赋初值而且某些编译器只允许类内初始化整数类型(如:int,bool,char等),还只能是常量。上面这个方法也能解决这种情况。但是上面这种方法不能解决在声明中用到常量的情况,可利用枚举(enum)类型可替代int的特性来解决。例如:头文件(不能解决在声明中用到常量的情况)classGamePlayer{staticconstintNUMS;intscores[NUMS];使用enum后的头文件classGamePlayer{enum{NUMS=5};//利用enum代替int,这种方法有很多好处intscores[NUMS];函数定义用宏定义函数还是要注意很多,例如:#definemax(a,b)((a)(b)?(a):(b))l必须在写宏体时对每个参数都要加上括号;(导致一堆括号!定位都很困难)l即使这样做了,还会有象下面这样奇怪的事发生:max(++a,b);//a的值增加了2次可以用内联函数替代:templateclassT//使用模板,巧妙的解决了宏当中变量类型的不确定性inlineconstT&max(constT&a,constT&b){returnab?a:b;}将常量写在==的左边防止出现(只有一个等号的情况),例:if(ptr=NULL),少写一个=则称为赋值操作,隐患不小。尽量使用C++风格的类型转换(cast系列)C风格的类型转换并不代表所有的类型转换功能:一、它们过于粗鲁,能允许你在任何类型之间进行转换。不会区分constànonconst,也不区分父类à子类。二、C风格的类型转换在程序语句中难以识别。在语法上,类型转换由圆括号和标识符组成,而这些可以用在C++中的任何地方,包括函数。对于C风格的类型转换,可以用static_cast来代替。static_cast在功能上基本上与C风格的类型转换一样强大,但也有限制。例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。const_cast用于类型转换掉表达式的const或volatileness属性。dynamic_cast,用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)。但是不能被用于缺乏虚函数的类型上,也不能用它来转换掉constness。reinterpret_cast的转换结果几乎都是执行期定义(implementation-defined),因此的代码很难移植。reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。使用C++的extern'C'禁止名变换名变换,就是C++编译器给程序的每个函数换一个独一无二的名字。在C中,这个过程是不需要的,因为没有函数重载,但几乎所有C++程序都有函数重名。如果只在C++范围内,名变换不会影响你。不必为每一个函数添加extern'C'。extern'C'可以对一组函数生效,只要将它们放入一对大括号中:externC{//disablenamemanglingforallthefollowingfunctionsvoiddrawLine(intx1,inty1,intx2,inty2);voidtwiddleBits(unsignedcharbits);voidsimulate(intiterations);...}使用C++的预定义宏__cplusplus数据结构的兼容性:struct不可能让C的函数了解C++的特性的,它们的交互必须限定在C可表示的概念上。因此,没有可移植的方法来传递对象或传递指向成员函数的指针给C写的函数。但是,C了解普通指针,所以想让你的C++和C编译器生产兼容的输出,两种语言间的函数可以安全地交换指向对象的指针和指向非成员的函数或静态成员函数的指针。自然地,结构和内建类型(如int、char等)的变量也可自由通过。因为C++中的struct的规则兼容了C中的规则,假设“在两类编译器下定义的同一结构将按同样的方式进行处理”是安全的。这样的结构可以在C++和C见安全地来回传递。如果你在C++版本中增加了非虚函数,其内存结构没有改变,所以,只有非虚函数的结构(或类)的对象兼容于它们在C中的孪生版本(其定义只是去掉了这些成员函数的申明)。增加虚函数将gameover,因为其对象将使用一个不同的内存结构。从其它结构(或类)进行继承的结构,通常也改变其内存结构,所以有基类的结构也不能与C函数交互。使用常见的预处理命令和预处理标识符#define宏定义#undef未定义宏#include文本包含#ifdef如果宏被定义就进行编译#ifndef如果宏未被定义就进行编译#endif结束编译块的控制#if表达式非零就对代码进行编译#else作为其他预处理的剩余选项进行编译#elif这是一种#else和#if的组合选项#line改变当前的行数和文件名称#line的语法如下:#linenumberfilename例如:#line30a.h其中,文件名a.h可以省略不写。这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号为30,文件名是a.h。初看起来似乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,我们知道编译器对C++源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件名是固定的,不会被这些中间文件代替,有利于进行分析#error输出一个错误信息#pragma为编译程序提供非常规的控制流信息#progma应该算是一个可有可无的预处理指令,按照C++之父Bjarne的话说,就是:#progma被过分的经常的用于将语言语义的变形隐藏到编译系统里,或者被用于提供带有特殊语义和笨拙语法的语言扩充。”为了处理一些有用的信息,预处理定义了一些预处理标识符,虽然各种编译器的预处理标识符不尽相同,但是他们都会处理下面的4种:__FILE__正在编译的文件的名字__LINE__正在编译的文件的行号__DATE__编译时刻的日期字符串例如:25Dec2000__TIME__编译时刻的时间字符串例如:12:30:55养成使用标准C++语言的习惯自1990年出版以来,《TheAnnotatedC++ReferenceManual》是最权威的参考手册供程序员们判断什么是C++拥有的而什么不是。在它出版后的这些年来,C++的ISO/ANSI标准已经发生了大大小小的变化了(主要是扩充)。作为权威手册,它已经不适用了。在《ARM》之后发生的最主要变化是以下内容:l增加了新的特性:RTTI、命名空间、bool,关键字mutable和explicit,对枚举的重载操作,已及在类的定义中初始化conststatic成员变量。l模板被扩展了:现在允许了成员模板,增加了强迫模板实例化的语法,模板函数允许无类型参数,模板类可以将它们自己作为模板参数。l异常处理被细化了:异常规格申明在编译期被进行更严格的检查,unexpected()函数现在可以抛一个bad_exception对象了。l内存分配函数被改良了:增加了operatornew[]和operatordelete[]函数,operatornew/new[]在内存分配失败时将抛出一个异常,并有一个返回为0(不抛异常)的版本供选择。(见EffectiveC++Item7)l增加了新的类型转换形式:static_cast、dynamic_cast、const_cast,和reinter
本文标题:C++学习笔记
链接地址:https://www.777doc.com/doc-4948321 .html