C/C++知识点:总结关于const知识点
Vivian 2018-06-19 来源 : 阅读 1203 评论 0

摘要:本文主要向大家介绍了C/C++知识点的总结关于const知识点,通过具体的代码向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点的总结关于const知识点,通过具体的代码向大家展示,希望对大家学习C/C++知识点有所帮助。

1、基本描述

定义变量的限定符,表示变量值不可改变

[cpp] view plain copy print?
1. const int a = 1024;  
2. a = 1024;//错误,不能向const对象写值

注意:const对象必须初始化。由于const创建后不可更改,所以必须初始化。(定义一个不可修改的默认值完全无意义)

[cpp] view plain copy print?
1. const int i = get_size();//正确  
2. const int j = 1024;//正确  
3. const int k;//错误,未初始化。

使用值传递初始化时,被初始化对象是否为const与初始化对象是否为const无关。

[cpp] view plain copy print?
1. int i = 0;  
2. const int j = i;//正确  
3. const int k = 1;  
4. const int m = k;//正确

 

2、const初始化引用时的例外

由于C++规定引用的类型必须与被引用的对象一致

[cpp] view plain copy print?
1. int i = 1;  
2. float &j = i;//错误,引用类型与对象类型不一样


同时引用必须绑定到左值

[cpp] view plain copy print?
1. int &i = 1;//错误,不允许用右值初始化  
2. int &j = a * 1; //错误,不允许用表达式初始化


但是我们发现const初始化引用时候会例外

(1)const引用类型与对象类型不一致(但可以转化)

[cpp] view plain copy print?
1. int i = 1;  
2. const double &j = i;//正确,j是常量引用  
3. float k = 1.0;  
4. const int &m = k;//正确,m是常量引用

 

(2)const引用绑定到一个非左值上(类型一致可以转化)

[cpp] view plain copy print?
1. const int &i = 1;//正确,i是常量引用  
2. const int &j = a * 1;//正确,j是常量引用

 

原因在于const引用会将额外创建一个临时变量,并且绑定上去

C++支持这种做法的目的在于,既然不能通过const引用修改对象值,那么额外创建一个常量和直接绑定对象并没有什么区别,所以干脆让const引用支持这种非常规做法。

3、顶层const和底层const

对于一般的变量来说,其实没有顶层const和底层const的区别,而只有像指针/引用这类复合类型的基本变量,才有这样的区别。

(1)const与指针

指针本身就是一个独立的对象,它又可以指向另外一个对象。

所以const和指针在一起使用,有两种情况,修饰前面与修饰后面。

[cpp] view plain copy print?
1. int i = 0;  
2. //指针j指向i,const修饰j指向的i,所以j的地址值可以修改,但是不能通过j修改i的值  
3. const int *j = &i;//底层const  
4. //k指针指向i,const修饰指针k本身,所以k的地址不允许修改,但是可以通过k修改i的值  
5. int *const k = &i;//顶层const


修饰指针k本身的const称之为顶层const,修饰j所指向变量i的const称之为底层const。底层const与顶层const是两个互相独立的修饰符,互不影响。

(2)const与引用

由于引用一但初始化,就不能再修改(绑定),所以引用本身就有“const”的性质。与指针相比,引用相当于内置了顶层的const。所以使用引用的时候就只需考虑是否为底层const。

[cpp] view plain copy print?
1. int i = 1;  
2. const int &j = i;//j为绑定到i的const引用,不允许使用j来修改i

(3)其他

3.1可以将底层const的指针(或引用)指向(或绑定)到非const对象,但不允许非底层const的指针(或引用)指向(或绑定)到const对象。 (即:const对象不允许通过任何方式(指针/引用)被修改。)

3.2修饰值本身的const均为顶层const:

4、const与函数

(1)值传递的const形参

[cpp] view plain copy print?
1. void function(const int i)  
2. {}

这个函数中,变量i为值传递形参,根据值传递的初始化规则,形参i是否为const与传入的实参是否为const是完全无关的。这里的const仅表示i在函数体中不允许修改。
如下的调用均为合法调用:

[cpp] view plain copy print?
1. int x = 0;  
2. function(x);  
3. const int y = 0;  
4. function(y);

因为值传递的const形参在调用上与非const形参没有区别,所以仅仅使用const无法区分参数类别,所以无法实现函数重载,如下的重载是错误的:

[cpp] view plain copy print?
1. void function(const int i) { /* ... */ }  
2. void function(int i) { /* ... */ } // 错误:重复定义函数,不能实现重载

(2)const指针/引用的形参

对于顶层const的指针,其const性质与实参无关,顶层const仅表示指针/引用本身在函数体中不允许修改。

所以我们只需要讨论底层const的指针/引用。

[cpp] view plain copy print?
1. void function(const int &x) { /* ... */ }   
2. // 接受const或非const的int引用,但是不允许通过x修改传入的对象  
3. void function(const int *y) { /* ... */ }   
4. // 接受const或非const的int指针,但是不允许通过y修改传入的对象

如下的调用都是合法的:

[cpp] view plain copy print?
1. int i = 0;  
2. function(i); // 正确:调用第一个函数  
3. function(&i); // 正确:调用第二个函数  
4.   
5. const int j = 0;  
6. function(j); // 正确:调用第一个函数  
7. function(&j); // 正确:调用第二个函数

由于底层const描述实参性质,可以在调用时区分const,所以使用底层const的指针/引用可以实现函数重载:

[cpp] view plain copy print?
1. void function(int &x) { /* ... */ }   
2. void function(const int &x) { /* ... */ } // 新函数,作用于const的引用

