文章目录
C/C++语言中的数据类型转换主要有两种:强制类型转换和隐式类型转换。
1 隐式类型转换
1.1 隐式类型转换的基本概念
我们首先需要知道,隐式类型转换是编译器主动进行的类型转换。
对于隐式数据类型转换来说:
- 低类型到高类型的隐式类型转换是安全的,不会产生截断。
- 高类型到低类型的隐式类型转换是不安全的,导致不正确的结果。
- 比如:浮点数赋值给整形时,会发生截断(小数部分丢失)。
注意: 标准C编译器的类型检查是比较宽松的,因此隐式类型转换可能带来意外的错误。
1.2 隐式类型转换的发生点
隐式类型转换的发生点如下:
- 算术运算式中,低类型转换为高类型。
- 赋值表达式中,表达式的值转换为左边变量的类型。
- 函数调用时,实参转换为形参的类型。
- 函数返回值,return表达式转换为返回值类型。
安全的隐式类型转换:
1.3 浮点数赋值给整形数分析
直接赋值:
可以看到,编译器直接将浮点数截断。
通过函数调用:
可以看到,函数调用时也是发生了隐式类型转换,编译器直接将浮点数进行截断。
注意:当使用如下语句时printf("%d\n", 3.14);
,将得到错误的结果。由于是变参,所以编译器根本不知道是什么类型,不会进行隐式类型转换。
2 C语言中的强制类型转换
强制类型转换的语法:
- (Type)var_name;
- (Type)value;
强制类型转换的结果:
- 目标类型能够容纳目标值,结果不变。
- 目标类型不能容纳目标值:结果将产生截断。
- 注意:不是所有的强制类型转换都能成功,当不能进行强制类型转换时,编译器将产生错误信息。
3 C++中的强制类型转换
C 方式强制类型转换存在的问题:
- 过于粗暴:任意类型之间都可以进行转换,编译器很难判断其正确性。
- 难于定位 :在源码中无法快速定位所有使用强制类型转换的语句。
问题:强制类型转换在实际工程中是很难完全避免的!如何进行更加安全可靠的转换?
C++ 将强制类型转换分为4种不同的类型: static_cast、const_cast、dynamic_cast、reinterpret_cast。
用法:xxx_cast(Expression)
3.1 static_cast
static_cast强制类型转换:
- 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。上行指针或引用(派生类到基类)转换安全,下行不安全。
- 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
- 把空指针转换成目标类型的空指针。
- 把任何类型的表达式转换成void指针类型。
#include <iostream>
using namespace std;
class Animal {
public:
virtual void cry() = 0;
};
class Cat :public Animal
{
public:
void cry()
{
cout << "喵喵瞄" << endl;
}
};
class Dog :public Animal
{
public:
void cry()
{
cout << "汪汪汪" << endl;
}
};
int main(void) {
//第一种情况 父子类之间的类型转换
Dog* dog1 = new Dog();
Animal* a1 = static_cast<Animal*>(dog1); //子类的指针转型到父类指针
Dog* dog1_1 = static_cast<Dog*>(a1); //父类的指针转型到子类的指针
Cat* cat1 = static_cast<Cat*>(a1); //父子到子类,有风险,这样时不行的,会出问题,但是我们如果去调用cat1相关的函数时,不管是不是虚函数都是Cat类的函数,这就是静态连编。如果两者之间的内存结构不同,则可能会出错。
Dog dog2;
Animal& a2 = static_cast<Animal&>(dog2); //子类的引用转型到父类的引用
Dog &dog2_2 = static_cast<Dog&>(a2); //父类到子类引用
//第二种 基本类型的转换
int kk = 234;
char cc = static_cast<char>(kk);
//第三种 把空指针转换成目标类型的空指针。
int* p = static_cast<int*>(NULL);
Dog* dp = static_cast<Dog*>(NULL);
//第四种 把任何类型的表达式转换成void类型
int* pi = new int[10];
void* vp = static_cast<void*>(pi);
vp = pi;
system("pause");
return 0;
}
3.2 const_cast
const_cast强制类型转换:
- 用于去除变量的只读属性。
- 强制转换的目标类型必须是指针或引用。
void const_cast_demo()
{
const int& j = 1;
int& k = const_cast<int&>(j);
const int x = 2; //真正意义上的常量
int& y = const_cast<int&>(x);//编译器会为x分配空间,y指向的是为x分配的空间
int z = const_cast<int>(x); //error
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
y = 8;
printf("x = %d\n", x);
printf("y = %d\n", y);
printf("&x = %p\n", &x);
printf("&y = %p\n", &y);
}
/*
k = 5
j = 5
x = 2
y = 8
*/
3.3 reinterpret_cast
reinterpret_cast强制类型转换:
- 用于指针类型间的强制转换。
- 用于整数和指针类型间的强制转换。
- 引用之间的转换。
目标类型必须是一个指针、引用、算术类型、函数指针!
忠告:滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
#include <iostream>
using namespace std;
class Animal {
public:
void cry() {
cout << "动物叫" << endl;
}
};
class Cat :public Animal
{
public:
void cry()
{
cout << "喵喵瞄" << endl;
}
};
class Dog :public Animal
{
public:
void cry()
{
cout << "汪汪汪" << endl;
}
};
int main(void) {
//用法一 数值与指针之间的转换
int* p = reinterpret_cast<int*>(0x99999);
int val = reinterpret_cast<int>(p);
//用法二 不同类型指针和引用之间的转换
Dog dog1;
Animal* a1 = &dog1;
a1->cry();
Dog* dog1_p = reinterpret_cast<Dog*>(a1);
Dog* dog2_p = static_cast<Dog*>(a1); //如果能用static_cast ,static_cast 优先
//Cat* cat1_p = static_cast<Cat*>(a1);
//Cat* cat2_p = static_cast<Cat*>(dog1_p);//NO! 不同类型指针转换不能使用static_cast
Cat* cat2_p = reinterpret_cast<Cat*>(dog1_p);
Animal& a2 = dog1;
Dog& dog3 = reinterpret_cast<Dog&>(a2);//引用强转用法,不同类型之间的引用也可以转换
dog1_p->cry();
dog2_p->cry();
cat2_p->cry();
system("pause");
return 0;
}
3.4 dynamic_cast
dynamic_cast强制类型转换:
- 用于有继承关系的类指针之间转换。
- 用于有交叉关系的类指针之间的转换。
- 具有类型检查的功能。
- 需要虚函数的支持。
dynamic是与继承相关的类型转换关键字,dynamic_cast要求相关的类中必须有虚函数。用于直接或间接继承关系的指针(引用之间)。
对于指针:
- 转换成功:得到目标类型指针。
- 转换失败:得到一个空指针。
对于引用:
- 转换成功:得到目标类型的引用。
- 转换失败:得到一个异常操作信息。
编译器会检查dynamic_cast的使用是否正确,类型转换的结果只能在运行阶段才能得到。
dynamic的使用:
#include <iostream>
using namespace std;
class Animal {
public:
virtual void cry() = 0;
};
class Cat :public Animal
{
public:
void cry()
{
cout << "喵喵瞄" << endl;
}
void play()
{
cout << "爬爬树"<<endl;
}
};
class Dog :public Animal
{
public:
void cry()
{
cout << "汪汪汪" << endl;
}
void play()
{
cout << "溜达溜达" << endl;
}
};
void animalPlay(Animal& animal) {
animal.cry();
try {
Dog& pDog = dynamic_cast<Dog&>(animal);
pDog.play();
}
catch (std::bad_cast bc) {
cout << "不是狗,那应该是猫" << endl;
}
try {
Cat& pCat = dynamic_cast<Cat&>(animal);
pCat.play();
}
catch (std::bad_cast bc) {
cout << "不是猫,那应该是上面的狗" << endl;
}
}
void animalPlay(Animal* animal) {
animal->cry();
Dog* pDog = dynamic_cast<Dog*>(animal);
if (pDog) {
pDog->play();
}
else {//pDog == NULL
cout << "不是狗,别骗我!" << endl;
}
Cat* pCat = dynamic_cast<Cat*>(animal);
if (pCat) {
pCat->play();
}
else {//pDog == NULL
cout << "不是猫,别骗我!" << endl;
}
}
int main(void) {
Dog* dog1 = new Dog();
Animal* a1 = dog1;
//animalPlay(a1);
Dog dog2;
animalPlay(dog2);
Cat* cat1 = new Cat();
Animal* a2 = cat1;
//animalPlay(a2);
Cat cat2;
animalPlay(cat2);
system("pause");
return 0;
}
新式类型转换以C++关键字的方式出现:
- 编译器能够帮助检查潜在的问题;
- 非常方便的在代码中定位;
- 支持动态类型识别(dynamic_cast)。
3.5 类型转换使用建议
1)static_cast静态类型转换,编译的时c++编译器会做编译时的类型检查;隐式转换;基本类型转换,父子类之间合理转换。
2)若不同类型之间,进行强制类型转换,用reinterpret_cast<>() 进行重新解释。建议:C语言中能隐式类型转换的,在c++中可用 static_cast<>()进行类型转换。因C++编译器在编译检查一般都能通过;C语言中不能隐式类型转换的,在c++中可以用 reinterpret_cast<>() 进行强制类型解释。
总结:static_cast<>()和reinterpret_cast<>() 基本上把C语言中的 强制类型转换给覆盖,注意reinterpret_cast<>()很难保证移植性。
3)dynamic_cast<>(),动态类型转换,安全的虚基类和子类之间转换;运行时类型检查。
4)const_cast<>(),去除变量的只读属性。
最后的忠告:程序员必须清楚的知道: 要转的变量,类型转换前是什么类型,类型转换后是什么类型,转换后有什么后果。
C++大牛建议:一般情况下,不建议进行类型转换;避免进行类型转换。
参考资料: