c++类型转换(隐式类型转换与显式类型转换)

隐式类型转换

发生隐式类型转换的时机

  • 大多数表达式中,比int小的整型值首先提升为较大的整数类型,称为整型提升。如对于boo、signed char、char、unsigned char、short、unsigned short等,只要他们所有可能的值都存在int范围里,他们就会被提升为int类型,否则就提升为unsigned int类型
  • 条件中,非布尔值转换为布尔值
  • 初始化过程中,初始值转换为变量类型
  • 赋值过程中,右侧运算对象类型转换为左侧运算对象类型
  • 算术运算或者关系运算中,需要按照规则统一转换之后进行运算
  • 函数调用发生的类型转换
  • 子类类型转换为父类类型

赋值转换

带符号类型之间的转换

遵循右侧运算对象类型转换为左侧运算对象类型的规则

	int i = 3.14;
	cout << i << endl;//3

无符号类型的转换

无符号类型转换需要注意的是,当赋一个超出该无符号类型的表示范围的值,c++将进行取模运算。

例如,当把-1赋值给unsigned char类型,其中unsigned char类型的数据占一个字节,8bit,其能表示的范围总数为2^8=256(0-255),故c=(256-1)%256

同理,unsigned short占据2个字节,16bit,故b=((2^16)-2)%(2^16)=655348

#include<iostream>
#include<typeinfo>
using namespace std;

void test()
{
	unsigned char c = -1;
	cout<<"unsigned char:" << sizeof(unsigned char) << endl;//1个字节,8bit,可表示范围0-255
	cout << static_cast<int>(c) << endl;//255=256-1


	unsigned short b = -2;
	cout<<"unsigned short:" << sizeof(b) << endl;//2个字节,16bit,可表示范围0-65535
	cout << static_cast<int>(b) << endl;//65534=65536-2
}

算术转换

算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换为最宽的类型

有符号类型的隐式转换

在算术运算的有符号类型的隐式转换中,首先将进行整型提升,如果结果类型匹配,则无需进行进一步转换,如果两个运算对象都是带符号的,则小类型转换为大类型

例如,假如一个运算对象的类型为long double,那么不论另一个运算对象是什么类型,它都将转换为long double之后进行运算。

再如,当表达式中都是整型和浮点型时,整数值将转换为相应的浮点型

#include<iostream>
#include<typeinfo>
using namespace std;

void test()
{
	int a = 4;
	long double b = 2.0;
	double c = 6;//整数6首先被转换为double

	cout<<"type(a+b):" << typeid(a + b).name() << endl;//int+long double
	cout<<"type(a+c):" << typeid(a + c).name() << endl;//int+double
	cout<<"type(b+c):" << typeid(b + c).name() << endl;//long double+double
}

无符号类型的隐式转换

在算术运算的无符号类型的隐式转换中,首先将进行整型提升,如果结果类型匹配,则无需进行进一步转换,如果两个运算对象都是无符号的,则小类型转换为大类型

#include<iostream>
#include<typeinfo>
using namespace std;

void test()
{
	unsigned int a = 4;
	unsigned short b = 5;
	cout<<"sizeof(unsigned int):" << sizeof(unsigned int) << endl;
	cout<<"sizeof(unsigned short):" << sizeof(unsigned short) << endl;
	cout << typeid(a + b).name() << endl;
}

混合符号的类型转换

如果两个运算符一个是有符号的,一个是无符号

  • 如果带符号类型小于等于无符号类型,那么带符号的运算对象将转换为无符号的类型对象
  • 如果带符号类型大于无符号类型,则转换结果将依赖于机器
    • 如果无符号类型的值都能存在于带符号类型中,那么此时无符号类型将转换为带符号类型
    • 如果不能,则带符号类型将转换为无符号类型
无符号类型与有符合类型相等
  • 均为正值,int转换为unsigned int
  • 运算顺序:
    1. a转换为unsigned int类型,结果为1
    2. a与b相加,结果为3
	int a = 1;
	unsigned int b = 2;
	cout << (a + b) << endl;//3
	cout << typeid(a + b).name() << endl;//unsigned int
  • 带符号为负值,无符号为正值
  • 运算顺序:
    1. a转换为unsigned int,结果为4,294,967,295
    2. 之后再与b相加,由于4,294,967,295+2,超过了unsigned int类型表示范围(0-4,294,967,295),因此结果最终会重新“绕回0”开始计算,故最终结果为1
	int a = -1;
	unsigned int b = 2;
	cout << (a + b) << endl;//1
	cout << typeid(a + b).name() << endl;//unsigned int
  • 带符号为正值,无符号为负值

  • 运算顺序:

    1. a转换为unsigned int类型,结果为1

    2. b进行负值转换,结果为2^32-1

    3. 两个unsigned int类型的数据相加

	int a = 1;
	unsigned int b = -2;//(2^32)-2
	cout << (a + b) << endl;//4294967295=((2^32)-2)+1
	cout << typeid(a + b).name() << endl;//unsigned int
无符号类型大于有符号类型