所以可以分别调用两个函数:

[cpp] view plain copy print?
1. int i = 0;  
2. function(i); // 正确:调用第一个函数  
3.   
4. const int j = 0;  
5. function(j); // 正确:调用第二个函数

当传递非常量对象时,编译器会优先调用非常量版本的函数。

(3)总结

· 顶层const的形式参数不能实现函数重载,但底层const形参可以

· 当函数不修改参数值时,尽可能将形式参数定义为(底层)const参数,因为(底层)const参数可以接受常量与非常量对象,但非(底层)const参数只能接受非常量对象。

5、const与类

(1)const与类的成员变量

一个类通常包含成员函数与成员变量,对象的const修饰表示该对象的成员变量不允许被修改:

[cpp] view plain copy print?
1. class Data  
2. {  
3. public:  
4.     int Year = 0;  
5. };  
6.   
7. int main()  
8. {  
9.     const Data n;  
10.     n.Year = 1; // 错误,n为const对象,不允许被修改  
11.     return 0;  
12. }

无论类的成员变量本身是否为const,只要对象声明为const,均不允许被修改。

(2)const与类的成员函数

当对象被声明为const时,该对象不能调用非const函数:

[cpp] view plain copy print?
1. class Data  
2. {  
3. public:  
4.     void get_year(int year) { Year = year; }  
5.     int get() { return Year; }  
6.   
7.     int Year = 0;  
8. };  
9.   
10. int main()  
11. {  
12.     const Data n;  
13.     n.get_year(1); // 错误,n为const对象,不能调用非const函数  
14.     cout << n.get() << endl; // 错误,原因同上  
15.     return 0;  
16. }

const对象不能调用非const函数的原因显而易见:非const函数可能修改成员变量。
将成员函数声明为const函数,则可以被const函数调用,声明const函数的方法为在其参数列表后添加const关键字:

[cpp] view plain copy print?
1. class Data  
2. {  
3. public:  
4.     void set(int year) const { Year = year; } // 错误:const函数不允许修改成员变量  
5.     int get() const { return year; } // 正确:函数没有修改成员变量,被声明为const函数  
6.   
7.     int Year = 0;  
8. };  
9.   
10. int main()  
11. {  
12.     const Data n;  
13.     n.set(1); // 错误,const函数不允许修改成员变量  
14.     cout << n.get() << endl; // 正确,const对象可以调用const函数  
15.     return 0;  
16. }

并非所有成员函数都可以被声明为const函数,C++会在编译时检查被声明为const的函数是否修改了成员变量,若是,则报错,编译不通过。

与底层const形参一样,const成员函数也可以实现重载:

[cpp] view plain copy print?
1. class T  
2. {  
3. public:  
4.     int function() { return 1; }  
5.     int function() const { return 2; } // 正确:定义了可以重载的新函数  
6. };  
7.   
8. int main()  
9. {  
10.     T t1;  
11.     cout << t1.function() << endl; // 调用第一个函数,输出"1"  
12.   
13.     const T t2;  
14.     cout << t2.function() << endl; // 调用第二个函数,输出"2"  
15.     return 0;  
16. }

同样,当非常量对象调用函数时,编译器会优先调用非常量版本的函数。

(3)总结

· 当函数不修改成员变量时,尽可能的将函数声明为const函数,因为const函数可以被非const对象和const对象调用,而非const函数只能被非const对象调用。

· const函数并不意味着数据安全,虽然不能通过const函数修改成员变量,但是这样的const仅为顶层const,若成员变量包含非底层const的指针/引用,则依然可以通过这些指针/引用修改其指向/绑定的对象。

(4)const成员函数实现机制

一个类包含成员变量和成员函数,更简单一点,一个类包含数据和代码。对象是类的实例,一个类可以构造许多对象,对象们的数据(成员变量)各自独立,而代码(成员函数)共用一份。

通常,我们调用成员函数和调用成员变量的方式类似:

[cpp] view plain copy print?
1. Data n;  
2. n.year; // 调用成员变量  
3. n.set(1); // 调用成员函数

实际上,由于成员函数共享,所以调用成员函数的机制与调用成员变量的机制略有区别,简而言之,编译器先找到类,然后调用类的函数,再隐式地在参数列表中传入一个对象指针(this指针),表示需要操作该对象。

所以,成员函数set()的声明和定义可以理解为:

[cpp] view plain copy print?
1. void Data::set(Data *const this, int year) { Year = year; }   
2. // 仅作为参考,实际上,C++规定显式定义this指针为非法操作

即,任何一个成员函数都隐式地接受了一个指向对象的this指针。

而在成员函数中对成员变量的默认调用实际上都是使用this指针的隐式调用,比如 Year = year 等价于 this->Year = year。

那么,C++编译器检查const函数是否修改了成员变量的机制就很好理解了。

只需要将this指针定义为底层const,以表示不能通过改指针修改成员变量:

[cpp] view plain copy print?
1. void Data::set(const Data *const this, int year) { Year = year; }   
2. // 仅作为参考,实际上,C++规定显式定义this指针为非法操作

第一个const声明了this指针为底层const,而函数中的 Year = year 实际为 this->Year = year,由于this为底层const,所不能通过this修改Year,该操作非法,所以该函数不能声明为const。

本质上,const函数还是通过传统的const机制逐条语句检查来实现的。

以上就介绍了C/C+的相关知识,希望对C/C+有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言C/C+频道!

本文由 @Vivian 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程