文章目录

  • const关键字概述
  • const的作用规律
  • 常变量的默认作用域
  • const对象和对象的const成员
  • const修饰函数的参数和函数的返回值
  • 常见的对const的错误理解

const关键字概述

const关键字时C++引入的一个关键字,const时英文“constant”的缩写,意思时“常量,常数”,用const关键字修饰的变量被称为常变量,即变量值不可改变的只读变量。

常变量本质上仍是一个变量,有别于字面常量,两者最大的区别是:通常情况下,常变量经编译后需要占用一定的内存空间,是可寻址的;而字面常量经编译后其值存储在代码区,是不可寻址的。但是,两者之间并非有绝对界限:在某些编译器对代码进行编译优化时,会将常变量转化成字面常量,这样常变量的值实际上也会存储在代码区,变得不可寻址。

const的作用规律

const可以用来修饰变量的值,也可以用来修饰指向变量地址的指针。

const用来修饰变量的值时,变量的值不可改变,这样的const在《C++ Primer》中被称为底层const(low-level-const);const用来修饰指向变量的指针时,指向变量的指针的值不可改变,这样的const在《C++ Primer》中被称为顶层const(top-level-const)。本文为叙述方便,将指向常变量的指针称为常指针,将值不可改变的指针称为指针常量

对于const的判定规则是:const到底是底层const还是顶层const取决于const关键字和解引用运算符 * 的相对位置:const若位于*之前,则表示const修饰的是指针类型属性,此时const是一个底层const;const若位于*之后,则表示const修饰的是变量属性,此时const是一个顶层const

const int i = 1;
const int *p1 = &i; //底层const
//
int j = 2;
int *const p2 = &j; //顶层const
//
const int * const p3 = &i;  //前者是底层const,后者是顶层cosnt

顶层cosnt的写法有两种,两者是等价的。

//写法1和写法2等价,都表示指针指向的变量的值不可改变,都是底层const
const int a = 3;
int const * p = &a;  //写法1
const int * q = &a;  //写法2

const用来修饰指针或者普通变量,首先要保证变量声明或者指针声明都正确。
指针类型声明的语法格式为:

S D;  //表明D是一个类型为S的变量
S * D; //表明D是个指向类型为S的变量的指针

类型S和变量D都可用const修饰。

const int i = 4
const * int k = &i;  //语法格式错误,编译失败

更多细节详见C++官方参考文档cppreference。

对常变量的引用也必须是常变量类型的引用。

const int i = 1;
const int &r1 = i;  //合法,对常变量的引用
int &r2 = i;  //编译失败,对常变量的引用是一个普通引用而非常变量类型的引用。

左值引用在内部实现时是一个指针常量,所以在左值引用前加上一个const修饰符没有意义,编译器会给出警告并自动忽略const的存在。

int i = 1
int & const r = i;//警告并自动忽略const

辨别底层const和顶层const的方法对多重指针仍然适用,此时要以第一个解引用运算符*为准。

//此处的const到底是底层const还是顶层const?
//实际上是底层const,一重指针是一个常量,即二重指针指向的变量是一个常量
int * const * p1;
int i = 1;
int * const p2 = &i;
p2 = &p1

常变量的默认作用域

const修饰的变量,默认仅在本文件内有效。之所以这样约定,是因为:编译器在编译时会默认将常变量用常变量的值进行替换,而要做到这一点,在编译时编译器需要知道常变量的初始值。当程序源文件很多时,在每一文件中都需要访问到常变量的初始值,为了做到这一点,每一个源文件中都需要有对常变量的定义,但是这样会导致对一个变量的重复定义。为了解决这个矛盾,我们约定常变量的默认作用域仅限于本文件,这相当于在不同的文件中定义了多个独立的同名变量。

对于想在多个文件中共享常变量的情形,可以通过在常变量前面添加extern关键字来解决,这样常变量就只用定义一次就可以了

