C++中const关键字的作用
公众号:阿Q技术站
1. const 用于常量
最常见的 const 用法是声明常量变量,它使得变量在初始化后不可再被修改。
1.1 基本常量声明
const int a = 10; // 常量变量,初始化后不可修改
a被声明为常量,因此在后续代码中不能修改它的值。- 如果尝试
a = 20;,编译器会报错。
1.2 常量指针
const 可以用于指针类型,限制指针本身或指针指向的内容不能被修改。具体来说,const 可以出现在两种位置:指针变量本身或指针指向的对象。
1.2.1 指向常量的指针(指向内容是常量)
const int* ptr = &a; // ptr 是指向常量整数的指针,指针指向的值不能修改
- 这里,
ptr是指向const int类型的指针,即指针指向的内容不能修改,但指针本身是可以指向其他地址的。 - 你可以修改
ptr本身指向其他地址,但不能通过ptr修改它指向的值。
1.2.2 常量指针(指针本身是常量)
int* const ptr = &a; // ptr 是常量指针,指针本身不可修改,但可以修改它指向的值
- 这里,
ptr是一个常量指针,即指针本身的地址不可更改,但指针指向的内容是可以修改的。 - 你不能让
ptr指向其他地址,但可以通过ptr修改它指向的值。
1.2.3 常量指针常量(指向常量的常量指针)
const int* const ptr = &a; // ptr 是常量指针,指向的内容也是常量
这里,ptr 是指向常量的常量指针,既不能改变指针指向的地址,也不能通过 ptr 修改它指向的值。
2. const 用于类成员函数
在类成员函数中,const 关键字常常用于标记不会修改类状态的函数。
2.1 常成员函数
常成员函数的声明中,const 位于函数声明的末尾,表示该函数不会修改类的成员变量。
class MyClass {
public:
int getValue() const { return value; } // 该函数不会修改类的成员变量
private:
int value;
};
getValue()被声明为const成员函数,表示它不会修改任何成员变量。- 这样做的好处是可以让你在
const对象上调用这些成员函数,即使该对象是常量,仍然能够调用不修改状态的函数。
2.2 this 指针的 const
在 const 成员函数中,this 指针是 const 的,意味着不能通过 this 指针修改成员变量。
class MyClass {
public:
void show() const {
// this->value = 10; // 错误:不能通过 const 成员函数修改成员变量
}
private:
int value;
};
在 show() 函数中,this 指针指向的是一个常量对象,因此你不能修改它的成员变量。
3. const 用于函数参数
使用 const 关键字声明函数参数,表示函数内部不能修改该参数。常用于传递引用或指针时,保证函数不会修改传入的参数。
3.1 常量引用参数
void printValue(const int& value) {
// value = 10; // 错误:不能修改常量引用的值
std::cout << value << std::endl;
}
const int& value表示value是一个常量引用,它指向一个整数值,但不能修改它的值。- 常量引用传递的好处是避免了不必要的拷贝,同时保证了参数在函数内部不会被修改。
3.2 常量指针参数
void printValue(const int* ptr) {
// *ptr = 10; // 错误:不能修改通过 const 指针访问的值
std::cout << *ptr << std::endl;
}
const int* ptr 表示指针 ptr 指向一个常量整数,不能通过指针修改其指向的内容。
3.3 作为参数的常量指针常量
void printValue(const int* const ptr) {
// *ptr = 10; // 错误:不能修改指针指向的值
// ptr = nullptr; // 错误:不能修改常量指针本身
std::cout << *ptr << std::endl;
}
const int* const ptr 是一个常量指针常量,既不能修改指针的地址,也不能修改指针指向的内容。
4. const 用于常量数组和字符串
常量数组和字符串也可以通过 const 来限制修改。
4.1 常量数组
const int arr[] = {1, 2, 3};
arr[0] = 10; // 错误:不能修改常量数组元素
arr 是一个常量数组,因此无法修改数组的元素。
4.2 常量字符串
const char* str = "Hello";
str[0] = 'h'; // 错误:不能修改字符串常量
字符串常量本身是不可修改的,因此尝试修改字符串中的内容会导致错误。
5. const 与类型推导
在 C++11 引入的 auto 关键字中,const 也可以与 auto 一起使用来推导常量类型。
5.1 const 与 auto
auto value = 10; // 推导为 int
const auto cvalue = 10; // 推导为 const int
如果使用 const 修饰 auto,则编译器会推导出常量类型。
6. const 与常量表达式
C++11 引入了 constexpr,它与 const 具有一些相似性,但也有区别。constexpr 是用于声明常量表达式,意味着在编译时可以求值的常量。
6.1 constexpr 与 const 的区别
const声明的是常量,值可以在运行时确定,但声明后不能更改。constexpr声明的是常量表达式,要求在编译时就能够求值。
const int x = 10; // x 的值可以在运行时赋值
constexpr int y = 20; // y 的值必须在编译时已知
const变量不一定是常量表达式,只是值在程序中不可修改。constexpr强制要求值在编译时已知,常用于数组大小或模板参数等需要编译时求值的场景。
6.2 在函数中使用 constexpr
constexpr int square(int x) {
return x * x;
}
int main() {
int arr[square(5)]; // 数组大小可以在编译时求值
}
constexpr 可以用于函数定义,它确保函数在编译时求值并返回常量。
7. const 与模板
const 在模板中的应用可以帮助提高代码的灵活性和类型安全性。
7.1 const 修饰模板类型参数
const 可以修饰模板类型参数,限制模板参数在模板实例化时不可修改。
template <typename T>
void printValue(const T& value) {
std::cout << value << std::endl;
}
这里 T& 表示 value 是一个对 T 类型的引用,const 修饰 T& 表明引用的值不可修改。
7.2 const 与模板特化
const 可以用于模板特化中,指定不同的行为。
template <typename T>
void printValue(T value) {
std::cout << value << std::endl;
}
template <>
void printValue<const char*>(const char* value) {
std::cout << "String: " << value << std::endl;
}
这里我们对 const char* 进行了模板特化,以便提供不同的行为。
8. const 与移动语义(C++11及以后)
C++11 引入了移动语义,const 可以与移动构造函数和移动赋值运算符一起使用来优化性能。
8.1 const 在移动构造函数中的应用
当使用 const 来修饰类的成员时,可以防止对象在移动操作中被意外修改。特别是在移动构造函数和移动赋值运算符中,const 能够确保某些不该改变的对象数据不会被改变。
class MyClass {
public:
const int x;
MyClass(int val) : x(val) {}
// 移动构造函数
MyClass(MyClass&& other) : x(other.x) {} // `x` 被移动但不可修改
};
9. const 与 Lambda 表达式
C++11 引入了 Lambda 表达式,const 可以用于限制 lambda 表达式内部的变量修改。
9.1 const 修饰捕获列表
int main() {
int x = 10;
auto lambda = [x]() {
std::cout << x << std::endl;
// x = 20; // 错误:不能修改捕获的常量变量
};
lambda();
}
在捕获列表中指定 const 表示捕获的变量不可修改。
9.2 const 修饰 lambda 函数参数
auto lambda = [](const int& x) {
// x = 10; // 错误:参数是常量引用,不能修改
std::cout << x << std::endl;
};
使用 const 修饰 lambda 参数,确保 lambda 内部不能修改传入的参数。
10. const 与并发编程
在并发编程中,const 可以用于保证线程安全。特别是当多线程访问共享资源时,使用 const 可以帮助避免对共享资源的无意修改,从而减少潜在的竞态条件。
10.1 常量数据与线程
const int MAX_THREADS = 8; // 将 `MAX_THREADS` 声明为常量,确保它的值在程序中不会被修改。
10.2 const 修饰共享资源
void processData(const std::vector<int>& data) {
// data 内容不可被修改,确保数据的一致性
}
在多线程环境中,通过将共享资源声明为 const,可以防止资源在并发操作中被修改。
11. 使用 const 优化性能
在传递大型对象(如容器、类对象等)时,使用 const 修饰参数,尤其是常量引用,可以避免不必要的复制,提高程序性能。
11.1 常量引用优化
void printVector(const std::vector<int>& vec) {
for (int i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
}
使用常量引用 const std::vector<int>& 可以避免复制整个 vector,提高性能。
12. const 与条件编译
const 也可以在条件编译中使用,来定义根据条件编译不同的常量。
12.1 条件编译常量
#ifdef DEBUG
const int bufferSize = 1024;
#else
const int bufferSize = 512;
#endif
根据 DEBUG 宏的定义,选择不同的常量值。
常量性的重要性
使用const可以提高代码的可读性和安全性,它告诉其他程序员和维护者这些值不应该被修改。此外,const还可以帮助编译器优化代码,因为编译器可以假设const变量的值在声明后不会改变。
总结
| 用法 | 说明 | 示例代码 |
|---|---|---|
const 修饰变量 | 声明常量变量,变量值不可修改 | const int x = 10; |
const 修饰指针 | 定义常量指针(指针不可修改)或指向常量的指针(内容不可修改) | const int* ptr; / int* const ptr; |
const 修饰成员函数 | 成员函数声明为常量,表示该函数不会修改类的状态 | void show() const; |
const 修饰函数参数 | 传递引用或指针时,保证函数内部不会修改传入的参数 | void func(const int& value); |
const 修饰数组 | 声明常量数组,数组元素不可修改 | const int arr[] = {1, 2, 3}; |
const 修饰字符串 | 声明常量字符串,字符串内容不可修改 | const char* str = "Hello"; |
const 与 auto | 与 auto 一起使用,推导常量类型 | const auto cvalue = 10; |
const 与 constexpr | 用于声明常量表达式,要求编译时求值 | constexpr int y = 20; |
const 与模板 | 修饰模板参数,限制模板参数不可修改 | template <typename T> void func(const T& value); |
const 与 Lambda | 在 Lambda 表达式中限制捕获或参数不可修改 | auto lambda = [x]() { ... }; |
const 与并发编程 | 确保在多线程环境中共享资源不可修改,保证数据一致性 | void processData(const std::vector<int>& data); |