类中的 mutable

mutable 从字面意思上来说,是「可变的」之意。

若是要「顾名思义」,那么这个关键词的含义就有些意思了。显然,「可变的」只能用来形容变量,而不可能是「函数」或者「类」本身。然而,既然是「变量」,那么它本来就是可变的,也没有必要使用 mutable 来修饰。那么,mutable 就只能用来形容某种不变的东西了。

C++ 中,不可变的变量,称之为常量,使用 const 来修饰。然而,若是 const mutable 联用,未免让人摸不着头脑——到底是可变还是不可变呢?

事实上,mutable 是用来修饰一个 const 示例的部分可变的数据成员的。如果要说得更清晰一点,就是说 mutable 的出现,将 C++ 中的 const 的概念分成了两种。

  • 二进制层面的 const,也就是「绝对的」常量,在任何情况下都不可修改(除非用 const_cast)。

  • 引入 mutable 之后,C++ 可以有逻辑层面的 const,也就是对一个常量实例来说,从外部观察,它是常量而不可修改;但是内部可以有非常量的状态。

当然,所谓的「逻辑 const」,在 C++ 标准中并没有这一称呼。这只是为了方便理解,而创造出来的名词。

显而易见,mutable 只能用来修饰类的数据成员;而被 mutable 修饰的数据成员,可以在 const 成员函数中修改。

这里举一个例子,展现这类情形。

class HashTable {
public:
//...
std::string lookup(const std::string& key) const
{
if (key == last_key_) {
return last_value_;
} std::string value{this->lookupInternal(key)}; last_key_ = key;
last_value_ = value; return value;
} private:
mutable std::string last_key_
mutable std::string last_value_;
};

这里,我们呈现了一个哈希表的部分实现。显然,对哈希表的查询操作,在逻辑上不应该修改哈希表本身。因此,HashTable::lookup 是一个 const 成员函数。在 HashTable::lookup 中,我们使用了 last_key_ 和 last_value_ 实现了一个简单的「缓存」逻辑。当传入的 key 与前次查询的 last_key_ 一致时,直接返回 last_value_;否则,则返回实际查询得到的 value 并更新 last_key_ 和 last_value_。

在这里,last_key_ 和 last_value_ 是 HashTable 的数据成员。按照一般的理解,const 成员函数是不允许修改数据成员的。但是,另一方面,last_key_ 和 last_value_ 从逻辑上说,修改它们的值,外部是无有感知的;因此也就不会破坏逻辑上的 const。为了解决这一矛盾,我们用 mutable 来修饰 last_key_ 和 last_value_,以便在 lookup 函数中更新缓存的键值。

Lambda 表达式中的 mutable

C++11 引入了 Lambda 表达式,程序员可以凭此创建匿名函数。在 Lambda 表达式的设计中,捕获变量有几种方式;其中按值捕获(Caputre by Value)的方式不允许程序员在 Lambda 函数的函数体中修改捕获的变量。而以 mutable 修饰 Lambda 函数,则可以打破这种限制。

int x{0};
auto f1 = [=]() mutable {x = 42;}; // okay, 创建了一个函数类型的实例
auto f2 = [=]() {x = 42;}; // error, 不允许修改按值捕获的外部变量的值

需要注意的是,上述 f1 的函数体中,虽然我们给 x 做了赋值操作,但是这一操作仅只在函数内部生效——即,实际是给拷贝至函数内部的 x 进行赋值——而外部的 x 的值依旧是 0。

const意思是“这个函数不修改对象内部状态”。

为了保证这一点,编译器也会主动替你检查,确保你没有修改对象成员变量——否则内部状态就变了。

mutable意思是“这个成员变量不算对象内部状态”。

比如,你搞了个变量,用来统计某个对象的访问次数(比如供debug用)。它变成什么显然并不影响对象功用,但编译器并不知道:它仍然会阻止一个声明为const的函数修改这个变量。

把这个计数变量声明为mutable,编译器就明白了:这个变量不算对象内部状态,修改它并不影响const语义,所以就不需要禁止const函数修改它了。

