-
引用不是常量引用,对象是非常量
int a = 0;
int& refa = a;//正常引用,没问题
refa = 1;//不是常量引用,对象也不是常量,可以通过引用赋值
这是最普通的情况,没有常量。
-
引用是常量引用,对象是非常量
int b = 1;
const int& refb = b;//常量引用,引用对象b是个变量,没问题
refb = 2;//会报错:不能给常量赋值。不可以通过常量引用对引用对象赋值
b = 2;//对变量可以正常赋值,没问题
这里和指针里的常量指针的情况类似。
不能通过常量指针给非常量对象进行赋值,但是对象可以以别的方式赋值。
-
引用是常量引用,对象是常量
这里和指针的情况不一样。指向常量的指针:对象是常量,指针可以是非常量。
但是在引用里,不能用非常量引用指向一个常量对象。也就是说,常量对象必须由常量引用来引用。
const int c = 2;
const int& refc1 = c;//常量引用,引用对象也是常量,没问题
int& refc2 = c;//会报错,不能用非常量引用指向一个常量对象
有点类似指向常量的常量指针,但是不能照着写。
const int d = 3;
const int(* const pd) = &d;//指向常量的常量指针
-
顶层const 底层const
顶层 const 表示指针本身是个常量,底层 const 表示指针所指的对象是一个常量。顶层 const 对任何数据类型通用,底层 const 只用于引用和指针。
顶层 const 的指针表示该指针是 const 对象,因此必须初始化。底层 const 的指针则不用。
实际上只有指针类型既可以是顶层 const 也可以是底层 const,因为引用实际上只能是底层 const,常量引用即为底层 const,不存在顶层 const 的引用。
-
引用只能做底层const
引用不能做顶层 const,因为引用本身并不是一个对象,它只是一个别名。引用可以是底层 const,但不能是顶层 const。
从右向左读来判断是顶层 const 还是底层 const。
对于指针和引用而言,顶层 const 在右边,底层 const 在左边。对于其他类型,全都是顶层 const
const int* const p3 = p2; // 从右向左读,右侧const是顶层const,表明p3是一个常量,左侧const是底层const,表明指针所指的对象是一个常量
const int* p2 = &c; // 这是一个底层const,允许改变 p2 的值
int* const p1 = &i; // 这是一个顶层const,不能改变 p1 的值
-
底层const的拷贝
执行对象的拷贝操作时,不能将底层 const 拷贝给非常量,反之可以,非常量将会转化为常量。
当进行对象的拷贝操作时,不能将底层 const 拷贝给非常量,因为这会导致常量性被破坏。但是可以将非常量拷贝给底层 const,因为非常量可以被隐式地转换为常量。例如:
int a = 10;
const int b = a; // 可以将非常量 a 拷贝给常量 b
int c = b; // 可以将常量 b 拷贝给非常量 c,b 隐式转换为非常量
在这个例子中,变量 a
是一个非常量,变量 b
是一个常量,变量 c
是一个非常量。可以将非常量 a
拷贝给常量 b
,但是不能将常量 b
拷贝给非常量 a
,因为这会破坏常量性。
对于指向常量的指针和非常量指针,可以使用 const_cast 进行类型转换。const_cast 可以将底层 const 转换为非底层 const,但是需要注意,这样做可能会破坏程序的安全性。例如:
const int* p1 = &a;
int* p2 = const_cast<int*>(p1); // 将指针常量 p1 转换为非常量指针 p2
在这个例子中,变量 a
是一个非常量,指针 p1
是一个指向常量 int 的指针常量。使用 const_cast 可以将指针常量 p1
转换为非常量指针 p2
,这样可以修改指向的变量的值。但是需要注意,这样做可能会破坏程序的安全性,因为指针 p1
指向的变量可能是一个常量,不能被修改。