const
关键字在 C++ 中有多种用法,旨在表示常量性,防止数据在程序执行期间被意外修改。下面从多个角度详细讲解 const
的用法,并提供相应的代码示例。
C++ const关键字用法
1. 常量变量
使用 const
定义的变量表示其值在初始化后不可修改。
const int MAX_SIZE = 100;
MAX_SIZE = 200; // 错误:无法修改 const 变量
2. 指针和 const
2.1 指向常量的指针
指针本身可以变化,但它指向的数据不能修改。
const int value = 10;
const int* ptr = &value;
*ptr = 20; // 错误:不能修改指向的常量数据
ptr = &another_value; // 合法:指针本身可以指向别的地址
2.2 常量指针
指针本身是常量,不能指向别的地址,但指向的数据可以修改。
int value = 10;
int* const ptr = &value;
*ptr = 20; // 合法:可以修改指向的数据
int another_value = 30;
ptr = &another_value; // 错误:指针本身是常量,不能修改指向
2.3 指向常量的常量指针
指针本身和它指向的数据都不能修改。
const int value = 10;
const int* const ptr = &value;
*ptr = 20; // 错误:不能修改指向的常量数据
ptr = &another_value; // 错误:指针本身也是常量
3. 常量成员函数
在类中声明的成员函数如果不修改成员变量,应将其声明为 const
,这表明该函数不修改对象的状态。
class MyClass {
public:
int getValue() const {
return value;
}
void setValue(int newValue) {
value = newValue;
}
private:
int value;
};
const MyClass obj;
obj.getValue(); // 合法
obj.setValue(5); // 错误:const 对象只能调用 const 成员函数
4. 常量引用
通过 const
引用传递参数可以避免不必要的拷贝,同时防止数据被修改。
void printValue(const int& value) {
std::cout << value << std::endl;
}
int main() {
int x = 10;
printValue(x); // 合法,传递 const 引用
}
5. 返回常量引用
函数可以返回一个常量引用,以避免调用者修改内部数据。
class MyClass {
public:
const int& getValue() const {
return value;
}
private:
int value;
};
int main() {
MyClass obj;
const int& val = obj.getValue();
val = 10; // 错误:不能修改常量引用
}
6.拓展
1. 常量表达式 constexpr
(C++11 及以后)
const
和 constexpr
都用于定义常量,但它们有不同的用途和限制。以下详细介绍这两个关键字的区别和各自的特点。
const
const
关键字用于声明一个常量,表示其值在程序运行期间不会改变。const
可以用于变量、指针、引用、函数参数和成员函数等。
const int a = 5; // a 是一个常量变量
const int* p = &a; // p 是一个指向常量的指针
特点
- 运行时常量:
const
常量可以在运行时初始化,但初始化后不可修改。 - 作用域限制:
const
的作用域可以是局部或全局。 - 编译时不一定知道值:
const
常量不要求编译时知道具体值。
constexpr
constexpr
是在 C++11 引入的,用于声明一个常量表达式,即在编译时就可以确定其值的表达式。constexpr
可用于变量、函数和构造函数。
constexpr int square(int x) {
return x * x;
}
constexpr int y = square(5); // y 是一个常量表达式,编译时计算
特点
- 编译时常量:
constexpr
保证表达式在编译时计算,这可以用于优化和提高效率。 - 函数和变量:
constexpr
可以用于函数,使得函数返回的值在编译时就能确定。 - 强制编译时计算:如果一个
constexpr
表达式在编译时无法确定其值,编译器将报错。
区别
-
编译时 vs 运行时:
const
可以在运行时初始化,而constexpr
必须在编译时能计算出值。- 例如,
const int x = someFunction();
是合法的,只要someFunction()
在运行时返回一个值。但constexpr int y = someFunction();
只有当someFunction()
是一个constexpr
函数并且可以在编译时计算时才合法。
-
优化和使用场景:
constexpr
常用于需要在编译时进行计算以便于编译器优化的场景,如模板元编程。const
常用于声明不可变的常量数据,但这些数据不一定要在编译时已知。
示例对比
以下是一个对比示例,展示了 const
和 constexpr
的不同用法及其限制:
#include <iostream>
// const 示例
const int runTimeValue() {
return 42;
}
void constExample() {
const int a = runTimeValue(); // 合法:运行时初始化
std::cout << "const value: " << a << std::endl;
}
// constexpr 示例
constexpr int compileTimeValue(int x) {
return x * x;
}
void constexprExample() {
constexpr int b = compileTimeValue(5); // 合法:编译时计算
std::cout << "constexpr value: " << b << std::endl;
}
// 错误示例:不能在编译时计算的 constexpr
// constexpr int invalid = runTimeValue(); // 错误:runTimeValue() 不是 constexpr 函数
int main() {
constExample();
constexprExample();
return 0;
}
总结
const
用于在运行时初始化后不可变的常量。constexpr
用于在编译时就能确定值的常量表达式,以便编译器优化和模板元编程。
2. 常量迭代器
在使用标准模板库(STL)容器时,可以使用 const_iterator
遍历容器,以确保迭代器不修改容器内容。
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (std::vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << std::endl;
*it = 10; // 错误:不能通过 const_iterator 修改内容
}
}
综上所述,const
是一个非常有用的关键字,可以在多种情况下使用,以增强代码的安全性和可维护性。通过上述示例,可以全面了解 const
的不同用法及其带来的好处。