mutable

1,理解

  • mutable字面意思是可变的,其实直接定义的local variable都是可变的,所以mutable对于修饰普通的local variable是没有意义的。事实上,编译器会禁止你这么做:
#include <iostream>

int main() {
    mutable int a{0};
    a = 5;
    std::cout << "a=" << a << std::endl;
}

编译报错:

mutable.cpp:4:17: error: 'mutable' can only be applied to member variables

由此可见,mutable用于修饰类的成员变量:

#include <iostream>

int main() {
    struct mu_st {
        mutable int a;
    };
    const mu_st ms{0};
    ms.a = 5;
    std::cout << "a=" << ms.a << std::endl;
}

这里,在一个类mu_st中定义了一个mutable的变量a,并实例化了一个const的对象ms,理论上,因为const性,不允许改变ms对象,但因为a被声明了mutable,它就可以突破const的限制,从而变成可变的。

这就是mutable的作用,在const类const成员函数中修改一些状态。到这里,不禁要问,声明为const就是为了防止改变对象,现在有搞出来个mutable,这不自相矛盾吗?

确实,上例中,mutable的应用是没有意义的,但在某些场合,mutable却是不可缺少的。其主要场景包括两类(参考 “Effective Modern C++”),第一是在const成员函数需要多线程并发时,第二是为了优化程序运行,做一些与类内部状态无关的缓存,第三类是在lambda表达式中去除采用赋值方式捕获的变量的const属性。这三类场景的应用实例见第二部分。

  • cv属性
    在进入实例之前,先简单说一下cv属性,cv是指const-volatile,它们都是c++的类型指定符(type specifier),用来指定变量的常性可变性。而mutable通常用来突破const限制,使变量处于永远可变状态。

2,典型场景

mutex -- const函数的并发支持

c++中实现线程安全的通常做法是使用std::mutex,但是在const成员函数中,对mutex的加锁和释放锁操作会违背const的不可变语义,所以,只能将mutex定义为mutable,从而可以在const修饰的函数中加锁,实现线程安全。

#include <iostream>
class Cal {
public: 
    Cal(int n) {num = n;}

    void inc_num() {
        std::lock_guard<std::mutex> lg(m);
        ++num;
    }

    int get_num() const {
        std::lock_guard<std::mutex> lg(m);
        return num;
    }
private:
    int num;
    mutable std::mutex m;
};

int main() {
    Cal c{0};
    std::cout << c.get_num() << std::endl;
}

Cal类的get_num()函数保证了读操作的线程安全性。

内部缓存(非类的内部状态)

内部缓存一般是与类的内部状态无关的,const函数语义上表示不修改类的状态,但有时为了缓存某些计算结果、某些中间数据,需要在const函数中修改一些成员变量,这些变量并不影响类的状态,所以这种const叫做logic const.

#include <iostream>
class Account {
public: 
    float query() const {
        ++query_cnt;
        return total;
    }
 
    float total; // 总账户
    mutable int query_cnt; // 查询次数
};

int main() {
    Account acc{100.0f};
    acc.query();
    acc.query();
    std::cout << "query times: " << acc.query_cnt << std::endl;
}

这里,query_cnt与账户本身状态无关,可以认为query()本身声明为const的语义为不改变账户余额,因此,虽然查询次数缓存被改变,但并不影响语义上的const。

lambda表达式

当lambda表达式用[=]捕获变量时,在表达式内部是不允许修改变量的,但是可用mutable允许修改变量:

#include <iostream>

int main() {
    int a = 0;
    auto cb = [=]() mutable {
        a = 1;
        std::cout << "a=" << a << std::endl;
    };
    cb();
    std::cout << "a=" << a << std::endl;
}

运行结果为:

a=1
a=0

注意虽然lambda表达式内改变了a的值,但实际上a是通过拷贝赋值的,main内定义的a是没有改变的。如果cb没有加mutable,则会出现编译错误:

mutable.cpp:6:11: error: cannot assign to a variable captured by copy in a non-mutable lambda
        a = 1;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容