1、前言
C++中有多种数据类型,在进行运算时难免会出现不同类型的变量共同参与运算的情况,这时需要进行类型转换。C++会自动执行一些类型转换,类型转换总的来说分为两种情况:范围大的类型赋值给范围小的类型和范围小的类型赋值给范围大的类型;类型转换分为隐式转换(自动转换)和显示转换(强制转换)
2、初始化和赋值时进行的转换
将一种类型的值赋给另一种类型时,值被转换为接收变量的类型。
赋值进行的转换分为以下两种情况:
1)、范围小的类型赋值给范围大的类型
例如:
long a;
short b = 12;
a = b; //short类型赋值给long,short类型的值变为long类型
这种赋值通常不会导致什么问题,b赋值给a并不会改变b的值,只是b会占用更多的字节而已。
特别地,bool类型的赋值给整型或浮点型时,若为false,则转换为0;若为true,则转换为1
2)、范围大的类型赋值给范围小的类型
例如:
long a = 2111122223;
float b = a;
由于float只有6位有效数字,赋值后b的值为2.11112E9,降低了精度。
特别地,若将0赋值给bool类型,则被转换为false;非零值转换为true
潜在的数值转换问题:
转换类型 | 潜在的问题 |
---|---|
较大的浮点类型转换为较小的浮点类型,如double转换为float | 精度(有效位数降低);值可能超出目标类型的取值范围,这种情况下结果不确定 |
浮点类型转换为整型 | 小数部分丢失(不会四舍五入,直接舍弃小数部分,如3.9转换为3而不是4);值可能超出目标类型的取值范围,这种情况下结果不确定 |
较大的整型转换为较小的整型,如long转换为short | 值可能超出目标类型的取值范围,通常只复制右边的字节 |
3、以{ }方式初始化时进行的转换
列表初始化的要求较为严格,总的原则是不允许缩窄,即不允许将范围大的类型赋值给范围小的类型。
例如下面这种情况是不允许的:
char ch = {31325};
在不同的整型之间(如long变量初始化为int值)转换或将整型转换为浮点型肯能被允许,前提是编译器知道目标变量能够正确地存储赋给它的值。
4、表达式中的转换
1)、当一个表达式中包含两种不同的算术类型(浮点型和整型)时,C++执行两种自动转换
(1)、一些类型在出现时便自动转换
在计算表达式时,C++将bool(true被转换为1,false被转换为0)、char、unsigned char、signed char和short值转换为int,这些转换称为整型提升。
其他的整型提升:
1)、若short比int短,则unsigned short类型被转换为int;若short和int长度相同,则unsigned short类型被转化为unsigned int类型;
2)、wchar_t被提升为int、unsigned int、long或unsigned long中足够存储wchar_t取值范围的类型。
(2)、不同类型进行算术运算时的转换
当运算涉及两种不同的类型时,较小的类型被转化为较大的类型。如:int类型和float类型变量相加,int类型的变量会被转换为float类型。在C++中编译器通过校验表来确定算数表达式中执行的转换。校验表如下:
(1)、若有一个操作数类型为long double,则将另一个转化为long double。
(2)、否则,若有一个操作数类型为double,则将另一个操作数转换为double类型。
(3)、否则,若有一个操作数的类型是float,则将另一个操作数转换为float。
(4)、否则,说明操作数都是整型,因此进行整型提升。
(5)、如果两个操作数都是有符号或者无符号的,且其中一个操作数的级别比另一个低,则转换为级别高的。
(6)、若一个操作数为有符号的,另一个操作数为无符号的,且无符号操作数的级别比有符号操作数的级别高,则将有符号操作数转换为无符号操作数所属的类型。
(7)、否则,如果有符号类型可表示无符号类型的所有可能取值,则将无符号操作数转换为有符号操作数所属的类型。
(8)、否则,将两个操作数都转化为有符号类型的无符号版本。
整型级别:
(1)、有符号整型按级别从高到低依次为:long long、long、int、short和signed char;
(2)、无符号整型的排列顺序与有符号整型相同;
(3)、char、signed char和unsig char的级别相同;
(4)、bool类型的级别最低;
(5)、wchar_t、char16_t和char32_t与其底层相同
枚举类型在算数表达式中被转换为整型
enum color {red, blue, green, yellow};
int a = red; //red提升为整型
blue = 5; //错误,int型不能自动转换为color类型
int b = blue + 3; //枚举在算数表达式中被转换为整型
2)、条件、循环中的测试条件表达式类型转换
for循环、while循环和do-while循环中的测试条件表达式会强制转换为bool类型;
if中的条件表达式也会强制转换为bool类型。
3)、关系表达式中的类型转换
关系表达式会将结果转换为bool类型。
5、传递参数时的转换
对char和short应用整型提升;
在取消函数原型对参数控制的情况下,C++将float参数提升为double;
当传递给函数的实参类型与形参类型不匹配时会自动转换为函数原型中指定的类型,前提是实参与形参都是算术类型(自动进行强制类型转换)
6、函数返回值时的类型转换
float cube()
{
return 3.14;
}
int a = cube(); //float类型转换为int类型(自动进行强制类型转换)
7、强制类型转换
强制类型转换方式有两种:
1)、格式:
(typeName)value或typeName (value)
如:
float a = 1.6;
int b = int (a); //C++转换格式
int b = (int) a; //C语言格式
2)、使用强制类型转换运算符转换
强制类型转换运算符有四种:
(1)、static_cast
(2)、const_cast
(3)、reinterpret_cast
(4)、dynamic_cast
<1>static_cast运算符
作用:用于静态类型的转换,不执行运行时检查。例如,它可以用来把一个基类指针转换为派生类指针。
语法:
static_cast < type-name > (expression)
仅当type-name可被隐式转换为expression所属的类型或expression可被隐式转换为type-name所属的类型时,转化才合法,否则会出错
示例:
class B { ... };
class D : public B { ... };
void f(B* pb, D* pd)
{
D* pd2 = static_cast<D*>(pb); // 不安全, pb可能只是B的指针
B* pb2 = static_cast<B*>(pd); // 安全的
}
<2>const_cast运算符
作用:用于修改类型的 const / volatile 属性。除了 const 或 volatile 属性之外,目标类型必须与源类型相同。这种类型的转换主要是用来操作所传对象的 const 属性,可以加上 const 属性,也可以去掉 const 属性。
语法:
const_cast < type-name > (expression)
示例:
#include<iostream>
using namespace std;
int main()
{
const int constant = 26;
const int* const_p = &constant;
int* modifier = const_cast< int* >(const_p);
*modifier = 27;
cout<< "constant: " << constant << endl;
cout<< "*modifier: " << *modifier << endl;
}
输出:
constant: 26
*modifier: 27
<3>reinterpret_cast运算符
作用:用于把某种类型指针改为其他类型的指针。它可以把一个指针转换为一个整数,也可以把一个整数转换为一个指针。
语法:
reinterpret_cast < type-name > (expression)
操作符修改了操作数类型,但仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。
例如:
int *n = new int ;
double *d = reinterpret_cast<double*> (n);
在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析。
因此, 需要谨慎使用 reinterpret_cast。
示例:
#include <iostream>
using namespace std;
int main()
{
int a[5];
cout << a << endl;
for ( int i = 0; i < 5; i++ )
{
unsigned int val = reinterpret_cast<unsigned int>( a + i );
cout << val << endl;
}
}
输出:
0x6dfed4 //十进制7208660
7208660
7208664
7208668
7208672
7208676
<4>dynamic_cast运算符
作用:用于在运行时执行转换,验证转换的有效性。如果转换未执行,则转换失败,表达式 expr 被判定为 null。dynamic_cast 执行动态转换时,type 必须是类的指针、类的引用或者 void*,如果 type 是类指针类型,那么 expr 也必须是一个指针,如果 type 是一个引用,那个 expr 也必须是一个引用。
语法:
dynamic_cast < type-name > (expression)
示例:
#include <iostream>
#include <typeinfo>
using namespace std;
class A
{
public:
virtual void Print() { cout<<"This is class A."<<endl; }
};
class B
{
public:
virtual void Print() { cout<<"This is class B."<<endl; }
};
class C : public A, public B
{
public:
void Print() { cout<<"This is class C."<<endl; }
};
int main()
{
A *pA = new C;
//C *pC = pA; // Wrong 编译器会提示错误
C *pC = dynamic_cast<C *>(pA);
if (pC != NULL)
{
pC->Print();
}
delete pA;
}
输出:
This is class C.
参考:
《c++ primer plus 第六版》