mutable关键词的更多相关文章

  1. 从零开始山寨Caffe·壹:仰望星空与脚踏实地

    请以“仰望星空与脚踏实地”作为题目,写一篇不少于800字的文章.除诗歌外,文体不限. ——2010·北京卷 仰望星空 规范性 Caffe诞生于12年末,如果偏要形容一下这个框架,可以用"须敬 ...

  2. 一、让自己习惯C++

    写在前面 第一遍看<Effective C++>时,在准备暑期实习生的招聘,没有时间好好地捋一下,将一些要点记录下来.现在实习回来,重读此书,并记录一些要点,为今后的复习亦或是学习铺垫. ...

  3. 深入理解C++中的Const,Mutable以及Volatile

    我一直认为const表示一个常量,常量就是一个无法被修改的值,但是没有深入理解const的实现,甚至不知道mutable和volatile的存在,最近在书中看到了这一部分的知识,所以本文将详细解析这几 ...

  4. C++笔记——类(0)定义、访问控制、友元、default、mutable、构造函数

    整理一下一些关于类的知识点,毕竟还是很经常用的(先总结一部分,太多了). 定义格式.访问控制 C++里面定义类的关键词有两个,一个是class,另一个是struct,他们基本没有区别,除了成员变量的默 ...

  5. 微软“.Net社区虚拟大会”dotnetConf2015:关键词:.NET 创新、开源、跨平台

    去年 11 月的时候,微软开源了 .NET CoreFX,然后是今年 2 月份的 .NET CoreCLR.自那时以来,已经有大约 3500 人在 GitHub 上进行了提交,而且贡献者的人数还在持续 ...

  6. JavaScript中‘this’关键词的优雅解释

    本文转载自:众成翻译 译者:MinweiShen 链接:http://www.zcfy.cc/article/901 原文:https://rainsoft.io/gentle-explanation ...

  7. [LeetCode] Range Sum Query 2D - Mutable 二维区域和检索 - 可变

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  8. [LeetCode] Range Sum Query - Mutable 区域和检索 - 可变

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  9. dede织梦批量导入关键词

    在后台替换对应的文件件即可. 注意:如果你的关键字长度超过16个字符的话,需要更改 dede 中 keywords 表中的keyword 字段字符长度 article_keywords_main.ph ...

  10. 使用python抓取百度搜索、百度新闻搜索的关键词个数

    由于实验的要求,需要统计一系列的字符串通过百度搜索得到的关键词个数,于是使用python写了一个相关的脚本. 在写这个脚本的过程中遇到了很多的问题,下面会一一道来. ps:我并没有系统地学习过pyth ...

随机推荐

  1. 【go语言】2.2.1 数组和切片

    数组和切片是 Go 语言中常用的数据结构,它们都可以存储多个同类型的元素. 数组 数组是具有固定长度的数据类型,它的长度在定义时就已经确定,不能随意改变. 你可以使用以下方式定义一个数组: var a ...

  2. MySQL_Explain详解

    当我们在工作中面临SQL优化的问题时,熟练掌握适合的工具,就能使事半功倍,提高工作效率.其中,EXPLAIN工具就是一种常用且高效的SQL优化工具. EXPLAIN关键字的使用方法是,在select语 ...

  3. SqlSugar本地缓存查询实现方式

    有C#的国产ORM SqlSugar 好久了,实在话还不错,不过毕竟是早期产物不能过分要求规范化,有些项目查询语句需要用到缓存,官方是redis,我写了个本地缓存借助ConcurrentBag,因为有 ...

  4. Blazor前后端框架Known-V1.2.9

    V1.2.9 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Gith ...

  5. C#的重载决策

    重载是许多编程语言支持的特性.所谓重载,就是指可以定义多个名称相同但参数(个数.类型和顺序)不同的方法(函数).先来看一个例子: void Main() { char cvalue = 'a'; ma ...

  6. .NET5从零基础到精通:全面掌握.NET5开发技能【第二章】

    章节: 第一章:https://www.cnblogs.com/kimiliucn/p/17613434.html 第二章:https://www.cnblogs.com/kimiliucn/p/17 ...

  7. Unity UGUI的Button组件的介绍及使用

    UGUI的Button(按钮)组件的介绍及使用 1. 什么是UGUI的Button组件? UGUI(Unity GUI)是Unity引擎中的一套用户界面系统,Button(按钮)是其中的一个常用组件. ...

  8. 小白python和pycharm安装大佬勿扰

    编程语言发展和Python安装 计算机语言的发展 机器语言 1946年2月14日,世界上第一台计算机ENIAC诞生,使用的是最原始的穿孔卡片.这种卡片上使用的语言是只有专家才能理解的语言,与人类语言差 ...

  9. Git命令详细使用指南

    Git命令详细使用指南 Git是一种广泛使用的版本控制系统,它可以帮助开发人员跟踪变更.协作项目和有效管理代码仓库.无论你是初学者还是有经验的用户,理解各种Git命令对于高效的代码管理至关重要. 安装 ...

  10. Falcon-7B大型语言模型在心理健康对话数据集上使用QLoRA进行微调

      文本是参考文献[1]的中文翻译,主要讲解了Falcon-7B大型语言模型在心理健康对话数据集上使用QLoRA进行微调的过程.项目GitHub链接为https://github.com/iamaru ...