C 中的mutable关键字
创始人
2025-07-06 20:51:42
0

在C++中mutable关键字正如字面意思所示,表示「可变的」之意。一般在以下两种情况中使用较多,一是修饰类中的变量,用来突破const的限制,二是在Lambda表达式中使用,用来捕获修改表达式之外的变量值。

下面我们就针对这两种使用场景逐个介绍。

mutable修饰类的成员变量

以下实例代码有一个类Person,内部有一个age成员变量表示年龄,有一个被const修饰的公共方法获取年龄:

#include 
class Person{
public:
    explicit Person(int a):age(a){

    }
    ~Person(){

    }
    int getAge() const{
        return age;
    }
private:
    int age{18};
};

int main() {
    const Person person(20);
    std::cout << "age = " << person.getAge() << std::endl;
    return 0;
}

假如我想扩展一下这个类Person的功能,在其内部增加一个counter的字段,用于统计getAge方法的调用次数,于是将其代码改成一下这样子:

#include 
class Person{
public:
    explicit Person(int a):age(a){

    }
    ~Person(){

    }
    int getAge() const{
        counter++;
        return age;
    }
private:
    int age{18};
    int counter{0};
};

int main() {
    const Person person(20);
    std::cout << "age = " << person.getAge() << std::endl;
    return 0;
}

我们发现代码无法编译通过了,因为getAge函数是被const修饰的,被const修饰的函数,在其内部无法修改该类的成员变量。为了可以让代码通过编译并能顺利运行, 于是我们把第10行和第20行的const去掉即可。

这个解决方案可以说是正确的,但是同时也在一定程度傻姑娘破坏了我们设计者的本意,因为程序设计者的本意仅仅是希望counter可以被修改,而age还是不能随意修改的, 把const删除后age也可以随意修改了,同时在《Effective C++》一书中作者也提到过一条准则就是只要可能就用 const,明显这个Person也是适合使用const修饰的, 那么我们怎样修改才能做到既使用const保证其他变量不可随意修改,又能保证在const函数体内counter可以修改呢?这时候mutable的关键字的作用就体现出来了。

我们仅仅需要在声明counter变量时使用mutable修饰一下即可,也就是:

class Person{
public:
    explicit Person(int a):age(a){

    }
    ~Person(){

    }
    int getAge() const{
        counter++;
        return age;
    }
private:
    int age{18};
    mutable int counter{0};
};

同理,如果我们希望在被const修饰的函数getAge内age变量也可被修改的话,也可以使用mutable修饰age变量。

mutable在Lambda表达式中的作用

C++11标准中引入了 Lambda 表达式,用于定义匿名函数,使得代码更加灵活简洁。

我们简单回顾一下Lambda表达式的语法,Lambda表达式的语法主要分为五个部分,对应为:

[捕获列表] (函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}

其中 mutable 或 exception 声明以及返回值类型是可以忽略不写的。

捕获列表的值又可以有以下几种形式:

  • [] 表示不捕获任何变量
  • [=] 表示按值传递的方法捕获父作用域的所有变量
  • [&] 表示按引用传递的方法捕获父作用域的所有变量
  • [=, &a] 表示按值传递的方法捕获父作用域的所有变量,但按引用传递的方法捕获变量a
  • [&, a] 表示按引用传递的方法捕获父作用域的所有变量,但按值传递的方法捕获变量a

其中按值捕获[=]的方式不允许程序员在 Lambda 函数的函数体中修改捕获的变量。而以 mutable 修饰 Lambda 函数,则可以打破这种限制。

例如一下代码是无法编译通过的:

#include 
int main() {
    int x{0} ;
    auto f1 = [=]() {return ++x;};
    f1();
    std::cout << "x = " << x << std::endl;
    return 0;
}

因为Lambda表达式f1,在内部修改了表达式外部x的值,但是又没有使用mutable关键字声明,此时我们只需要使用mutable关键字声明一下f1即可:

#include 
int main() {
    int x{0} ;
    auto f1 = [=]() mutable {return ++x;};
    f1();
    std::cout << "x = " << x << std::endl;
    return 0;
}

在这里考考大家一个简单的问题,为什么在f1内部改变了外部x的值,但是打印x的值还是0呢?为何没有生效呢?

针对以上例子如果想要在表达式外部修改x的值,笔者觉得直接在捕获列表中使用引用传递不是更加方便明了吗,mutable关键字在Lambda表达式中是否有点脱裤子放屁的感觉?

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
粉嫩如何诠释霸道 东芝M805... “霸道粉”是个什么玩意东芝M805拿过来的时候,笔者扑哧笑了,不是笑这款笔记本,而是笑这款产品的颜色...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
30分钟搞定iOS自定义相机 最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...