const限定符
const限定符修饰的变量,只有只读功能,而无法改变值。
一、const的特点
1.const对象一旦创建不可更改,因此必须初始化。
const int bufSize = 512;
bufSize = 512; //错误,试图改变值
const int i = 12;
const int j; //错误,没有赋初值
2.默认状态下,const对象只在该文件内有效
编译器在编译过程中,将所有const变量替换为相应的值。因此,在有多个文件的程序中,为使所有const变量能够被替换,必须在每个文件都进行定义。为了避免同一变量的重复定义,规定const对象只在该文件内有效。
而对于许多需要文件之间共享的const变量,解决的办法是,在定义和声明之前都加上一个extern。
extern const int bufSize; //file.h
extern const int bufSize = 512; //file.cpp
上面两条语句中的bufSize其实是一个。第一条中的extern表示bufSize不是本文件独有的,其定义在其他文件中;第二条中的extern表示bufSize可以被其他文件使用。
二、const的引用
1.引用是const,引用的对象也是const。
const int a = 10; //a不可被修改
const int &b = a; //b不可被修改
2.引用不是const,引用的对象是const。这种情况不合法。
const int c = 9;
int &d = c;
在这种情况下,c的值无法被改变,而允许通过d改变c的值,因此非法。
3.引用是const,引用的对象不是const。
在这种情况下,允许被引用的为对象或字面值,只是对引用的操作做出了限定,即不可通过引用来改变对象的值,但可通过其他方式(如对象本身)改变对象值,这是它与不带const的引用的唯一区别。
int d = 8;
const int &e = d;
const int &f = 7; //可赋值字面值
const int &g = d * 2;
std::cout << d << std::endl << e << std::endl; //d = 8,e = 8
d = 6;
std::cout << d << std::endl << e << std::endl; //d = 6,e = 6
三、const和指针
1.指向常量的指针
指向常量的指针,仅代表不能通过指针更改对象,指针值可更改。
- 对象为常量,则只能用指向常量的指针指向。可以更改指针值,不可更改对象。
const double pi = 3.14;
const double pii = 3.1;
const double *p1 = π
std::cout << p1 << std::endl << *p1 << std::endl;
//指针值可更改
p1 = &pii;
std::cout << p1 << std::endl << *p1 << std::endl;
//错误,使用不是指向常量的指针指向常量。
const double pi = 3.14;
double *ptr = π
- 对象为变量,可以更改指针值,可以更改对象值,但不可通过指针更改对象。
double s = 3.3;
const double *p2 = &s;
std::cout << p2 << std::endl << *p2 << std::endl;
s = 3.2;
std::cout << p2 << std::endl << *p2 << std::endl;
2.常量指针
即指针是常量,因此必须初始化,可以将常量指针看做引用。
int u = 10;
const int v = 9;
int *const p3 = &u; //指针为常量,对象为变量。
int *const p4; //错误,没有初始化
const int *const p4 = &u; //指针为常量,对象为变量,不可以通过指针更改对象。
const int *const p5 = &v; //指针为常量,对象为常量。
四、顶层const
狭义上来讲,顶层const—指针本身是常量,底层const—指针指向的对象是常量。
广义上来讲,顶层const—任意类型的对象是常量,底层const—复合类型的基本数据类型为常量。
int k = 0;
int *const p6 = &k; //顶层,指针为常量
const int ci = 42; //顶层,对象ci为常量
const int *p7 = &ci; //底层,指针为变量,*p7为常量
//第一个const为底层(表明*p8为常量),第二个const为顶层(表明p8为常量)
const int *const p8 = p7;
const int &r = ci; //底层,复合类型的基本数据类型
五、constexpr和常量表达式
1.常量表达式
常量表达式需满足两个条件:
- 表达式的值不会改变
- 表达式的值在编译过程中就可以确定
2.constexpr变量
C++11新标准提供给我们constexpr类型,以便由编译器验证变量是否是常量表达式,当我们认为一个变量是常量时,就可以将该变量声明为constexpr类型。
将一个变量声明为constexpr类型也需要满足两个条件:
- 变量一定是个常量
变量必须用常量表达式初始化
constexpr int mf = 20; constexpr int limit = mf + 1; constexpr int sz = size();
在最后一个例子中,size()是运行时才可以获得结果的,而为了可以使函数作为constexpr变量的初始值,提供了一种特殊的constexpr函数使在编译过程中就可以获得结果。
3.constexpr和指针
首先,当指针的初值为0或指向存储于某个固定地址的对象时,才可声明为constexpr类型。
其次,const和constexpr修饰的指针相差甚远。
const int *six = nullptr; //six指向对象为常量
constexpr int *day = nullptr; //day为常量
同样,constexpr也可以指向常量和非常量。
int fancy = 20;
constexpr int *fan = &fancy; //指向变量
std::cout << &fancy << std::endl << fan << std::endl << *fan << std::endl;
fancy = 18;
std::cout << &fancy << std::endl << fan << std::endl << *fan << std::endl;
const int shi = 23;
constexpr const int *baba = &shi; //指向常量