C++中的类型转换运算符dynamic_cast、static_cast、const_cast和reinterpret_cast详解

零、小序

C++程序员都知道,C语言中的强制转换和万能转换指针“void*”很常用,然而这样的转换有时候很不安全,甚至有时候是无意义的转换!C风格的强制转换和万能转换都需要程序员自己把握转换的安全性,出现错误的可能性很大。面对C风格的松散转换,C++增加了4个类型转换运算符,用于规范类型转换,程序员可以根据需要选择一个类型转换运算符,并让编译器去检查类型转换的安全性,提高代码的安全性和健壮性!

一、dynamic_cast

1、关于dynamic_cast

dynamic_cast运算符是C++程序员经常使用的转换类型,它也是最常用的RTTI(运行阶段类型识别)组件,是基于类的继承层次之间一种动态的转换,这就要求基类必须有虚函数才能和子类之间进行转换。

它既允许向上转换(子类转换为父类)、也允许向下转换(父类转换为子类)。只有指针类型与转换的对象类型(或者是对象的直接或间接的基类)相同时才一定是安全的。因此向上转换是安全的,而向下转换不一定安全。转换不成功时,指针会返回空值。

dynamic_cast也可以用于引用的转换,但是由于没有和空指针对应的引用值,当引用转换不正确时,会抛出异常。

2、代码示例

// DynamicCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

using namespace std;

class Grand
{
public:
    Grand() {}
    ~Grand() {}

    virtual void printName()
    {
        cout << "I'm Grand!" << endl;
    }
private:

};

class Son : public Grand
{
public:
    Son() {}
    ~Son() {}
    virtual void printName()
    {
        cout << "I'm Son!" << endl;
    }

    void sayName()
    {
        cout << "I'm Jack!" << endl;
    }

private:

};

class GrandSon : public Son
{
public:
    GrandSon() {}
    ~GrandSon() {}
    virtual void printName()
    {
        cout << "I'm GrandSon!" << endl;
    }

    void sayAge()
    {
        cout << "My age is 18!" << endl;
    }
private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
    cout << "-----------------dynamic_cast类型转换------------------" << endl;
    Grand *pGrand = new Grand;
    Son *pSon = new Son;
    GrandSon *pGrandSon = new GrandSon;
    cout << "-----------------向上转换安全------------------" << endl;
    Grand *pTmpGrand1 = dynamic_cast<Grand*>(pSon); // 向上转换安全
    pTmpGrand1->printName();
    Grand *pTmpGrand2 = dynamic_cast<Grand*>(pGrandSon); // 向上转换安全
    pTmpGrand2->printName();
    Son *pTmpGrandSon1 = dynamic_cast<GrandSon*>(pGrandSon); // 向上转换安全
    pTmpGrandSon1->printName();

    cout << "-----------------向下转换不一定安全------------------" << endl;
    Son *pSon1 = dynamic_cast<Son*>(pGrand);
    if (pSon1 != nullptr)
    {
        pSon1->sayName();
    }
    else
    {
        cout << "指针pSon1是空的!" << endl;
    }
    GrandSon *pGrandSon1 = dynamic_cast<GrandSon*>(pGrand);
    if (pGrandSon1 != nullptr)
    {
        pGrandSon1->sayAge();
    }
    else
    {
        cout << "指针pGrandSon1是空的!" << endl;
    }

    // 只需要释放这三个指针就行,一块内存只允许释放一次,其他指针都是由这三个指针转换的,地址都是同一块
    DELETE_PTR(pGrand);
    DELETE_PTR(pSon);
    DELETE_PTR(pGrandSon);

    std::cout << "Hello World!n";

    getchar();
}

运行结果:
在这里插入图片描述

二、static_cast

1、关于static_cast

static_cast运算符也是C++程序员经常使用的转换类型,与dynamic_cast相比,它是一种静态的转换,使用它在基类和子类之间的互相转换是合法的,但合法并不代表安全!

