c++中的const是一个很强大的助手,它允许你指定一个语义约束,表明某一个值不应该被更改,编译器会帮你强制执行这个约束。

const修饰指针

const可以修饰变量,数组,指针,函数,对象等等,几乎可以修饰任何东西,其中比较晦涩的应该是指针了,看下面这段代码

char arr[] = "hello world";
char* p = arr;
const char* p = arr; //const修饰arr
char* const p = arr; // const修饰p
const char* const p = arr;//既修饰arr又修饰p

const在*号左边修饰的是指针所指向的值,在*号右边修饰的是指针本身,当然你可以二者结合使用

请注意:以const在*号的左右来辨别其修饰哪一部分,因为有些程序员喜欢将const放在类型之后,*号之前来修饰指针所指向的值,如下两种写法本质是一样的

const char* p;
char const* p;

谈到指针,就不得不谈STL中的迭代器了,使用迭代器就像使用指针一样,但在const层面上,它和指针还是有一些细微差别的

当我们声明一个iterator是const时,其实是表明迭代器本身是const,约束它不能指向别的东西,相当于T* const,而如果想要约束其指向的值,那么你应该使用const_iterator

const std::vector<int>::iterator it;//实际上是修饰it

const修饰函数

const在面对函数声明时,能发挥出很大的威力

const可以修饰函数的返回值

const T& GetValue(int pos);//这个函数返回一个数组对应下标的值的引用

当你确定函数的返回值并不需要被更改时,就加上const,这样做能够有效避免一些令人困扰的问题

上述函数如果我们不加const,用户就可能对数组下标进行无理的更改,这种操作应该被避免,当然也可以不反回引用,这样的话用户得到的是一份拷贝,也就无法进行更改(由于临时变量的常量性,也可能直接给用户报错)。

const可以修饰函数的参数

const修饰参数,可以防止我们编写代码时候由于忘记而去更改不该更改的值,所以在必要的时候使用它

const修饰成员函数

const函数能够告诉我们,哪一个函数能够改动对象内容,哪一个不能。更重要的是,const成员函数提供了一种能够操作const 对象的方法。

在c++类和对象中,const对象是只能调用const 成员函数而不饿能调用非const成员函数的,这是因为const对象将其this指针设置成了const属性,而非const成员函数的函数原型是这样的:

class test
{...
public:char& operator[](test& this/*非const成员函数接收非const指针*/,\std::size_t pos);
};

非const成员函数,接收的是非const this指针,而const对象调用非const成员函数传入的是const指针,权限放大,当然不行。

因此const对象只能调用const成员函数,而非cosnt对象既能调用const又能调用非const, 但是非const对象优先调用非const函数,如果没有非const函数才会去调用const函数。

更重要的是,仅是常量性不同的两个函数是可以构成重载的,所以可以实现const对象和非const对象调用同一函数却返回不同结果。

class test
{std::string _text;
public:test(std::string text):_text(text){}const char& operator[](std::size_t pos) const{std::cout << "I am const" << std::endl;return _text[pos];}char& operator[](std::size_t pos){std::cout << "I am not const" << std::endl;return _text[pos];}
};
int main()
{const test t1("yoxi");test t2("hello");t1.operator[](0);t2[1];return 0;
}//结果
I am const
I am not const

bitwise constness 和 logical constness

bitwise constness要求:const成员函数必须保证不能更改对象内的任何一个bit,编译器就是以这种观点进行检测的,所以只要const成员函数中有对成员变量的赋值动作就会立即报错。这种观点为编译器提供了便利,但是有些时候,尽管成员函数不那么const,编译器仍然会让其通过,比如说下面这段代码

class test
{char* _text;
public:test(char* text):_text(text){}char& operator[](std::size_t pos) const{return _text[pos];}
};

这段代码,返回的是对象内部一个数组的引用,因此在外部用户可以随意更改对象内部的值,那我们能说这个const函数是完全const的吗?它确确实实可能导致成员变量的更改,只不过是间接的,这明显和bitwise constness不合。

这也引出了logical constness,这一观点认为,const成员函数可以修改它所处理的对象内部的一些bits。但是编译器只认bitwise constness,因此要使用mutable声明变量以表明他们即使在const成员函数中也能被更改。

#include <iostream>
#include <string>
class test
{mutable int a = 1;
public:void changeValue() const{std::cout << "修改前的 a:" << a << std::endl;a = 2;std::cout << "修改后的 a:" << a << std::endl;}
};
int main()
{test t;t.changeValue();return 0;
}
//运行结果
修改前的 a:1
修改后的 a:2

避免non-const和const的重复

前面说过,可以利用non-const和const的重载来完成对const和non-const对象的不同返回结果,但是如果两个函数的完成的工作有较多重复,就可以考虑使用non-const调用const来减少代码重复。比如说一个time类,对于const和non-const对象都需要返回且仅返回年,月,日等信息,那么就可以考虑写出以下代码

