第二章_变量和基本类型_2.4const限定符


const 对象一旦创建后其值就不能再改变,所以 const 对象必须初始化。初始值可以是任意复杂的表达式:

const int i = get_size();
const int i = 24;
const int k; // 错误: 必须有明确的初始化值来初始化 const

初始化和 const

对象的类型决定了其上的操作。只能在 const 对象上进行不改变其内容的操作。

用一个对象去初始化另外一个对象,不会改变其内容,所以与 const 无关,如:

int i = 24;
const int ci = 25;
int j = ci; // 正确: j 被初始化,但是 ci 的内容并没有改变

默认状态下,const 对象仅在文件内有效

如果程序包含多个文件,则每个用了 const 对象的文件都必须能够访问到对象的值。这样就必须在每一个用到变量的文件中都有对它的定义,为了支持这种用法,同时为了避免对同一变量的重复定义,默认状态下,const 对象被设定为仅在文件内有效。当多个文件中出现了同名的 const 对象时,其实等同于在不同文件中分别定义了独立的对象。

要在不同文件中共享同一个 const 对象,只在一个文件中定义 const,而在其他多个文件中声明并使用它,那么需要对 const 对象不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了:

// file_1.cpp 定义并初始化了一个常量,该常量能被其他文件访问 
extern const int bufSize = fen();
// file_1.h 头文件  
extern const int bufSize; // 与file_1.cpp中定义的bufSize是同一个

2.4.1 const 的引用

把引用绑定到 const 对象上,称为对常量的引用(reference to const),与普通引用不同的是,对常量的引用不能被用作修改它绑定的对象。

const int ci = 1024;
const int &r1 = ci; // 正确:引用及其对象都是常量
r1 = 42;            // 错误:对一个常量的引用进行赋值
int &r2 = r1;       // 错误:将一个非常量的引用指向一个常量的引用

引用的对象是常量还是非常量可以决定其所能参与的操作,却无论如何都不会影响到引用和对象的绑定关系关系。

初始化和对 const 的引用

引用的类型必须与其所引用对象的类型相一致,但是有两个例外:

  • 初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型,允许为一个常量引用绑定非常量的对象、字面值、表达式
int i = 42;
const int &r1 = i;      // 允许将 const int & 绑定到一个普通 int 对象上
const int &r2 = 24;     // 正确: r2 是一个常量引用
const int &r3 = r1 * 2; // 正确: r3 是一个常量引用
int &r4 = r1 * 2;       // 错误: r4 是一个普通的非常量引用

注意如果 r4 不是 const,那么意味着可以通过 r4 改变引用对象的内容,示例中是 r4 引用的对象是 const 的,所以错误

  • 对 const 的引用可能引用一个并非 const 的对象

    常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未做限定。因为对象可能是非常量,所以允许通过其他途径修改引用对象的值.

int i = 42;
int &r = i;         // 引用 r 绑定对象 i
const int &cr = i;  // 常量引用 cr 绑定对象 i
r = 0;              // 正确: 可以通过非常量引用 r 修改 i 的内容
cr = 0;             // 错误:cr 是常量引用,无法通过常量引用修改绑定对象的内容
// 实际上,此时 cr 的内容也被改变了,因为其他途径修改了 i 的内容

2.4.2 指针和 const

指针可以指向常量或非常量。指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针

const double pi = 3.14;
double *p1 = π       // 错误: p1 是一个普通指针
const double *p2 = π // 正确:p2 是一个 const
*p2 = 42;               // 错误:不能给 const 赋值

指针的类型必须与其所指类型相一致,但是有两个例外:

  • 允许令一个指向常量的指针指向一个非常量对象
double dval = 3.14
const double *cp = &dval;   // 正确:但不能通过 cp 更改 dval 的值

所谓指向常量的指针仅仅要求不能通过该指针改变对象的内容,而没有规定那个对象的值不能通过其他途径改变。

**所谓指向常量的指针或引用,不过是指针或引用自以为是罢了,它们觉得自己指向了常量,所以自觉地不去改变所指向对象的内容。

const 指针

因为指针是对象,所以允许把指针本身定义为常量。常量指针必须初始化,而且一旦初始化完成,则它的值(存放在指针中的那个地址)就不能再改变了。

int errNumb = 0;
int *const curErr = &errNumb;   // curErr 将一直指向 &errNumb
const int *errPtr = &errNumb;   // 不能通过 errPtr 修改 errNumb 的值 
const double pi = 3.14159;      // pi 是一个 const
const double *const pip = π  // pip 将一直指向 &pi 且不能通过 pip 修改 pi 的值

