基础1:顶层const与底层const

const int* const p = new int(10); 中,同时具有顶层const与底层const。顶层const表示修饰的元素本身不可变,如 const int a = 10;,底层const表示指向的内容不可变,常量引用与常量指针相同,如 const int &ra = 10;

image-20260304193134629

《Primer C++》P58提到,当执行对象的拷贝操作时,常量是顶层const还是底层const区别明显。其中,顶层const不受什么影响;另一方面,底层const的限制却不能忽视。当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行。

我们可以看到指针pa不是任意一种const,因此并不能被p赋值。我们并不关心pa的指向是否可变,反而更注重pa指向的数据是否可变,也就是底层const,这关系到赋值是否合法。

image-20260304194622112

对于const int a = 10;,虽然a是顶层const,但对a取地址(const int* pb = &a;)时将变为底层const。这是因为常量a从本身不能改变变为指针指向的数据不能改变,因此仍然需要增加底层const来适配。

需要注意:

  1. 引用不是对象也不进行拷贝,不满足上面的原则。
  2. 常量引用在左侧时右侧可以跟任何元素:const int &ra = 10; // ...,但去除const后会报错。有一说法是在常量引用被字面量赋值时会创建一临时变量tmp,引用的是变量的引用&tmp。并且对于常量a是本身适配的,对于变量int b = 10;也仍然适配,意味ra是b的别名且仍然可以对b进行修改。
  3. 用常量给非常量引用赋值会引发报错:int &rb = a; // ERROR,如果允许非常量引用对数据进行修改,则常量失去了意义。
  4. 引用在等号右侧时忽略引用:在忽略&ra&后,ra为常量,这与3是一样的,int &rb = ra; // ERROR。引用本质上是别名,当我们使用引用时,也就是在引用原始的数据。
  5. 非常量可以被常量引用赋值,这是因为顶层const在元素的赋值中不受什么影响。