前言

  • const 的作用很多,而且在被用作 常量指针 和 指针常量 的时候经常容易搞混,今天就来总结一下 所有 const 的用法;

一、const 在普通变量中的应用

1、修饰内置类型

i)左右皆可

  • const 修饰内置类型时,位置出现在 变量类型 的左边或者右边,其含义一样,代表被修饰的对象是一个常量,生命周期内不能被改变;
 const int maxn = 1024;int const maxm = 1024;
复制代码

ii)常量的非法修改

  • 直接通过赋值修改常量,编译器会报错;
  • 但是我们可以通过取地址的方式取得常量的地址,然后再强转成 int 指针,再在对应地址上去取值修改,然后在 watch 窗口观察,发现变量的值的确被修改了!!!但是用 printf 打印出来还是原来的值,所以说明这是一种未定义行为,编译器没想到你会干出这种事,写代码的时候应该坚决避免;
 const int maxn = 1024;*((int *)&maxn) = 1;printf("%d\n", maxn);      // 1024
复制代码

2、修饰指针类型

i)常量指针

  • 定义:是一个指针,指针变量指向常量;
  • 记忆方法:常量(const)在指针(*)左边,所以从左往右跟我读:常量指针!
  • 特性:指向的对象不可变;类型 和 const 的相对位置可以交换;
 const int cInt = 1024;const int* p = &cInt;*p = 43;                    // 错误行为,企图修改指针指向对象
复制代码

ii)指针常量

  • 定义:是一个常量,指针常量指向变量;
  • 记忆方法:指针(*)在常量(const)左边,所以从左往右跟我读:指针常量!
  • 特性:指针本身不可变;
 int iInt;int * const q = &iInt;q = &iInt;                  // 错误行为,企图修改指针本身
复制代码

iii)常量指针常量

  • 定义:是一个指针常量,指针常量指向常量;
  • 记忆方法:常量(const)、指针(*)、常量(const)从左往右读!(这个名字是我编的)
  • 特性:指针本身不可变,指向对象亦不可变;
 const int cw = 1024;const int * const w = &cw;
复制代码

3、修饰引用类型

i)常量引用

  • 定义:是一个引用,并且值不能被修改;
  • 引用就是变量的 “别名” ,必须被初始化,并且需要初始化为已有的变量,但是当它被限定为 const 常量以后,可以被初始化为常量;但是一旦初始化以后,引用的值就不能被修改了;
 int cw = 1024;const int& cw1 = cw;const int& cw2 = 4;cw = 5;          // 正确行为cw1 = 6;         // 错误行为,引用在初始化以后值不能被修改cw2 = 7;         // 错误行为,引用在初始化以后值不能被修改
复制代码

ii)引用常量

  • 定义:不存在;
 int cw = 1024;int& const cc = cw;
复制代码
  • 编译后报警告:warning C4227: anachronism used : qualifiers on reference are ignored
  • 原因是引用实质是一个指针常量,所以已经不需要用 const 修饰了;

二、const 在函数中的应用

1、修饰函数传参

  • 函数传参 作为 常量 传入的目的,主要是为了函数的调用不要修改实参的值;

i)内置类型参数

  • 函数传参会拷贝一份数据,所以对于内置类型来说,加上 const 作用不大;
void XFunc(const int x) {printf("%p\n", &x);
}int x;
printf("%p\n", &x);
XFunc(x);
复制代码

008FFD80
008FFCA0

  • 可以看到,输出的实参和传参的地址是不同的,所以不用担心函数的调用会修改实参的值;

ii)指针类型参数

  • 指针传入的时候,实际是传入对应类型对象的一个地址;
  • 传指针的好处是,函数传递过程中不需要进行类的构造和析构;
  • 希望传递的对象本身不被修改,就传常量指针;希望传递的指针本身不被修改,就传指针常量;
class A {
public:A() {}A(int a) {}
};
void YFunc(const A *x, A *const y) {*x = 1;      // 错误行为*y = 1;      // 正确行为x = NULL;    // 正确行为y = NULL;    // 错误行为
}
复制代码

