C/C++中的const是一个修饰符,它允许你指定一个语义上的约束,也就是可以通过const指定一个“不该被改动”的对象,而编译器会强制实施这项约束。
const语法很多 但都不复杂 下面我将分别讲述各种语法及其作用。
在讲述const修饰符用在指针上有什么效果之前先来看下指针不加const修饰符的情况:
int Num1 = 10;
int Num2 = 20;
int* p = &Num1; //将指向int型指针变量p指向Num1
*p = 55; //Good!
p = &Num2; //Good!
以上代码在编译时是没有任何问题的 指针变量p可随意改变所指向的地址 也可以随意改变指针p所指向的数据 此时该指针p是没有约束的。
但是当指针p加上const修饰符进行约束后 又会是怎样的效果呢?看如下代码:
int Num1 = 10;
int Num2 = 20;
//形式1
const int* p = &Num1;
p* = 55; //error!
p = &Num2; //Good!
//形式2
int* const p1 = &Num1;
*p1 = 66; //Good!
p1 = &Num2; //error!
//形式3
const int* const p2 = &Num1;
*p2 = 77; //error!
p2 = &Num2; //error!
如上所示 对指针进行const修饰有3种形式
第1种形式 const在 * 前面 表示的是常量指针 也就是说该指针变量p所存储的地址可以改变 但是地址所处的数据无法更改,而p* = 55;这行代码因为试图更改地址所处的数据而被编译器所禁止 所以编译不通过。而该种形式又有俩种写法:const int* p;和int const *p; 这俩种写法是等价的。
第2种形式 const在 * 后面 表示的是指针常量 也就是说该指针变量p1所存储的地址不能改变 但是地址所处的数据可以更改,而p1 = &Num2;这行代码试图改变p1所存储的地址所以编译器也是不允许的 从而编译不通过。
第3种形式 是前俩种形式的结合 表示该指针变量p2即不可以改变所存储的地址 也不能更改地址所处的数据,所以*p2 = 77;试图改变地址所处的数据和p2 = &Num2;试图改变指针变量所存储的地址 均是编译器不允许的。
另外 const修饰符也可运用在内置类型和用户自定义类型上 如下代码:
int main(void){
const int SIZE = 10; //不可修改
const float f1 = 13.5; //不可修改
const char c = 'A'; //不可修改
int Num[SIZE]; //定义一个int数组 有SIZE(10)个元素
return 0;
}
用内置类型定义变量并且用const进行修饰 则该变量的值不可修改(常变量)而且必须在定义的时候进行初始化。
假设有如下类定义:
class Base{
private:
static const int SIZE = 5;
int NumArr[SIZE];
const int ID;
public:
Base(int id_t);
void show()const;
void setValue(int index_t,int value_t);
};
Base::Base(int id_t):ID(id_t){ //ID为常变量 需要在成员初始化列表中进行初始化 注:成员初始化列表只能在构造函数中使用
for(int i=0;i<SIZE;++i){
NumArr[i] = 0;
}
}
void Base::show()const{
std::cout << "ID:" << ID << std::endl;
for(int i=0;i<SIZE;++i){
std::cout << NumArr[i] << " ";
}
std::cout << std::endl;
}
void Base::setValue(int index_t,int value_t){
NumArr[index_t] = value_t;
}
如上代码void Base::show()const; 将const修饰符用在成员函数身上,将约束该成员函数,规定其不允许对当前调用对象进行修改,也就是只能读取该对象的数据但不能修改,并且当对成员函数进行const修饰后,该成员函数将不允许调用非const修饰的成员函数,也就是说 在当前代码中 show()的实现体不能对成员函数setValue()进行调用,因为它没有经过const修饰,所以可能会对调用对象进行修改,而show()的意义就是不允许修改对象,这将引起矛盾,所以编译器是绝对禁止该调用行为的。但是setValue()函数可以内部调用show()函数。const成员函数还有一个作用就是使const对象成为可能,这将在下面讲述。
假设有如下代码:
Base s1(1001);
s1.show(); //Good!
s1.setValue(1,6); //Good!
s1.show(); //Good!
const Base s2(1002);
s2.show(); //Good!
s2.setValue(1,8); //error!
s1为非const对象 所以调用该对象的任何成员函数都是合法的 并且setValue()是非const成员函数所以也可以对该对象进行修改
s2是加了const修饰符的对象 所以该对象是不允许修改的 而且只能调用const成员函数 非const成员函数不允许调用,因为上面说了,对象本身是不允许修改的,而const成员函数本身也进行约束了不修改调用对象,所以符合const对象要求,但是非const成员函数,因为他没有加const进行修饰 所以可以对调用对象进行修改 这将违背const对象的意愿,编译器是不允许这么做的。
然后 上面代码中出现的 static const int SIZE = 5;这是在类中定义常量的一种方式,还有一种方式是通过enum定义枚举常量,static也是一种修饰符。
static const 和enum定义的常量一个主要区别就是 static const 是分配存储空间的,而enum定义的不分配,所以static const常量是可以对其进行取地址的,而enum则不允许!
在某些东西声明为const还可以帮助编译器进行侦测错误用法 在这里就不挨个讲述了。
写这篇文章主要是为了整理好后帮助记忆,因为const有很多种用法,稍有不慎就可能导致错误,当然掌握好了const的用法也会对编程有很大的帮助,还有就是为了帮助其他对const不是很了解的人,最后如果文章中写的有哪些问题的话,也麻烦指出来,共同进步,非常感谢!