C++中的const意味着“只读”,合理的使用const将大大改善程序的健壮性:
1)声明一个参数为常量是可以向用户传达有用的信息,告知这个参数的应用目的。在规模稍大一点的软件开发中,可以减少阅读程序的人的困惑,避免其他接手工作的程序员花时间清理无用的垃圾。懂得用const的程序员很少会留下的垃圾让别人来清理。
2)const关键字通过给优化器一些附加的信息,产生更紧凑的代码。
3)合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改,减少bug的出现。
1,const的基本概念
请看下面代码段:
const
int
a;
int const a;
const int * a;
int * const a;
int const * a const ;
int const a;
const int * a;
int * const a;
int const * a const ;
如果关键字不涉及指针,很好理解。前两个其实一个意思,a是一个常整型数。
后面三句出现了指针,就有点意思了。参考《Effective C++》中所述,若const在”*”的左边,则const修饰指针所指向的变量;若const在”*”的右边,const就是修饰指针本身,即这个指针是个常指针。因此,第三句意味着a是一个指向常整型数的指针(整型数是不可修改的,但指针可以指向其他地址)。第四个意思a是一个指向整型数的常指针(即指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(即指针指向的整型数是不可修改的,同时指针也是不可修改的)。
const还可修饰函数的参数和返回值;对于类,const可以修饰成员变量和成员函数(const成员变量通常由const成员函数访问)。偶就不一一举例了。
2,定义const变量
不涉及指针的const变量的定义很简单,没什么陷阱。因此我重点总结一下涉及指针和引用的const变量定义中需要注意的问题。
例1:
int
a
=
10
;
const int * p = & a;
int * q = p;
const int * p = & a;
int * q = p;
但是,请注意,a本身并不是const,并且上例中的q也不是指向const。因此,通过a本身或指针q,都可以修改a的值。这下p这个指向const变量的指针就彻底变成了个废物。
例2:
int
*
const
p
=
new
int
;
int * q = p;
int * q = p;
例2是一种正确的使用方法,虽然p是一个const指针,但是该指针所指向的变量是可以改变的,因此非const指针q指向p,通过q来修改p所指向的值并无不妥。
例3:
A a;
const A & aa = a;
const A & aa = a;
例3中,aa被定义成对常量的引用,因此,aa只能访问声明为const的成员函数,不能访问其他成员函数。
3,const参数和返回值
参数const通常用于参数为指针或引用的情况。一般,如果函数的形参是指针或引用,我们可以在函数体内对指针指向的对象或引用的对象进行修改。因此,如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const修饰,否则该参数将失去输出功能。
如果参数不作输出用,则可以用const修饰参数。对于const参数,在函数体中按照const所修饰的部分进行常量化,如形参为const A* a,则不能对传递进来的指针的内容进行改变;如形参为const A& a,则不能对传递进来的引用对象进行改变。
林博士在《高质量C++/C编程指南》中给我们的建议:对于非内部数据类型(例如用户自定义的类)的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率;对于内部数据类型(int,short,long,char等等)的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。
对于const的函数返回值,通常用于二目操作符重载函数并产生新对象时,起相应的保护作用。例如:
const
Rational operator
*
(
const
Rational
&
lhs,
const
Rational
&
rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
Rational a,b;
Radional c;
(a * b) = c;
Radional c;
(a * b) = c;
但是通常不建议将返回值设定为const。因为如果返回值为某个const对象或const引用,则返回的实例只能访问类的public数据成员和const的成员函数,这样生成的新的对象似乎没有什么作用。
再看看例4:
const
A
&
operator
=
(
const
A
&
a);
如果连续赋值,就会出现问题:
A a,b,c;
(a = b) = c;
(a = b) = c;
4,const成员函数
任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。