C和C++const的区别及尽可能使用const

 const是一个C语言的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一定帮助

用法
  C中const修饰的变量称作常变量,处理机制是在编译阶段,判断这个常变量是否作为左值,如果作为左值则会报错,除过这个其他处理和变量是一样的。例如下面代码,只有a=20;这一语句会报错,其余语句都是正确的。

int main()
{
const int a;
a = 20;//报错
int *p =&a;
*p = 20;
printf("a:%d\n",a);
return 0;
}
C++        const 限定符,它把一个对象转换成一个常量。

用法
  C++中const修饰的变量称为常量,即可以将修饰的变量作为数组长度(int arr[a];)处理机制是在编译阶段,把用到常量的地方替换成了常量初始化的数值。

注意事项
一定要初始化,如下代码我们通过调试会发现在执行完*p=20;这一行后会显示a的值为20,
但是在我们真正执行后会打印出a:10

会出现这样的原因是因为在C++中被const修饰的变量在编译阶段已经将所有用到a的地方替换为常量10,所以在运行阶段真正运行的是cout<<“a:”<<10<<endl;这一语句,因为我们要在编译阶段将用到的变量进行替换所以一定要进行初始化。
int main()
{
const int a = 10;
int *p = (int*)&a;
*p = 20;
cout<<"a:"<<a<<endl;//cout<<"a:"<<10<<endl;
}

杜绝间接访问来修改常量内存的风险
int main()
{
const int a = 10;
//int *p = &a;//报错,杜绝风险
const int *p = &a;//改为这样便正确
*p = 20;
cout<<"a:"<<a<<endl;
}

进行分析可知const修饰a,则直接访问为a,间接访问a都为*p,所以为了杜绝访问修改常量内存的风险,在 *p前面加上const即可。

判断const用法的正确与否
根据大小权限的判断,如下代码我们可以发现&a类型为int *,p类型为int *const,这是其中int *权限大于int *const权限也就是我们所说的权限缩小,所以这行代码是正确的。接下来q的类型为int *这是用大权限指向小权限也就是我们常说的权限扩大,这是不允许的,按道理说这行代码会出错。但是,最终结果并没有报错。
int main
{
int a = 10;
int *const p = &a;
cout<<typeid(p).name()<<endl;
int *q = p;
}

其实如果我们用C++中const中的第2个注意事项来判断会更加合理些,即杜绝间接访问来修改常量内存的风险,我总结的分析步骤如下:

找到const修饰的对象,然后从这行代码往下开始逐句分析。
判断const修饰对象的直接访问量。
判断对直接访问量的间接访问量。
如果间接访问量没有加const修饰我们加上const关键字即可。
接下来我们来分析一下上面的这道例题。

const修饰对象为p
直接访问量为p
间接访问量无。因为p为一级指针所以其间接访问量至少要达到二级指针。
通过以上分析我们可以发现并无间接访问来修改常量内存的风险,所以说代码是正确的。

为了加深印象我们再分析一道例题

int main
{
int a = 10;
int *p = &a;
const int ** q = &p;
//改为const int *const* q = &p或者
//int const* p = &a;
}

const修饰对象为**q
直接访问量为a
由于下面没有代码了所以很多人以外没有间接访问量了,其实不然。当前行还包含了一个隐藏的间接访问量*q。

  我们通过模拟内存指向可以发现*q=&a,即 *q间接访问了a,所以我们在 *q前面加上const关键字即可。而且我们可以发现 *q=&a与上一行的int *p=&a是一样的所以在 *p前加上const也是可以的。

 C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的。如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助。

1.const 修饰成员变量 

复制代码
 1 #include<iostream>
 2 using namespace std;
 3 int main(){
 4     int a1=3;   ///non-const data
 5     const int a2=a1;    ///const data
 6 
 7     int * a3 = &a1;   ///non-const data,non-const pointer
 8     const int * a4 = &a1;   ///const data,non-const pointer
 9     int * const a5 = &a1;   ///non-const data,const pointer
10     int const * const a6 = &a1;   ///const data,const pointer
11     const int * const a7 = &a1;   ///const data,const pointer
12 
13     return 0;
14 }
复制代码

const修饰指针变量时:

  (1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。

  (2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。

  (3)两个const,*左右各一个,表示指针和指针所指数据都不能修改。

2.const修饰函数参数

  传递过来的参数在函数内不可以改变,与上面修饰变量时的性质一样。

void testModifyConst(const int _x) {
     _x=5;   ///编译出错
}

3.const修饰成员函数

(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)

(2)const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 class Point{
 4     public :
 5     Point(int _x):x(_x){}
 6 
 7     void testConstFunction(int _x) const{
 8 
 9         ///错误,在const成员函数中,不能修改任何类成员变量
10         x=_x;
11 
12         ///错误,const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量
13         modify_x(_x);
14     }
15 
16     void modify_x(int _x){
17         x=_x;
18     }
19 
20     int x;
21 };
复制代码

 4.const修饰函数返回值

(1)指针传递

如果返回const data,non-const pointer,返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。

复制代码
 1 const int * mallocA(){  ///const data,non-const pointer
 2     int *a=new int(2);
 3     return a;
 4 }
 5 
 6 int main()
 7 {
 8     const int *a = mallocA();
 9     ///int *b = mallocA();  ///编译错误
10     return 0;
11 }
复制代码

(2)值传递

 如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。所以,对于值传递来说,加const没有太多意义。

所以:

  不要把函数int GetInt(void) 写成const int GetInt(void)。
  不要把函数A GetA(void) 写成const A GetA(void),其中A 为用户自定义的数据类型。

 

  在编程中要尽可能多的使用const,这样可以获得编译器的帮助,以便写出健壮性的代码。

posted @ 2021-06-02 18:05  konglingbin  阅读(163)  评论(0编辑  收藏  举报