首页 > 其他分享 >39.volatile、mutable和explicit关键字的用法

39.volatile、mutable和explicit关键字的用法

时间:2023-07-03 21:45:30浏览次数:43  
标签:39 const 变量 int explicit volatile 修饰 函数

39.volatile、mutable和explicit关键字的用法

1.volatile

☀警告

volatile的确切含义与机器有关,只能通过阅读编译器文档来理解。要想让使用了volatile的程序在移植到新机器或新编译器后仍然有效,通常需要对该程序进行某些改变。

直接处理硬件的程序常常包含这样的数据元素,它们的值由程序直接控制之外的过程控制。例如,程序可能包含一个由系统时钟定时更新的变量。当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为volatile。关键字volatile告诉编译器不应对这样的对象进行优化。(比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。)

当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。

volatile定义变量的值是易变的,每次用到这个变量的值的时候都要去重新读取这个变量的值,而不是读寄存器内的备份。多线程中被几个任务共享的变量需要定义为volatile类型。

1.1volatile指针

volatile限定符的用法和const很相似,它起到对类型额外修饰的作用:

volatile int display_register;  // 该int值可能发生改变
volatile Task *curr_task;  // curr task指向一个volatile对象 
volatile int iax [max_size];  // iax的每个元素都是volatile 
volatile Screen bitmapBuf;  // bitmapBuf的每个成员都是volatile

const和volatile限定符互相没什么影响,某种类型可能既是const的也是volatile的,此时它同时具有二者的属性。

就像一个类可以定义const成员函数一样,它也可以将成员函数定义成volatile的。 只有volatile的成员函数才能被volatile的对象调用。

const限定符和指针的相互作用,在volatile限定符和指针之间也存在类似的关系。我们可以声明volatile指针、指向volatile对象的指针以及指向volatile对象的volatile指针:

volatile int v; // v是一个volatile int 
int *volatile vip; // vip是一个volatile指针,它指向int
volatile int *ivp; // ivp是一个指针,它指向一个volatile int
volatile int *volatile vivp; // vivp是一个volatile指针,它指向一个volatile int 
int intv;
int *ip = &v;//错误:必须使用指向volatile的指针
ivp = &v; //正确:ivp是一个指向volatile的指针
vivp = &v;//正确:vivp是一个指向volatile的volatile指针
v = intv;//正确:可以把一个非volatile int赋给volatile int,但是不能把非volatile对象赋给一个volatile对象。
intv = v;//错误

●和const一样,我们只能将一个 volatile对象的地址(或者拷贝一个指向volatile类型的指针)赋给一个指向volatile的指针。同时,只有当某个引用是volatile的时,我们才能使用一个volatile对象初始化该引用。

●C++中一个有volatile标识符的类只能访问它接口的子集,一个由类的实现者控制的子集。用户只能用const_cast来获得对类型接口的完全访问。此外,volatile向const一样会从类传递到它的成员。

1.2合成的拷贝对volatile对象无效

const和volatile的一个重要区别是我们不能使用合成的拷贝/移动构造函数及赋值运算符初始化volatile对象或从volatile对象赋值。合成的成员接受的形参类型是(非volatile)常量引用,显然我们不能把一个非volatile引用绑定到一个volatile对象上。

如果一个类希望拷贝、移动或赋值它的volatile对象,则该类必须自定义拷贝或移动操作。例如,我们可以将形参类型指定为const volatile引用,这样我们就能利用任意类型的Foo进行拷贝或赋值操作了:

class Foo
{
public:
	Foo(const volatile Foo&);//从一个volatile对象进行拷贝
	Foo& operator=(volatile const Foo&);//将一个volatile对象赋值给一个非volatile对象
	Foo& operator=(volatile const Foo&) volatile;//将一个volatile对象赋值给一个volatile对象
	//Foo类的剩余部分
};

尽管我们可以为volatile对象定义拷贝和赋值操作,但是一个更深层次的问题是拷贝volatile对象是否有意义呢?不同程序使用volatile的目的各不相同,对上述问题的同答与具体的使用目的密切相关。

1.3多线程下的volatile

有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。

如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。

volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。

1.4保证变量在内存中的可见性

有数据一致性的隐患

1.5禁止编译器做过度优化

image-20230701114139700

1.6禁止指令重排









voltile具有内存屏障的功能


参考资料来源:

阿秀、C++Primer、在划水里划水

2.mutable

2.1const修饰成员函数

●用const修饰的成员函数时,const修饰this指针指向的内存区域,成员函数体内不可以修改本类中的任何普通成员变量

● 当成员变量类型符前用mutable修饰时例外。