//file1.h中定义
extern const int a = 1;
//在file2中共享file1中的变量
extern const int a;

const对象和对象的const成员

const可以用来修饰类的对象,也可以用来修饰类的成员函数。

被const修饰的类对象被称为只读对象。

被const修饰的类的成员函数被称为常函数,常函数的语法格式是在函数参数列表的小括号)后面、函数体的大括号{前面添加const关键字。只有类的非静态成员函数才能被声明为常函数,其他函数(如全局函数、静态成员函数)均不可声明为常函数。

只读对象的所有数据成员都是常变量,在对象生成后其值不可再改变,所以编译器不会为只读对象提供默认构造构造函数,因为编译器提供的默认构造函数不会为常对象的数据成员提供有意义的初始值

在常函数中不允许改变类的任何成员变量的值。通过只读对象只能调用对象的常函数

常函数可以进行重载。如果两个成员函数的返回值、函数名、参数列表都相同,区别仅仅是一个函数为常函数而另一个不是,则它们之间构成重载。非只读对象在调用重载的常函数时会默认优先调用非const类型的成员函数,若想调用常函数则可通过强制类型转换进行

((const A &)a).function();
//或者
((const A *)&a).function();

值得注意的是,常函数的声明和定义分开的话,两边都要使用const关键字进行修饰。

const修饰函数的参数和函数的返回值

使用const修饰函数参数时可以防止参数在函数体内部被修改。在传引用参数时使用const修饰引用,既可以保证函数调用的效率,又可以保证参数的安全性。但是,传值调用时,使用const修饰则没有意义,因为传值调用仅仅时传给函数一份实参的拷贝,在函数体内部改变拷贝值不影响实参的值。

在返回值是普通数据,而非引用时,使用const修饰返回值没有意义,因为函数的普通数据返回值本身时一个非左值,不可被改变。当函数的返回值时一个引用时,使用const修饰可以有效阻止对引用对象的篡改,保护数据安全。

常见的对const的错误理解

使用const修饰的变量的值一定不可以改变吗?
答案时否定的。在特殊情况下通过获取常变量的地址,临时改变常变量的值也是可以实现的。

//这里输出结果是8
#include<iostream>void Print(const int & i)
{std::cout<<i<<std::endl;
}int main()
{const int j = 7;int * p;void * q = (void *)&j;p = (int *)q;(*p)++;Print(j);return 0;
}

在编译过程中,编译器做了优化,将常变量j直接替换为7。

//这里输出结果是7
#include<iostream>//void Print(const int & i)
//{//   std::cout<<i<<std::endl;
//}int main()
{const int j = 7;int * p;void * q = (void *)&j;p = (int *)q;(*p)++;///Print(j);std::cout<<j<<std::endl;return 0;
}

由以上的例子可以看出,常变量并非值一定不能改变,在特殊情况下仍然可以改变,所以编译器会进行优化来杜绝改变常变量值的情形。

常引用或者常指针只能指向常变量吗?
答案是否定的。常引用或者常指针只表明不能通过引用或者指针来改变对象的值,不代表不能通过其他引用或者其他指针来该改变对象的值。

C++中const的理解相关推荐

  1. 【小知识】C、C++ 中const的实现机制

    之前对于const的理解,可以说是知道"然",但不知道"所以然",意思是我知道被const修饰的标识符是一个"常量",但是不知道为什么是这样 ...

  2. uniapp 获取到js文件var一个变量怎么获取到这个变量值_浅析Js中const,let,var的区别及作用域...

    理解:let变量的作用域只能在当前函数中 js中const,let,var的区别及作用域_lianzhang861的博客-CSDN博客​blog.csdn.net 全局作用域中,用 const 和 l ...

  3. C与C++中const差别

    一.C中的const.功能比較单一,较easy理解. · 作用      : 被修饰的内容不可更改. · 使用场合: 修饰变量.函数參数,返回值等. (c++中应用场合要丰富的多) · 特点      ...

  4. React中JSX的理解

    React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ...children)的语法糖,同时JSX也是J ...

  5. Vue中$nextTick的理解

    Vue中$nextTick的理解 Vue中$nextTick方法将回调延迟到下次DOM更新循环之后执行,也就是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,能够获取更新后的 ...

  6. const的理解、const指针、指向const的指针

    1.const 的理解 const 是C语言的一个关键字,需要注意的是,const 关键字是把变量变为一个只读的变量(也就是不可以作为左值),绝对不是将这个变量变为常量.也就是说经过const 修饰的 ...

  7. C/C++中宏概念理解

    C/C++中宏概念理解 C/C++中宏概念理解 宏替换是C/C++系列语言的技术特色,C/C++语言提供了强大的宏替换功能,源代码在进入编译器之前,要先经过一个称为"预处理器"的模 ...

  8. STM32中堆栈的理解

    STM32中堆栈的理解 关于程序的内存分配 栈区(stack):由编译器自动分配和释放,存放函数的参数与返回值.局部变量等. 堆区(heap):由程序员分配管理,一般未使用(malloc函数). 全局 ...

  9. C++中const关键字用法详解及实例和源码下载(一)

    最近在学习C++基础部分,看了两天书,已经看过了一遍,半知半解,回过头来重新看第二遍,深入了解一下C++的基础知识.现在读到了const关键字的用法,书上面讲解的时候并没有给出完整的实例,只是理论的讲 ...

