C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应用。参考链接四种类型分析
注意使用这几种类型转换,得到的是对象,而不是类型
-
const_cast,字面上理解就是去const属性。
-
static_cast,命名上理解是静态类型转换。如int转换成char。参考链接static_cast详解
-
dynamic_cast,命名上理解是动态类型转换。如子类和父类之间的多态类型转换。
-
reinterpreter_cast,仅仅重新解释类型,但没有进行二进制的转换。
1、static_cast分析
类似于C风格的强制转换。无条件转换,静态类型转换,风险较低的转换。用于:
我的分析如下:
static_cast实质是通过调用类型转换,不管是隐式转换的或者是显示的构造,例如std::vector v = static_cast<std::vector>(10);,实质是vector< int>(10),尽管vector的构造是explicit
-
基类和子类之间转换:其中子类指针转换成父类指针是安全的;但父类指针转换成子类指针是不安全的。(基类和子类之间的动态类型转换建议用dynamic_cast)
-
基本数据类型转换。enum, struct, int, char, float等。static_cast不能进行无关类型(如非基类和子类)指针之间的转换。
-
把空指针转换成目标类型的空指针。
-
把任何类型的表达式转换成void类型。void 指针和具体类型指针之间的转换,例如void *转int *、char *转void *等;
-
static_cast不能去掉类型的const、volitale属性(用const_cast)。
-
static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。
代码1
#include <iostream>
#include <cstdlib>
using namespace std;
class Complex{
public:
Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ }
public:
operator double() const { return m_real; } //类型转换函数
private:
double m_real;
double m_imag;
};
int main(){
//下面是正确的用法
int m = 100;
Complex c(12.5, 23.8);
long n = static_cast<long>(m); //宽转换,没有信息丢失
char ch = static_cast<char>(m); //窄转换,可能会丢失信息
int *p1 = static_cast<int*>( malloc(10 * sizeof(int)) ); //将void指针转换为具体类型指针
void *p2 = static_cast<void*>(p1); //将具体类型指针,转换为void指针
double real= static_cast<double>(c); //调用类型转换函数
//下面的用法是错误的
float *p3 = static_cast<float*>(p1); //不能在两个具体类型的指针之间进行转换
p3 = static_cast<float*>(0X2DF9); //不能将整数转换为指针类型
return 0;
}
代码2
#include <vector>
#include <iostream>
struct B {
int m = 0;
void hello() const {
std::cout << "Hello world, this is B!\n";
}
};
struct D : B {
void hello() const {
std::cout << "Hello world, this is D!\n";
}
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
// 1: 初始化转换
int n = static_cast<int>(3.14);
std::cout << "n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "v.size() = " << v.size() << '\n';
// 2: 静态向下转型
D d;
B& br = d; // 通过隐式转换向上转型
br.hello();
D& another_d = static_cast<D&>(br); // 向下转型
another_d.hello();
// 3: 左值到右值
std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
std::cout << "after move, v.size() = " << v.size() << '\n';
// 4: 弃值表达式
static_cast<void>(v2.size());
// 5. 隐式转换的逆
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "*ni = " << *ni << '\n';
// 6. 数组到指针后随向上转型
D a[10];
B* dp = static_cast<B*>(a);
// 7. 有作用域枚举到 int 或 float
E e = E::ONE;
int one = static_cast<int>(e);
std::cout << one << '\n';
// 8. int 到枚举,枚举到另一枚举
E e2 = static_cast<E>(one);
EU eu = static_cast<EU>(e2);
// 9. 指向成员指针向上转型
int D::*pm = &D::m;
std::cout << br.*static_cast<int B::*>(pm) << '\n';
// 10. void* 到任何类型
void* voidp = &e;
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}
2、dynamic_cast分析
有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
-
安全的基类和子类之间转换。
-
必须要有虚函数。
-
相同基类不同子类之间的交叉转换。但结果是NULL。
代码1
#include <iostream>
using namespace std;
class A{
public:
virtual void func() const { cout<<"Class A"<<endl; }
private:
int m_a;
};
class B: public A{
public:
virtual void func() const { cout<<"Class B"<<endl; }
private:
int m_b;
};
class C: public B{
public:
virtual void func() const { cout<<"Class C"<<endl; }
private:
int m_c;
};
class D: public C{
public:
virtual void func() const { cout<<"Class D"<<endl; }
private:
int m_d;
};
int main(){
A *pa = new A();
B *pb;
C *pc;
//情况①
pb = dynamic_cast<B*>(pa); //向下转型失败
if(pb == NULL){
cout<<"Downcasting failed: A* to B*"<<endl;
}else{
cout<<"Downcasting successfully: A* to B*"<<endl;
pb -> func();
}
pc = dynamic_cast<C*>(pa); //向下转型失败
if(pc == NULL){
cout<<"Downcasting failed: A* to C*"<<endl;
}else{
cout<<"Downcasting successfully: A* to C*"<<endl;
pc -> func();
}
cout<<"-------------------------"<<endl;
//情况②
pa = new D(); //向上转型都是允许的
pb = dynamic_cast<B*>(pa); //向下转型成功
if(pb == NULL){
cout<<"Downcasting failed: A* to B*"<<endl;
}else{
cout<<"Downcasting successfully: A* to B*"<<endl;
pb -> func();
}
pc = dynamic_cast<C*>(pa); //向下转型成功
if(pc == NULL){
cout<<"Downcasting failed: A* to C*"<<endl;
}else{
cout<<"Downcasting successfully: A* to C*"<<endl;
pc -> func();
}
return 0;
}
代码2
class BaseClass {
public:
int m_iNum;
virtual void foo(){};
//基类必须有虚函数。保持多台特性才能使用dynamic_cast
};
class DerivedClass: public BaseClass {
public:
char *m_szName[100];
void bar(){};
};
BaseClass* pb = new DerivedClass();
DerivedClass *pd1 = static_cast<DerivedClass *>(pb);
//子类->父类,静态类型转换,正确但不推荐
DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb);
//子类->父类,动态类型转换,正确
BaseClass* pb2 = new BaseClass();
DerivedClass *pd21 = static_cast<DerivedClass *>(pb2);
//父类->子类,静态类型转换,危险!访问子类m_szName成员越界
DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2);
//父类->子类,动态类型转换,安全的。结果是NULL
3、reinterpret_cast
-
转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。不能进行普通类型之间转换,比如int与double
-
在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
-
最普通的用途就是在函数指针类型之间进行转换。
-
很难保证移植性。
#include <iostream>
using namespace std;
class A{
public:
A(int a = 0, int b = 0): m_a(a), m_b(b){}
private:
int m_a;
int m_b;
};
int main(){
//将 char* 转换为 float*
char str[]="http://c.biancheng.net";
float *p1 = reinterpret_cast<float*>(str);
cout<<*p1<<endl;
//将 int 转换为 int*
int *p = reinterpret_cast<int*>(100);
//将 A* 转换为 int*
p = reinterpret_cast<int*>(new A(25, 96));
cout<<*p<<endl;
return 0;
}
4 、const_cast
const_cast去掉类型的const或volatile属性。
struct SA {
int i;
};
const SA ra;
//ra.i = 10; //直接修改const类型,编译错误
SA &rb = const_cast<SA&>(ra);
rb.i = 10;
总 结
去const属性用const_cast。
基本类型转换用static_cast。
多态类之间的类型转换用daynamic_cast。
不同类型的指针类型转换用reinterpreter_cast。