它像dynamic_cast一样既允许向上转换(子类转换为父类)、也允许向下转换(父类转换为子类)。只要相互转换的类型有关系(直接或者间接继承)转换都是合法的,切记合法不一定安全!它的使用接近C风格的强制类型转换,安全性需要程序员自己把握。

由于static_cast是无需进行类型转换的,所以它可以将枚举值转换成整型,也可以将整型转换为枚举值;同样可以将doube、int、float、long等各种数值之间进行转换,但要注意转换精度,确保是自己需要的转换。

2、代码示例

// StaticCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

using namespace std;

class Grand
{
public:
    Grand() {}
    ~Grand() {}

    virtual void printName()
    {
        cout << "I'm Grand!" << endl;
    }
private:

};

class Son : public Grand
{
public:
    Son() {}
    ~Son() {}
    virtual void printName()
    {
        cout << "I'm Son!" << endl;
    }

    void sayName()
    {
        cout << "I'm Jack!" << endl;
    }

private:

};

class GrandSon : public Son
{
public:
    GrandSon() {}
    ~GrandSon() {}
    virtual void printName()
    {
        cout << "I'm GrandSon!" << endl;
    }

    void sayAge()
    {
        cout << "My age is 18!" << endl;
    }
private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
    cout << "-----------------static_cast类型转换------------------" << endl;
    cout << "-----------------static_cast类型用于指针转换------------------" << endl;
    Grand *pGrand = new Grand;
    Son *pSon = new Son;
    GrandSon *pGrandSon = new GrandSon;
    cout << "-----------------向上转换安全------------------" << endl;
    Grand *pTmpGrand1 = static_cast<Grand*>(pSon); // 向上转换安全
    pTmpGrand1->printName();
    Grand *pTmpGrand2 = static_cast<Grand*>(pGrandSon); // 向上转换安全
    pTmpGrand2->printName();
    Son *pTmpGrandSon1 = static_cast<GrandSon*>(pGrandSon); // 向上转换安全
    pTmpGrandSon1->printName();

    cout << "-----------------向下转换不一定安全------------------" << endl;
    Son *pSon1 = static_cast<Son*>(pGrand);
    if (pSon1 != nullptr)
    {
        pSon1->sayName();
    }
    else
    {
        cout << "指针pSon1是空的!" << endl;
    }
    GrandSon *pGrandSon1 = static_cast<GrandSon*>(pGrand);
    if (pGrandSon1 != nullptr)
    {
        pGrandSon1->sayAge();
    }
    else
    {
        cout << "指针pGrandSon1是空的!" << endl;
    }
    cout << "-----------------static_cast类型用于数值转换------------------" << endl;
    int iNum = 100;
    float fNum = 150.5;
    short sNum = static_cast<short>(iNum);
    int iTmpNum = static_cast<int>(fNum);
    double dNum = static_cast<double>(iNum);
    cout << "iNum=" << iNum << endl;
    cout << "fNum=" << fNum << endl;
    cout << "sNum=" << sNum << endl;
    cout << "iTmpNum=" << iTmpNum << endl;
    cout << "dNum=" << dNum << endl;

    // 只需要释放这三个指针就行,一块内存只允许释放一次,其他指针都是由这三个指针转换的,地址都是同一块
    DELETE_PTR(pGrand);
    DELETE_PTR(pSon);
    DELETE_PTR(pGrandSon);


    std::cout << "Hello World!n";

    getchar();
}

运行结果:
在这里插入图片描述

三、const_cast

1、关于const_cast

const_cast运算符的只有一个作用就是去const化,将一个常量类型转换为一个可以修改其他值的非常量类型。

这个运算符使用的场景是,需要一个值大多数时候都是不可变的,把这个值声明为const类型,而在某种时刻又是需要可以修改的,此时可以使用const_cast进行修改它。

2、代码示例

// ConstCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

class Grand
{
public:
    Grand() {}
    ~Grand() {}

    virtual void printName()
    {
        cout << "I'm Grand!" << endl;
    }
private:

};