#include <iostream>
#include <vector>
class Time
{int _year = 1;int _month = 1;int _day = 1;
public:void getTime() const{std::cout << "I am const" << std::endl;std::cout << "年: " << _year << std::endl;std::cout << "月: " << _month << std::endl;std::cout << "日: " << _day << std::endl;}void getTime(){//对this指针作类型转换,以便传入static_cast<const Time&>(*this).getTime();}
};
int main()
{const Time t1;Time t2;t1.getTime();t2.getTime();return 0;
}
//运行结果
I am const
年: 1
月: 1
日: 1
I am const
年: 1
月: 1
日: 1

通过const_cast将const函数的this指针去const化,也能实现const调用non-const,但是最好不要那样做,因为const本身就是约束不能更改对象内部的值,而非const则是自由自在的,因此用non-const调用const是安全的,而用const调用non-const就无法保证其安全性了,也失去了const本身的意义

一文带你理顺C++中的const相关推荐

  1. 一文带你理解Java中Lock的实现原理

    转载自   一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...

  2. 独家|一文带你理解机器学习中的“归纳偏好”真正含义!

    ↑ 点击上方[计算机视觉联盟]关注我们 举例子: 三种属性:{白色.黄色.黑色}+{眼睛颜色蓝色.眼睛颜色不一样}+{有铃铛.没铃铛}   判断是不是"猫" 通过机器学习,现在给了 ...

  3. elasticsearch删除索引_一文带您了解 Elasticsearch 中,如何进行索引管理(图文教程)

    在 Elasticsearch 中,索引是一个非常重要的概念,它是具有相同结构的文档集合.类比关系型数据库,比如 Mysql, 你可以把它对标看成和库同级别的概念. 今天小哈将带着大家了解, 在 El ...

  4. 如何进阶TypeScript功底?一文带你理解TS中各种高级语法

    引言 TypeScript 的重要性我不在强调了,我相信仍然会有大多数前端开发者碰到复杂类型一概使用 any 处理. 我写这篇文章的目的就是为了让你告别 AnyScript ,文章告别晦涩的概念结合实 ...

  5. 一文带你了解 MySQL 中的各种锁机制!

    MySQL中的锁机制,按粒度分为行级锁,页级锁,表级锁,其中按用法还分为共享锁和排他锁. 行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁. 行级锁能大大减少数据库操作 ...

  6. mysql 获取距离当前最新的记录_一文带你了解 MySQL 中的各种锁机制!

    云栖号资讯:[点击查看更多行业资讯] 在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! MySQL中的锁机制,按粒度分为行级锁,页级锁,表级锁,其中按用法还分为共享锁和排他锁. 行级锁 ...

  7. 一文带你领略JS中原型链的精妙设计!

    对于很多前端初学者来说,JavaScript中的原型链理解是一个很难的过程,有工作了好几年还无法理解原型链设计的大有人在,今天小千就来通过例子给大家介绍这个js原型链. 我们想要理解原型链,首先我们先 ...

  8. 一文带你了解MySQL中的各种锁机制!

    云栖号资讯:[点击查看更多行业资讯] 在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! MySQL中的锁机制,按粒度分为行级锁,页级锁,表级锁,其中按用法还分为共享锁和排他锁. 行级锁 ...

  9. 一文带你领略JS中原型链的精妙设计

    对于很多前端初学者来说,JavaScript中的原型链理解是一个很难的过程,有工作了好几年还无法理解原型链设计的大有人在,今天小千就来通过例子给大家介绍这个js原型链. 我们想要理解原型链,首先我们先 ...

最新文章

  1. 制药行业SAP项目里的那些LIMS系统
  2. c语言 编码规范 C Coding Standard
  3. 安利一下这个群投票的小程序,比较好用
  4. 什么是泛型、为什么要使用以及泛型擦除
  5. 如何在Java中转义JSON字符串-Eclipse IDE技巧
  6. 我的移动混合开发之旅
  7. mitmproxy https抓包的原理是什么?
  8. [UML]UML系列——时序图(顺序图)sequence diagram
  9. 误删D盘数据怎么办?推荐使用数据恢复软件EasyRecovery
  10. eclipse中导入dtd文件实现xml的自动提示功能
  11. JAVA 泛型 入门
  12. Swift TouchId指纹解锁,FaceId面部解锁
  13. BZOJ 1011: [HNOI2008]遥远的行星( )
  14. 微信小程序点击激活类
  15. get与navigate方法
  16. 华为开启管理员模式_华为root权限怎么开启,详细的步骤以及图文教程
  17. php实现简易的搜索功能
  18. 博雅互动(静态网页)分享
  19. 图匹配(Graph Matching)入门学习笔记——以《Factorized Graph Matching》为例(二)
  20. (免费分享)基于javaweb,ssm旅游信息系统

热门文章

  1. 校园闲置物品(跳蚤市场)交易平台的设计与实现
  2. OSPF路由协议及工作原理(一)
  3. 数据逻辑结构 的 二元组表示法
  4. 微信windows版_微信悄悄更新,这个烦人的功能限制,终于被取消
  5. 安徽省软考报名时间成绩查询安徽省教育考试院安徽省人事考试网报名入口
  6. 湖仓一体化:铁打的数据仓 流水的数据湖产品
  7. 微信扫一扫功能扫描二维码调用外部浏览器打开指定页面实现微信中下载APP的功能
  8. 数字电路中的建立时间与保持时间是什么
  9. 最简单的Greenplum节点扩展操作步骤
  10. guid linux 识别的分区表_GUID分区与MBR分区有什么区别?