Const(常量)
const
关键字在C和C++中是一个非常有用的工具,帮助开发者编写更加健壮和安全的代码。它可以用于:
- 防止变量的值被意外修改。
- 保护指针指向的数据不被修改。
- 保证函数参数在函数内部不被修改。
- 确保类成员函数不修改对象的状态。
定义一个不可修改的变量
const int x = 10;
const指针
指向常量的指针(const T* ptr)
const int value = 10;
const int* ptr = &value;
int anotherValue = 20;
ptr = &anotherValue; // 合法,指针本身可以修改
*ptr = 30; // 非法,不能通过该指针修改指向的数据
这种情况表示指针指向的内容是常量,不能通过该指针修改指向的数据,但指针本身可以修改,指向其他数据
常量指针或指针常量(T* const ptr)
int value = 10;
int* const ptr = &value;
*ptr = 20; // 合法,可以通过该指针修改指向的数据
int anotherValue = 30;
ptr = &anotherValue; // 非法,不能修改指针本身
指针本身是常量,不能修改指针的值,但是可以通过该指针修改指向的数据。
指向常量的指针常量(const T* const ptr)
const int value = 10;
const int* const ptr = &value;
*ptr = 20; // 非法,不能通过该指针修改指向的数据
int anotherValue = 30;
ptr = &anotherValue; // 非法,不能修改指针本身
这种情况指针和指针指向都是常量,指针本身和指向的数据都不能修改。
* 指向常量对象的指针(const Objcet)
class MyClass {
public:
MyClass(int v) : value(v) {}
int getValue() const { return value; }
void setValue(int v) { value = v; }
private:
int value;
};
const MyClass obj(10);
const MyClass* ptr = &obj;
std::cout << ptr->getValue() << std::endl; // 合法,可以调用 const 成员函数
ptr->setValue(20); // 非法,不能调用非 const 成员函数
与基本类型类似 指向常量对象的指针不能通过该指针调用修改对象状态的成员函数。
const在类中的使用
const成员函数
class MyClass {
public:
void display() const {
// 不能修改成员变量
}
private:
int value;
};
确保成员函数不会修改对象的状态。
const成员变量
class MyClass {
public:
MyClass(int val) : constValue(val) {}
private:
const int constValue;
};
定义一旦对象创建就不可修改的成员变量。
mutable 关键字
class MyClass {
public:
int getValue() const {
if (!cacheValid) {
cachedValue = computeValue();
cacheValid = true;
}
return cachedValue;
}
private:
mutable int cachedValue;
mutable bool cacheValid;
};
允许在 const
成员函数中修改成员变量。
用于更复杂的情况。
const修饰函数参数
void printValue(const int value) {
std::cout << value << std::endl;
// value = 5; // 非法,不能修改 const 参数
}
当一个函数参数被 const
修饰时,表示在函数体内不能修改该参数的值。这种做法的主要优点是防止在函数体内意外修改传入的参数,同时提高了函数的可读性(自文档性),表明该参数在函数内不会被改变。
const修饰返回值
class MyClass {
public:
MyClass(const std::string& name) : name(name) {}
const std::string& getName() const {
return name;
}
private:
std::string name;
};
const
可以用来修饰返回值,特别是在返回引用或指针时,确保返回的对象不能通过该引用或指针修改。
更详细的实例
#include <iostream>
#include <string>
class User {
public:
User(const std::string& username) : username(username) {}
void setUsername(const std::string& newUsername) {
username = newUsername;
}
const std::string& getUsername() const {
return username;
}
void printUsername() const {
std::cout << "Username: " << getUsername() << std::endl;
}
private:
std::string username;
};
int main() {
User user("john_doe");
user.printUsername(); // 输出: Username: john_doe
user.setUsername("jane_doe");
user.printUsername(); // 输出: Username: jane_doe
const std::string& name = user.getUsername();
std::cout << "Retrieved Username: " << name << std::endl; // 输出: Retrieved Username: jane_doe
// name = "hacker"; // 非法,不能修改 const 引用
return 0;
}
const实现原理
- 编译时检查:编译器在编译阶段会对所有
const
声明进行检查,确保变量、指针、成员函数、参数和返回值在被声明为const
后不会被修改。 - 类型系统:编译器会将
const
变量、指针、成员函数、参数和返回值视为类型系统的一部分,从而在类型检查时确保常量性。例如,在const
成员函数中,编译器会将this
指针视为const Type* const
。 - 生成错误:如果在代码中尝试修改
const
声明的元素,编译器会生成编译错误,阻止不合法的修改操作。
const和define预编译指令优缺点
const
和 #define
都可以用于定义常量,但它们的实现机制和适用场景不同。const
提供了类型安全和作用域控制,适合用于复杂和大型项目中。而 #define
提供了简单的文本替换,适合用于小型和快速开发的场景。选择哪种方式取决于具体的需求和代码风格。
const 相比于 define 优点
const
提供比 #define
更好的类型安全,原因包括:
- 类型检查:
const
具有完整的类型信息,编译器可以进行类型检查,确保类型一致性。 - 作用域控制:
const
遵循 C++ 的作用域规则,避免了全局命名冲突的问题,而define是全局的容易造成意外的替换。 - 内存管理:
const
常量在内存中有明确的类型,有助于编译器进行优化。
const
在调试时也比define更加方便 例如:类型错误时const报错会定位到具体行数 而define只会定义到define那句话。