本质
在 C++ 里,const 的核心本质是一种约定,它向编译器和代码阅读者承诺:
被 const 修饰的实体(变量、对象、函数等)在特定的作用域或规则下不会被修改。
编译器会依据这个契约对代码进行检查,一旦发现违反契约的修改操作,就会在编译阶段报错,以此来增强代码的安全性和可维护性。
内核
从编译器角度来看,const 是编译时的概念。
编译器在编译过程中会严格检查对 const 修饰实体的操作,确保其只读性。
在内存层面,const 对象和普通对象一样占用内存空间,但编译器通过限制对其内存地址的写操作来实现只读特性。不过,如果通过一些非正规手段(如强制类型转换)绕过编译器检查,理论上是可以修改 const 对象的值的,但这违背了 const 的设计初衷。
使用场景
1. 常量
const int MAX_SIZE = 100;
// MAX_SIZE = 200; // 编译错误,不能修改常量的值
用于定义程序中不会改变的常量,提高代码的可读性和可维护性,避免硬编码。
2. 变量
int value = 5;
const int constValue = value;
// constValue = 10; // 编译错误,不能修改 const 变量
将变量声明为 const 可以防止意外修改,常用于需要保证值不变的场景。
3. 指针
- 常量指针:指针指向的内容不能修改,但指针本身可以修改。
int num = 10;
const int* ptr1 = #
// *ptr1 = 20; // 编译错误
int num2 = 20;
ptr1 = &num2;
- 指针常量:指针本身不能修改,但指针指向的内容可以修改。
int num = 10;
int* const ptr2 = #
*ptr2 = 30;
// ptr2 = &num2; // 编译错误
- 指向常量的常量指针:指针本身和指向的内容都不能修改。
int num = 10;
const int* const ptr3 = #
// *ptr3 = 20; // 编译错误
// ptr3 = &num2; // 编译错误
4. 引用
int num = 10;
const int& ref = num;
// ref = 20; // 编译错误,不能通过 const 引用修改引用的对象
使用 const 引用可以避免不必要的复制,同时保证引用的对象不被修改,常用于函数参数传递。
5. 函数的参数
void printValue(const int& value) {
// value = 10; // 编译错误,不能修改常量引用的值
std::cout << value << std::endl;
}
int main() {
int num = 5;
printValue(num);
return 0;
}
将函数参数声明为 const 可以防止函数内部修改传入的参数,增强代码的安全性,同时对于引用传递可以避免复制开销。
6. 函数的返回值
const int getValue() {
return 10;
}
int main() {
// getValue() = 20; // 编译错误,不能修改常量返回值
int result = getValue();
return 0;
}
当函数返回一个常量值时,调用者不能修改这个返回值,常用于返回一些不应被修改的计算结果。
7. 类的成员变量
class MyClass {
private:
const int id;
public:
MyClass(int value) : id(value) {}
// 成员变量 id 一旦初始化就不能再修改
};
将类的成员变量声明为 const,意味着该成员变量在对象创建时初始化后就不能再被修改,常用于表示对象的唯一标识等不变的属性。
8. 类的成员函数
收起
cpp
class MyClass {
private:
int data;
public:
MyClass(int value) : data(value) {}
int getData() const {
// data = 20; // 编译错误,不能在常量成员函数中修改成员变量
return data;
}
};
int main() {
const MyClass obj(10);
int value = obj.getData();
return 0;
}
使用 const 修饰类的成员函数表示该函数不会修改类的任何非 mutable 成员变量,这样的函数可以被 const 对象调用。
注意事项
1.const对象只能调用const成员函数
class MyClass {
public:
void nonConstFunction() {}
void constFunction() const {}
};
int main() {
const MyClass obj;
// obj.nonConstFunction(); // 编译错误
obj.constFunction();
return 0;
}
const 对象的状态不能被改变,因此只能调用不会修改对象状态的 const 成员函数。
2.const与mutable结合使用
class MyClass {
private:
mutable int counter;
public:
MyClass() : counter(0) {}
void incrementCounter() const {
counter++; // 可以在常量成员函数中修改 mutable 成员变量
}
int getCounter() const {
return counter;
}
};
int main() {
const MyClass obj;
obj.incrementCounter();
std::cout << obj.getCounter() << std::endl;
return 0;
}
mutable 关键字用于修饰类的成员变量,使得这些变量即使在 const 成员函数中也可以被修改,常用于实现一些与对象逻辑状态无关的计数或缓存功能。
3. 强制类型转换绕过const限制
虽然可以使用 const_cast 等强制类型转换来绕过 const 限制,但这是非常危险的操作,可能会导致未定义行为,应该尽量避免。
const int num = 10;
int* ptr = const_cast(&num);
*ptr = 20; // 未定义行
4.const修饰函数重载
const 成员函数可以和非 const 成员函数构成重载,根据对象是否为 const 来选择调用不同的函数。
class MyClass {
private:
int data;
public:
MyClass(int value) : data(value) {}
int& getData() { return data; }
const int& getData() const { return data; }
};
int main() {
MyClass obj(10);
obj.getData() = 20; // 调用非 const 版本
const MyClass constObj(10);
// constObj.getData() = 20; // 编译错误,调用 const 版本
return 0;
}