iii)引用类型参数

  • Effective C++ 一书中曾反复提到,宁以 pass-by-reference-to-const 替换 pass-by-value,说的就是函数作为常引用传参;
  • 当以引用的形式传入参数的时候,并且不希望函数内部修改对应参数的值,那就加上 const 限定符;
  • 最经典的例子就是类的拷贝构造函数;
class B {
public:B() {}B(const B& other) {i = other.i;}
private:int  i;
};B b1;
B b2(b1);
复制代码

2、修饰函数返回值

  • const 修饰返回值传参的相似之处不再累述;
  • 这里举个例子来说明,如果期望返回值不被修改,但是又没有加 const 造成的一些困扰;
class AddObject {
public:AddObject() {v = 0;}AddObject(int iv) {v = iv;}const AddObject operator+(const AddObject& other){v += other.v;return *this;}...
private:int v;
};
复制代码
 AddObject a, b, c;if(a + b = c) {...}
复制代码
  • 这里如果类 AddObject 的 返回值不定义成 const ,那么上面那个表达式将会成为未定义行为,仅仅是因为把 “==” 写成了 “=” ;

三、const 在类中的应用

1、修饰成员变量

i)声明即定义

  • 可以在常量被声明的时候直接初始化他的值;
  • 一般类变量如果定义为 const,代表不会变,那么可以加上 static 关键字,所有对象公用一个数据;
class InitTest {
public:InitTest() {}
private:const int a = 1;
};
复制代码

ii)初始化列表

  • 修饰成员变量的时候,可以在构造函数的初始化列表(initialization list)中将 const 常量初始化;
class InitTest {
public:InitTest() : a(2) {}
private:const int a;
};
复制代码

2、修饰成员函数

i)修饰方式

  • 当修饰类的成员函数时,const 放置在函数声明(圆括号后)和函数体(花括号前)之间;
  • 含义是:这个函数中所有的非静态(non-static)成员变量的值都不允许被修改;
class ConstTest {
public:ConstTest() {}void setval(int iv) {v = iv;}int getval() const {return v;}
private:int v;
};
复制代码
  • 思考题:如果有两个成员函数实现同样的功能,一个是const成员函数,一个是非const成员函数,那么为了避免代码冗余,比较常用的做法是:一个函数调用另一个?那么请问,应该是谁调用谁?

四、const 在 STL 中的应用

  • STLStandard Template Library 的简称,中文名:标准模板库;
  • 其中 迭代器 是 STL 里面一种遍历容器的指针;

1、常量迭代器

  • std::vector< T >::const_iterator 等同于 const T*,是一个常量迭代器,迭代器指向的数据不可被更改;
 std::vector<int>::const_iterator iter1 = vec.begin();*iter1 = 13;                // 错误行为iter1 = vec.end();          // 正确行为
复制代码

2、迭代器常量

  • const std::vector< T >::iterator 等同于 T* const,是一个迭代器常量,迭代器本身不可被更改;
 const std::vector<int>::iterator iter2 = vec.begin();*iter2 = 13;               // 正确行为iter2 = vec.end();         // 错误行为
复制代码

五、const 和 #define 的区别

- const #define
实现原理 走C++语法 不走C++语法,单纯进行字符替换
类型检查
处理阶段 编译阶段 预处理阶段
存储方式 存储在符号表 不存储

六、突破 const 限定

1、mutable 关键字

  • 还是以成员函数为例,如果 const 成员函数中,期望某些非静态成员变量能够改变,则加上 mutable 关键字即可;
class ConstTest {
public:ConstTest() {}void setval(int iv) {v = iv;}int getval() const {++c;return v;}
private:int v;mutable int c;
};

