背景
类型转换的需求,C中有显示/隐式的强制类型转换。C++为了兼容保留了,但也提供了四种运算符来实现转换。
C语言强制类型转换的缺点
-
从形式上无法判断功能和风险
-
将多态基类指针转换成派生类指针时,没有检查安全性。无法判断转换后的指针是否指向一个派生类对象
-
难以寻找使用转换的地方
C++提供的强制转换运算符:
格式:xxxx_cast<目标类型>(表达式)
static_cast
用处:
-
整型、浮点型、字符型之间转换;
-
如果对象重载了强制类型转换运算符T,也可以使用。
-
子类可以转成父类,父类不能转成子类
特点:
-
低风险
-
编译时会进行类型转换的检查,需要保证转换的安全性和正确性
(比如int* 不能转为int)
不能:(下面都是风险高的转换)
-
不同类型的指针之间
-
整型 指针之间
-
不同类型的引用之间
例子:
#include <iostream> using namespace std; class A { public: operator int() { return 1; } operator char*() { return NULL; } }; int main() { A a; int n; char* p = "New Dragon Inn"; n = static_cast <int> (3.14); // n 的值变为 3 n = static_cast <int> (a); //调用 a.operator int,n 的值变为 1 p = static_cast <char*> (a); //调用 a.operator char*,p 的值变为 NULL n = static_cast <int> (p); //编译错误,static_cast不能将指针转换成整型 p = static_cast <char*> (n); //编译错误,static_cast 不能将整型转换成指针 return 0; }
reinterpret_cast
用处:不同类型指针/引用之间、指针和整数类型之间。(但不会检查安全性)
特点:
-
转换时,是逐个比特复制的操作
-
强灵活性,安全性较差,由用户自己保证
-
编译时进行类型转换检查
-
可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针。但不会检查安全性
例子:
#include <iostream> using namespace std; class A { public: int i; int j; A(int n):i(n),j(n) { } }; int main() { A a(100); int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a r = 200; //把 a.i 变成了 200 cout << a.i << "," << a.j << endl; // 输出 200,100 int n = 300; A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n pa->i = 400; // n 变成 400 pa->j = 500; //此条语句不安全,很可能导致程序崩溃 cout << n << endl; // 输出 400 long long la = 0x12345678abcdLL; pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa unsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到u cout << hex << u << endl; //输出 5678abcd typedef void (* PF1) (int); typedef int (* PF2) (int,char *); PF1 pf1; PF2 pf2; pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换 }
程序的输出结果是: 200, 100 400 5678abed
reinterpret_cast体现了 C++ 语言的设计思想:用户可以做任何操作,但要为自己的行为负责。
const_cast
用法:用于去除 const 属性
特点:
-
编译时进行类型转换的检查
例子:
const string s = "Inception"; string& p = const_cast <string&> (s); string* ps = const_cast <string*> (&s); // &s 的类型是 const string*
dynamic_cast
用法:
是 reinterpret_cast 的补充,dynamic_cast专门用于将【多态基类的指针或引用】 强制转换为 【派生类的指针或引用】,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。
特点:
-
运行时类型检查来保证安全性
不能:
-
不能用于将【非多态基类】(即基类必须有 virtual )的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性
例子:
#include <iostream> #include <string> using namespace std; class Base { //有虚函数,因此是多态基类 public: virtual ~Base() {} }; class Derived : public Base { }; int main() { Base b; Derived d; Derived* pd; pd = reinterpret_cast <Derived*> (&b); if (pd == NULL) //此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换 cout << "unsafe reinterpret_cast" << endl; //不会执行 pd = dynamic_cast <Derived*> (&b); if (pd == NULL) //结果会是NULL,因为 &b 不指向派生类对象,此转换不安全 cout << "unsafe dynamic_cast1" << endl; //会执行 pd = dynamic_cast <Derived*> (&d); //安全的转换 if (pd == NULL) //此处 pd 不会为 NULL cout << "unsafe dynamic_cast2" << endl; //不会执行 return 0; }
程序的输出结果是: unsafe dynamic_cast1
如果上面的程序中出现了下面的语句:
Derived & r = dynamic_cast <Derived &> (b);
那该如何判断该转换是否安全呢?不存在空引用,因此不能通过返回值来判断转换是否安全。
C++ 的解决办法是:dynamic_cast 在进行引用的强制转换时,如果发现转换不安全,就会拋出一个异常,通过处理异常,就能发现不安全的转换。
总结
1)强制类型转换一般不建议使用,因为会干扰系统的正常类型检查,很多异常转换本来编译器会报错的,但是一旦用了这些类型转换,就会抑制编译器的报错行为
2)有句话:使用reinterpret_cast很危险,而使用const_cast意味着设计有缺陷
参考资料:
https://blog.csdn.net/weixin_42203498/article/details/120288397
《C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)》
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/279500.html