C++中的volatile和mutable有什么区别?

93

我有一个关于volatile和mutable的差异问题。我注意到两者都表示它们可能会改变。除此之外还有什么?它们是同样的东西吗?它们的区别在哪里?它们适用于哪些情况?为什么提出这两个想法?如何以不同方式使用它们?

非常感谢。

6个回答

121

mutable字段可以在通过const指针或引用访问的对象中被更改,也可以在const对象中被更改,因此编译器知道不要将其存储在只读内存中。 volatile位置是可以被编译器不知道的代码更改的位置(例如某个内核级驱动程序),因此编译器知道不要优化该值的寄存器分配,因为它可能已经自上次加载后被更改了。这是给编译器提供的非常不同类型的信息,以阻止非常不同类型的无效优化。


14
「volatile」对象也可能被与 CPU 无关的进程更改。例如,通信外设中的「bytes-received」寄存器在接收到字节时会自增(这甚至可以触发中断)。另一个例子是外设中的「pending-interrupts」标志寄存器。 - Mike DeSimone
58
另外,“volatile” 不仅意味着对象可以在编译器不知情的情况下更改 - 它还意味着即使这些写操作似乎没有用处,编译器也不能消除对该对象的写入。例如: x = 1; x = 0; 如果 x 是 volatile 的,则编译器必须发出两个写操作(这可能在硬件级别上很重要)。但是,对于非 volatile 对象,编译器可能会选择不费力地写入 1,因为它从未被使用过。 - Michael Burr
17
一个对象可以同时被标记为 constvolatile!你不能改变这个对象,但是它在背后可能会被修改。 - CTMacUser
2
@Destructor:通常情况下,是针对硬件设备寄存器的写入操作。 - Michael Burr
6
假设你正在控制一个LED的状态。写入0会关闭它,写入1会打开它。如果我需要闪烁LED以传达某个错误状态,但编译器决定优化掉除最后一个写入外的所有写入,因为没有使用任何值,那么LED就永远不会闪烁,并且我所期望的行为也无法实现。 - iheanyi
显示剩余2条评论

28

mutable: mutable关键字可以覆盖任何封闭的const语句。一个const对象的可变成员可以被修改。

volatile: volatile关键字是一个依赖实现的修饰符,用于声明变量,防止编译器优化这些变量。应该在值可能以意想不到的方式改变(例如通过中断)的变量上使用volatile,这可能会与编译器执行的优化相冲突。

来源


你说“Volatile 应该与值可能以意外方式更改的变量一起使用”,我们是否应该更喜欢将其与随机数一起使用? - Asif Mushtaq
@AsifMushtaq 不是值。方式。可变的影响您编写的代码所具有的权限。因此,您可以通过const ptr或const引用访问变量。如果不是您的代码更改它呢?编译器无法检查指针或引用类型吗?那就是volatile。而且volatile还会强制缓存写回主内存。因此,这在多线程代码中经常使用。 :) - Dan

23

它们绝对不是同一件事。 Mutable 与 const 相互作用。如果您有一个 const 指针,则通常不能更改成员变量。 Mutable提供了这个规则的例外。

另一方面,Volatile与程序所做的更改完全无关。它意味着内存可能因编译器无法控制的原因而发生变化,因此编译器必须每次读取或写入内存地址,并且无法将内容缓存到寄存器中。


“另一方面,volatile与程序所做的更改完全无关…” - 嗯,将成员声明为volatile并查看编译期间会出现什么问题。事后添加volatile很像事后添加const…非常痛苦。 - jww
@jww:这与程序所做的写入操作完全无关。您可以获取类型为T的对象的地址,并将其存储到const T *中并从中读取。如果将该对象设置为volatile,即使您从未尝试写入,将其地址存储到const T *中也会失败。 volatile和程序代码中的更改/修改/内存写入是完全不相关的。 - Ben Voigt

19

一个粗糙但有效的比较方式是:

  • 编译器知道可变对象何时发生更改。
  • 编译器无法知道易失性对象何时发生更改。

1
在这个方面:volatile bytes_received,mutable reference_count。 - Mike DeSimone

11

在声明为const的方法中,标记为mutable的变量可以被修改。

标记为volatile的变量告诉编译器每次按照代码所示都必须读取/写入该变量(即不能优化掉对变量的访问)。


5
我想补充一点,volatile在处理多线程应用程序时也非常有用。比如,你有一个主线程(main()函数所在的线程)和一个工作线程,当变量“app_running”为true时,工作线程会不断运行。main()控制“app_running”的真假,因此如果您没有在声明“app_running”时添加volatile属性,那么如果编译器在辅助线程中运行的代码中优化访问“app_running”,main()可能会将“app_running”改为false,但辅助线程仍将继续运行,因为该值已被缓存。我在Linux上使用gcc和VisualC++上看到了相同的行为。在“app_running”声明中放置“volatile”属性解决了问题。因此,这是一个在更改此类变量的值时不涉及硬件中断或内核的情况。

2
不!这是一个常见的误解。C++11和C11引入了原子操作,用于此目的。https://dev59.com/qWoy5IYBdhLWcg3wCpkz - KristianR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接