在C++中,const
修饰符用于声明常量,有两种常见的形式:顶层const和底层const,它们之间的区别在于它们修饰的对象及其在不同场景中的作用。
1. 顶层const (Top-level const)
顶层const用于修饰变量本身,使其成为常量。这意味着变量的值不能被修改。例如:
const int a = 10;
在这段代码中,a
是顶层const,表示a
的值不能被改变。
- 作用:限制变量本身,使其不能被修改。
- 应用场景:通常用于声明某个变量的值不可改变。
2. 底层const (Low-level const)
底层const则用于修饰指针或引用所指向的对象,使得通过指针或引用不能修改该对象的值。例如:
const int* ptr;
这里,ptr
是一个指向int
常量的指针,ptr
本身可以指向不同的地址,但不能通过ptr
修改所指向对象的值。
- 作用:限制指针或引用指向的对象的可修改性。
- 应用场景:用于指针或引用,使其无法修改所指向的对象的值。
例子区分顶层const和底层const:
int x = 5;
const int* p1 = &x; // p1是底层const,不能通过p1修改x的值
int* const p2 = &x; // p2是顶层const,不能改变p2指向的地址
const int* const p3 = &x; // p3既是顶层const又是底层const,既不能修改p3指向的地址,也不能修改x的值
p1
是一个指向int
常量的指针(底层const),可以改变指针的指向,但不能通过p1
修改所指向对象的值。p2
是一个常量指针(顶层const),即指针本身是常量,不能改变其指向的地址,但可以通过p2
修改指向对象的值。p3
同时是顶层和底层const,既不能修改指针的指向,也不能修改指针指向对象的值。
总结:顶层const用于限制变量本身的修改,底层const用于限制通过指针或引用修改所指向的对象。
我将逐行分析这些代码并解释其中的关键点,帮助你理解顶层const和底层const的作用。
const int a = 10;
int b = a;
a
是顶层const,表示它是一个常量,值为10,不能被修改。b
是一个普通的整数,它的值被初始化为a
的值(即10),这在C++中是合法的,因为b
不是const类型。
const int *const p = new int(10);
int *p1 = p; // 错误
int *const p2 = p; // 错误
const int *p3 = p;
p
是一个顶层和底层const指针:const int*
表示p
指向一个const int
(底层const),即通过p
不能修改其所指向的值;const p
(顶层const)表示p
本身是一个常量指针,不能修改它指向的地址。p1
的类型是int*
,试图将const int*
类型的p
赋值给非const的p1
,这是非法的,编译器会报错。因为p1
可以修改所指向对象的值,但p
不能。p2
的类型是int *const
,表示p2
是一个常量指针,不能改变指向的地址,但可以通过p2
修改指向对象的值。由于p
是const int*
,不能通过p2
修改对象的值,因此也是非法的。p3
的类型是const int*
,与p
兼容,因此可以赋值。
int *p4 = &a; // 错误
a
是const int
,但是p4
是一个普通的int*
,这意味着通过p4
可以修改a
的值。这是非法的,因为a
是一个常量,不能通过普通指针来修改。
const int &r1 = 20;
int &r2 = a; // 错误
int &r3 = r1; // 错误
r1
是一个对const int
的引用,绑定到字面值20
上,这是合法的,因为C++允许const引用绑定到字面值或常量表达式。r2
是一个普通的引用,试图引用a
,但a
是const
,不能通过非const引用绑定到常量。r3
也是一个普通的引用,试图引用r1
,但由于r1
是const,r3
不能通过非const引用绑定到一个const
引用上。
结论:
- 顶层const限制变量本身的修改,如
p
指向的地址不能被修改。 - 底层const限制通过指针或引用对对象值的修改,如
p
指向的值不能被修改。 - 非const指针或引用不能指向const对象或通过非const引用绑定const对象。
p3的类型是const int*,与p兼容,因此可以赋值。这个如何理解
这句话的意思是:p3
的类型是const int*
,而p
的类型是const int *const
。虽然p
是一个常量指针(顶层const),但它指向的是一个const int
类型的值(底层const),因此p
的底层类型与p3
相同,都是指向const int
的指针。
详细解释:
const int *const p = new int(10); // p 是 const 指针,指向 const int
const int *p3 = p; // p3 是指向 const int 的普通指针
-
p
的类型:const int *const p
const int *
:表示指针指向一个const int
类型的对象(底层const),即不能通过该指针修改所指向的整数值。const p
:表示p
本身是一个常量指针(顶层const),也就是说指针的地址不能改变。
-
p3的类型
:const int *p3
const int *
:表示指针p3
指向一个const int
类型的对象(底层const),即不能通过该指针修改所指向的整数值。
为什么p3
可以赋值为p
?
尽管p
是一个常量指针(顶层const),但p3
不需要关心p
本身是否是常量指针,p3
只关心p
指向的对象类型(底层const)。由于p
指向的是一个const int
,这与p3
的类型兼容,因此可以赋值。
具体来说:
p
指向的是const int
,这与p3
的底层const要求兼容。- 赋值时,
p
的顶层const(即指针本身不可修改)并不影响赋值给p3
,因为p3
本身不是常量指针,允许指针指向其他地址。
因此,p3
可以指向与p
相同的地址,但不能通过p3
修改指向的值(因为是const int*
)。