class Son : public Grand
{
public:
    Son() {}
    ~Son() {}
    virtual void printName()
    {
        cout << "I'm Son!" << endl;
    }

    void sayName()
    {
        cout << "I'm Jack!" << endl;
    }

private:

};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}

int main()
{
    cout << "-----------------const_cast类型转换------------------" << endl;
    const char * cStr = "ISmileLI";
    char* cTmpStr = const_cast<char*>(cStr); // cStr本身是个常量没有变,它的返回值已经是一个非常量
    cout << "cStr:" << cStr << endl;
    cout << "cTmpStr:" << cTmpStr << endl;

    cout << "-----------------const_cast允许去掉一个类对象的const------------------" << endl;
    const Grand *pGrand = new Grand;
    Grand *pTmpGrand = const_cast<Grand *>(pGrand);
    if (pTmpGrand != nullptr)
    {
        pTmpGrand->printName();
    }
    else
    {
        cout << "指针pTmpGrand是空的!" << endl;
    }
    cout << "-----------------const_cast不允许转换类对象的类型------------------" << endl;
    const Son *pSon = new Son;
    //Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 编译器会提示错误
    cout << "//Grand *pTmpGrand2 = const_cast<Grand *>(pSon); // 编译器会提示错误" << endl;

    // const类型的指针的内存空间不需要手动释放,所以不需要调用下面的代码
    //DELETE_PTR(cStr);
    //DELETE_PTR(pGrand);

    std::cout << "Hello World!n";
    getchar();
}

运行结果:
在这里插入图片描述

四、reinterpret_cast

1、关于reinterpret_cast

reinterpret_cast运算符是这四种转换运算符中最不安全的、风险最大的转换类型,它主要适用于依赖底层实现的编程技术,因为不系统一个类型存储的字节可能是不一样的,所以具有不可移植性、对平台的依赖性强。

存在即合理!它可以对无关类指针进行转换,甚至可以直接将整型值转成指针,也可以将指针转换成足以存储指针的整型。

2、代码示例

// ReinterpretCastType.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;

class  Data
{
public:
    short a;
    short b;
};

#define DELETE_PTR(p) {if(p!=nullptr){delete (p); (p)=nullptr;}}
int main()
{
    cout << "-----------------reinterpret_cast类型转换------------------" << endl;
    long value = 0xA222B111;
    Data *pData = reinterpret_cast<Data*>(&value);
    cout << "pData->a的十六进制输出:"<< hex << pData->a << endl;
    cout << "pData->b的十六进制输出:" << hex << pData->b << endl;
    // 不需要释放指针pData的空间,它的地址和变量value相同,会自动释放掉
    // DELETE_PTR(pData);
    std::cout << "Hello World!n";
    getchar();
}

运行结果:
在这里插入图片描述

五、总述

dynamic_cast:主要用于动态类型转换,可以进行向上和向下转换,向上转换是安全的,向下转换不一定安全,转换失败会返回空指针或者抛出异常。
static_cast:主要用于静态类型转换,可以进行向上和向下转换,向上或者向下转换都是合法的,但合法并不代表安全!安全性需要程序员自己把握,类似于C风格的强制转换。
const_cast:只有一个作用就是去const化,将一个常量类型转换为一个可以修改其他值的非常量类型。
reinterpret_cast:最不安全的、风险最大的转换类型,它主要适用于依赖底层实现的编程技术,因为不系统一个类型存储的字节可能是不一样的,所以具有不可移植性、对平台的依赖性强。它可以对无关类指针进行转换,甚至可以直接将整型值转成指针,也可以将指针转换成足以存储指针的整型。

原创不易,点赞鼓励一下吧!

原文链接: https://www.cnblogs.com/ISmileLi/p/12699726.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++中的类型转换运算符dynamic_cast、static_cast、const_cast和reinterpret_cast详解

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/339849

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午12:18
下一篇 2023年3月2日 上午12:18

相关推荐