类型转换

C语言的类型转换

赋值转换

  将一种类型的值赋给另一种类型的变量,此时值会转变为接收变量的类型

1
2
3
4
5
int ival = 3.14;  //3.14被截断为3


int* ip;
ip = 0; //0被转换为int*型的空指针

  当把一个超出其范围的值赋给一个指定类型的对象时,不同的系统有不同的操作。将int类型的数赋值给short类型时,大多数系统将int的低字节赋值,高字节舍去:

1
short a = 0x1111FFFF; //a的值为0xFFFF

  当把一个取值范围小的值赋给取值范围大的值,会根据符号位自动补值:

1
2
char a = 0xe0;
int b = a; //符号位是1,补1,此时b=0xffffffe0

表达式转换

  当同一个表达式中出现不同类型的量时,会根据不同的情况对操作数进行自动转换,这些转换可分为“整数提升”和“运算时的转换”:

  1. 整数提升:表达式计算时,bool、char、unsigned char、short都会自动转换成int型,bool值类型中true转为1,false转为0;
  2. 运算时转换:运算涉及两种类型时,较小的类型(表达力低)会转换为较大的类型(表达力高),表达力从低到高的排序为“int➡unsigned int➡long➡unsigned long➡fload➡double➡long double”;

  表达式转换时long和unsigned int的转换需要注意,32位机器上long和int通常都是一个字长,所以表达式中包含unsigned int和long两种类型时,这俩统一转换为unsigend long;

  表达式中出现同类型的signed和unsigned数时,统一转换为unsigned,只是把符号位当作数值位计算。比如int型的-1会转换为unsigned int的\(2^{32}-1\),这就是常说的溢出了。要注意此时内存中的内容并没有改变,只是解释不一样。

显式转换

  显示转换也称为强制类型转换,格式为“(类型说明符)(表达式)”,比如double f = double(1)/double(2);。基本类型的指针之间不含隐式转换(void*除外),都需要显示转换。

隐式转换

  把一个表达式传递给一个函数调用、从函数中返回一个表达式会发生隐式类型转换.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
double f = 1/2;
//虽然用double变量来接收结果,但表达式运算全是整型数,会转换为整形数的除法
//运算结果为0,转换为double后为0.0


double f = 1.0/2;
//由于1.0是浮点型,所以2会发生隐式类型转换变为2.0,最终结果是0.5


int ival;
if(ival) //ival发生隐式类型转换为bool
{

}

C++的类型转换

const_case

  用于转换指针或引用,去掉类型的const属性。

  这几种类型转换,只有const_cast可以将const性质转换掉,其余都不行。相应的,除了添加或删除const属性,const_cast也无法执行其他的类型转换任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//const_case使用

int main()
{
// C++ const_cast
const int a = 10;
//int* pA = &a; //const int*类型不能用于初始化int*类型实体
int* pA = const_cast<int*>(&a);
*pA = 100;

//需要注意,虽然我们用指针把a的值改变了,实际上也确实改变了
//但假如我们要使用a的值,比如cout一下,那么a原来的值(10)会直接赋值到行为上
//cout<<a<<endl;输出的结果是10,而不是100
return 0;
}

reinterpret_cast

  这是一种很危险的类型转换。既不检查指向的内容,也不检查指针类型本身,只是做了类型的重新解释。reinterpret_cast需要保证转换前后的类型所占用的内存大小一致,否则将引发编译时错误。

  虽然危险,但工程中的使用常见还是比较广泛,比如void*和其他类型的转换,遇到这种场景最好使用reinterpret_cast而不是C语言的强制类型转换,因为C语言的强制转换不做任何的检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//reinterpret_cast使用

int Test()
{
return 0;
}

int main()
{
// C++ reinterpret_cast
typedef void(*FuncPtr) (); //定义函数指针,返回值void,参数也是void

FuncPtr funcPtr;
//funcPtr = &Test; //两个函数模型不匹配,不能赋值
funcPtr = reinterpret_cast<FuncPtr>(&Test);

return 0;
}

static_case和dynamic_cast

  static_cast用于基本的类型转换(这种方式就是类似于C语言的类型转换方式)。仅当类型之间可隐式转换时,static_cast的转换才是合法的,否则将出错,比如基本类型的指针之间不含有隐式转换,那么使用static_cast进行显式转换将编译错误。

  static_cast也可以用于有继承关系类对象和类指针之间的转换,但需要由程序员来确保转换是安全的,它不会产生动态转换的类型安全检查的开销,因为类型检查不是编译器能检测出来的,必须要等到运行时才能动态检查。

  dynamic_cast是为了弥补static_cast的不足,可以做类型的检查。类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,因此它只能用于含有虚函数的类,必须用于多态体系中,用于类层次间的向上和向下转化;向上转化时和static_cast效果相同,向下转化时,如果是非法的对于指针返回NULL,引用抛出bad_cast异常(向上转化:子类转换为父类;向下转化:父类转化为子类。子类转化为父类比较安全,但父类转化为子类不安全,因为子类一定有父类的属性,但父类未必有子类的属性)。

  dynamic_cast只能用于类的指针、类的引用或void*。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//static_cast 和 dynamic_cast

class Base
{
public:
Base() : _i(0) { ; }
virtual void T() { cout << "Base:T" << _i << endl; } //必须有虚函数,dynamic_cast才做检查
private:
int _i;
};

class Derived : public Base
{
public:
Derived() :_j(1) { ; }
virtual void T() { cout << "Derived:T" << _j << endl; }

private:
int _j;
};

int main()
{
// static_cast
int i = 6;
double d = static_cast<double>(i); //基本类型转换 int -> double
double d2 = 5.6;
int i2 = static_cast<int>(d2); //基本类型转换 double -> int

int ii = 5;
double dd = static_cast<double>(ii);
double dd2 = 5.6;
int ii2 = static_cast<int>(dd2);

// static_cast与dynamic_cast

Base cb;
Derived cd;
Base* pcb;
Derived* pcd;

// 子类--》 父类
// 这个是安全的
pcb = static_cast<Base*>(&cd);
if (pcb == NULL)
{
cout << "unsafe static_cast from Derived to Base" << endl;
}
pcb = dynamic_cast<Base*>(&cd);
if (pcb == NULL)
{
cout << "unsafe dynamic_cast from Derived to Base" << endl;
}

// 父类--》 子类
// 这个有风险,dynamic_cast会做检查导致失败
pcd = static_cast<Derived*>(&cb);
if (pcd == NULL)
{
cout << "unsafe static_cast from Base to Derived" << endl;
}
pcd = dynamic_cast<Derived*>(&cb);
if (pcd== NULL)
{
cout << "unsafe dynamic_cast from Base to Derived" << endl;
}

return 0;
}