C++中volatile和mutable关键字用法详解

时间:2022-05-30 08:18:35

C/C++中的volatile关键字和const对应,用来修饰变量,用于告诉编译器该变量值是不稳定的,可能被更改。使用volatile注意事项:

(1). 编译器会对带有volatile关键字的变量禁用优化(A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided)。

(2). 当多个线程都要用到某一个变量且该变量的值会被改变时应该用volatile声明,该关键字的作用是防止编译器优化把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么多个线程有可能有的使用内存中的变量,有的使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中取出,而不是使用已经存在寄存器中的值(It cannot cache the variables in register)。

(3). 中断服务程序中访问到的变量最好带上volatile。

(4). 并行设备的硬件寄存器的变量最好带上volatile。

(5). 声明的变量可以同时带有const和volatile关键字。

(6). 多个volatile变量间的操作,是不会被编译器交换顺序的,能够保证volatile变量间的顺序性,编译器不会进行乱序优化(The value cannot change in order of assignment)。但volatile变量和非volatile变量之间的顺序,编译器不保证顺序,可能会进行乱序优化。

C++中的mutable关键字使用场景

(1). 允许即使包含它的对象被声明为const时仍可修改声明为mutable的类成员(sometimes there is requirement to modify one or more data members of class/struct through const function even though you don't want the function to update other members of class/struct. This task can be easily performed by using mutable keyword)。

(2). 应用在C++11 lambda表达式来表示按值捕获的值是可修改的,默认情况下是不可修改的,但修改仅在lambda式内有效(since c++11 mutable can be used on a lambda to denote that things captured by value are modifiable (they aren't by default))。

详细用法见下面的测试代码,下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include "volatile_mutable.hpp"
#include <iostream>
#include <stdio.h>
#include <time.h>
#include <mutex>
#include <string.h>
 
namespace volatile_mutable_ {
 
///////////////////////////////////////////////////////////
int test_volatile_1()
{
 volatile int i1 = 0; // correct
 int volatile i2 = 0; // correct
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://en.cppreference.com/w/c/language/volatile
int test_volatile_2()
{
{ // Any attempt to read or write to an object whose type is volatile-qualified through a non-volatile lvalue results in undefined behavior
 volatile int n = 1; // object of volatile-qualified type
 int* p = (int*)&n;
 int val = *p; // undefined behavior in C, Note: link does not report an error under C++
 fprintf(stdout, "val: %d\n", val);
}
 
{ // A member of a volatile-qualified structure or union type acquires the qualification of the type it belongs to
 typedef struct ss { int i; const int ci; } s;
 // the type of s.i is int, the type of s.ci is const int
 volatile s vs = { 1, 2 };
 // the types of vs.i and vs.ci are volatile int and const volatile int
}
 
{ // If an array type is declared with the volatile type qualifier (through the use of typedef), the array type is not volatile-qualified, but its element type is
 typedef int A[2][3];
 volatile A a = { {4, 5, 6}, {7, 8, 9} }; // array of array of volatile int
 //int* pi = a[0]; // Error: a[0] has type volatile int*
 volatile int* pi = a[0];
}
 
{ // A pointer to a non-volatile type can be implicitly converted to a pointer to the volatile-qualified version of the same or compatible type. The reverse conversion can be performed with a cast expression
 int* p = nullptr;
 volatile int* vp = p; // OK: adds qualifiers (int to volatile int)
 //p = vp; // Error: discards qualifiers (volatile int to int)
 p = (int*)vp; // OK: cast
}
 
{ // volatile disable optimizations
 clock_t t = clock();
 double d = 0.0;
 for (int n = 0; n < 10000; ++n)
 for (int m = 0; m < 10000; ++m)
  d += d * n*m; // reads and writes to a non-volatile
 fprintf(stdout, "Modified a non-volatile variable 100m times. Time used: %.2f seconds\n", (double)(clock() - t) / CLOCKS_PER_SEC);
 
 t = clock();
 volatile double vd = 0.0;
 for (int n = 0; n < 10000; ++n)
 for (int m = 0; m < 10000; ++m)
  vd += vd * n*m; // reads and writes to a volatile
 fprintf(stdout, "Modified a volatile variable 100m times. Time used: %.2f seconds\n", (double)(clock() - t) / CLOCKS_PER_SEC);
}
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://en.cppreference.com/w/cpp/language/cv
int test_volatile_3()
{
 int n1 = 0;      // non-const object
 const int n2 = 0;   // const object
 int const n3 = 0;   // const object (same as n2)
 volatile int n4 = 0; // volatile object
 const struct {
 int n1;
 mutable int n2;
 } x = { 0, 0 };   // const object with mutable member
 
 n1 = 1; // ok, modifiable object
 //n2 = 2; // error: non-modifiable object
 n4 = 3; // ok, treated as a side-effect
 //x.n1 = 4; // error: member of a const object is const
 x.n2 = 4; // ok, mutable member of a const object isn't const
 
 const int& r1 = n1; // reference to const bound to non-const object
 //r1 = 2; // error: attempt to modify through reference to const
 const_cast<int&>(r1) = 2; // ok, modifies non-const object n1
 fprintf(stdout, "n1: %d\n", n1); // 2
 
 const int& r2 = n2; // reference to const bound to const object
 //r2 = 2; // error: attempt to modify through reference to const
 const_cast<int&>(r2) = 2; // undefined behavior: attempt to modify const object n2, Note: link does not report an error under C++
 fprintf(stdout, "n2: %d\n", n2); // 0
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/
int test_volatile_4()
{
{
 const int local = 10;
 int *ptr = (int*)&local;
 fprintf(stdout, "Initial value of local : %d \n", local); // 10
 
 *ptr = 100;
 fprintf(stdout, "Modified value of local: %d \n", local); // 10
}
 
{
 const volatile int local = 10;
 int *ptr = (int*)&local;
 fprintf(stdout, "Initial value of local : %d \n", local); // 10
 
 *ptr = 100;
 fprintf(stdout, "Modified value of local: %d \n", local); // 100
}
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://en.cppreference.com/w/cpp/language/cv
int test_mutable_1()
{
 // Mutable is used to specify that the member does not affect the externally visible state of the class (as often used for mutexes,
 // memo caches, lazy evaluation, and access instrumentation)
 class ThreadsafeCounter {
 public:
 int get() const {
  std::lock_guard<std::mutex> lk(m);
  return data;
 }
 void inc() {
  std::lock_guard<std::mutex> lk(m);
  ++data;
 }
 
 private:
 mutable std::mutex m; // The "M&M rule": mutable and mutex go together
 int data = 0;
 };
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://www.tutorialspoint.com/cplusplus-mutable-keyword
int test_mutable_2()
{
 class Test {
 public:
 Test(int x = 0, int y = 0) : a(x), b(y) {}
 
 void seta(int x = 0) { a = x; }
 void setb(int y = 0) { b = y; }
 void disp() { fprintf(stdout, "a: %d, b: %d\n", a, b); }
 
 public:
 int a;
 mutable int b;
 };
 
 const Test t(10, 20);
 fprintf(stdout, "t.a: %d, t.b: %d \n", t.a, t.b); // 10, 20
 
 //t.a=30; // Error occurs because a can not be changed, because object is constant.
 t.b = 100; // b still can be changed, because b is mutable.
 fprintf(stdout, "t.a: %d, t.b: %d \n", t.a, t.b); // 10, 100
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://www.geeksforgeeks.org/c-mutable-keyword/
int test_mutable_3()
{
 using std::cout;
 using std::endl;
 
 class Customer {
 public:
 Customer(char* s, char* m, int a, int p)
 {
  strcpy(name, s);
  strcpy(placedorder, m);
  tableno = a;
  bill = p;
 }
 
 void changePlacedOrder(char* p) const { strcpy(placedorder, p); }
 void changeBill(int s) const { bill = s; }
 
 void display() const
 {
  cout << "Customer name is: " << name << endl;
  cout << "Food ordered by customer is: " << placedorder << endl;
  cout << "table no is: " << tableno << endl;
  cout << "Total payable amount: " << bill << endl;
 }
 
 private:
 char name[25];
 mutable char placedorder[50];
 int tableno;
 mutable int bill;
 };
 
 const Customer c1("Pravasi Meet", "Ice Cream", 3, 100);
 c1.display();
 c1.changePlacedOrder("GulabJammuns");
 c1.changeBill(150);
 c1.display();
 
 return 0;
}
 
///////////////////////////////////////////////////////////
// reference: https://*.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-the-variable-to
int test_mutable_4()
{
 int x = 0;
 auto f1 = [=]() mutable { x = 42; }; // OK
 //auto f2 = [=]() { x = 42; }; // Error: a by-value capture cannot be modified in a non-mutable lambda
 fprintf(stdout, "x: %d\n", x); // 0
 
 return 0;
}
 
} // namespace volatile_mutable_

GitHub:https://github.com/fengbingchun/Messy_Test

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/fengbingchun/article/details/104109696