学习C++已经很久了,由于工作原因,很长一段时间没有用了,再加上经常用一些脚本语言,使得对C++的一些概念,反而有些混乱。所以这里我跟大家一起讨论一下C++的类型转换。如文章中有任何纰漏,还请指正。
众所周知,在PYTHON和其他一些脚本语言中,声明变量是不需要指定类型的。
a=5
b="hello"
但是在C++中这样声明变量,显然是不能通过编译的。需要指定变量的类型:
int a=5;
char b[]="hello"
如果经常用脚本语言你想要a+b
那么在PYTHON中你直接这样就行了
c=chr(a)+b
print(c)
解释器会自动根据你指定的类型转换函数 ,直接操作并得出结果,下面是PYTHON中的一些类型转换函数:
[python]
<span style="white-space:pre"> </span>int a = 5;
<span style="white-space:pre"> </span>char b[] = "hello";
<span style="white-space:pre"> </span>char c[10];
<span style="white-space:pre"> </span>_itoa_s(a, c, 10);
<span style="white-space:pre"> </span>strcat_s(c, b);
<span style="white-space:pre"> </span>printf("%s", c);
这样看来是相当的复杂了。所以我们还是有必要学习一下C++的类型转换。也是为了方便以后的应用。我们在实际应用中一般很少对对象直接进行类型转换。而且这样也经常是不合法的。
例如
struct m {
int a;
char b;
};
struct r {
float a;
double b;
};
void printm(r* c)
{printf("%f,%f",c->a,c->b) }
int main(int argc, char* argv[])
{
m mystruct;
mystruct.a = 5;
mystruct.b = 'a';
printm(&mystruct);
return 0;
}
编译器会提示不存在用户定义的类型转换。那么我们怎么才能让程序顺利的运行呢。只能用一些类型转换的技巧。
struct m {
int a;
char b;
};
struct r {
float a;
double b;
};
void printm(r* c)
{
printf("%f,%f", c->a, c->b);
}
int main(int argc, char* argv[])
{
m mystruct;
mystruct.a = 6;
mystruct.b = 'b';
LPVOID t = &mystruct;
printm((r*)t);
system("pause");
}
这样就可以顺利的编译通过,虽然打印的值跟自己想要的结果大相径庭。我们这里用到一个概念就是 VOID LPVOID的指针。通过转换指针类型,来强制满足编译器对类型的要求。我们之所以这样做,还是因为C++中的各种数据类型所占的内存空间是不一样的。
16位编译器
char :1个字节
char*(即指针变量): 2个字节
short int : 2个字节
int: 2个字节
unsigned int : 2个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
32位编译器
char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节
64位编译器
char :1个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节
正是因为如此,所以我们想要直接转换数据类型是行不通的。只能通过一些技巧才能进行转换。尤其是在用户自定义的数据类型中。特别重要。很多时候我们会被各种各样的类型转换弄得晕头转向。下面这段代码说明了为什么指针之间的类型转换会被编译器所接受。
struct m {
int a;
char b;
};
int main(int argc, char* argv[])
{
int a = sizeof(int*);
int b = sizeof(double*);
int c = sizeof(char*);
int d = sizeof(m*);
<span style="font-family: Arial, Helvetica, sans-serif;"> int f = sizeof(LPVOID);</span>
printf("%d,%d,%d,%d,%d",a, b, c, d,f);
system("pause");
return 0;
}
用好C++的关键除了OOP之外,就是我们能够正确的把一个数据类型传递给函数。有时候我们编写了自己的类/结构体。但是需要传递的函数参数的类型却与我们的大相径庭。如何才能把自己的数据结构正确的传递给函数的参数。决定了我们对C++数据类型的掌握程度。有些时候我们的转换虽然通过了编译器,但是却隐藏了致命的错误,使得程序在发布之后出现各种BUG,因此我觉得这个类型转换真的需要好好的学习,理解。
当然我们还需要知道C++中的4种强制类型转换。
1、static_cast
用法:static_cast (expression)
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
(1)用于类层次结构中基类和派生类之间指针或引用的转换
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的
(2)用于基本数据类型之间的转换,如把int转换成char。这种转换的安全也要开发人员来保证
(3)把空指针转换成目标类型的空指针
(4)把任何类型的表达式转换为void类型
注意:static_cast不能转换掉expression的const、volitale或者__unaligned属性。
2、dynamic_cast
用法:dynamic_cast (expression)
该运算符把expression转换成type_id类型的对象。type_id必须是类的指针、引用或者void*;
如果type_id是类指针类型,那么expression也必须是一个指针,如果type_id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
3、reinterpret_cast
用法:reinterpret_cast (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,
在把该整数转换成原类型的指针,还可以得到原先的指针值)。
该运算符的用法比较多。
(static_cast .与. reinterpret_cast比较,见下面 )
该运算符平台移植性比价差。
4、const_cast
用法:const_cast (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
为什么都说C++是一个复杂的语言,我觉得不是他的OOP特性,而是这些基于底层的细节导致的。在敏捷型语言快速发展遍地开花的今天。我们要想把C++学好,只能通过不断的反复学习,总结经验才能掌握好它的本质。