《C++-const-精髓.docx》由会员分享,可在线阅读,更多相关《C++-const-精髓.docx(27页珍藏版)》请在第一文库网上搜索。
1、1. const的用法:看到COnSt关键字,C+程序员首先想到的可能是COnSt常量。这可不是良好的条件反射。如果只知道用const定义常量,那么相当于把火药仅用于制作鞭炮。const更大的魅力是它可以修饰函数的参数、返回值,甚至函数的定义体。const是constant的缩写,“恒定不变”的意思。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。所以很多C+程序设计书籍建议:Useconstwheneveryouneed1 .用COnSt修饰函数的参数如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加ConSt修饰,否则该
2、参数将失去输出功能。COiISt只能修饰输入参数:如果输入参数采用“指针传递”,那么加COnSt修饰可以防止意外地改动该指针,起到保护作用。例如StringCopy函数:voidStringCopy(char31cStrDestination,constchar*strSource);其中StrSOUrCe是输入参数,StrDestination是输出参数。给StrSOUrCe加上ConSt修饰后,如果函数体内的语句试图改动StrSOUrCe的内容,编译器将指出错误。如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加ConSt修饰。例如不
3、要将函数voidFunc1(intx)写成voidFunc!(constintx)。同理不要将函数voidFunc2(Aa)写成voidFunc2(constAa)其中A为用户自定义的数据类型。对于非内部数据类型的参数而言,象VOidFunc(Aa)这样声明的函数注定效率比较底。因为函数体内将产生A类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为VoidF1InC(A&a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数VoidFUnC(A&a)存在一个缺点:“引用传递”有可能改变参数a,这是我们不期望的。解决
4、这个问题很容易,加COiISt修饰即可,因此函数最终成为voidFunc(constA&a)以此类推,是否应将voidFunc(intx)改写为voidFunc(constint&x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。问题是如此的缠绵,我只好将“const&”修饰输入参数的用法总结一下。对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将voidFunc(Aa)改为voidFunc(constA&a)时于内部数据类型的输入参数,不要将“值传递”的方
5、式改为const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如VoidFUnC(intx)不应该改为VOidFunc(constint&x)。2 .用COnSt修饰函数的返回值如果给以“指针传递”方式的函数返回值加ConSi修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加COrm修饰的同类型指针。例如函数constchar*GetString(Void);如下语句将出现编译错误:char*str=GetStringO;正确的用法是constchar*str=GetStringO;如果函数返回值采用“金传递方式”,由于函数会把返回值复制到外部临时的存储单
6、元中,加const修饰没有任何价值。例如不要把函数intGet1nt(Void)写成constintGetInt(Void)o同理不要把函数AGetA(Void)写成ConStAGetA(VOid),其中A为用户自定义的数据类型。如果返回值不是内部数据类型,将函数AGetA(Void)改写为constA&GetA(VOid)的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了,否则程序会出错。函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。例如:c1assA(A&operate=(co
7、nstA&other);/赋值函数1;Aa,b,c;/a,b,c为A的对象a=b=c;/正常的链式赋值(a=b)=c;不正常的链式赋值,但合法如果将赋值函数的返回值加ConSt修饰,那么该返回值的内容不允许被改动。上例中,语句a=b=c仍然正确,但是语句(a=b)=c则是非法的。3 .const成员函数任何不会修改数据成员(即函数中的变量)的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非COnS1成员函数,编译器将指出错误,这无疑会提高程序的健壮性。以下程序中,类StaCk的成员函数GetCoUnt仅用于计数,从逻辑上讲GetCOUnt应当
8、为COnSt函数。编译器将指出GetCOUnt函数中的错误。c1assStack(pub1ic:voidPush(inte1em);intPop(void);intGetCount(Void)const;/const成员函数private:intm_num;intm-data100;1 ;intStack:GetCount(void)const(+m_num;/编译错误,企图修改数据成员m_numPopO;/编译错误,企图调用非COnSt函数returnm_num;)const成员函数的声明看起来怪怪的:Co1ISt关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。关于COnSt
9、函数的几点规则:a. const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.b. const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.c. const成员函数不可以修改对象的数据,不管对象是否具有ConSt性质.它在编译时,以是否修改成员数据为依据,进行检查.e.然而加上mutab1e修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的为什么使用const?采用符号常量写出的代码更容易维护;指针常常是边读边移动,而不是边写边移动;许多函数参数是只读不写的。COnS
10、t最常见用途是作为数组的界和SWitCh分情况标号(也可以用枚举符代替)用法1:常量取代了C中的宏定义,声明时必须进行初始化。const限制了常量的使用方式,并没有描述常量应该如何分配。如果编译器知道了某ConSt的所有使用,它甚至可以不为该COnSt分配空间。最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。一C+PrOgram1angUage用const声明的变量虽然增加了分配空间,但是可以保证类型安全。C标准中,const定义的常量是全局的,C+中视声明位置而定。用法2:指针和常量使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用ConSt预先固定”将使
11、那个对象而不是使这个指针成为常量。要将指针本身而不是被指对象声明为常量,必须使用声明运算符*consto所以出现在*之前的ConSt是作为基础类型的一部分:char*constcp;到Char的ConSt指针charconst*pc1;/到COnStChar的指针constchar*pc2;到ConStChar的指针(后两个声明是等同的)从右向左读的记忆方式:cpisaconstpointertochar.pc2isapointertoconstchar.用法3:COnSt修饰函数传入参数将函数传入参数声明为ConSt,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值
12、。同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。通常修饰指针参数和引用参数:voidFun(constA*in);修饰指针型传入参数voidFun(constA&in);/修饰引用型传入参数用法4:修饰函数返回值可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。用法5:ConSt修饰成员函数const对象只能访问ConSt成员函数,而非const对象可以访问任意的成员函数,包括Co1ISt成员函数;const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的:ConSt成员函数不可以修改对象的数据,不管对象是否具有COnSt性质。编译时以是否修改成员数
13、据为依据进行检查。Const深度解析我们也许学习过ConSt的使用,但是对于COnSt的细致的技术细节却不一定掌握。const的用法在许多的教材上只是简单的介绍,在这里我们对ConSt进行细致的概念以及用法剖析。const是由c+采用,并加进标准C中,但是他们的意义完全不同,在旧版本(标准前)的C中,如果想建立一个常量,必须使用预处理器:definePI3.14159此后无论在何处使用PI,都会被预处理器以3.14159替代。编译器不对P1进行类型检查,也就是说可以不受限制的建立宏并用它来替代值,如果使用不慎,很可能由预处理引入错误,这些错误往往很难发现。我们也不能得到PT的地址(即不能向P1
14、传递指针和引用)。c+引入了命名常量的概念,命名常量就像变量一样,只是它的值不能改变,如果试图改变一个const对象,编译器将会产生错误。const和正常变量一样有作用域,所以函数内部的ConSt也不会影响程序的其余部分。在c+中ConSt可以取代预处理器#define来进行值替代,const有安全的类型检查,所以不用担心会像预处理器样引入错误。在通常的情况下const同预处理器枇Iefine一样只是将所赋值保存入编译器的符号表中(符号表仅仅在编译时存在,在编译过程中编译器将程序中的名字与之在符号表中定义的数值作简单的替换),在使用的时候进行值替换,并不为COnSt创建存储空间。我们将ConS
15、t的定义放进头文件里,这样通过包含头文件,可以把ConSt定义单独放在一个地方并把它分配给一个编译单元,const默认为内部连接(内部连接意味着只对正在编译的文件创建存储空间,别的文件可以使用相同的标示符和全局变量,编译器不会发现冲突,外部连接意味着为所有被编译过的文件创建一片单独的存储空间,一般全局变量和函数名的外部连接通过extern声明,可以通过其他的文件访问)也就是说COnSt仅能被它所定义过的文件访问,在定义一个COnSt时,必须赋一个值给它,除非用extern做出说明:externconstinta;这表示ConSt的定义在其他的什么地方,这里仅仅是一个声明,但是这样的做法使const使用了外部连接,也就是说上面的extern强制进行了对COnSt的存储空间分配,这样我们就无法再用COnSt作为常量折叠(在可能的情况下,符号常量的值会代替改名字的出现,这个替代过程叫做常量折叠)使用了,即使我们在其他地方定义了const的值,如:externconstinta=3;因为COnSt的值被放入了存储单元,在编译的过程中,编译器不会去读存储单元的内容。如果我们这样做:intba;编译器就会给我们个错误信息。想不为COnSt分配存储空间是不可能的,因为对于杂的结构,例如集合,编译器不会复杂到将集合保存到它的符号表中,所以必须分