数据类型转换
一 隐式类型转换
1.1 算数转换
(+,-,*,/,%)
char , int, long, long long, float, double
15 + 3.14 => 15.0 + 3.14
1.2 赋值转换
#include <stdio.h>
int main(void)
{
int x;
// 计算结果31.4 转换为int类型,因为赋值符号的左边变量的类型是int类型
x = 3.14 * 10.0;
cout << x << endl; //31
return 0;
}
1.3 输出转换
#include <stdio.h>
int main(void)
{
printf("%c\n", 255+50); // 305(0001 0011 0001) -> 0x31(一个字节是8个位) -> 49 ('1');
printf("%d\n", 255+50); // 305
return 0;
}
二 强制类型转换
2.1 直接使用数据类型
int* addr = (int*)0x003B66D0; // 强制类型转换,整数直接变指针
2.2 c++新式类型转换
C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。
格式:
TYPE b = 类型操作符<TYPE> ( a )
类型操作符= static_cast | reinterpreter_cast | dynamic_cast | const_cast
2.2.1 static_cast
静态类型转换(斯文的劝导,温柔的转换)。如int转换成char
主要用法:
用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。上行指针或引用(派生类到基类)转换安全,下行不安全
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
把空指针转换成目标类型的空指针。
把任何类型的表达式转换成void*类型。
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void cry() = 0;
};
class Cat :public Animal
{
public:
void cry() override
{
cout << "喵喵喵" << endl;
}
};
class Dog :public Animal
{
public:
void cry() override
{
cout << "汪汪汪" << endl;
}
};
int main()
{
// 1.用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
// 上行指针或引用(派生类到基类)转换安全,下行不安全
Dog* dog1 = new Dog();
Animal* a1 = dog1; // 隐式转换
Animal* a2 = static_cast<Animal*>(dog1); // 显示转换,子类的指针转成父类的指针
Cat* cat1 = static_cast<Cat*>(a1); // 不安全,把父类的指针转换成子类的指针
Dog dog2;
Animal& a3 = dog2;
Animal& a4 = static_cast<Animal&>(dog2); // 显示转换,子类的引用转成父类的引用
Cat& cat2 = static_cast<Cat&>(a3); // 不安全,把父类的引用转换成子类的引用
// 2.用于基本数据类型之间的转换,如把int转换成char
double PI = 3.1415926;
int num1 = PI; // 隐式转换
int num2 = static_cast<int>(PI); // 显示转换
// 3.把空指针转换成目标类型的空指针
int* p1 = NULL;
int* p2 = static_cast<int*>(NULL);
Dog* dp1 = NULL;
Dog* dp2 = static_cast<Dog*>(NULL);
// 4.把任何类型的表达式转换成void*类型。
int* p = new int[10];
void* vp = static_cast<void*>(p);
return 0;
}
2.2.2 reinterpert_cast
重新解释类型(挂羊头,卖狗肉) 不同类型间的互转,数值与指针间的互转
用法: TYPE b = reinterpret_cast<TYPE>( a )
TYPE必须是一个指针、引用、算术类型、函数指针.
忠告:滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
#include <iostream>
using namespace std;
class Animal
{
public:
void cry()
{
cout << "Animal cry" << endl;
}
};
class Cat :public Animal
{
public:
void cry()
{
cout << "喵喵喵" << endl;
}
};
class Dog :public Animal
{
public:
void cry()
{
cout << "汪汪汪" << endl;
}
};
int main()
{
// 1. 数值与指针之间的转换
//int* p = static_cast<int*>(0xFFFF1234); // 报错,类型转换无效
int* p = reinterpret_cast<int*>(0xFFFF1234);
int val = reinterpret_cast<int>(p);
// 2. 不同类型指针和引用之间的转换
Dog dog1;
Animal* a1 = &dog1;
Animal& a2 = dog1;
Dog* pdog1 = reinterpret_cast<Dog*>(a1);
Dog* pdog2 = static_cast<Dog*>(a1); // 如果能用static_cast,优先使用static_cast
Dog& pdog3 = reinterpret_cast<Dog&>(a2);
Cat* pcat1 = reinterpret_cast<Cat*>(a1);
Cat* pcat2 = static_cast<Cat*>(a1); // 因为这里有继承关系,所以这里可以使用static_cast
//Cat* pcat3 = static_cast<Cat*>(pdog1); // 报错,类型转换无效 不同类型指针转换不能使用static_cast
Cat* pcat4 = reinterpret_cast<Cat*>(pdog1); // 这样转是有风险的
return 0;
}
2.2.3 dynamic_cast
动态类型转换:
将一个基类对象指针cast到继承类指针,dynamic_cast 会根据基类指针是否真正指向继承类指针来做相应处理。失败返回null,成功返回正常cast后的对象指针;
将一个基类对象引用cast 继承类对象,dynamic_cast 会根据基类对象是否真正属于继承类来做相应处理。失败抛出异常bad_cast
注意:dynamic_cast在将父类cast到子类时,父类必须要有虚函数一起玩。
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void cry() = 0;
};
class Cat :public Animal
{
public:
void cry()
{
cout << "喵喵喵" << endl;
}
void play()
{
cout << "cat play" << endl;
}
};
class Dog :public Animal
{
public:
void cry()
{
cout << "汪汪汪" << endl;
}
void play()
{
cout << "dog play" << endl;
}
};
void animalPlay(Animal* animal)
{
animal->cry();
Dog* pDog = dynamic_cast<Dog*>(animal);
if (pDog)
{
pDog->play();
}
else
{
cout << "不是dog" << endl;
}
Cat* pCat = dynamic_cast<Cat*>(animal);
if (pCat)
{
pCat->play();
}
else
{
cout << "不是cat" << endl;
}
}
void animalPlay(Animal& animal)
{
animal.cry();
try
{
Dog& dog = dynamic_cast<Dog&>(animal);
dog.play();
}
catch(std::bad_cast bc)
{
cout << "不是dog,那应该是cat" << endl;
}
try
{
Cat& cat = dynamic_cast<Cat&>(animal);
cat.play();
}
catch (std::bad_cast bc)
{
cout << "不是cat,那应该是上面的dog" << endl;
}
}
int main()
{
cout << "test 指针" << endl;
Dog* dog1 = new Dog();
Animal* al = dog1;
Cat* cat1 = new Cat();
Animal* a2 = cat1;
animalPlay(al);
animalPlay(a2);
cout << endl << "test 引用" << endl;
Dog dog2;
animalPlay(dog2);
Cat cat2;
animalPlay(cat2);
return 0;
}
2.2.4 const_cast
去const属性。(仅针对于指针和引用)
#include <iostream>
using namespace std;
void fun1(const char* p)
{
// 1.对指针去掉const,再重新赋值
char* p1 = const_cast<char*>(p);
p1[0] = 'A';
// 2.直接去掉const修改
const_cast<char*>(p)[1] = 'B';
}
void fun2(const int& value)
{
const_cast<int&>(value) = 5;
}
int main()
{
cout << "test 指针" << endl;
char arr[] = "12345678";
fun1(arr);
for (int i = 0; i < sizeof(arr) / sizeof(char); i++)
{
cout << arr[i] << " ";
}
cout << endl;
// 常量字符串是不能去掉const修改的,因为常量字符串是存在常量区的
// 在去掉常量限定符之前,保证指针所指向的内存能够修改,不能修改则会引起异常
//const char* cP = "hello";
//fun1(cP); // 报错,常量字符串不能修改
cout << endl <<"test 引用" << endl;
int num1 = 1;
const int &num2 = num1;
fun2(num2);
//const_cast<int&>(num2) = 2;
cout << num1 << endl;
return 0;
}
三 类型转换使用建议
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++大牛建议:一般情况下,不建议进行类型转换;避免进行类型转换。