//const修饰成员函数
class Person{
public:
	Person(){
		this->mAge = 0;
		this->mID = 0;
	}
	//在函数括号后面加上const,修饰成员变量不可修改,除了mutable变量
	void sonmeOperate() const{
		//this->mAge = 200; //mAge不可修改
		this->mID = 10; //const Person* const this;
	}
	void ShowPerson(){
		cout << "ID:" << mID << " mAge:" << mAge << endl;
	}
private:
	int mAge;
	mutable int mID;
};

int main(){

	Person person;
	person.sonmeOperate();
	person.ShowPerson();

	system("pause");
	return EXIT_SUCCESS;
}

2.2const修饰对象(常对象)

●常对象只能调用const的成员函数

● 常对象可访问 const 或非 const 数据成员,不能修改,除非成员用mutable修饰

程序1:

class Person
{
public:
    Person()
    {
		this->mAge = 0;
		this->mID = 0;
    }
    void ChangePerson() const
    {
		mAge = 100;
		mID = 100;
    }
    void ShowPerson()
    {
		cout << "ID:" << this->mID << " Age:" << this->mAge << endl;
    }

public:
    int mAge;
    mutable int mID;
};

void test()
{	
	const Person person;
	//1. 可访问数据成员
	cout << "Age:" << person.mAge << endl;
	//person.mAge = 300; //不可修改
	person.mID = 1001; //但是可以修改mutable修饰的成员变量
	//2. 只能访问const修饰的函数
	//person.ShowPerson();
	person.ChangePerson();
}

程序2:

#pragma warning(disable:4996)
//2022年9月22日21:46:00
#include <iostream>
using namespace std;

class Maker
{
public:
    int id;
    int mAge;
    mutable int score;//mutable修饰的成员变量

public:
    Maker(int id, int age)
    {
        this->id = id;
        this->mAge = age;
        score = 100;
    }
    //常函数
    void printMaker() const//1.函数后面加上const,该函数就是常函数
    {
        //id = 100;//err 2.常函数内不能修改普通成员变量
        //3.const修饰的是this指针指向的空间中的变量,不能修改
        //Maker *const this;
        //变成
        //const Maker *const this;//这是常函数修饰的
        score = 200; //4.mutable修饰的变量在常函数中可以修改
        cout << "score = " << score << endl;
    }
};

void test01()
{
    Maker m(1, 18);
    m.printMaker();
}

int main()
{
    test01();
    system("pause");
    return EXIT_SUCCESS;
}

输出结果:

score = 200
请按任意键继续. . .


程序3:

#pragma warning(disable:4996)
//2022年9月22日22:28:03
#include <iostream>
using namespace std;

class Maker
{
public:
    int id;
    int mAge;
    mutable int score;//mutable修饰的成员变量

public:
    Maker(int id, int age)
    {
        this->id = id;
        this->mAge = age;
        score = 100;
    }
    //常函数
    void printMaker() const//1.函数后面加上const,该函数就是常函数
    {
        //id = 100;//err 2.常函数内不能修改普通成员变量
        //3.const修饰的是this指针指向的空间中的变量,不能修改
        //Maker *const this;
        //变成
        //const Maker *const this;//这是常函数修饰的
        score = 200; //4.mutable修饰的变量在常函数中可以修改
        cout << "score = " << score << endl;
    }
};
void test01()
{
    Maker m(1, 18);
    m.printMaker();
}

void test()
{
    //1.在数据类型前面加上const,让对象成为常对象
    const Maker m(1, 18);

    //m.id = 100;常对象不能改变普通成员变量的值
    //m.func();//常对象不能调用普通成员函数
    m.printMaker();//常对象可以调用常函数
    m.score = 500;//常对象可以修改mutable修饰的成员变量

    Maker m2(2, 20);
    m2.printMaker();//普通对象可以调用常函数
}

int main()
{
    test();
    system("pause");
    return EXIT_SUCCESS;
}

输出结果:

score = 200
score = 200
请按任意键继续. . .

参考资料来源于:

黑马程序员等

3.explicit

  c++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit的构造函数不能在隐式转换中使用。

[explicit注意]

● explicit用于修饰构造函数,防止隐式转化。

● 是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言。

class MyString{
public:
	explicit MyString(int n){
		cout << "MyString(int n)!" << endl;
	}
	MyString(const char* str){
		cout << "MyString(const char* str)" << endl;
	}
};

int main(){

	//给字符串赋值?还是初始化?
	//MyString str1 = 1; //会进行隐式类型转换MyString(1)
	MyString str2(10);

	//寓意非常明确,给字符串赋值
	MyString str3 = "abcd";
	MyString str4("abcd");

	return EXIT_SUCCESS;
}

错误:

//2022年9月20日20:23:11
#include <iostream>
using namespace std;