最新文章

  1. Weighted-Entropy-based Quantization for Deep Neural Networks 论文笔记
  2. 推荐JVM的9款编程语言杀手开发利器
  3. 趣味物理中的计算机科学,【趣味物理】10个有趣的科学实验,揭示物理原理。...
  4. 数组reduce()方法
  5. access做仓库管理
  6. OPPO Reno6 6Pro刷root强解BL锁 oppo reno6 Root教程
  7. python 回归方程及回归系数的显著性检验_回归方程及回归系数的显著性检验演示教学...
  8. 计算机三级考点3:构建宽带城域网的基本技术与方案。
  9. 串口485接法图_RS232转换为RS485的接线方法最好有图
  10. 化繁为简,弱监督目标定位领域的新SOTA - 伪监督目标定位方法(PSOL) | CVPR 2020
  11. jQuery插件实现瀑布流
  12. 单片机中存储器扩展位地址线怎么算_单片机程序存储器的扩展
  13. Java 程序员 金三银四面试必备:高速突击学习框架 + 性能优化
  14. GNE: 4行代码实现新闻类网站通用爬虫
  15. 世界杯“诈骗杯”?小心这些就对了
  16. HG30-3a型数字式多功能校准仪
  17. Classless 和 Classful
  18. 快速学习-XXL-JOB调度中心/执行器 RESTful API
  19. SRPG游戏开发(二十二)第六章 基本框架 - 七 视图(ViewUI)
  20. 超级计算机预测2月有雪寒潮,科学网—被证实的预测:寒潮影响马上结束,倒春寒要退场了! - 杨学祥的博文...

热门文章

  1. 华为新机发布:苹果三星已退出群聊
  2. 【C++ Opencv】读写灰度图像,RGB图像的某个像素、修改像素值、图像取反(源码+API)
  3. Weego工作室——Meego Application Laboratory
  4. 计算机房应急演练剧本,计算机机房环境设施应急演练方案
  5. 使用FFmpeg解码 丢包 花屏
  6. 关于计算机的英语辩论,【辩论训练】英文辩论,勇于创新
  7. js弹框,并指定的url
  8. C# 检测轮询超过指定的时间,并退出
  9. UILabel字符大小适应NSDate转NSStringCell抬手取消高亮
  10. xp系统电脑如何链接宽带连接服务器地址,电脑如何查看连接路由器的登录地址?...