本文最后更新于:1 年前
(一)概念:
隐式类型转换是编译器自动隐式进行的,需要在代码中体现,而显式类型转换由程序员明确指定。
C++支持C风格的强制转换(将类型名作为强制类型转换运算符的做法是C语言的老式做法),但是C风格的强制转换可能带来一些隐患,让一些问题难以发现。
所以C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换。
强制转换运算符是一种特殊的运算符,它把一种数据类型转换为另一种数据类型。强制转换运算符是一元运算符,它的优先级与其他一元运算符相同。
大多数的 C++ 编译器都支持大部分通用的强制转换运算符。
形式为:**强制类型转换运算符<type> (expression) **
type为转换后的数据类型,expression为待转换的表达式
c++的四种强制类型转换运算符分别为:static_cast、reinterpret_cast、const_cast和dynamic_cast。
(二)风险:
强制类型转换是有一定风险的,有的转换并不一定安全,如把整型数值转换成指针,把基类指针转换成派生类指针,把一种函数指针转换成另一种函数指针,把常量指针转换成非常量指针等。
C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点。
例如,将int强制转换成double是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分。
如果采用C语言的老式做法,要在程序中找出所有进行了强制类型转换的地方,显然是很麻烦的,因为这些转换没有统一的格式。
而用 C++ 的方式,则只需要查找_cast字符串就可以了。甚至可以根据错误的类型,有针对性地专门查找某一种强制类型转换。例如,怀疑一个错误可能是由于使用了reinterpret_cast导致的,就可以只查找reinterpret_cast字符串。
(三)四种转换操作:
1. const_cast:
const_cast 运算符用于修改类型的const /volatile属性。除了const或volatile属性之外,目标类型必须与源类型相同(即type和expression结果类型相同)。这种类型的转换主要是用来操作所传对象的const属性,可以加上const属性,也可以去掉const属性(常用)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <iostream> using namespace std; int main() { const string s = "Inception"; string& p = const_cast <string&> (s); string* ps = const_cast <string*> (&s); p += "1"; *ps += "2"; cout << typeid(p).name() << endl; cout << typeid(ps).name() << endl; cout << p << endl; return 0; }
|
2. reinterpret_cast:
reinterpret_cast 运算符用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。
reinterpret_cast 功能十分强大,可以实现任意类型之间的转换。
这个转换是“最不安全”的。不推荐使用。
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
| #include <iostream> using namespace std; class A { public: int i; int j; A(int n1,int n2) :i(n1), j(n2) { } }; void show1(int) { cout << "show1" << endl; return; } int show2(int, char*) { cout << "show2" << endl; return 1; } int main() { A a(100,200);
int& r = reinterpret_cast<int&>(a); r = 300; std::cout << a.i << "," << a.j << std::endl;
int n = 400; A* pa = reinterpret_cast<A*> (&n); pa->i = 500; cout << n << endl;
long long la = 0x12345678abcdLL; pa = reinterpret_cast<A*>(la); unsigned int u = reinterpret_cast<unsigned int>(pa); cout << hex << u << endl;
typedef void (*PF1) (int); typedef int (*PF2) (int, char*); PF1 pf1=show1; PF2 pf2=show2; pf1(1); cout << pf2(1, (char*)("1")) << endl; pf2 = reinterpret_cast<PF2>(pf1); pf1(1); cout << pf2(1, (char*)("1")) << endl; return 0; }
|
3. static_cast:
编译器隐式执行的任何类型转换都可以由static_cast显式完成。
static_cast主要用于基本数据类型之间的转换,如把char转换为 int,把int转换为double等。
使用static_cast可以明确告诉编译器,损失精度的转换是在知情的情况下进行的,也可以让阅读程序的其他程序员明确转换的目的而不是由于疏忽。
把精度大的类型转换为精度小的类型,static_cast使用位截断进行处理。
static_cast执行的是非动态转换,没有运行时的类型检查来保证转换的安全性。
例如,对于类层次的转换:
使用static_cast可以找回存放在void*指针中的值。
static_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
| #include <iostream> using namespace std; class A { public: int i; int j; operator int() { return 1; } operator char* () { return NULL; } }; class B :public A { public: int z; }; int main() { A a; int n; char* p = (char*)"New Dragon Inn"; n = static_cast <int> (a); cout << n << endl; p = static_cast <char*> (a); cout << &p << endl; n = static_cast <int> (3.14); cout << n << endl; int x = -1; unsigned int y = static_cast<unsigned int>(x); cout << y << endl;
double da = 2.991; void* vp = static_cast<void*>(&da); double* dp = static_cast<double*>(vp); cout << *dp << endl; A* pa = new A; B* pb = new B; pa->i = 1; pa->j = 2; pb->i = 10; pb->j = 20; pb->z = 30; A* pab = static_cast<B*>(pb); cout << pab->i << "," << pab->j << endl; B* pba = static_cast<B*>(pa); cout << pba->z << "," << pba->i << "," << pba->j << endl; return 0; }
|
4. dynamic_cast:
dynamic_cast 主要用于类层次间的上行转换或安全的下行转换(实际就是专门用于将多态基类指针或引用转换为派生类指针或引用)。在进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的,但在下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。
对于“向下转型”有两种情况:
dynamic_cast最特殊的地方在于它支持运行时识别指针或引用。
dynamic_cast 是在运行时执行转换,进行类型检查的。如果转换未执行,则转换失败,表达式 expr 被判定为 null。dynamic_cast 执行动态转换时,type 必须是类的指针、类的引用或者 void*,如果 type 是类指针类型,那么 expr 也必须是一个指针,如果 type 是一个引用,那么 expr 也必须是一个引用。
dynamic_cast检查的来源是虚函数表。
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 70 71
| #include <iostream> using namespace std;
class Base { public: int i; virtual void Show() { cout << "I am Base." << endl; } };
class Derive : public Base { public: int j; virtual void Show() { cout << "I am Derive." << endl; } };
int main() { Derive* d1 = new Derive(); cout << "d1: " << d1 << endl; Base* b1 = dynamic_cast<Base*>(d1); cout << "b1: " << b1 << endl;
Base* b2 = new Derive; cout << "b2: " << b2 << endl; if (Derive* d2 = dynamic_cast<Derive*>(b2)) { cout << "第一种情况转换成功" << endl; cout << "d2: " << d2 << endl; d2->Show(); } else cout << "第一种情况转换失败" << endl; Base* b3 = new Base; cout << "b3: " << b3 << endl; if (Derive* d3 = dynamic_cast<Derive*>(b3)) { cout << "第二种情况转换成功" << endl; d3->Show(); } else { cout << "第二种情况转换失败" << endl; cout << "d3: " << d3 << endl; } delete b1; delete b2; delete b3;
Base bbb; Base& bbbb = bbb; return 0; }
|