• C/C++类型转换


    1.C语言显式/隐式类型转换

    1.0类型转换发生

    赋值运算符左右两侧类型不同
    形参与实参类型不匹配
    返回值类型与接收返回值类型不一致

    1.1隐式类型转换

    编译器在编译阶段自动进行,能转就转,不能转就编译失败

    C语言允许**意义相近的类型(比如整形家族和浮点型家族)**进行隐式类型转换:

    1. 表示数据大小
    2. 占空间不同
    3. 表示范围不同
    4. 浮点数存储机制不同

    隐式类型转换可以发生在赋值和关系运算符之间

    整形转整形存在: 整形提升和整形截断
    整形和浮点型转换: 丢失精度

    1.2显示强制类型转换

    用户自己处理

    C语言允许意义不相近的类型进行显式类型转换:

    意义不相近但转换后有意义

    比如指针转整形 指针是一个地址 地址编号有大小 有意义
    一个类转整形:无意义
    在这里插入图片描述

    1.3隐式类型转换存在的问题

    void Problem(size_t pos, char ch)
    {
    	string _str[10] = { 0 };
    	size_t _size = 5;
    
    	//若pos为0 end为unsigned int 永远 >=0  -- 死循环
    	//size_t end = _size - 1;
    	// 
    	//改进: 搞成int 仍然不行 因为在关系运算符 >= 会发生隐士类型转换
    	//-1与0比较 -1从u_int->int -1 => MAX_INT [ ]越界访问
    
    	//我们只能改变end的始终取值范围 来避免这种情况
    
    	int end = _size - 1;
    	while (end >= pos) 
    	{
    		_str[end + 1] = _str[end];
    		--end;
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.C++自己的类型转换

    C++自己搞了一套 说明C语言的不够好 不好在哪里呢?

    1. 隐式类型转换可能出问题:比如数据精度丢失 以及上面的例子
    2. 显式类型转换的可视性比较差,所有转换形式都是以一种相同形式书写,难以跟踪错误 代码不够清晰
    3. C++要兼容C语言,C++中还可以使用C语言的转换风格

    2.1static_cast非多态类型转换(静态转换)

    编译器隐式类型转换可用static_cast,但它不能用于两个不相关的类型进行转换

    int main()
    {
    	double d = 12.34;
    	int a = static_cast<int>(d);
    	cout << a << endl;  //12
    	
    	int* p = &a;
    	//int address = static_cast(p);   不支持
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2 reinterpret_cast

    为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型

    在这里插入图片描述

    一个问题

    在这里插入图片描述
    在这里插入图片描述

    pb仅仅是把10当成一个地址去指向
    但是地址为10的空间没有数据
    是无效的  不允许访问和修改
    
    • 1
    • 2
    • 3

    2.3const_cast

    删除变量的const属性,进行赋值

    在这里插入图片描述

    1.程序阅读

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    int main()
    {
    	//int b = 10;
    	//int* pb = reinterpret_cast(b);
    	cout << *pb << endl;
    
    	//const局部变量 存在内存的栈上 不可直接修改 但可间接修改 
    	//某些编译器认为const变量不会被修改 于是直接放进寄存器 使读的速度快 而不可写
    	const int a = 2; 
    	int* p = const_cast<int*>(&a);
    	//某些编译器: 内存中的a被间接修改 但是寄存器上的a没有被改
    	*p = 3;
    	
    	cout << a << endl;   //VS:直接访问a的2
    	cout << *p << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    const_cast将 const int* --> int* 很危险!!!

    2.程序改动

    在这里插入图片描述

    1. const int a = 2;

    这是一个常量定义,a被声明为一个常量,其值被固定为2,不能在程序运行过程中被改变。在整个程序执行过程中,试图修改a的操作都是非法的。

    1. volatile const int a = 2;

    这里将a声明为volatile const类型,表示这是一个同时具有常量和易失性(volatile)特性的变量。volatile关键字的作用是告诉编译器,该变量的值可能会在不同的时间点被意外更改,仅依赖于编译器优化的常量值可能会导致问题。因此,编译器不会对a进行一些优化操作,如缓存变量值或重新排序指令,以确保对a每次访问都从其内存地址读取最新的值

    综上所述,const int a = 2;声明的a是一个常量,而volatile const int a = 2;声明的a是一个常量且具有易失性特性
    在这里插入图片描述
    在这里插入图片描述

    2.4dynamic_cast

    1.对象间的转换

    子类对象可以赋值给父类对象[切割赋值]
    父类的指针或引用可以指向子类
    父类对象无法转换成子类对象

    class A
    {
    public:
    	int _a = 0;
    };
    
    class B : public A
    {
    public:
    	int _b = 1;
    };
    int main()
    {
    	int i = 0;
    	//double tmp = i; double d = tmp;
    	double d = i;
    
    	//double& rd = i; 
    	// 1.引用的是临时变量
    	// 2.临时变量具有常性
    	const double& rd1 = i;                      //C语言
    	const double& rd2 = static_cast<double>(i); //C++
    
    	//类的继承没有产生tmp
    	//因为这里有类的切割赋值
    	B bb;
    	//子类对象赋值给父类对象
    	A aa1 = bb;
    	//父类的指针或引用指向子类对象
    	A& ra1 = bb;
    
    	父类对象无法转换成子类对象
        B bb = aa;
        B bb = (B)aa;
        B bb = dynamic_cast<B>(aa);
    }
    
    • 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

    2.dynamic_cast的引出

    本来 父类类型指针可以指向父类/子类对象 但是即便指向子类对象却不可以访问子类对象的数据

    由此引发了父类类型指针如何转换成子类类型指针的问题

    dynamic_cast通过将指向子类对象的父类指针转成指向子类对象的子类指针 从而使得可以访问子类对象的数据

    int main()
    {
    	A aa;
        B bb;
    	//父类指针指向父类对象
    	A* pa1 = &aa;
    	pa1->_a;
    
    	//父类指针指向子类对象
    	//父类指针指向了子类中父类的那一部分 
    	A* pa2 = &bb;
    	pa2->_a;
    	//pa2->_b; 访问不了 pa1/pa2不管指向谁都是A类型的 只能访问_a 
    	
    	//父类指针强制转换成子类指针
    	B* pb = (B*)pa1;
    	pb->_a;
    	//pb->_b;
    	//仍然无法访问_b: 引发异常 存在越界风险
    	
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    dynamic_cast的引出

    class A
    {
    public:
    	virtual void f(){}
    public:
    	int _a = 0;
    };
    
    class B : public A
    {
    public:
    	int _b = 1;
    };
    
    //p指向父类对象: 父类指针指向父类对象
    //p指向子类对象: 父类指针指向子类对象   但只可以访问父类数据
    void fun(A* p)
    {
    	// p指向父类  不能转换  转换表达式返回nullptr
    	// p指向子类  可以转换  转换表达式返回正确的地址
    	B* pb = dynamic_cast<B*>(p); 
    	//dynamic_cast安全性在于它不仅可以判断转换成功 而且转换成功后还可以访问
    	//B* pb = (B*)p; //都可以转换成功 但是不可成功访问
    
    	if (pb == nullptr)
    	{
    		cout << "当前父类类型指针指向父类对象 父类类型指针 没有转换成 子类类型指针" << endl;
    		//防止p传进来的就是nullptr
    		if (p != nullptr)
    		{
    			p->_a = 1;
    			cout << "p->_a: " << p->_a << endl;
    		}
    		else 
    		{
    			cout << "且p指针本就是一个nullptr!" << endl;
    		}
    		
    	}
    	else
    	{
    		cout << "当前父类类型指针指向子类对象 父类类型指针 成功转换成 子类类型指针" << endl;
    		pb->_a = 1;
    		pb->_b = 2;
    		cout << "pb->_a: " << pb->_a << endl << "pb->_b: " << pb->_b << endl;
    		cout << "p: " << p << " --- " << "pb: " << pb << endl;
    	}
    }
    
    int main()
    {
    	A aa;
        B bb;
    	//父类指针指向父类对象
    	A* pa1 = &aa;
    	pa1->_a;
    
    	//父类指针指向子类对象
    	//父类指针指向了子类中父类的那一部分 
    	A* pa2 = &bb;
    	pa2->_a;
    	//pa2->_b; 访问不了 pa1/pa2不管指向谁都是A类型的 只能访问_a 
    	
    	//父类指针强制转换成子类指针
    	B* pb = (B*)pa1;
    	pb->_a;
    	//pb->_b;
    	//仍然无法访问_b: 引发异常 存在越界风险
    
    	fun(&aa);
    	cout << endl;
    	fun(&bb);
    	cout << endl;
    
    	fun(nullptr);
    
    	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
    • 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

    在这里插入图片描述

    3.dynamic_cast的注意事项

    dynamic_cast用于将一个父类类型的指针/引用转换为子类类型的指针或引用(动态转换)

    向上转型:子类类型的指针或引用转换为父类类型的指针/引用(不需要转换,赋值兼容规则)
    向下转型:父类类型的指针/引用转换为子类类型的指针或引用(dynamic_cast转型较安全)
    注意:

    1. dynamic_cast只能用于含有虚函数的父类
    2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回nullptr
    3. 强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换
    4. 多继承情况了解在这里插入图片描述

    3.Run-time Type identification(RTTI)

    运行时类型识别

    C++支持RTTI的方式:

    1. typeid运算符
    2. dynamic_cast运算符
    3. decltype
    template<class T1, class T2>
    void Func(T1 a, T2 b)
    {
    	decltype(a * b) x;
    	cout << typeid(x).name() << endl;
    }
    int main()
    {
    //关键字decltype
    //将变量的类型 声明为 表达式指定的类型
    const int X = 1;
    double y = 2.2;
    cout << typeid(x * y).name() << endl;
    
    decltype(x* y) j ;
    decltype(&x) k ;
    cout << typeid(j).name() << endl;
    cout << typeid(k).name() << endl;
    //使用场景
    //1.传类型
    vector<decltype(x* y)> v;
    //2.函数内部
    Func(1, 'a');
    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
  • 相关阅读:
    Shell——查看基础信息脚本
    工信部证书的作用是什么
    python实现三维应力云图
    875. 快速幂
    生产环境重大bug,update加上索引字段会走索引进行更新?还是走全表扫描
    面向接口编程实践之aspnetcoreapi的抽象
    vue中使用echarts渐变柱状图 Cannot read properties of undefined (reading ‘graphic‘)解决方法
    Java中的代码优雅重构实战
    Ubuntu 18.04.6 LTS安装NVIDIA显卡驱动&CUDA&cuDNN
    【学生网页作业】航海王动漫网页 html+ css + JavaScript 简单的学生网页作业源码
  • 原文地址:https://blog.csdn.net/LHRan_ran_/article/details/134073638