从右向左阅读声明,离对象名最近的声明符指定对象的属性

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型。

*pip = 2.72;  // 错误: pip 指向的对象是一个常量

if(*curErr){
  errorHandler();
  *curErr = 0;  // 正确: curErr 指向的对象是非常量
}

练习 2.27

int i = -1, &r = 0; // 非法: 非常量引用 r 不能引用字面值 0
int *const p2 = &i2;  // 合法
const int i = -1, &r = 0; // 合法:常量引用可以引用字面值 0
const int *const p3 = &i2;  // 合法:
const int *p1 = &i2;  // 合法:
const int &const &2;  // 非法: 引用不是对象
const int i2 = i, &r = i; // 合法:

练习 2.28

int i, *const cp; // 非法:cp 是常量指针,必须初始化
int *p1, *const p2; // 非法:p2 是常量指针,必须初始化
const int ic, &r = ic;  // 非法:ic 是常量,必须初始化
const int *const p3;  // 非法: p3 是常量指针,必须初始化
const int *p; // 合法

2.4.3 顶层 const

顶层 const (top-level const) 表示指针本身是常量

底层 const (low-level const) 表示指针所指的对象是常量

更一般的,顶层 const 可以表示任意的对象是常量,对所有数据类型都适用,底层 const 则与指针和引用等复合类型的基本类型部分有关。其中指针类型既可以是顶层 cosnt 也可以是底层 const。

int i = 0;
int *const p1 = &i;       // p1 是顶层 const
const int ci = 24;        // ci 是顶层 const
const int *p2 = &ci;      // p2 本身的值可以更改,p2 是底层 const
const int *const p3 = p2; // 靠右的 const 是顶层 const(p3本身),靠左的 const 是底层 const(p3指向的值),p3 既是底层 const 又是底层 const
const int &r = ci;        // 常量引用,底层 const

执行对象的拷贝操作时,拷贝操作不会改变被拷贝对象的值。常量是顶层 const 和底层 const 区别明显,顶层 const 不受任何影响

i = ci;   // 正确:
p2 = p3;  // 正确:p3 顶层 const 的部分不受影响

当执行对象的拷贝时,拷入和拷出的对象必须具有相同的底层 const 资格,或者两个对象的数据类型必须能够转换:如非常量可以转换成常量,反之不行

int *p = p3;        // 错误:p3 的底层 const 是const,p 不是 const 
p2 = p3;            // 正确:p2 本身就是底层 const,
p2 = &i;            // 正确:非常量 i 可以转换为常量
int &r = ci;        // 错误:引用类型必须与被引用对象一致
const int &r2 = i;  // 正确:常量引用的对象可以是非常量

2.4.4 constexpr 和常量表达式 (const expression)

常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。

一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定

const int max_files = 20;         // max_files 是常量表达式
const int limit = max_files + 1;  // limit 是常量表达式
int staff_size = 28;              // staff_size 不是常量表达式,值会改变
const int sz = get_size();        // sz 不是常量表达式,在编译过程中不能得到计算结果

constexpr 变量

将变量声明为constexpr类型,由编译器来验证变量的值是否是一个常量表达式,声明为constexpr的变量一定是一个常量,且必须用常量表达式初始化:

constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size();  // 只有当 size() 函数是一个 constexpr 函数才正确

字面值类型 (literal value)

一个constexpr指针的初始值必须是nullptr或者 0 ,或者是存储在某个固定地址中的对象。

算术类型、引用和指针都属于字面值类型。

指针和 constexpr

constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指向的对象无关。

const int *p = nullptr;     // p 是一个指向整型常量的指针,即 p 指向的对象是 const
constexpr int *q = nullptr; // q 是一个指向整型的常量指针,即 q 本身是 const

constexpr把它所定义的对象置为了顶层 const,

与常量指针类型,constexpr指针既可以指向常量也可以指向非常量

constexpr int *np = nullptr;  // np 是一个指向整数的常量指针,初始值为空
int j = 0;                    // j 是一个整型非常量
constexpr int i = 42;         // i 是一个整型常量
constexpr const int *p = &i;  // p 是一个指向整数的常量指针,所指向的对象是常量 i
constexpr int *p1 = &j;       // p1 是一个指向整数的常量指针,初始值是 j

练习 2.32

int null = 0, *p = null;

错误,null是一个整型变量,p是一个指针,类型不同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值