一、const初始化与作用域
当我们不希望某一个变量的值被改变时候,我们在定义该变量的时候可以使用const限定符,则该变量的值在定义初始化后不可以被改变,且该变量必须在定义的时候完成初始化,格式如下:
const 类型名 变量名 = 初始化值;
类型名 const 变量名 = 初始化值;//这样也合法,但一般不这样写
const int bufSize = 1024; //用常量初始化
const int bufSize = get_size(); //用表达式或函数初始化
- const变量在编译的时候编译器会将所有用到该变量的地方直接替换成对应的值
- cosnt会对整个一句话有效
const int a = 1, b = 2; //a,b均为const常量
- const仅在本文件中有效
file1中:
const int a = 1 ;
file2中:
const int a = 10;
那么在file1中所有的a被替换成1,而在file2中所有的a被替换成10
二、关键字extern与const对象共享
extern关键字
当我们在多个文件中共享一个变量的时候,需要在定义和声明该变量的时候加上extern关键字来声明外部变量,一般其应用格式如下:
file1.cpp中
extern int a = 1; //在cpp文件中使用extern定义变量
file1.h中
extern int a; //在cpp对应的头文件中使用extern声明变量
其他文件中:
//其他文件中只要包含该头文件即可使用该外部变量
#include "file1.h"
int b = a;
头文件引用
头文件引用有两种方式
<>用于引用标准库中的头文件,编译器直接在标准库中搜索,找不到就报错
#include <iostream>
""用于引用用户自己定义的头文件,编译器先在程序当前目录搜索,然后再去标准库搜索;
如果都找不到,才会报错
#include "file1.h"
const对象多文件共享
要实现const对象在多个文件中共享,也需要应用extern关键字,格式如下(先extern后const):
file1.cpp中
extern const int a = 1; //在cpp文件中使用extern定义变量
file1.h中
extern const int a; //在cpp对应的头文件中使用extern声明变量
其他文件中:
//其他文件中只要包含该头文件即可使用该外部变量
#include "file1.h"
int b = a;
三、const的引用
- 被const限定的引用称为常量引用,其格式为:
const 类型名 &引用名 = 初始值
- const的引用有三种初始化绑定方式:
绑定const量或字面值:
const int a = 1;
const int &b = a;
cosnt int &b = 3;
这种情况是引用b绑定的是a或者字面值,引用b和其绑定对象a均不可以被修改值
注意,不能为非常量引用绑定字面值,如int &b = 3;是非法的
常量引用可以绑定字面值或者表达式结果,而一般引用不能绑定字面值或者表达式结果。这是因为字面值或者表达式结果对我们来说是一个临时量,而一般引用具有通过引用更改被引用对象值的功能,而更改临时量的值没有意义,因此这是非法的。而常量引用不可以通过常量引用更改被引用对象的值,因此合法
绑定同类型的非const量:
int a = 1;
const int &b = a;
这种情况是引用b绑定的变量a;
不可以通过b更改a的值,但可以通过a更改a的值;
且b的值也会随着a的改变而改变
绑定不同类型的非const值或者表达式:
double a = 1.0;
int b = 2;
const int &c = a; //绑定不同类型的值,这里是引用可以引用不同类型的特例之一
const int &c = b*2; //绑定表达式
这种引用方式引用c绑定的其实是一个中间量,实际上相当于这样:
double a = 1.0;
const int a1 = a; //这里应该要保证这种类型转换是合法的
const int &c = a1; //绑定不同类型的值,a1为中间变量
int b = 2;
const int b1 = b*2;
const int &c = b1; //绑定表达式,b1为中间变量
这种情况引用c绑定的是中间量;
不可以通过c更改a或b的值,但可以通过a或b更改自己的值;
但此时c的值不会跟着a或b改变而改变;
- 用一个非const引用去绑定一个const量是非法的
四、指针和const
顶层const与底层const
顶层const表示变量本身是常量,底层const表示变量指向的或者引用的对象是常量。
顶层const和底层const只有在指针这里有区别,基本类型的const均是顶层const,引用的const均为底层const,但读程序的时候一般直接将引用替换成其引用的变量,可以更加清晰的读懂程序。
顶层const指针
顶层const表示指针的值(即指针指向的地址不能改变),其格式为:
类型名 *const 指针名 = 初始化值
- 顶层const指针必须初始化,一经初始化,其指向的地址就不可以改变
- 顶层const指针只限定指针地址不可以更改,可以通过顶层const指针更改指向对象的值
- 顶层const指针不能指向const常量
- 顶层const没有拷贝限制
底层const指针
底层const指针表示的是指针指向的是一个const常量,格式如下:
const 类型名 *指针名 = 初始化值
类型名 const *指针名 = 初始化值 //一般不这么写
- 底层const指针可以不初始化,且初始化后可以更改指向的地址
- 只有底层const指针可以指向const常量
- 底层const指针可以指向变量,但不能通过底层const指针修改变量的值,但不限制变量通过其他方式修改自己的值(相当于底层const指针只有变量的可读权限)
- 顶层const和底层const分别对指针的地址和指针是否能够修改值做出了约束,因此可以叠加使用:
const int a = 1;
const int *const b = &a;
底层const的拷贝限制
在进行对象的拷贝时,底层const拷贝给底层const,非底层const拷贝给非底层const,非底层const拷贝给底层const均是合法的;但底层const拷贝给非底层cosnt是非法的。
int i = 0;
int j = 0;
const int *a = &i; //底层const
const int *b = &j; //底层const
int *c = &i; //非底层const
- 底层const拷贝给底层const
a = b; //合法
- 非底层const拷贝给非底层const
i = j; //合法
- 非底层const拷贝给底层const
a = c; //合法
- 底层const拷贝给非底层cosnt
c = a; //非法
- 注意:只要包括底层const在拷贝中就收到限制,如:
const int *const a = &i; a即使顶层const也是底层const,也会收到拷贝限制
五、常量表达式和constexpr变量
常量表达式
常量表达式是值不会改变,且在编译过程中就能得到值的表达式或对象。
- 判断是否为常量表达式需要同时满足值不改变,编译过程中就能得到值两点
- 对象(或表达式)的类型决定了值是否会改变
- 对象(或表达式)的初始化方式决定了是否能你在编译过程中得到值
constexpr变量
使用constexpr定义的变量一定是常量表达式,格式如下:
constexpr 类型名 变量名 = 常量初始化值;
- constexpr类型必须使用常量表达式进行初始化,即必须保证其在编译时候就能确定初始化值
- 只有算数类型,指针,引用可以定义为constexpr,而自定义的复杂类型不能定义为constexpr
- 指针定义成constexpr,其初始值只能为nullptr,0,或者指向一个地址不变的对象
- 只有定义在所有函数体之外的对象和定义在函数体内但有效范围超出函数体的对象的地址是不变的
- constexpr对于指针来说类似于顶层const,只对指针指向的地址有限制