# C++ 中 const 关键字

面试高频指数:★★★★★

在 C/C++ 中,const 是一个关键字,用于表示常量。

const 可以用于修饰变量、函数、指针等,主要作用有以下几种:

# 1. 修饰变量

const 修饰变量时,该变量将被视为只读变量,即不能被修改。

对于确定不会被修改的变量,应该加上 const,这样可以保证变量的值不会被无意中修改,也可以使编译器在代码优化时更加智能。

例如:

const int a = 10;
a = 20; // 编译错误,a 是只读变量,不能被修改

但是!!!请注意!!!

这里的变量只读,其实只是编译器层面的保证,实际上可以通过指针在运行时去间接修改这个变量的值,当然这个方法比较trick。

const int 类型取指针,就是 const int* 类型的指针,将其强制转换为 int* 类型,就去掉了 const 限制,从而修改变量的值。

在 C++ 中,将 const 类型的指针强制转换为非const 类型的指针被称为类型强制转换(Type Casting),这种行为称为 const_cast

关于 const_cast 可以看下这篇文章: C++几种类型转换的区别 (opens new window)

虽然可以这样操作,但这违反了 const的语义,可能会导致程序崩溃或者产生未定义行为(undefined behavior),大家学习了解即可,实际编程中切莫如此操作。

因为编译器可能会做一些优化!!也就是在你用到 const 变量的地方,编译器可能生成的代码直接就替换为常量的值,而不是访问一遍常量的指令。

所以极大可能你虽然修改了值,但是却不起作用!

下面👇这个例子,展示了使用 const_cast 修改 const变量的值却不会起作用:

const int a = 10;
const int* p = &a;
int* q = const_cast<int*>(p);
*q = 20;  // 通过指针间接修改 const 变量的值
std::cout << "a = " << a << std::endl;  // 输出 a 的值,结果为 10

在上面的例子中,将 p 声明为 const int* 类型,指向只读变量 a 的地址。

然后使用 const_castp 强制转换为 int* 类型的指针 q,从而去掉了 const限制。

接下来,通过指针 q 间接修改了变量 a 的值。

但是请注意,即使 a 的值被修改了,但在程序中输出a 的值仍然是 10

正如前面所有,因为 a 是只读变量,所以编译器做了优化,早就把代码实际替换为了👇下面这样:

std::cout << "a = " << 10 << std::endl;

所以,咱们还是要老老实实按照语言标准编程,切莫搞各种骚操作。

总之,使用 const_cast 去掉 const 限制是不推荐的,这会破坏程序的正确性和稳定性。

我们应该遵循 C/C++ 语言中 const 的语义,尽量不修改只读变量的值。

# 2. 修饰函数参数,表示函数不会修改参数

const 修饰函数参数时,表示函数内部不会修改该参数的值。这样做可以使代码更加安全,避免在函数内部无意中修改传入的参数值。

尤其是 引用 作为参数时,如果确定不会修改引用,那么一定要使用 const 引用。

例如:

void func(const int a) {
    // 编译错误,不能修改 a 的值
    a = 10;
}

# 3. 修饰函数返回值

const 修饰函数返回值时,表示函数的返回值为只读,不能被修改。这样做可以使函数返回的值更加安全,避免被误修改。

例如:

const int func() {
    int a = 10;
    return a;
}

int main() {
    const int b = func(); // b 的值为 10,不能被修改
    b = 20; // 编译错误,b 是只读变量,不能被修改
    return 0;
}

# 4. 修饰指针或引用

在 C/C++ 中,const 关键字可以用来修饰指针,用于声明指针本身为只读变量或者指向只读变量的指针。

根据 const 关键字的位置和类型,可以将 const 指针分为以下三种情况:

# 4.1. 指向只读变量的指针

这种情况下,const 关键字修饰的是指针所指向的变量,而不是指针本身。

因此,指针本身可以被修改(意思是指针可以指向新的变量),但是不能通过指针修改所指向的变量。

const int* p;  // 声明一个指向只读变量的指针,可以指向 int 类型的只读变量
int a = 10;
const int b = 20;
p = &a;  // 合法,指针可以指向普通变量
p = &b;  // 合法,指针可以指向只读变量
*p = 30;  // 非法,无法通过指针修改只读变量的值

在上面的例子中,我们使用 const int* 声明了一个指向只读变量的指针 p

我们可以将指针指向普通变量或者只读变量,但是无法通过指针修改只读变量的值。

# 4.2 只读指针

这种情况下,const 关键字修饰的是指针本身,使得指针本身成为只读变量。

因此,指针本身不能被修改(即指针一旦初始化就不能指向其它变量),但是可以通过指针修改所指向的变量。

int a = 10;
int b = 20;
int* const p = &a;  // 声明一个只读指针,指向 a
*p = 30;  // 合法,可以通过指针修改 a 的值
p = &b;  // 非法,无法修改只读指针的值

在上面的例子中,我们使用 int* const 声明了一个只读指针 p,指向变量 a。我们可以通过指针修改 a 的值,但是无法修改指针的值。

# 4.3 只读指针指向只读变量

这种情况下,const 关键字同时修饰了指针本身和指针所指向的变量,使得指针本身和所指向的变量都成为只读变量。

因此,指针本身不能被修改,也不能通过指针修改所指向的变量。

const int a = 10;
const int* const p = &a;  // 声明一个只读指针,指向只读变量 a
*p = 20;  // 非法,无法通过指针修改只读变量的值
p = nullptr;  // 非法,无法修改只读指针的值

# 4.4 常量引用

常量引用是指引用一个只读变量的引用,因此不能通过常量引用修改变量的值。

const int a = 10;
const int& b = a;  // 声明一个常量引用,引用常量 a
b = 20;  // 非法,无法通过常量引用修改常量 a 的值

# 5. 修饰成员函数

const 修饰成员函数时,表示该函数不会修改对象的状态(就是不会修改成员变量)。

这样有个好处是,const 的对象就可以调用这些成员方法了,因为 const 对象不允许调用非 const 的成员方法。

也很好理解,既然对象是 const 的,那我怎么保证调用完这个成员方法,你不会修改我的对象成员变量呢?那就只能你自己把方法声明未 const 的呢~

例如:

class A {
public:
    int func() const {
        // 编译错误,不能修改成员变量的值
        m_value = 10;
        return m_value;
    }
private:
    int m_value;
};

这里还要注意,const 的成员函数不能调用非 const 的成员函数,原因在于 const 的成员函数保证了不修改对象状态,但是如果调用了非 const 成员函数,那么这个保证可能会被破坏。

总之,const 关键字的作用是为了保证变量的安全性和代码可读性。

最新原创的文章都先发布在公众号,欢迎关注哦~,
扫描下方二维码回复「CS」可以获得我汇总整理的计算机学习资料~

编程指北图片
@2021-2024 编程指北 版权所有 粤ICP备2021169086号-2