C++超详细讲解强制类型转换的用法
作者:loongknown
static_cast
static_cast<type-id>(expression)
将 expression
转换为 type-id
类型。static_cast
是静态类型转换,发生在编译期。这种转换不会进行运行时的动态检查(RTTI),因而这种转换可能是不安全的。static_cast
典型应用场景如下:
1. 类的层级结构中,基类和子类之间指针或者引用的转换。
上行转换(Upcasting),也即子类像基类方向转换,是安全的。
下行转换(Downcasting),也即基类向子类方向转换,是不安全的,需要程序员自行保证安全转换。
下面举例说明:
class A { public: virtual void func() { std::cout << "A::func()" << std::endl; } }; class B : public A { public: virtual void func() { std::cout << "B::func()" << std::endl; } void print() { std::cout << "B::print()" << std::endl; } };
对于上行转换,肯定是安全的。
B* pb = new B(); A* pa = static_cast<A*>(pa); pa->func();
对于下行转换:
A* pa = new B(); B* pb = static_cast<B*>(pa); pb->print();
这里,A* pa = new B();
,由于 C++ 的多态的支持,可以使用基类指针指向子类。这里的转换是安全的,因为 pa
初始化就指向的就是 B
。而下面的转换则是不安全的:
A* pa = new A(); B* pb = static_cast<B*>(pa); pb->print();
此外,对于两个不存在继承关系的两个类之间转换,总是失败的,编译器报错:
#include <iostream> class A { virtual void func(){} }; class B { virtual void func(){} }; int main(){ A* pa = new A(); B* pb = static_cast<B*>(pa); return 0; }
2. 基本数据类型间的转换。这种转换也是不安全的,需要程序员自行保证安全转换。 例如 int
转 short
,直接高位截断;而 short
转 int
则高位根据符号位填充。两种不同类型指针间相互转换是相当危险的,例如 int*
转 float*
。将 int 转换为指针类型也是危险的转换,例如 float* p = static_cast<float*>(0X2edf);
3. 将 void
类型转换为其他类型的指针。 显然这种转换也是不安全的,需要程序员自行保证安全转换。
4. 把其他类型转换为 void
类型。
有转换构造函数或者类型转换函数的类与其它类型之间的转换。例如:
#include <iostream> class Point{ public: Point(double x, double y): m_x(x), m_y(y){ } Point(double& x): m_x(x), m_y(1.1){ } public: operator double() const { return m_x; } //类型转换函数 void print() { std::cout << "m_x: " << m_x << " m_y: " << m_y << std::endl; } private: double m_x; double m_y; }; int main() { Point p1(12.5, 23.8); double x= static_cast<double>(p1); // std::cout << x << std::endl; Point p2 = static_cast<Point>(x); // p2.print(); return 0; }
dynamic_cast
dynamic_cast<type-id>(expression)
把 expression
转换为 type-id
类型,type-id
必须是类的指针、类的引用或者是 void *
;如果 type-id
是指针类型,那么 expression
也必须是一个指针;如果 type-id
是一个引用,那么 expression
也必须是一个引用。
dynamic_cast
提供了运行时的检查。对于指针类型,在运行时会检查 expression
是否真正的指向一个 type-id
类型的对象,如果是,则能进行正确的转换;否则返回 nullptr
。对于引用类型,若是无效转换,则在运行时会抛出异常 std::bad_cast
。
T1 obj; T2* pObj = dynamic_cast<T2*>(&obj); // 无效转换返回 nullptr T2& refObj = dynamic_cast<T2&>(obj); // 无效转换抛出 bad_cast 异常
上行转换:其实和 static_cast 是一样的,一般肯定能成功。例如前面用到的例子:
// A->B B* pb = new B(); A* pa = static_cast<A*>(pa);
但是,下面这种继承关系会转换失败:
#include <iostream> /* A / \ V V B C \/ v D */ class A { virtual void func(){} }; class B : public A { void func(){} }; class C : public A { void func(){} }; class D : public B, public C { void func(){} }; int main(){ D* pd = new D(); A* pa = dynamic_cast<A*>(pd); return 0; }
上面这个例子,虽然也是上行转换,但是存在两条路径,在 B 和 C 都继承于 A,并且有虚函数实现,上行转换不知道从哪条路径进行转换。下面的写法则没问题:
D* pd = new D(); B* pb = dynamic_cast<B*>(pd); A* pa = dynamic_cast<A*>(pb);
下行转换:看个例子。
#include <iostream> class A { virtual void func(){} }; class B : public A { void func(){} }; int main(){ A* pa1 = new B(); A* pa2 = new A(); B *pb1 = dynamic_cast<B*>(pa1); // ok B *pb2 = dynamic_cast<B*>(pa2); // pb2 is a nullptr! return 0; }
其实 dynamic_cast
本质只支持上行转换,只会沿着继承链向上遍历,找到目标类型则转换成功,否则失败。dynamic_cast
看似支持下行转换,这都是多态的缘故。上面的例子,pa1
虽然类型是 A
,但实际指向 B
,沿着 B
向上可以找到 B
,因为第一个转换可以成功。而 pa2
指向 A
,沿着 A
向上找不到 B
类型,因而转换失败。
因而在有继承关系的类的转换时候, static_cast
转换总是成功的, dynamic_cast
显然比 static_cast
更加安全。
const_cast
const_cast
用来去掉表达式的 const
修饰或 volatile
修饰,也就是将 const
或 volatile
类型转换为非 const
或 非 volatile
类型。
#include <iostream> int main(){ const int n = 111; int *p = const_cast<int*>(&n); *p = 222; std::cout<< "n = " << n << std::endl; std::cout<< "*p = " << *p << std::endl; return 0; }
这里需要注意:按照正常理解,n 的打印值应该是 222。但是,由于编译器的常量传播优化,std::cout<< "n = " << n << std::endl;
会被编译器替换成类似 std::cout<< "n = " << 111 << std::endl;
的语义。
reinterpret_cast
reinterpret_cast
转换直接对二进制位按照目标类型重新解释,非常粗暴,所以风险很高,慎重使用。
#include <iostream> int main(){ char str[]="hello world!"; float *p = reinterpret_cast<float*>(str); std::cout << *p << std::endl; // 1.14314e+27 return 0; }
到此这篇关于C++超详细讲解强制类型转换的用法的文章就介绍到这了,更多相关C++强制类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!