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;
	} 
}

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<int>(p);   不支持
	return 0;
}

2.2 reinterpret_cast

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

在这里插入图片描述

一个问题

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

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

2.3const_cast

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

在这里插入图片描述

1.程序阅读

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

int main()
{
	//int b = 10;
	//int* pb = reinterpret_cast<int*>(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;
}

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);
}

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;
}

在这里插入图片描述

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;
}

在这里插入图片描述

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值