c++ 数据类型转换:static_cast dynamic_cast reinterpret_cast const_cast

c语言中我们经常使用类似于 int a = (int)3.14等这种强制类型转换。
标准C++中有四个类型转换符:static_cast、dynamic_cast、reinterpret_cast、const_cast

1. static_cast 静态类型转换

用法:

static_cast < type-id > ( expression )

介绍:

该运算符把expression转换为type-id类型,但运行时没有类型检查来保证转换的安全性。它主要有如下几种用法:

① 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

#include <iostream>
using namespace std;

enum Color {
    red,
    black,
    white
};

int main()
{
    int a = 0;
    Color b = static_cast<Color>(a); //int 转成enum
    int c = 97;
    char d = static_cast<char>(c); // int 转成char

    cout << "enum b: " << b << endl;
    cout << "char d: " << d << endl;
    return 0;
}
// 结果
// enum b: 0
// char d: a

② 把空指针转换成目标类型的空指针。

#include <iostream>
using namespace std;
int main()
{
    void *pPtr = NULL;
    int *iPtr = static_cast<int*>(pPtr);

    cout << "pPtr地址: " << &pPtr << endl;
    cout << "iPtr地址: " << &iPtr << "     iPtr内容: " << iPtr << endl;

    return 0;
}
// 结果
// pPtr地址: 0x7ffeea08fad0
// iPtr地址: 0x7ffeea08fac8     iPtr内容: 0x0

③ 把任何类型的表达式转换成void类型。

//任何指针转化为空指针类型
int *aInt = NULL;
void *aVoid = static_cast<void*>(aInt);

注意: static_cast不能进行出void外的指针强制互转

#include <iostream>
using namespace std;
int main()
{
    char *tmp = "abc";

    // 不能将char*型的数据转换为int*,但C语言强转可以
    // cout << static_cast<int*>(tmp) << endl;
    int *tmp_ = (int*)(tmp); 
    cout << tmp_ << endl;
    cout << static_cast<int*>(tmp_) << endl;//转为自身可以

    return 0;
}
// 结果
// 0x10d899f1c
// 0x10d899f1c

④ 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。

进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;

进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。

class Person{

};

class Son :public Person{

};

class My{};

#include <iostream>
using namespace std;
int main()
{
    /*********************1. 父类和子类指针之间转换********************/
    //父类指针转为子类
    Person *p = NULL;
    // Son *s = p; // 不能强转
    Son *s = static_cast<Son*>(p);

    //子类指针转为父类
    Son *s0 = NULL;
    Person *p0 = s0; //父类指针指向之类,可以
    Person *p0 = static_cast<Person*>(s0);
    /*********************1. 父类和子类指针之间转换********************/

    // 无继承关系的自定义数据类型不能相互转换
    // My* my= static_cast<My*>(p);

    /*********************2. 父类和子类对象之间转换********************/
    //父类对象无法转为子类对象
    // Person p1;
    // Son s1 = static_cast<Son>(p1);
    
    //子类对象可以赋值,初始化父类对象
    Son s2;
    Person p2 = s2;
    Person p2 = static_cast<Person>(s2);
    /*********************2. 父类和子类对象之间转换********************/

    /*********************3. 父类和子类引用之间转换********************/
    //父类引用转为子类
    Person p_ ;
    Person &p3 = p_;
    // Son &s3 = p3; // 不能强转
    Son &s3 = static_cast<Son&>(p3);

    //子类引用转为父类
    Son s_;
    Son &s4 = s_;
    Person &p4 = s4;
    Person &p4 = static_cast<Person&>(s4);
    /*********************3. 父类和子类引用之间转换********************/
    return 0;
}

注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性

2. dynamic_cast 动态类型转换

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换,不能操作普通数据。

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,downcast不成功返回null,比static_cast更安全,static_cast的问题只有在运行时才能体现出来(错误解释)。对于dynamic_cast下行转换基类要有虚函数,因为运行时信息保存在虚表中,如果没有会编译错误;

#include <iostream>
using namespace std;

class BaseClass {
public:
    int m_iNum;
    virtual void foo(){}; //基类必须有虚函数。保持多态特性才能使用dynamic_cast
};

class DerivedClass: public BaseClass {
public:
    char*m_szName[100];
    void bar(){};
};

int main()
{
    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
    return 0;
}

3. const_cast 常量转换

const_cast,用于修改类型的const或volatile属性,不能对非指针或非引用的变量添加或移除const。

const int g = 20;
//int h = const_cast<int>(g); //不允许对普通数据进行操作
int *h = const_cast<int*>(&g);//去掉const常量const属性

const int g0 = 20;
const int &g2 = g0;
int &h = const_cast<int &>(g0);//去掉const引用const属性
int &h2 = const_cast<int &>(g2);//去掉const引用const属性

const char *g1 = "hello";
char *h = const_cast<char *>(g1);//去掉const指针const属性

4.reinterpret_cast 重新解释转换

可以将任意类型转换为任意类型,因此非常不安全。只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数有完全相同的比特位。按照reinterpret的字面意思“重新解释”,即对数据的比特位重新解释。

从指针类型到一个足够大的整数类型
从整数类型或者枚举类型到指针类型
从一个指向函数的指针到另一个不同类型的指向函数的指针
从一个指向对象的指针到另一个不同类型的指向对象的指针
从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

总结来说:reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。

总结
去const属性用const_cast。 基本类型转换用static_cast。 多态类之间的类型转换用daynamic_cast。 不同类型的指针类型转换用reinterpret_cast。

posted on 2020-04-17 21:54  JJ_S  阅读(208)  评论(0编辑  收藏  举报