class Maker
{
public:
    //explicit只能放在构造函数前面,构造函数只有一个参数或其他参数有默认值时
    explicit Maker(int n)//防止编译器优化Maker m = 10;这种格式
    {

    }
};
int main()
{
    Maker m = 10;
    system("pause");
    return EXIT_SUCCESS;
}

参考资料

参考资料来源于黑马程序员、阿秀等

标签:39,const,变量,int,explicit,volatile,修饰,函数
From: https://www.cnblogs.com/codemagiciant/p/17524184.html

相关文章

  • 【转】python踩坑(FileNotFoundError: Could not find module '此处省略了一些路径win_
    1、报错(FileNotFoundError:Couldnotfindmodule'此处省略了一些路径\site-packages\scipy\.libs\libbanded5x.GL5FZ7Y77HIKQFNMZKUOMV5GID6YMX2V.gfortran-win_amd64.dll'(oroneofitsdependencies).Tryusingthefullpathwithconstructorsyntax.) 2、分析&a......
  • dmPython连接达梦数据库,查询含有特殊字符报错'gbk' codec can't decode byte 0xaa in
     解决方法,可以在应用连接字符串url连接属性加上local_code=1指定客户端字符编码方式。#local_code=1表示执行客户端的本地编码是UTF-8,否则默认是GBKdm_conn=dmPython.connect(user=dm_user,password=dm_password,server='localhost',port=5236,local_code=1) 参考网......
  • [GPT] 用 document.querySelector('.xxx') 选择下级的第二个 div 要怎么写
     要选择类名为.xxx的元素下的第二个子<div>元素,可以将querySelectorAll()方法与CSS选择器一起使用。 以下是一个示例:constsecondChildDiv=document.querySelectorAll('.xxx>div')[1];在本例中,document.querySelectorAll('.xxx>div')选择类名为.xxx的元素下的所有直......
  • 在Vscode使用命令npm报错-The operation was rejected by your operating system. npm
    报错信息:PSD:\disk\xubo\个人博客文章\27-Vue\资料(含课件)\vuedemo\vueproject>npmipubsub-jsnpmERR!codeEPERMnpmERR!syscallopennpmERR!pathD:\disk\soft\node.js\node_cache_cacache\index-v5\1d\32\0400202fc22af03ff2926f006e455fe92c77b8136b8fbe......
  • P3951 小凯的疑惑 / 买不到的数目【这题简单】
    基础数论题花了一会儿,不难想出来题意:求互质的两个数\(a,b\)的线性组合所不能表示的最大数字这题简单,设\(a<b\)如果一个数\(k\)可以被表示,哪么就可以写成:\(k=x*a+y*b\)例如5,9,将非负整数划分为许多个区间\([n*b,(n+1)*b)\),分别为:\([0,9),[9,18),[18,27)\)......
  • volatile是如何保证有序性的?
    为什么需要保证有序性?有如下代码,在inti=a;执行了的情况下,i的值最终会为几?publicclassNoVolatileExample{inta=0;booleanflag=false;publicvoidwriter(){a=1;flag=true;}publicvoidreader(){if(flag......
  • Definition of 'Cash Settlement( versus physical delivery of the reference obliga
    Definitionof'CashSettlement(versus physicaldeliveryofthereferenceobligation)Asettlementmethodusedincertainfutureandoptioncontractswhereby,uponexpiryorexercise,thesellerofthefinancialinstrumentdoesnotdelivertheactual......
  • Buyer's Call
    http://www.investopedia.com/terms/b/buyerscall.asp#axzz1r7gvLjmmhttp://www.investopedia.com/terms/c/call.asp#axzz1r7gvLjmmDefinitionof'Buyer'sCall'Anagreementbetweenabuyerandsellerwherebyacommoditypurchaseoccursataspecifi......
  • 39. 拓扑排序
    一、什么是拓扑排序  拓扑排序是对有向无圈图的顶点的一种排序,它使得如果存在一条从\(v_{i}\)到\(v_{j}\)的路径,那么排序中\(v_{j}\)出现在\(v_{j}\)的后面。有向边(v,w)表明任务v必须在任务w前完成。显然,如果图含有圈,那么拓扑排序是不可能的,因为对于圈上的两个......
  • 51.pyinstaller打包后,打开exe程序提示SyntaxError: Non-UTF-8 code starting with '\
    最后开发了一款小工具,然后确定一切测试没有问题,想通过pyinstaller将其打包成exe,像类似的打包以前也经常打包的,复杂一点的也都是打包成功的,但这里感觉程序很简单,打包居然出现了以下错误。我的python版本是3.8.9,然后pyinstaller版本是5.9.0,不知道会不会是版本不兼容的问题,看网上哪......