• volatile关键字及C++数据类型转换


    volatile关键字及C++数据类型转换

    volatile关键字

    参考链接:https://blog.csdn.net/jokerro/article/details/107734379

      volatile关键字在C/C++中与const同级用来修饰变量,用它声明的变量可以被某些编译器中未知的因素修改,如操作系统、硬件、其它线程等。遇到这个关键字声明的变量,编译器就不再对其进行优化,当使用volatile声明变量的时候,系统总是从它所在的内存空间直接读取数据,而一般情况下,编译器会对代码进行优化。在连续读取某个内存地址存储的变量时,如果地址没有变化,且在该单线程中没有其它语句改变这个变量中的值时,编译器不会直接去读内存中变量的值,而是从寄存器中直接读取原先变量的值,所以当有操作系统、硬件、或者其它线程(大多数是其他线程争抢共享变量时会发生)对该变量进行修改,就会出现读错误,读出的数据并不是变量现在真正存储的值,从而导致错误的发生。

      综上,volatile关键字可以保证每次对变量的访问都是从内存地址直接读取,而不是被编译器优化后可能导致的读错误。

    C++数据类型转换

    static_cast(静态强制)

    学习参考链接:https://blog.csdn.net/liranke/article/details/5295133

    用于非多态类型转换(静态转换),任何标准转换都可以用它,但是不能用于两个不相关的类型转换。

    普通类型使用static_cast

    int main()
    {
    	char ch = 'a';
    	short sh = 10;
    	int i1 = static_cast<char>(ch);//success
    	int i2 = static_cast<short>(sh);//success
    
    	cout << i1 << endl;//97
    	cout << i2 << endl;//10
    
    	double* d = new double;
    	void* v = static_cast<double*>(d);//success
    
    	int i = 20;
    	const int iConst = static_cast<const int>(i);//success
    
    	const int jCount = 30;
    	//int* p = static_cast<int*>(&jCount);//fail
        //static_cast不能移除变量的const属性。
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    派生类和基类使用static_cast

    class Base {
    public:
    	int a;
    	void fun1() { cout << "Base::fun1" << endl; }
    	void fun2() { cout << "Base::fun2" << endl; }
    };
    
    class Derive : public Base {
    public:
    	int b;
    	void fun2() { cout << "Derive::fun2" << endl; }
    	void fun4() { cout << "Derive::fun4" << endl; }
    
    };
    
    int main()
    {
    	Base b;
    	Derive d;
    
    	Base* pB = static_cast<Base*>(&d);//派生类指针转换为基类指针
    	Derive* pD = static_cast<Derive*>(&b);//基类指针转换为派生类指针
    
    	pB->fun1();
    	pB->fun2();
    	//pB->fun4();//fail,因为fun4是派生类的成员函数,只能通过派生类对象进行访问
    
    	pD->fun1();//调用Base的func1
    	pD->fun2();//调用Derive的func2,符合就近原则
    	pD->fun4();//调用Derive的func4,fun4是派生类的成员函数,而不是父类的成员函数。
    	return 0;
    }
    
    • 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

    互不相关的类之间使用static_cast

    class A
    {
    public:
    	int a;
    	void func1() { cout << "A:func1" << endl; }
    };
    
    
    class B
    {
    public:
    	int b;
    	B(A& a) { cout << "B::Constructor" << endl; }
    
    	void func2() { cout << "B:func2" << endl; }
    };
    
    
    int main()
    {
    	A a;
    	B b = static_cast<B>(a);//将A类转换为B类
    	//b.func1();//fail,B类中不存在func1
    	b.func2();
    
    	/*
    	输出结果:
    	B::Constructor
    	B:func2
    	*/
    	return 0;
    }
    
    • 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

      但是如果B类中不存在B(A& a)该拷贝构造函数,就会发生编译错误。

      可见,如果A和B没有继承关系的两个互不相关的类,想要由A转换为B,则在B中必须定义“以A为参数”的构造函数。

    reinterpret_cast(任意类型)

    学习参考链接:https://blog.csdn.net/liranke/article/details/5297087

    将一种数据类型转换为另一种不同的数据类型,类似于C中的强制类型转换

    普通类型的转换

    class A
    {
    public:
    	char a;
    	int c;
    	void func() { cout << "A::func1" << endl; }
    };
    
    class B
    {
    public:
    	int b;
    	void func2() { cout << "B::func2" << endl; }
    };
    
    void test_func(int* pi, char* pc, A* pA, B* pB, int i)
    {
    	char* pc2 = reinterpret_cast<char*>(pB);//将B类指针转换为char类型指针
    	int* pi2 = reinterpret_cast<int*>(pc);//将char类型指针转化为int类型指针
    	int* pi3 = reinterpret_cast<int*>(i);//将int类型转换为int类型指针
    
    	A* pA2 = reinterpret_cast<A*>(pi);//int类型指针转换为A类指针
    	A* pA3 = reinterpret_cast<A*>(pB);//将B类指针转化为A类指针
    
    	long i2 = reinterpret_cast<long>(pA2);
    	cout << "i2 = " << i2 << endl;//i = 7338552
    
    	pA2->func();//A::func1
    	cout << "pA2->a " << pA2->a << endl;//pA2->a a
    	cout << "pA2->c " << pA2->c << endl;//pA2->c -858993460,即实际上是内存映射关系,int i = 97,映射为char类型,就是asciima中的a字符
    	pA3->func();//A::func1
    	//pA3->func2();//fail,虽然pB是B类型的指针,但是,pA3的类型是指向A的指针。符合就近原则
    
    	A a;
    	B b;
    	//B b2 = reinterpret_cast<B>(a);//fail,不允许
    	//A a1 = reinterpret_cast<A>(b);//fail,不允许
    }
    
    int main()
    {
    	int i = 97;
    	int* p1 = &i;
    	char* ch = (char*)"hello";
    
    	A a;
    	B b;
    
    	test_func(p1, ch, &a, &b, i);
    	return 0;
    }
    
    • 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

    在继承体系中的转换

    class A
    {
    public:
    	char a;
    	int c;
    	void func() { cout << "A::func1" << endl; }
    };
    
    
    class B
    {
    public:
    	int b;
    	void func2() { cout << "B::func2" << endl; }
    };
    
    
    class D :public A, public B
    {
    public:
    	void func4() { cout << "D::func4" << endl; }
    };
    
    
    void test_func1(B* pb)
    {
    	D* pd1 = reinterpret_cast<D*>(pb);//success
    	pd1->func();//A::func1
    	pd1->func2();//A::func2
    	pd1->func4();//A::func4
    	D* pd2 = static_cast<D*>(pb);//success
    	pd2->func();//A::func1
    	pd2->func2();//A::func2
    	pd2->func4();//A::func4
    }
    
    int main()
    {
    	D d;
    	test_func1(&d);
    	return 0;
    }
    
    • 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

    函数指针的转换

    void thump(char* p) { *p = 'x'; }
    typedef void (*PF)(const char*);
    
    PF pf;
    
    void g(const char* pc)
    {
    	//thump(pc);//fail,参数不匹配
    
    	//pf = &thump;//fail,不能将“void (*)(char* p) ”类型的值分配给“PF”实体
    
    	//pf = static_cast<PF>(&thump);//fail,无效类型转换
    
    	pf = reinterpret_cast<PF>(&thump);//success
    
    	pf(pc);//success
    }
    
    int main()
    {
        char* str = "h";
        g(str);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

      可以知道,让pf去指向thump是很危险的,因为这样做就是欺骗类型系统,使它能允许将一个常量的地址传递到某个要修改它的地方去,这样就是为什么我们必须使用强制类型转换的地方。

    reinterpret_cast和static_cast的比较

      static_cast就是利用C++类型之间的继承关系图和聚合关系图(编译器必须知道),根据一个子对象地址计算另外一个子对象的地址。reinterpret_cast不关心继承关系,直接把数据类型A的地址解释成另外一个数据类型B的地址。

      所以,对于无继承关系的类的转换,static_cast需要进行构造函数的重载,参数必须是要被转换的类的类型。而reinterpret_cast则没有这个限制。

    例如,对于

    class A {};
    class B {}
    void test_fun() {
        A a;
        B *pB1 = reinterpret_cast<B*>(&a);//success
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    而对于如下

    B b1 = static_cast<B>(a);//fail
    
    • 1

    如果将B类定义如下拷贝构造:

    class B{
    public:
        B(A& a) {}
    };
    
    • 1
    • 2
    • 3
    • 4

    B b1 = static_cast<B>(a);//success
    
    • 1

    小结

    1. reinterpret_cast并不在类的层次中穿行,即不关心继承体系。
    2. reinterpret_cast用于任意类型的转换,当然和其它强制类型一样,存在安全性。
    3. reinterpret_cast不会强制去掉const。
    4. reinterpret_cast后的尖括号中的类型是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转成一个整数,也可以把一个整数转换成一个指针。

    const_cast

    学习资料:https://blog.csdn.net/liranke/article/details/5423378

    const_cast用于删除变量的const属性,方便赋值。

    一个简单的通过const*指针进行修改原值的例子

    int main()
    {
        int variable = 10;
    	int* constP = &variable;
    	int* modifier = const_cast<int*>(constP);
    
    	*modifier = 20;
    	cout << "*modifier = " << *modifier << endl;
    	cout << "modifier = " << modifier << endl;
    	cout << "*constP = " << *constP << endl;
    	cout << "constP = " << constP << endl;
    	cout << "variable = " << variable << endl;
    	/*
    	输出结果:
    		*modifier = 20
    		modifier = 010FFA9C
    		*constP = 20
    		constP = 010FFA9C
    		variable = 20
    	*/
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      分析:

      以上代码,通过modifier指针对variable进行了值的修改。

      我们定义了一个非const的变量,但用带const限定的指针去指向它,在某一处我们突然又想修改了,可是我们当前场景下只有指针,这时我们可以用const_cast来修改。

      例子如下:

    int a = 20;
    const int* const p = &a;
    int* const p1 = const_cast<int* const>(p);
    *p1 = 21;
    cout << a << endl;//21·
    
    • 1
    • 2
    • 3
    • 4
    • 5

      在看一个例子:

    void output(int* val)
    {
    	cout << val << endl;
    }
    
    int main()
    {
    	const int i = 20;
    	const int* p = &i;
    	cout << p << endl;//输出:012FF934
    	//output(p);//fail
    	output(const_cast<int*>(p));//输出:012FF934
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      分析,定义了常量i,以及常量指针p,将p作为参数传递给output函数,结果编译失败;

      使用const_cast以后,将i的地址去除const后,传递给output函数,编译正常运行;

      可见,const_cast的主要作用是去除const。

    dynamic_cast(动态强制)

    学习资料:https://blog.csdn.net/liranke/article/details/5145787

    用于将一个父类对象的指针或引用转换为子类对象的指针或引用。(动态交换)必须包含一个多态类型(虚函数)。

    简单示例

    class A
    {
    	int a;
    	virtual void func() {}
    };
    
    class B :public A { int b; };
    
    class C :public A { int c; };
    
    int main()
    {
    	B b;
    	A* a = dynamic_cast<B*>(&b);
    
    	if (nullptr == a)
    		cout << "error" << endl;
    	else
    		cout << "success" << endl;//输出:success,子类转换为父类成功
    
    	C* c = dynamic_cast<C*>(a);
    	if (nullptr == c)
    		cout << "error" << endl;//输出:error,父类转换为子类失败
    	else
    		cout << "success" << endl;
        return 0;
    }
    
    • 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

    子类转化为父类(虚基类)

    //美术家
    class Artist
    {
    	public:
    		virtual void draw() { cout << "Artist draw" << endl; }
    };
    
    
    //音乐家
    class Musician
    {
    	public:
    };
    
    
    //教师
    class Teacher
    {
    	public:
    		virtual void teachStu() { cout << "Teacher teachStu" << endl; }
    };
    
    //既是美术家,又是音乐家,同时是教师的人
    class People :public virtual Artist, public virtual Musician, public Teacher
    {
    	public:
    };
    
    int main()
    {
    	People* p1 = new People();
    	p1->draw();
    	p1->teachStu();
    	cout << "dynamic_cast" << endl;
    
    	Artist* a1 = dynamic_cast<Artist*>(p1);//success
    	//Artist* a1 = p1;//success,向上转换,C++总是能正确识别,即将派生类的指针赋值给基类指针
    	
    	a1->draw();//success
    	//a1->teachStu();//fail
    	return 0;
    }
    
    • 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

    说明:

    1. 继承关系:People继承自Artist、Musician、Teacher三个类
    2. 语义:Artist美术家、Musician音乐家、Teacher教师。

    People类:定义一个特殊的People类,它的特点都是:既是美术家,又是音乐家,同时是教师。

    上面的代码,运行结果如下:

    Artist draw
    Teacher teachStu
    dynamic_cast
    Artist draw
    
    • 1
    • 2
    • 3
    • 4

    分析:

    下面两条语句均正确,都能由子类People转化为父类Artist。并且调用父类Artist的成员函数均能成功。

    Artist *a1 =  dynamic_cast<Artist*>(p1); 等价于 Artist *a1 =  p1; 
    a1->draw();     //success: 打印people teachStu
    
    • 1
    • 2

    可见,向上转换,无论是否用dynamic_cast,C++总是能够正确识别,即将派生类的指针赋值给基类指针。

    父类(虚基类)转换为子类,采用虚继承

    void test1()
    {
        People* p1 = new People();
    	printf("dynamic_cast test:\n");
    	Artist* a1 = p1;//success
    	//People* p2 = (People*)a1;//fail
    	People* p3 = dynamic_cast<People*>(a1);	//success
    	p3->draw();
    	p3->teachStu();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    分析:

    1. People *p2 = (People*)a1;明确说明,无法直接用(T*)b的形式去进行由基类指针到派生类指针的转换。
    2. People *p3 = dynamic_cast<People*>(a1);添加动态强制类型转换即可。

      由基类到派生类指针的转换通过dynamic_cast来完成。

      那么为什么使用dynamic_cast,基类指针a1就能转换为派生类指针p3呢?

      这就是RTTI机制。

      RTTI:Run Time Type Identification,即通过运行时类型识别。程序能够使用基类的指针或引用来检查这些指针或引用所指向对象实际的派生类型。

      C++提供在RTTI机制中逻辑上,存在类似如下的内存模型图:

    效果图

      在这个内存模型中,每个类都会在内存中保存一份类型信息,编译器会将存在继承关系的类的类型信息使用指针“连接”起来,从而形成一个继承链(Inheritance Chain)。

      dynamic_cast的使用注意事项:

    1. 查找规则:当使用dynamic_cast对指针或引用类型进行类型转换时,会先找到要转换目标指针指向的对象,再根据对象找到当前类(指针或引用指向的对象类型)的类型信息,并从此节点开始沿着继承链向上遍历(注意是向上),如果找到了要转换的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大风险,不能够转换(返回nullptr)。
    2. 作用对象:注意dynamic_cast转换符只能用于含有虚函数的类。

      现在,用RTTI来解释test1()中的函数代码:

      p1是派生类指针,a1是基类指针,a1要转换的指针类型为People,根据RTTI机制,从类People类向上查找,能找到a1所指的类Artist,所以,dynamic_cast转换是合法的。

    父类(非虚函数)转换为子类,采用虚继承

    void test2()
    {
        People* p1 = new People();
    	printf("dynamic_cast test:\n");
    	Musician* m1 = p1;//success
    	//People* p2 = (People*)m1;//fail
    	//People* p3 = dynamic_cast<People*>(m1);//fail
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    分析:

      根据dynamic_cast使用注意事项中“dynamic_cast”转换符只能用于含有虚函数的类(虚基类),Musician是非虚基类,所以无法进行动态强制转换。

    父类(虚基类)转换为子类,采用非虚继承

    void test3() {
    	People *p1 = new People();
    	printf("\ndynamic_cast test:\n");
    	Teacher *t1 = p1; //success
    	People *p2 = (People*)t1;//success,继承自Teacher,采用非虚继承的方式(不使用virtual),所以ok。
    	People *p3 = dynamic_cast<People*>(t1);//success:加了dynamic_cast,进行强转
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    分析:

    1. People *p2 = (People*)t1; People继承自Teacher,而采用非虚继承的方式,成功,因为采用的是非虚继承;
    2. People *p3 = dynamic_cast<People*>(t1); 加了dynamic_cast,进行强转,成功,因为Teacher是虚基类,而且符合RTTI机制。
  • 相关阅读:
    点亮三盏灯
    131. 分割回文串、93. 复原 IP 地址(切割问题)
    对进程、线程和协程的理解以及它们的区别
    慕课9、消息驱动的微服务-Spring Cloud Alibaba RocketMQ
    磺基罗丹明101,60311-02-6
    基于Python实现的特征选择的遗传算法(GA)
    Android打包脚本和上传apk托管平台
    Selenium自动化测试 —— 通过cookie绕过验证码的操作!
    【C++数据结构】B树概念及其实现(详解)
    1206. 设计跳表 : 数据结构实现题
  • 原文地址:https://blog.csdn.net/qq135595696/article/details/124974801