const int bufSize =512;
bufSize变成了一个常量
const对象一旦创建就不能改变,所以const对象必须初始化。
初始化和const
用一个对象去初始化另一个对象,是不是const都不要紧
默认状态下,const对象仅在文件内有效
//file_1.cc定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize =fcn();
//file_1.h头文件
extern const int bufSize;//与file_1.cc中定义的bufSize是同一个
extern做了限定指名bufSize并非文件所独有,它的定义将出现别处
Note: 多个文件共享const对象,必须在变量的定义之前添加extern关键字
2.4.1 const的引用
可以把引用绑定到const对象上,我们称之为 对常量的引用(reference to const)
const int ci = 1024;
const int &r1 = ci;//正确:引用及其对应的对象都是常量
r1 = 42; //错误: r1是对常量的引用
int &r2 = ci; //错误:试图让一个非常量引用指向一个常量对象
术语:常量引用 是对 const 的引用
初始化和对const的引用
引用对象必须与所引用对象的类型一致,但有两种例外
第一种情况是在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可
int I =42;
const int &r1 = I; //允许将const int& 绑定到一个普通int对象上
const int &r2 = 42; // 正确:r2是一个常量引用
const int &r3 = r1 * 2; //正确: r3是一个常量引用
int &r4 = r1 * 2; //错误:r4是一个普通的非常量引用
例:
double dval = 3.14;
const int &ri = dval;
编译器将代码变为如下:
const int temp = dval;// 由双精度浮点数临时生成一个整型常量
const int &ri = temp; // 让ri绑定这个临时值
temp为临时值(temporary)
C++将这种行为归为非法
对 const 的引用可能引用一个并非const的对象
const int &r2 = I; //r2绑定对象I,但是不允许通过r2修改i的值
r2 = 0; //错误,r2是一个常量引用
2.4.2 指针和const
指向常量的指针(pointer to const) 不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。
const double pi = 3.14; //pi是个常量,它的值不能改变
double *ptr = π //错: ptr是一个普通指针
const double *cptr = π //正确:cptr可以指向一个双精度常量
*cptr = 42; //错误:不能给*cptr赋值
和常量引用一样,指向常量的指针没有要求对象必须是一个常量。
就是不能通过该指针修改,没有规定不能通过其他途径修改。
const指针(有点难理解)
指针是对象而引用不是,因此就像其他对象类型一样,允许把指针本身定为常量。
常量指针(const pointer)必须初始化。
一旦初始化完成,它的值(就是存放在指针内的地址(不是对象本身的值))就不能改变了。
把*放在const之前用以说明指针是一个常量,
即不变的是指针本身的值而不是指向的那个值。
int errNumb =0;
int *const curErr = &errNumb //curErr将一直指向errNumb
const double pi = 3.14;
const double *const pip = pi; //pip将一直指向pi并且pi本身的值也是一个常量(pip是指向常量对象的常量指针)
pip所指的对象的值和对象的地址都不能改变
curErr指向的对象是一个非常量整数,可以用curErr去修改errNumb的值:
*curErr = 0 ; //正确:把所指对象的值重制
2.4.3 顶层const
指针本身是不是常量以及指针所指的是不是一个常量是两个相互独立的问题
名词 顶层 const(top-level const)表示指针本身是个常量
名词 底层 const(low-level const)表示指针所指的对象是一个常量
顶层const 可以表示任意的对象是常量,这一点对任何数据类型都适用,如算数类型,类,指针等。
底层const 则与指针和引用等复合类型的基本类型部分有关。
指针即可以是顶层const也可以是底层const
int I =0;
int *const p1 =&I; //顶层不能改p1的值
const int ci = 42; //顶层不能改 ci的值
const int *p2 = &ci; //底层 可以p2的值
const int *const p3 = p2; //靠右顶层 ,靠左底层
const int &r =ci; //用于声明引用的const都是底层const
拷贝操作时,顶层const不受什么影响
I = ci //正确:拷贝ci的值,ci是一个顶层const,对此操作无影响
p2 = p3 ; //正确: p2和p3指向的对象类型相同,p3顶层const的部分不影响
另一方面,底层const拷贝和拷出需要具有相同的底层 const资格;
int *p = p3; //错误:p3包含底层const的定义,p没有
p2 = p3; // 正确: p2和p3都是底层const
p2 =&I; // 正确 int*能转换成 const int*
int &r = ci; //错误:普通的int&不能绑定到int常量上(常量int&可以随便绑(๑•̀ㅂ•́)و✧)
const int &e2 = I ; //正确: const int&可以绑定到一个普通int
2.4.4 constexpr和常量表达式
常量表达式(const expression)是指值不会改变并且在编译过程中就能得到计算结果的表达式
int staff_size =27; //数据类型是普通int而非 const int
const ins sz = get_size(); // 具体值运行时才能得到
两者不是常量表达式
一个对象是不是常量表达式由它的数据类型和初始值共同决定。
constexpr变量
声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。
constexpr int sz = size(); // 只有当size是一个constexpr函数时才是一条正确的声明语句。
一般来说,如果认定变量是一个常量表达式,那就把它声明成constexpr类型。
字面值类型
目前接触过的数据类型中
算数类型,引用和指针都属于字面值类型
自定义类 Sales_item, IO库,string类型都不属于字面值类型,也就不能定义成constexpr
尽管指针和引用都能定义成constexpr,但他们的初始值受到限制。
一个指针初始值必须是nullptr,或者是储存于某个固定地址的对象。
指针和constexpr
constexpr声明中如果定义了一个指针,限定符constexpr只对指针有效(即常量指针)
constexpr int *p =nullptr; //常量指针
constexpr const int *p1 = &j; // 指向j的常量指针
//j定义在函数外