使用const的好处在于它允许指定一种语意上的约束------某种对象不能被修改--------编译器具体来实施这种约束。
一、const用于定义常量。
例如:const int N = 100;const int M = 200;
这样程序中只要用到 N、M就分别代表为整型100、200,N、M为一常量,在程序中不可改变。
用const定义常量不仅能方便我们编程而且能提高程序的清晰性。
二、const修饰函数形式参数
形式参数有输入形式参数和输出形式参数。参数用于输出时不能加const修饰,那样会使函数失去输出功能。因为const修饰的东西是不能改变的。
const只能用于修饰输入参数。
C++函数的三种传递方式为:值传递、指针传递和引用传递。
值传递 | 指针传递 | 引用传递 |
void fun(int x) { //修改实参在栈中副本 x += 5; } | void fun(int *x) { //修改x指向的内存单元值 } | void fun(int &x) { //修改的是x引用的对象值 } |
2. 当输入参数为ADT/UDT (用户自定义类型和抽象数据类型)时,应该将" 值传递" 改为"const & 传递" ,目的可以提高效率。
例如:
void fun(A a);// 效率底。 函数体内产生A 类型的临时对象用于复制参数 a 。 构造、复制、析构过程都将消耗时间
void fun(A const &a);//提高效率。 用" 引用传递" 不需要产生临时对象 。 引用有可能改变a, 所以加const
3. 当输入参数用"指针传递"方式时,加const修饰可防止意外修改指针指向的内存单元,起到保护作用。
例如:
void funstrcopy(char *strdest,const char *strsrc)
任何改变strsrc指向的内存单元,编译器都将报错。保护了指针的内存单元,也可以保护指针本身,防止其地址改变。
例如:
void funstrcopy(char *strdest,const char *const strsrc)
三、const修饰函数的返回值
如给"指针传递"的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰的同类型指针。
例如:
const char *GetChar(void){};
char *ch = GetChar();//错误
const char *ch = GetChar();//正确
四、 const修饰类的成员函数(函数定义体)
任何不会修改数据成员的函数都应用const修饰,这样当不小心修改了数据成员或调用了非const成员函数时,编译器都会报错。
const修饰类的成员函数形式为:
int GetCount(void)const;
五、const与指针的关系:
1. const char *p="hello";
非const指针,const数据,就是说p指向的那个内存空间的数据是不可变的,但p还可以指向新的内存地址。
2. char *const p="hello"; ?à char const *p="hello";
const指针,非const数据,就是说这个指针p一旦赋值或初始化,就不能在指向其他位置了,但其指向的位置的数据值是可变的。
3. const char *const p="hello";
const指针, const数据,这个就很明显了,上述两者都不可变。
六、 在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数
const1 int fun(int const2 &)const3 {}
const属性(1)是保证函数的返回值不被修改,也许你会质疑这种可能性,但是这种可能性确实存在,
例如:
const rational operator *(const rational &lhs, const rational &rhs);
为什么operator*的返回结果是一个const对象?因为如果不是这样,用户就可以做下面这样的坏事:
rational a, b, c;
...
(a*b)=c;//对a*b的结果赋值
如果a,b和c是固定类型,这样做显然是不合法的。一个好的用户自定义类型的特征是,它会避免那种没道理的与固定类型不兼容的行为。对两个数的运算结果赋值是非常没道理的。声明operator*的返回值为const可以防止这种情况,所以这样做才是正确的。
const属性(2)一般用引用传递,是为了保证该参数在函数中不允许被修改,一旦修改,编译器会报错。
const属性(3)一般用于成员函数了,它有以下属性:
(1)const成员函数不被允许修改它所在对象的任何一个数据成员。
(2)const成员函数能够访问对象的const成员,而其他成员函数不可以。