c++ const 总结相关推荐

  1. c/c++中的const

    关于const能否修改 c语言 #include <stdio.h> int main() {const int i = 10;//const int i; //错误,const变量必须在 ...

  2. 微信小程序var,let,const的区别

    var 用var的方式声明的变量,为全局变量 let 声明块级变量,即局部变量 const 用于声明常量,也具有块级作用域 const PI=3.14;

  3. js中定义变量之②var let const的区别

    var 上一篇文章有讲过,是js定义变量的关键词. 但是在es6中,新添加了两个关键词,用于变量声明的关键词:let 和const 接下来就说一下var let 和const的区别: 首先说var 用 ...

  4. C++ 笔记(15)— 引用(声明引用、引用作为参数、引用作为函数返回值、const 用于引用)

    引用是变量的别名.也就是说,它是某个已存在变量的另一个名字.一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量. 1. 创建引用 要声明引用,可使用引用运算符 & ,如下面的 ...

  5. C++ 笔记(07)— 常量(字面常量、const定义常量、constexpr 定义常量、enum 定义常量、define 定义常量)

    在 C++ 中,常量类似于变量,只是不能修改.与变量一样,常量也占用内存空间,并使用名称标识为其预留的空间的地址,但不能覆盖该空间的内容. 常量可以是任何的基本数据类型,可分为整型数字.浮点数字.字符 ...

  6. Const 重载解析

    1. Const重载应用场景 首先,对于函数值传递的情况,因为参数传递是通过复制实参创建一个临时变量传递进函数的,函数内只能改变临时变量,但无法改变实参.则这个时候无论加不加const对实参不会产生任 ...

  7. 【C++自我精讲】基础系列二 const

    [C++自我精讲]基础系列二 const 0 前言 分三部分:const用法.const和#define比较.const作用. 1 const用法 const常量:const可以用来定义常量,不可改变 ...

  8. C++const关键字作用

    修饰普通变量,表示不可修改(在定义的时候必须初始化) #include <iostream> using namespace std; const int a1 = 10; int mai ...

  9. const与define相比优点_const与#define的区别、优点

    const与#define的区别 编译器处理方式不同 define宏是在预处理阶段展开. 补充:预处理器根据以#开头的命令,修改原始的程序.比如我们常见的#include 命令告诉处理器读取系统头文件 ...

  10. inline函数返回值_C++知识补充-指针,const,函数指针,指针数组,运算符重载

    嵌入式Linux:C++ 面试准备珍藏版本​zhuanlan.zhihu.com 明月照我心:123道c++笔试题汇总(含答案)​zhuanlan.zhihu.com 阿贵:常见C++笔试面试题整理​ ...

最新文章

  1. 线性回归与梯度下降法——原理与实现
  2. 【Nginx】错误: [emerg] “proxy_pass“ cannot have URI part in location given by regular expression,...
  3. Android Studio 3.3 Beta提供了新的Android代码压缩器R8
  4. linux man 后面的数字,man命令后面的数字
  5. NHibernate配置 使用经验
  6. struts教程笔记6
  7. 【VRP】基于matlab禁忌搜索算法求解初始点和终点确定的取送货路径问题【含Matlab源码 1224期】
  8. 重启Windows的PowerShell
  9. ASP.NET ASHX 一般处理程序教程
  10. 智能聊天对话机器人的对比
  11. linux redis-trib.rb,redis集群配置 执行 redis-trib.rb 报错解决方法
  12. mmap内存映射原理
  13. 惠普f5静音键指示灯不亮(转载)
  14. 第8讲 - C语言关键字(8)
  15. IE文档模式的切换,Quirks模式
  16. python 学习之Windows 下的编码处理!
  17. 设计,看上去很美 wayfarer
  18. linux如何修改用户密码(passwd)
  19. 深富策略:消费白马迎来反攻 能否配置?
  20. 5G图传设备VR+5G直播4K+5G直播

热门文章

  1. djangoday02
  2. 求出小于45岁的各个老师所带的大于12岁的学生人数
  3. 关于Maven项目里所有代码凭空消失的问题
  4. XXL-Job执行器部署
  5. 生成伪随机数的函数int rand(void)和void srand(unsigned seed);
  6. Java中原生(native)函数的用法
  7. MPLS拓扑设计与VRF、RD、RT详解
  8. carsim中质心加速度_CarSim仿真快速入门(七)—车辆参数化建模
  9. 伯乐在线 android,伯乐在线博客
  10. 美国GeneSiC推出目前世界最高等级6.5kV/300mΩ SiC MOSFET产品