const的作用
-
定义变量(局部变量或全局变量)为常量,常量值不能被修改。且常量在定义时必须初始化
-
修饰函数的参数,表示在函数体类不能修改这个参数的值
-
修改函数的返回值
1)如果const修饰的返回值为指针,那么函数返回值(即指针)的内容是不能被修改的,而且这个返回值只能赋给被const修饰的指针。例如:
const char * GetString(); //定义一个函数
char *str = GetString(); //错误,因为str没有被const修饰
const char *str = GetString(); //正确
2)如果const修饰的返回值为引用,那么这个函数调用表达式不能被作为左值使用。例如:
const int & minus(int &i, int &j); //定义函数的返回值为const引用
minus(a, b) = 4; //错误,因为minus的返回值不能作为左值
3)如果用const修饰普通的返回值,由于这个返回值是一个临时变量,在函数调用结束后这个临时变量的生命周期就结束了,因此把这些返回值修饰为const是没有意义的。
- 修饰类的成员变量。表示这个成员变量只在某个对象生存周期内是常量,而对于整个类而言,却是可变的。因为一个类可以创建多个对象,不同的对象其const成员变量可以有不同的值。因此,const成员变量不能在类声明中初始化。例如:
class A
{
const int size; //正确
const int height = 170; //错误,const成员变量不能在类声明中初始化
};
- 修饰类的成员函数。那么这个函数就不能修改对象的成员变量
使用const的优点
-
进行类型检查,使编译器对处理内容有更多的了解,消除了一些隐患。例如,
void f(const int n){}
,编译器就会知道n是一个常量,不允许被修改。 -
避免意义模糊的数字出现,方便进行参数的调整和修改。同宏定义一样,可以做到不变则已,一变都变。
-
保护被修饰的东西,防止被意外修改,增强了程序的健壮性。
-
为函数重载提供参考。例如:
class A
{
void f(int i){} //定义一个函数
void f(int i) const {} //重载函数f,对象被定义为const时调用这个函数
};
通常,只有const对象才调用const方法;而非const对象会调用非const方法。
- 节省空间,避免不必要的内存分配。
从汇编的角度看,const定义常量知识给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一个复制品,而#define定义的常量在内存中有若干个复制品。
- 提高了程序的效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储和读内存的操作,它的效率也很高。
什么情况下需要使用const关键字
-
修饰一般变量。一般变量指简单类型的常量。这种常量在定义时,修饰符const可以在类型说明符前,也可以用在类型说明符后。例如:
int const x = 2;
或const int x = 2;
-
修饰常数组。定义或说明一个常数组可以采用如下格式:
int const a[3] = {1, 2, 3};
const int a[3] = {1, 2, 3};
- 修饰常对象。常对象是指对象常量,定义格式如下:
class A;
const A a;
A const a;
定义常对象时,同样要进行初始化,并且该对象不能再被更新。
- 修饰常指针。
const int *p; //const修饰int,说明p可变,p指向的int变量的值不可变
int const *p; //const修饰*p(*p也是int变量)。p可变,p指向的int变量的值不可变
int *const p; //const修饰指针p,p的指向不可变,p指向的对象可变
const int *const p; //指针p的指向和p指向的对象都不可变
-
修饰常引用。被const修饰的引用变量为常引用,一旦被初始化,就不能再指向其他对象了。
-
修饰函数的常参数。
-
修饰函数的返回值。表明该返回值不可被改变。
-
修饰类的成员变量。
-
在另一连接文件中引用const常量。使用方式:
extern const int i; //正确
extern const int j = 10; //错误,因为常量不可以被再次赋值。
常引用与普通引用有什么区别
一般引用的初始化表达式必须是左值,而常引用初始化表达式可以是右值。例如:
int& Ref = 10; //错误,因为10不是左值
const int& Ref = 10; //正确
C++和C语言的编译器在处理const的时候有什么不同
C++
C++中使用const定义了一个常量后,并不会为这个常量分配空间,而是将这个常量值写入符号表中,这使得它成为一个编译期间的常量,没有存储与读内存的操作。在编译过程中若发现使用常量,则直接以符号表中的值替换;为了兼容C语言,编译过程中若发现对const使用了extern或者&操作符,则给对应的常量另外分配存储空间。
但是,当使用&去取这个常量的地址时,编译器会分配一个空间给这个变量,但之后这个变量的值仍旧从符号表中读取,不管这个变量的存储空间中的值如何变化,都不会对这个常量的值产生影响。例如:
const int n = 10;
int* p = (int*)(&n);
*p = 20;
cout << n << endl; //输出10,符号表中的值,这个值在程序运行的过程中保持不变
cout << *p << endl; //输出20,读取为n分配的一个临时存储空间中的值,这个值可以被修改
C语言
C语言编译器则不同,当使用const定义一个常量的时候,编译器会直接开辟一个内存空间来存储该常量,每次存取的时候都是从这个内存空间中存取,因此可以取得const变量的地址,然后用这个地址来间接地修改const变量。例如:
const int n = 10;
int* p = (int*)(&n);
*p = 20; //间接修改了const变量n的值
printf("%d\n", n); //输出20
printf("%d\n", *p); //输出20
正因为如此,在C语言中如果使用下面的方式来定义数组,那么编译器会报错。
const int n = 10;
int arr[n]; //gcc编译器会认为n是一个变量,而数组的长度必须是常量,因此编译器会报错