运算顺序

  1. a转换为unsiged long long
  2. 二者相加,但结果超出unsiged long long表示范围,故“绕回0”后再计算,最终为1
	int a = -1;
	unsigned long long b = 2;
	cout << (a + b) << endl;//1
	cout << typeid(a + b).name() << endl;//unsigned __int64

运算顺序:

  1. a转换为unsiged long long
  2. b进行负值的无符号类型转换,见无符号赋值类型转换
  3. 结果相加
	int a = 1;
	unsigned long long b = -2;
	cout << (a + b) << endl;//18446744073709551615
	cout << typeid(a + b).name() << endl;//unsigned __int64
无符号类型小于有符号类型

运算顺序:

  1. unsigned short首先进行整型提升,转换为int
  2. 进行a+b结果相加
	unsigned short a = 2;
	int b = 1;
	cout << a + b << endl;//3
	cout << typeid(a + b).name() << endl;//int

注意,当我们把b换成short时,运算结果的类型仍然是int,原因是unsigned short 与short都要进行整型提升转换为int之后再进行计算 

	unsigned short a = 2;
	short b = 1;
	cout << a + b << endl;//3
	cout << typeid(a + b).name() << endl;//int

总结

通过以上案例可以看出,c++算术运算的类型转换原则就是尽可能的避免损失精度,当进行算术运算类型转换时,首先进行整型提升,将小整数转换为大整数,之后根据不同的算术运算对象进行类型转换后进行计算,若都为带符号类型或者都为无符号类型,则遵循小转大原则;如果一个带符号,一个不带符号,当两者表示范围相同时带符号将转换为无符号,否则将根据二者表示范围进行转换,优先原则是“整型提升、小转大、有转无

其他隐式类型转换

  • 数组转换为指针(当数组被用作decltype关键字参数,或者作为取地址符&,以及typeid等运算符对象时,上述转换不会发生)
  • 指针的转换
    • 整数0或nullptr课转换为任意指针类型
    • 任意非常量指针可转换为void*
    • 任意指针可转换为const void*
  • 非常量转换为常量

显式类型转换

推荐使用显示类型转换,因为显式类型转换有助于调试,而隐式类型转换相当隐晦,一旦报错有时将很难查出来问题,另外新版本的显式类型转换支持将const转换为非const,且支持将父类对象转换为子类对象

static_cast

 任何具有明确定义的类型转换,只要不包含底层const,都可以只要static_const

这是一个显示的说明类型转换的关键字,如下述显式的将double转换为int,与旧式的显式类型转换相同

	double a = 3.14;
//static_cast<int>等价于int(a)
	cout << typeid(static_cast<int>(a)).name() << endl;//int

`但这种方式的显式转换不能将const类型转换为非const

const_cast

专门用于将const类型转换为非const

不使用const_cast

使用conts_cast

dynamic_cast

用于将基类对象转换为子类对象

dynamic_cast运算符能将父类指针或者引用安全地转换为子类的指针或者引用。

使用dynamic_cast运算符的前提条件是:父类中必须至少有一个虚函数。

一般情况下,使用该类型转换的场景是因为

如果子类中有一个父类中没有的普通成员函数(非虚函数),那么,即便是父类指针指向了该子类对象,但也没办法用父类指针调用子类中的这个普通成员函数。那么如果就想用父类指针调用子类中的这个普通成员函数,该怎样做呢? 能够想到的办法可能是:

  • 把这个函数在父类和子类中都写成虚函数(其实在父类中只要是虚函数,在子类中自然就是虚函数),但是,这样做比较啰唆,而且显然,子类中每增加一个新成员函数就要在父类中增加等同的虚函数,不管怎样说,这种解决方案虽然可以,但不太让人满意。
  • 既然虚函数这种解决方案不太让人满意,那么RTTI运算符就能派上用场了。可以使用dynamic_cast运算符进行类型转换,在写程序时必须要很小心,要清楚地知道转换的目标类型并且转换类型后还需要检查这种转换是否成功。
class Human
{
public:
	Human(){};
	Human(std::string name):_name(name){};
	virtual void show()
	{
		std::cout<<"this is human!"<<std::endl;
	}
	std::string getName() const
	{
		return this->_name;
	}
private:
	std::string _name;
};

class Man:public Human
{
public:
	Man(std::string name):Human(name){};
	void show()
	{
		std::cout<<"this is Man!"<<std::endl;
	}

	void testFun() const
	{
		std::cout<<"this is dynamic_cast test!"<<std::endl;
	}
};

如上所示,我们定义了一个父类和一个子类,并在子类中单独定义了父类中没有的普通成员函数testFun,接下来,我们在测试代码中进行测试

可以看到,当使用human->testFun()时,代码会给出报错,而如果想调用这个子类中单独有的普通函数,需要使用dynamic_cast将父类指针的类型转换为子类类型

reinterpret_cast

可以将整型转换为指针,也可以把指针转换为数组;可以在指针和引⽤⾥进⾏肆⽆忌惮的转换,平台移植性⽐价差。一般很少使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值