🍓const限定符
2022-5-1
| 2023-8-2
0  |  阅读时长 0 分钟
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的引用,原因:
  1. 引用不是对象,const只能修饰对象
  1. 引用一经初始化就不能在绑定到别的对象,所以严格说所有的引用自己都是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也可以是底层 constconst引用总是底层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修饰表示数组大小的对象,因为数组的大小必须在编译期间确定且不能改变。
    • C++
    • 引用和指针类型别名
      目录