type
status
date
slug
summary
tags
category
icon
password
Property
常量
使用
const
可以定义值不可变的变量,这种值是常量。通常,常量用于缓冲大小、最大限度等对于程序来说有特殊含义的值。由于不能改变常量的值,所以常量必须初始化,初始化与变量初始化一样,特别是可以将常量初始化为运行时值,而
#define
只能让你定义字面量的常量。对于内置类型来说只要不涉及到赋值就不会改变其值,所以常量也能进行所有这些操作。当使用对象去初始化另外一个对象时,不管是常量还是非常量都是可以的,初始化别的对象并不会改变当前对象。复制对象并不会改变原始对象,一旦复制完成,新的对象就不能够再访问原始对象了。
默认情况下,const 变量仅在文件内有效
默认情况下
const
对象就像是static
变量一样是存在于文件作用域中的:编译器将在编译过程中把用到该变量的地方都替换成对应的值——找到代码中所有用到
bufSize
的地方,然后用512
替换。为了执行上述替换,编译器必须知道变量的初始值。如果程序包含多个文件,则每个用了
const
对象的文件都必须得能访问到它的初始值才行。要做到这一点,就必须在每一个用到变量的文件中都有对它的定义。为了支持这一用法,同时避免对同一变量的重复定义,默认情况下,const
对象被设定为仅在文件内有效。当多个文件中出现了同名的const
变量时,其实等同于在不同文件中分别定义了独立的变量。有时,希望多个文件访问同一个
const
变量,必须在定义和声明的地方都加上extern
关键字:const 引用
可以让引用绑定一个
const
类型对象,这样的引用不能改变绑定对象的值,const对象只能被const引用绑定:const
引用只是对绑定const对象的引用的缩写,并不存在引用自己是const的引用,原因:- 引用不是对象,
const
只能修饰对象
- 引用一经初始化就不能在绑定到别的对象,所以严格说所有的引用自己都是
const
的。引用的const
属性决定的是是否可以通过引用改变其绑定的对象,而与引用本身无关系
const
引用可以绑定到任何可以转换到引用类型的表达式上,包括非const
对象、字面量和通用表达式:非
const
引用必须与绑定的对象类型严格匹配,而const
是允许转换的,所以int
类型的const
引用可以绑定double
类型的值:原因在于
ri
不是真正绑定到dval
对象上,而是绑定到一个编译器生成的临时对象上。所谓临时对象就是编译器在需要一个内存块来存储表达式求值时所创建的对象。dval
并没有直接绑定到ri
引用上,而是转换并存储到临时对象上。如果允许将非const
引用绑定到需要转换的对象上,那么对引用的操作将改变临时对象而不是真正的对象,这肯定不是我们想要的结果。所以,C++
不允许这种操作。对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
对象,比如可以用 curErr 改变 errNumb 的值,虽然 curErr 是 const 指针。顶层 const(Top-Level const)
指针可以分开独立讨论指针本身是否为
const
的和指针指向的对象是否为const
。- 指针本身是
const
为顶层const
(top-level const),顶层const
可以出现在任何对象类型
- 指针指向一个
const
对象为底层const
(low-level const),只能出现在复合类型的基础类型中
指针类型既可以是顶层
const
也可以是底层 const
,const
引用总是底层const
。当执行拷贝操作时,常量是顶层
const
还是底层const
区别明显:- 顶层
const
没有影响。拷贝操作不会改变被拷贝对象的值,因此拷入和拷出的对象是否是常量无关紧要:
- 底层
const
的限制不能忽视。拷入和拷出的对象必须具有相同的底层const
资格。或者两个对象的数据类型可以相互转换。一般来说,非常量可以转换成常量,反之则不行。
复制对象不会改变被复制的对象,所以对象本身的
const
即顶层const
可以被忽略。然而底层const
是不能被忽略的,两个对象间必须有相同的底层const
限定符。或者将非const
转为const
,但不能做相反的转换。constexpr 和常量表达式
常量表达式(constant expression)指值不会改变并且在编译过程就能得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的
const
对象也是常量表达式。非const
对象或者不是由常量表达式初始化的const
对象都不是常量表达式。一个对象是否为常量表达式由它的数据类型和初始值共同决定:
constexpr 变量
自己去判断常量表达式是很困难的,而在某些情况下又必须使用常量表达式,通常定义和使用的地方是分离的。
C++11
允许将变量声明为constexpr
类型以便由编译器来验证变量的值是否是一个常量表达式。constexpr
函数要足够简单,使得可以在编译期间求值。常量函数可以用于初始化constexpr
变量。建议将所有的常量表达式初始化的变量都定义为constexpr
,强制要求编译器进行检查。字面值类型(Literal Types)
因为常量表达式必须在编译期进行求值,所以
constexpr
变量的类型必须符合这一限制,这些类型必须足够简单使得可以在编译期进行求值,所以称为字面类型。这些类型包括算术类型、引用和指针,但是不包括常见的类类型。指针和引用都能定义成
constexpr
,但是初始值受到严格限制。constexpr
指针的初始值必须是0、nullptr
或者是存储在某个固定地址中的对象。函数体内定义的普通变量一般并非存放在固定地址中,因此 constexpr
指针不能指向这样的变量。相反,函数体外定义的变量地址固定不变,可以用来初始化constexpr
指针。在
constexpr
声明中如果定义了一个指针,限定符 constexpr
仅对指针本身有效,与指针所指的对象无关:constexpr
把它所定义的对象置为了顶层 const
。与其他常量指针类似,
constexpr
指针既可以指向常量也可以指向一个非常量:const
和 constexpr
限定的值都是常量。但 constexpr
对象的值必须在编译期间确定,而 const
对象的值可以延迟到运行期间确定。建议使用 constexpr
修饰表示数组大小的对象,因为数组的大小必须在编译期间确定且不能改变。