C++ 类型转换

      在C++中,如果两种类型有关联,那么就可以相互转换(conversion)。

      以加法运算为例,C++不会将两个不同类型的值相加,而是先根据类型转换规则设法先将运算对象统一后在求值。上述的类型转换是自动执行的,无须程序员的介入,因此被称为隐式转换(implicit conversion

      算术类型之间的隐式转换被设计得尽可能避免损失精度,例如,如果表达式中既有整数类型也有浮点数类型,那么整型会转换成浮点数类型。

对于初始化来说,因为被初始化的对象类型无法改变,所以初始值被转换成该对象的类型。如果使用double型的值初始化int型的变量,那么double型的值会隐式转换成int并忽略小数部分(有些编译器会警告运算损失了精度)。

何时发生隐式类型转换

1.        在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型;

2.        在条件中,非布尔值转换成布尔值;

3.        初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。

4.        如果算术运算或关系运算的运算对象有多种类型,需要转换成一种类型;

5.        如1.5章要介绍的,函数调用也会发生类型转换;


1)   算术转换

      算术转换(arithmetic conversion)是把一种算术类型转换成了另外一种算术类型。算术转换的规则定义了一套类型转换的层次,其中该运算符的运算对象将转换成最宽的类型。举例,如果一个运算对象的类型是long double,那么不管另一个运算对象的类型是什么都会被转换成long double。一般地,同时有浮点类型和整数类型时,整数值将转换成相应的浮点数。

整型提升

      整数提升(integral promotion)负责把小整数类型转换成较大的整数类型。对于bool、char、signed char、unsigned char、short和unsigned short等类型来说,只要它们所有可能的值都能存在int里,它们就会提升成int类型;否则提升成unsigned int型。常见是false提升成0,true提升成1。

      较大的char类型(wchar_t、char16_t、char32_t)提升成int、unsigned int、long、unsigned long、long long、unsigned longlong中个最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。

无符号类型的运算对象

      如果某个运算符的运算对象类型不一致,这些运算对象将转换成同一种类型。但是如果其中存在无符号类型,那么转换的结果依赖于机器中各个整数的相对大小。需要进行以下处理:

1.        首先进行整型提升;

2.        如果结果的类型都是带符号或不带符号的:

      a)        如果类型匹配则无须进行转换;

      b)       如果类型不匹配则小类型转换成较大的类型;

3.        如果一个是有符号另一个是无符号的:

      a)        无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的;

      举例:如果两个类型分别是unsigned int和int,那么int类型的运算对象转换成unsigned int类型。注意:如果int型的值恰好为负数,那么转换会发生问题。

      b)       带符号的类型大于无符号类型,那么转换结果依赖于机器。

                         i.        如果无符号类型的所有值都能存在带符号类型中,那么无符号类型的对象转换成带符号类型;

                         ii.       如果不能,则带符号的转换成无符号类型。

      举例:两个运算对象分别是long和unsigned int,如果int和long的大小相同,那么long转换成unsigned int型;如果long比int大,则unsigned int型的对象转换成long类型的。

理解算术转换

      举例说明:

bool        flag;       char              cval;
short       sval;       unsigned short   uval;
int         ival;       unsigned int     uival;
long        lval;       unsigned long    ulval;
float       fval;       double            dval;

3.14159L + 'a';        //'a'提升成int,然后该int值转换成long double
dval + ival;           //ival转换成double
dval + fval;           //fval转换成double
ival = dval;           //dval转换成(切除小数部分后)int
flag = dval;            //如果dval是0,则flag是false,否则flag是true
cval + fval;           //cval提升成int,然后该int值转换成float
sval + cval;           //sval和cval都提升成int
cval + lval;           //cval转换成long
ival + ulval;          //ival转换成unsigned long,无符号的类型大于等于有符号类型      				
usval + ival;          //根据unsigned short 和int所占空间的大小进行提升
uival + lval;          //根据unsigned int和long所占空间的大小进行转换

2)   其它隐式转换

      除了算术转换之外,还有几种隐式转换:

数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:

int ia[10];             //含有10个整数的数组
int* ip = ia;           //ia转换成指向数组首元素的指针

       当数组被用作一下情况时,不会被转换成指针:

