Menu
CoddyTech

mutable 关键字

本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 30 课,共 104 课。

你已经学过,const 成员函数不能修改任何成员变量。但有时你需要修改不会影响对象逻辑状态的成员,比如缓存或访问计数器。mutable 关键字可以作为 const 限制的例外。

可变成员即使在 const 成员函数中也可以修改:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // 可以在 const 函数中修改
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // 可以这样做,因为 accessCount 是 mutable 的
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

如果没有 mutable,在常量函数 getData() 中递增 accessCount 会导致编译器错误。mutable 关键字告诉编译器:“即使对象被视为常量,也允许更改这个成员。”

mutable 的常见用途包括缓存计算结果、跟踪访问统计信息,以及实现互斥锁等同步原语。在每种情况下,修改都不会改变对象在逻辑上所代表的内容。缓存的值只是优化手段,访问计数器则是关于使用情况的元数据。

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

谨慎使用 mutable,它只应应用于不会影响对象可观察状态的成员。

challenge icon

挑战

简单

让我们构建一个文档查看器,用来跟踪文档被查看的次数,同时不破坏只读方法的 const 正确性。这是使用 mutable 关键字的绝佳场景。

你将创建两个文件来组织代码:

  • Document.h:定义一个 Document 类,用于存储内容并悄悄跟踪查看统计信息。你的类需要包含:
    • 私有成员:title(字符串)、content(字符串),以及一个即使在 const 方法中也能修改的 mutable int viewCount
    • 一个接收标题和内容的构造函数,并将 viewCount 初始化为 0
    • 一个返回标题的 getTitle() const 方法
    • 一个 getContent() const 方法,用于递增 viewCount(因为它是 mutable,所以可以这样做)并返回内容
    • 一个 getViewCount() const 方法,用于返回内容被访问的次数
    • 一个 getPreview() const 方法,用于返回内容的前 20 个字符,后面接 "..."。这不应该递增查看次数,因为这只是预览
  • main.cpp:演示 mutable 如何在保持方法逻辑上为 const 的同时跟踪访问统计信息。从输入中读取文档标题和内容(各占一行),然后:
    • 使用输入的值创建一个 Document
    • 打印 "Title: <title>"
    • 打印 "Preview: <preview>"
    • 打印 "Views after preview: <count>"
    • 使用 getContent() 访问完整内容,并打印 "Content: <content>"
    • 打印 "Views after first read: <count>"
    • 再次访问内容,并打印 "Content: <content>"
    • 打印 "Views after second read: <count>"
    • 创建一个辅助函数 void displayDocument(const Document& doc),用于打印 "[Const Access] <title>: <content>"。这可以证明我们的 const 方法能够与 const 引用配合使用
    • 使用你的文档调用 displayDocument()
    • 打印 "Final view count: <count>"

预览后,查看次数应该是 0(因为预览不算完整查看),之后每次调用 getContent() 时都会递增。mutable 关键字让这一点成为可能。如果没有它,你就无法在 getContent() 这样的 const 方法中修改 viewCount。

如果内容不足 20 个字符,getPreview() 应该返回完整内容,后面接 "..."。

亲手试试

#include <iostream>
#include <string>
#include "Document.h"
using namespace std;

// TODO: 实现 displayDocument 函数
// 它应输出:"[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
    // TODO: 实现这个函数
}

int main() {
    // 读取输入
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: 用输入的值创建一个 Document 对象
    
    // TODO: 输出 "Title: <title>"
    
    // TODO: 输出 "Preview: <preview>"
    
    // TODO: 输出 "Views after preview: <count>"
    
    // TODO: 访问完整内容并输出 "Content: <content>"
    
    // TODO: 输出 "Views after first read: <count>"
    
    // TODO: 再次访问内容并输出 "Content: <content>"
    
    // TODO: 输出 "Views after second read: <count>"
    
    // TODO: 用你创建的文档调用 displayDocument()
    
    // TODO: 输出 "Final view count: <count>"
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器