表达式是否合法,取决于操作数的类型, 而且合法的表达式其含义也由其操作数类型决定。
在C++中,某些类型之间存在相关的依赖关系。
如果两种类型
相关,则可在需要某种类型的操作数位置上,
使用该类型的相关类型对象或值。
所谓相关,指的就是两种类型可以互相转换。
考虑一下例子:
int ival = 0;
ival = 3.541 + 3;
则ival的值 为 6
这种是把等式右边的操作按精度最高处理,然后强制转为左边的类型。这两种都是隐式类型转换
C++定义了算术类型之间的内置转换以尽可能防止精度损失
那么,何时发生隐式类型转换:
编译器在必要时将类型转换规则应用到内置类型和类类型的对象上。
1 int ival;
double dval;
ival >= dval; int 转为double
在混合类型的表达式中,其操作数被转换为相同的类型,一般会往高精度转换。
2
用作条件的表达式被转换为bool类型:
int ival;
if(ival)
while(cin)
这些可以将 表达式转换为bool类型。
1 赋值时的强制类型转换
2 函数调用时的转换
这些转换都需要类型之间相互兼容
算术转换:
算术转换规则定义了一个类型转换层次, 将二元操作符的两个操作数转换为同一类型, 并使表达式的值也具有相同类型。在包含多种类型的表达式中,转换规则要确保计算值的精度。
最简单的转换为整形提升: 对于所有比int小的整形,包括char、 signed char、short 和unsigned short, 如果他们的所有可能的值都包容在int 内,那么它们就会被提升为int型。否则,它们将被提升为unsigned int。 bool值 false转换为0, true转换为1.
有符号与无符号类型之间的转换
若表达式中使用了无符号(unsigned)数值, 所定义的转换规则需保护操作数的精度。确保精度,就是说,表示范围低的类型要向表示范围高的类型转。
unsigned 操作数的转换依赖于机器中整形的相对大小。因此,这类转换本质上依赖于机器。
转换规则,如果 int型足够表示所有unsigned short型的值,则将unsigned short转换为int , 否则,将两个操作数均转换为unsigned int。 如果short用半字表示而 int用一个字表示, 则所有的unsigned值都能包容在int内。
对于包含signed和unsigned int 型的 表达式, 其转换可能出乎我们的意料,表达式中 的signed 型数值会被转换为unsigned型。
如果 int型的值为负数,则可以将其转换为unsigned int对应的机器码,然后跟另一个 unsigned int型的值相加,如果产生溢出, 则会结果错误。丧失精度
2
理解算术转换
提升与转换
提升 就是将一个低于CPU处理位数的数值,提升为CPU处理的位数。
而转换,就是讲一个CPU处理位数的数值转换为大于CPU处理的位数的数值
其他隐式转换
1
指针转换
大多数情况下,数组都会自动转换为指向第一个元素的指针。只有一下几种情况例外:
数组用作取地址& 操作符的操作数 ; sizeof操作符的操作数时。 用数组对数组的引用进行初始化时。 用数组对数组的引用进行初始化
关于 判断语句中的非bool类型向bool类型,及 bool与其它类型的转换,就不一一说了
下面重点看下,
转换与枚举类型:
C++自动将枚举类型的对象或枚举成员转换为整形, 其转换结果可用于任何要求使用整数值的地方
enum Points { point2d=3, point2w, point3d=3, point3w};
const size_t array_size = 1024
int chunk_size = array_size * pt2w;
转换为const对象:
当使用非const对象初始化const对象的引用时, 系统将非const对象转换为const对象。 此外,还可以将非const对象的地址,转换为指向相关const类型的指针
int i;
const int ci = 0;
const int &j = i;
const int *p = &ci;
由标准库类型定义的转换:
从istream中读取数据,并将此表达式作为while循环条件:
string s;
while(cin>>s);
这里使用了IO标准库定义的类型转换。 cin>>s, 无论读入成功与否,该表达式的结果都是cin。
while循环条件应为bool类型的值。所以,这里,istream类型的值应转换为 bool类型。 将istream类型转换为bool类型意味着要检验流的状态: 如果最后一次读cin 的尝试是成功的,则流的状态将导致上述类型转换bool类型后获得true值。反之,获得false值
显示转换:它也称为强制类型转换:cast
强制类型转换的操作符有:
static_cast, dynamic_cast, const_cast, reinterpret_cast
何时需要强制类型转换
命名的强制类型转换:
cast-name<type>(expression)
其中,cast-name有如下几种:
dynamic_cast:
支持运行时识别指针或引用所指向的对象。
const_cast:
const char *pc_str;
char *pc = string_copy(const_cast<char*>(pc_str));
static_cast: 是将高精度的值转为低精度的值,这样做的好处是, 可以告诉编译器或读者,可以不用担心精度损失,而放心的去执行。
也可以找回原来精度的值:
void *p = &d;
double *dp = static_cast<double*> (p);
reinterpret_cast
这个可以将任意类型转换为任意类型, 但是转换后的类型其实扔属于原先被转换的类型:如下所示,
int *ip;
char *pc = reinterpret_cast<char *>(ip);
其实,在这里,pc扔属于int类型,所以不能再进行一下操作: string_copy(pc);
那么,这个强制转换存在的意义为何呢?
它其实,是在要对某一中类型进行操作时,如果这种操作用另一种类型更加简单,那么便可以先用该语句,将其转换为该类型。
建议: 避免使用强制类型转换:
强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的C++程序。
旧式强制类型转换:
显示强制类型转换用圆括号将类型括起来实现:
char *pc = (char *) ip;
这种强制类型转换的可视性比较差,其效果与使用reinterpret_cast符号相同, 难以跟踪错误的转换。
用C++命名式的强制类型转换的好处是:
可以让程序员清楚的辨别 各个强制类型转换的风险等级。
最有风险的是: reinterpret_cast, 其次是 const_cast, 再次是static_cast. dynamic_cast是最特殊的一个:
dynamic_cast运算符,应该算是四个里面最特殊的一个,因为它涉及到编译器的属性设置,而且牵扯到的面向对象的多态性跟程序运行时的状态也有关系,所以不能完全的使用传统的转换方式来替代。但是也因此它是最常用,最不可缺少的一个运算符。
与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。