1.        decltype关键字的参数;

2.        取地址符(&);

3.        Sizeof;

4.        Typeid(在运行时类型识别那一节将介绍);

5.        给数组引用来绑定数组时;

指针的转换:C++还规定了几种其它的指针转换方式:

1.  包括常量整数值0或者字面值nullptr能转换成任意指针类型;

2.  指向任意非常量的指针能转换成void*;

3.  指向任意对象的指针能转换成const void*;

4.  基类指针可以指向派生类对象;

转换成布尔类型:如果指针或算术类型的值是0,那么转换结果是false;否则转换结果是true。

转换成常量:允许将指向非常量类型的指针转换成相应的常量类型的指针,引用也是这样。

类类型定义的转换:

       类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型转换。在1.6.5节中将看到一个例子,该例子同时提出多个转换请求,这些请求将被拒绝。

       举例说明:

       string s,t = "avalue";    //字符串字面值转换成string类型

while(cin >> s)             //while的条件部分把cin转换成布尔值

       条件(cin >> s)实际检查的是istream类型的值,IO库定义了从istream向布尔值转换的规则,根据这一规则,cin自动地转换成布尔值,即最后一次读入成功则返回true;否则是false。


3)   显式转换

      有时我们需要显式地将对象强制转换成另外一种类型。

Tips:虽然有时不得不使用强制类型转换,但这种方法本质上是非常危险的。

命名的强制类型转换

      一个命名的强制类型转换有如下形式:

cast-name<type>(expression);

      其中,type是转换的目标类型而expression是要转换的值。如果type是左值,那么结果是左值。Cast-name是static_cast、dynamic_cast、const_castreinterpret_cast中的一种。dynamic_cast支持运行时类型识别,一般只支持子类向基类的转换,将在运行时类型识别那一节做更详细的介绍。

static_cast

      任何具有明确定义的类型转换,只要不包含底层cosnt,都可以使用static_cast。举例如下:

//进行强制类型转换以便执行浮点数除法
int i,j;
double slope = static_cast<double>(j) / i;

static_cast有两个主要的作用:

1.        把一个较大的算术类型转换成较小的类型。如果不使用static_cast,那么编译器一般会警告有精度损失,使用了static_cast则该警告消失;

2.        对于编译器无法自动执行的类型转换也非常有用,比如可以使用static_cast找回存在于void*指针中的值,即将void*指针转回其原来的类型。

const_cast

       const_cast只能改变对象的底层const

const char *pc;
char *p = const_cast<char*>(pc);   //正确,但是通过p写值是未定义的行为

      这种将常量转换成非常量的行为称为“去掉const性质(cast away the const)”。去掉某个对象的const性质可以使编译器不再阻止我们对该对象的写操作。但是使用const_cast获得写操作的权限然后执行写操作会产生未定义的操作。

      只有const_cast才能改变表达式的常量属性,同时它也只能做改变常量属性这一件事,不能改变表达式的类型。

reinterpret_cast

       reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。使用reinterpret_cast可以将某个类型强制转换为另外一个类型。如:

int *ip;
char *pc = reinterpret_cast<char*>(ip);

      此时如果调用以下代码:

string str(pc);

      编译器不会报错,因为已经显式地转换了格式,但是可能导致异常的运行时行为。因此使用reinterpret_cast是非常危险的

Tips: reinterpret_cast本质上依赖于机器,要想安全地使用reinterpret_cast必须对设计的类型和编译器实现转换的过程都非常了解。所以尽量避免使用强制类型转换,特别是reinterpret_cast,每次使用了一条强制类型转换语句时,都应反复斟酌能否以其他方式实现相同的目标。

旧式的强制类型转换

      在早期的C++语言中,显式类型转换包含两种形式:

      type(expr);          //函数形式的强制类型转换

      (type) expr;        //C语言风格的强制类型转换

      根据所涉及的类型不同,旧式的强制类型转换分别具有与const_caststatic_cast、reinterpret_cast相似的行为。

Tips:与命名的强制类型转换相比,旧式的强制类型转换从表现形式上来说不清晰,容易被看漏,所以一旦转换过程出现问题,追踪起来也更加困难。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值