mutable在C语言中如何使用?

99ANYc3cd6
预计阅读时长 17 分钟
位置: 首页 C语言 正文

这是一个在 C++ 中非常常见但在标准 C 语言中不存在的关键字,很多人会混淆这两者,所以理解它们的区别至关重要。

mutable c语言
(图片来源网络,侵删)

核心结论:mutable 不是 C 语言的关键字

最重要的一点是:mutable 是 C++ 的一个关键字,不是标准 C 语言的一部分。

如果你在标准的 C 编译器(如 GCC 使用 c99c11 标准,MSVC 的 /std:c11 等)中使用 mutable,编译器会报错,提示“未知类型”或“未定义的标识符”。


mutable 在 C++ 中是做什么的?

在 C++ 中,mutable 的主要作用是:允许一个成员变量的值可以被 const 成员函数修改

为什么需要 mutable?—— const 成员函数的限制

在 C++ 中,如果一个成员函数被声明为 const,它承诺不会修改该对象的任何非静态成员变量,这是为了确保 const 对象的安全性,防止其状态被意外改变。

mutable c语言
(图片来源网络,侵删)
class MyClass {
public:
    int value;
    int* counter;
    MyClass(int v) : value(v), counter(new int(0)) {}
    // 这是一个 const 成员函数
    void print() const {
        std::cout << "Value: " << value << std::endl;
        // value = 10; // 错误!不能修改非静态成员变量
        // counter = nullptr; // 错误!同样不能修改
    }
};

mutable 的解决方案

我们确实需要在 const 成员函数中修改一些变量,但这些变量的修改并不影响对象的核心逻辑或“状态”。

  • 缓存计算结果。
  • 记录某个函数被调用的次数。
  • 在多线程环境中进行线程同步(如使用互斥锁)。

mutable 关键字就是为这种场景设计的,被 mutable 修饰的成员变量,即使在 const 成员函数中,也可以被修改。

mutable 的使用示例

让我们来看一个经典的缓存示例:

#include <iostream>
class Calculator {
private:
    int last_result; // 存储上一次的计算结果
    mutable bool cache_valid; // 标记缓存是否有效
    mutable int cache; // 缓存值
public:
    Calculator() : last_result(0), cache_valid(false), cache(0) {}
    // 计算平方,这是一个 const 成员函数
    int square(int x) const {
        // 即使是 const 函数,我们也可以修改 mutable 变量
        if (!cache_valid || x != last_result) {
            cache = x * x;      // 重新计算并更新缓存
            cache_valid = true; // 标记缓存已更新
            last_result = x;
        }
        std::cout << "Using cached value for " << x << std::endl;
        return cache;
    }
};
int main() {
    const Calculator calc; // 创建一个 const 对象
    std::cout << calc.square(5) << std::endl; // 第一次计算,缓存未命中
    std::cout << calc.square(5) << std::endl; // 第二次,使用缓存
    std::cout << calc.square(6) << std::endl; // 新的输入,缓存未命中
    return 0;
}

代码解析:

  1. 我们有一个 const Calculator 对象 calc
  2. square 函数被声明为 const,因为它只读取输入 x 并返回结果,不改变对象的核心“计算能力”。
  3. 为了提高效率,我们想缓存上一次的计算结果,这里 cachecache_valid 变量的修改,不应该破坏 const 对象的“逻辑常量性”(logical constness)。
  4. 通过将 cache_validcache 声明为 mutable,我们就可以在 const 成员函数 square 中安全地修改它们,实现了缓存功能,而不会违反 const 的承诺。

C 语言中如何实现类似 mutable 的效果?

既然 C 语言没有 mutable,如果遇到类似的需求(在某个函数中需要修改一个“看起来不应该变”的变量),我们可以采用以下几种方法:

使用 const 修饰指针(模拟 const 成员函数)

在 C 中,const 更多是用来修饰指针和变量,而不是像 C++ 那样作用于成员函数,我们可以模拟一个 const 成员函数的行为。

#include <stdio.h>
// 结构体相当于 C++ 的类
typedef struct {
    int value;
    int* counter; // 模拟一个需要修改的“内部状态”
} Data;
// “const” 成员函数:通过 const 指针访问
void print_data(const Data* d) {
    printf("Value: %d\n", d->value);
    // d->value = 10; // 错误!不能通过 const 指针修改
}
// 一个需要修改内部状态的函数(非 const)
void update_counter(Data* d) {
    if (d->counter) {
        (*d->counter)++;
    }
}
int main() {
    Data my_data = {42, &(int){0}};
    Data* const p_data = &my_data; // 指针本身是 const,指向的 Data 可变
    print_data(p_data); // 安全地读取
    update_counter(p_data); // 明确调用一个可以修改的函数
    printf("Counter is now: %d\n", *my_data.counter);
    return 0;
}

在这个 C 示例中,print_data 接收一个 const Data*,这模拟了 C++ 中 const 成员函数的行为,它承诺不会通过这个指针修改 Data 的内容,而 update_counter 是一个普通的函数,可以修改 Data,C++ 的 mutable 将这两种行为合并在一个函数中,而 C 语言则需要通过函数签名来区分。

使用全局变量或静态变量(不推荐,破坏封装性)

如果只是想记录一个调用次数,可以使用全局变量或函数内的静态变量,但这会破坏数据的封装性,通常不推荐。

#include <stdio.h>
void function_with_counter() {
    static int call_count = 0; // 静态变量,生命周期是整个程序
    call_count++;
    printf("Function called %d times.\n", call_count);
}
int main() {
    function_with_counter(); // 1
    function_with_counter(); // 2
    function_with_counter(); // 3
    return 0;
}

使用 void* 上下文指针(回调函数常用)

在一些高级 API(如回调函数)中,会传递一个 void* 指针作为上下文,让用户可以在回调中修改外部数据。

#include <stdio.h>
void process_data(void* context) {
    int* counter = (int*)context;
    (*counter)++;
}
int main() {
    int my_counter = 0;
    process_data(&my_counter); // 传递上下文
    printf("Counter is now: %d\n", my_counter); // 1
    return 0;
}

特性 C++ mutable C 语言
存在性 C++ 的关键字 不是 C 语言的关键字
核心作用 允许 const 成员函数修改特定成员变量 无直接对应物
设计哲学 支持逻辑常量性,允许在 const 函数中修改不影响对象状态的变量(如缓存、计数器) 通过函数签名(const 指针)来区分“只读”和“读写”操作
实现方式 在成员变量前加 mutable 关键字 使用 const 修饰指针、全局变量、静态变量或 void* 上下文等
推荐用法 用于实现缓存、线程同步等,保持 const 成员函数的纯净性 严格区分“只读”和“读写”函数,避免使用全局变量破坏封装性

如果你在写 C++ 代码,并且需要在 const 成员函数里修改一些“无关紧要”的变量,mutable 是你的不二之选,如果你在写 C 代码,你需要换一种思路,通过函数签名和指针来明确表达数据的可变性。

-- 展开阅读全文 --
头像
strlwr函数在C语言中如何正确使用?
« 上一篇 04-15
dede栏目如何增加自定义字段?
下一篇 » 04-15

相关文章

取消
微信二维码
支付宝二维码

目录[+]