文章目录
- 一、C++中的const如何理解?
- 二、C++中的const与C语言中的const有何区别?
- 三、const与指针、引用的结合使用
一、C++中的const如何理解?
在C++中,const是一个关键字,用来表示常量性,意在告诉编译器某些变量或对象的值是不可修改的,从而提高代码的安全性和可读性。
二、C++中的const与C语言中的const有何区别?
1. 语义层面的不同
在C语言中,const更多地是一个承诺,它表示变量在当前作用域中不能被修改,但这种约束不是绝对的,可以通过特定的方式绕过,例如强制类型转换。
在C++中,const语义更加严格和完善。它不仅约束变量,还能作用于成员函数、引用、指针等,且配合C++的类型系统,实现了更强的编译时检查。
2. const与引用
在C语言中没有引用的概念,因此const无法结合引用使用。
C++支持常量引用,用来避免拷贝,提高效率,同时保护原始数据不被修改。
void func(const int& ref) {
// ref 是只读的,不能修改原始值
}
这种用法在C++中非常常见,特别是在传递大型对象时。
3. 修饰函数
C语言没有成员函数的概念,因此const无法作用于函数级别。
C++允许const修饰成员函数,表示该函数不会修改对象的状态。这种语义在面向对象编程中非常重要。
class MyClass {
public:
void display() const { /* 不会修改成员变量 */ }
};
4.绕过const的方式
C语言可以通过强制类型转换((int*))来绕过const的限制。在C语言中const修饰的变量,可以不用初始化,它不叫常量,而是叫常变量。
const int a = 20;
int *p = (int*)&a;
*p = 30;
printf("%d %d %d\n", a, *p, *(&a));
输出结果:30 30 30
虽然C++也支持强制类型转换,但C++提供了更安全的const_cast,明确表示是移除const限定符。
const int a = 10;
int* p = const_cast<int*>(&a);
*p = 20; // 合法,但可能导致未定义行为
5.编译方式不同
在C语言中const就是被当作一个变量来编译生成指令的。
在C++中,所有出现const常量名字的地方,在编译时都被常量的初始化值替换了。且不能作为左值。
在C++中const用字面常量去初始化,如:
int main() {
const int a = 20;//a是使用立即数进行的初始化,所以a为常量
int array[a] = {};
int* p = (int*)&a;
*p = 30;
std::cout << a << "," << *p << std::endl;
return 0;
}
输出结果:20,30
在C++中const用变量去初始化,如:
int main() {
int b = 20;
const int a = b;//a的初始值不是立即数,是一个变量,所以a此时为常变量
//int array[a] = {}; //无法使用变量初始化数组
int* p = (int*)&a;
*p = 30;
std::cout << a << "," << *p << std::endl;
return 0;
}
输出结果:30,30
三、const与指针、引用的结合使用
const修饰的变量常出现的错误是:
常量不能作为左值(不能直接修改常量的值)。
不能把常量的地址泄露给一个普通指针或者普通的引用变量(不能间接修改常量的值)。
int main() {
const int a = 10;
int b = 20;
a = b; //错误:常量a不能再作为左值,表达式必须是可修改的左值
int* p = &a; //错误: 不能将const int* 转换为int*,这样就会间接修改a的值
return 0;
}
1.const和一级指针的结合
注意:const修饰的是离它最近的类型
const int *p:const离int最近,所以修饰的是int类型,而const修饰的表达式是*p,这个时候就不能再修改*p的值,即指针的指向不能再做修改(指针的指向是常量),但是指针的本身是可以被修改的,比如p = &b。换句话说就是p可以指向不同的int类型的内存,但是不能通过指针间接修改指向的内存的值。
int const *p:const离int最近,修饰的是int类型,所以const修饰的表达式是*p,作用同上。
int *const p:const离int*最近,修饰的是int*类型,所以const修饰的是p本身。即这个指针的本身是常量,所以一旦p初始化指向某块内存,那么就不能再更改它的指向。但是可以通过指针解引用修改指向的内存的值。
const int *const p:其作用是const int *p与int *const p的结合。
int main() {
int* q1 = nullptr;
int* const q2 = nullptr;
std::cout << typeid(q1).name() << std::endl;
std::cout << typeid(q2).name() << std::endl;
// const如果右边没有指针*的话,const是不参与类型的
// 比如说下面的const p3,表明p3是一个常量,即指针的指向不能再做改变
int a = 10;
int* p1 = &a;
const int* p2 = &a; // const int* 转换为 int*
int* const p3 = &a; // int* 转换为 int*
int* p4 = p3; //int* 转换为 int*
return 0;
}
2.const和二级指针的结合
const和二级指针结合的几种方式
const int**q:const修饰的类型是int,而它修饰的表达式是**q,所以**q不能被赋值,但是*q可以被赋值,q本身可以被赋值。
int *const *q:const修饰的类型是int *,而它修饰的表达式是const *q,所以*q不能被赋值,但是**q可以被赋值,q本身也可以被赋值。
int ** const q:const修饰的类型是int**,而它修饰的表达式是q,所以q本身不能被赋值,但是*q和**q可以被赋值。
3.总结const和指针的类型转换公式
int* 转换为 const int* 是不可行的。
const int* 转换为 int* 是可行的。
int ** 转换为 const int** 是不可行的。
const int** 转换为 int **是不可行的。
int** 转换为int* const*是错误的。
int* const*转换为int**是可行的
4.const和一级指针,引用的结合使用
//写一句代码:在内存的0x0018ff44处写一个4字节的10
int *p = (int*)0x0018ff44;
int *const &p1 = (int*)0x0018ff44; //(非常量引用的初始值必须为左值)0x0018ff44已经为常量了,如果使用引用,需要使用const修饰
int *&&p2 = (int*)0x0018ff44; //也可以使用右值引用(什么是右值:没内存,没名字,即字面常量)
饰