C++强制类型转换运算符-dynamic_cast、const_cast、static_cast、reinterpret_cast、dynamic_pointer_cast、const_pointer

C++强制类型转换

dynamic_cast

回答的问题:是否能将某个对象的地址安全地赋值给一个特定类型的指针;同时也回答了强制类型转换是否安全的问题。
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行安全的向下转型(safe downcasting),两种情况:

  • 基类指针所指对象是派生类类型的,将其转换为派生类型的指针,是安全的,转换成功。
  • 基类指针所指对象为基类类型,要将其转换为派生类型指针,这是不安全的,转换失败。

向上转型本身就是安全的,即基类指针可以指向派生类指针,可以直接使用=符号,无需使用dynamic_cast运算符。

格式:dynamic_cast<Type *>(pt)
    pt能否安全的类型转换(*pt为Type或者*pt为Type的继承类)为Type *,如果可以,则返回pg的地址;反之,返回0
也可以使用引用(应该不常用)dynamic_cast<Superb &>(rg)
    pt能否安全的类型转换(pt为Type或者pt为Type的继承类)为Type &,如果可以,则返回pg的地址;反之,引发bad_cast异常(在typeinfo头文件中)

const_cast

  • 主要用途:对于常量指针,移除对象的常量性。
  • 转换格式:const_cast < type-name > (expression)
  • 要求:type-name和expression必须是同一种类型,他们只在const和volatile上存在区别,转换成功就赋值就行,转换失败则返回0。
  • volatile:提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
  • 与常规类型转换的不同:常规类型转换可以转换为不同类型,但是const_cast不允许不同类型的转换
  • 基本原则:不允许将定义为const的变量转换为非const类型(这个是针对非指针)。

分几种情况:其实就是基本原则的体现

  • 原来就是常量,用一个常量指针指向它,要将这个常量指针转换为非常量指针并修改其值,这是不允许的。
  • 原来不是常量,用一个常量指针指向它,要将这个常量指针转换为非常量指针并修改其值,这是允许的。

static_cast

格式:static_cast < type-name > (expression)
当且仅当type-name类型可以隐式转换为expression类型,或反之,否则类型转换会报错,两者能互相转换

允许的转换

  • 允许向上转换(继承类指针转换为基类指针)
  • 允许向下转换(基类指针转换为继承类指针)
  • 由于枚举类可以隐式转换为int,因此static_cast允许这类转换,并且允许将int类型转换为枚举类
  • 允许将double转换为int,将int转换为double
  • 允许将float转换为long,允许将long转换为float

不允许的转换

  • 不同类型的指针之间互相转换
  • 非指针类型和指针类型之间的相互转换
  • 不同类型的引用之间的转换

reinterpret_cast

格式:reinterpret_cast < type-name > (expression)
转换时,执行的是逐个比特复制的操作。转换的安全性由程序员负责。

允许的转换:

  • 可以将指针类型转换为能够容纳该指针类型的整型,反之不能转换。
  • 不同类型的指针之间的转换
  • 不同类型的引用之间的转换

不允许的转换:

  • 不能将函数指针转换为数据指针

代码

rtti1.h

#pragma once
#include <iostream>
using std::cout;
#ifndef _RTTI1_H_
#define _RTTI1_H_
class Grand
{
private:
    int hold;

public:
    Grand(int h = 0) : hold(h) {}
    virtual void Speak() const { cout << "I am a grand class!\n"; }
    virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
    Superb(int h = 0) : Grand(h) {}
    void Speak() const { cout << "I am a superb class!!\n"; }
    virtual void Say() const
    {
        cout << "I hold the superb value of " << Value() << "!\n";
    }
};
class Magnificent : public Superb
{
private:
    char ch;

public:
    Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
    void Speak() const { cout << "I am a magnificent class!!!\n"; }
    void Say() const
    {
        cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";
    }
};
#endif

main.cpp

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "rtti1.h"
using std::cout;
using std::endl;

Grand *GetOne();
void change(const int *pt, int n);
int main()
{
    /*
    dynamic_cast:
        dynamic_cast是最常用的RTTI操作符。
        回答的问题:是否能将某个对象的地址安全地赋值给一个特定类型的指针;同时也回答了强制类型转换是否安全的问题
        格式:dynamic_cast<Type *>(pt)
            pt能否安全的类型转换(*pt为Type或者*pt为Type的继承类)为Type *,如果可以,则返回pg的地址;反之,返回0
        也可以使用引用(应该不常用):dynamic_cast<Superb &>(rg)
            pt能否安全的类型转换(pt为Type或者pt为Type的继承类)为Type &,如果可以,则返回pg的地址;反之,引发bad_cast异常(在typeinfo头文件中)
    */
    cout << "dynamic_cast***************************************************************" << endl;
    std::srand(std::time(0));
    Grand *pg;
    Superb *ps;
    for (int i = 0; i < 5; i++)
    {
        pg = GetOne();
        pg->Speak();
        if (ps = dynamic_cast<Superb *>(pg)) // 如果可以转换,则if条件判断为1,则执行ps->Say();反之,不执行
            ps->Say();
    }
    /*
    const_cast:
        转换格式:const_cast < type-name > (expression)
        要求:type-name和expression必须是同一种类型,他们只在const和volatile上存在区别,转换成功就赋值就行,转换失败则返回0
        与常规类型转换的不同:常规类型转换可以转换为不同类型,但是const_cast不允许不同类型的转换
        基本原则:不允许将定义为const的变量转换为非const类型。
    */
    cout << "const_cast***************************************************************" << endl;
    int pop1 = 38383;
    const int pop2 = 2000;
    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
    change(&pop1, -103); // 转换了,因为pop1是非const类型
    change(&pop2, -103); // 没转换,因为pop2是const类型
    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
    /*
    static_cast:
        格式:static_cast < type-name > (expression)
        当且仅当type-name类型可以隐式转换为expression类型,或反之,否则类型转换会报错,两者能互相转换
        1.允许向上转换(继承类指针转换为基类指针)
        2.允许向下转换(基类指针转换为继承类指针)
        3.由于枚举类可以隐式转换为int,因此static_cast允许这类转换,并且允许将int类型转换为枚举类
        4.允许将double转换为int,将int转换为double
        5.允许将float转换为long,允许将long转换为float
    */
    cout << "static_cast***************************************************************" << endl;
    Grand x = Grand(10);   // 基类对象
    Grand *w = &x;         // 基类指针
    Superb z = Superb(11); // 继承类对象
    Superb *pointer1 = &z;
    Superb *y = static_cast<Superb *>(w);      // 将基类指针转换为继承类指针 允许
    y->Speak();                                // 只是允许但是不推荐,因为转换为继承类指针后可以调用继承类的成员方法,而基类没有该成员方法,会引发异常
    Grand *a = static_cast<Grand *>(pointer1); // 将继承类指针转换为基类指针
    a->Speak();
    int varint1 = 99;
    double vardouble1 = 99.99;
    int var = static_cast<int>(vardouble1);
    std::cout << "var = " << var << std::endl;
    double var1 = static_cast<double>(varint1);
    std::cout << std::fixed << "var1 = " << var1 << std::endl;
    /*
    reinterpret_cast:
        格式:reinterpret_cast < type-name > (expression)
        可以将指针类型转换为能够容纳该指针类型的整型,反之不能转换。
        不能将函数指针转换为数据指针
    */
    struct dat
    {
        short a;
        short b;
    };
    long value = 0xA224B118;
    dat *pd = reinterpret_cast<dat *>(&value);
    std::cout << std::hex << pd->a; // display first 2 bytes of value

    return 0;
}
Grand *GetOne() // generate one of three kinds of objects randomly
{
    Grand *p = 0;
    switch (std::rand() % 3)
    {
        case 0:
            p = new Grand(std::rand() % 100);
            break;
        case 1:
            p = new Superb(std::rand() % 100);
            break;
        case 2:
            p = new Magnificent(std::rand() % 100,
                                'A' + std::rand() % 26);
            break;
    }
    return p;
}

void change(const int *pt, int n)
{
    int *pc;
    pc = const_cast<int *>(pt);
    *pc += n;
}

运行结果:

C:\Users\15495\Documents\Jasmine\Work\coding\cmake-build-debug\coding.exe
dynamic_cast***************************************************************
I am a magnificent class!!!
I hold the character A and the integer 23!
I am a superb class!!
I hold the superb value of 8!
I am a superb class!!
I hold the superb value of 54!
I am a magnificent class!!!
I hold the character R and the integer 72!
I am a magnificent class!!!
I hold the character Q and the integer 65!
const_cast***************************************************************
pop1, pop2: 38383, 2000
pop1, pop2: 38280, 2000
50
100
static_cast***************************************************************
I am a grand class!
I am a superb class!!
var = 99
var1 = 99.000000
b118
Process finished with exit code 0

智能指针强制类型转换

dynamic_pointer_cast

适用对象:shared_ptr。
格式:std::shared_ptr<Superb> ps = std::dynamic_pointer_cast<Superb>(pa)
函数原理:首先,把指针拿出来,然后通过dynamic转化指针类型,然后再生成对应的智能指针,三个步骤。源码如下:

template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
dynamic_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
    using _Sp = shared_ptr<_Tp>;
    if (auto* __p = dynamic_cast<typename _Sp::element_type*>(__r.get()))
        return _Sp(__r, __p);
    return _Sp();
}

如果需要对std::unique_ptr进行转换,可以模仿这个方式自己写,在标准库中,并没有提供。

const_pointer_cast

template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
const_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
    using _Sp = shared_ptr<_Tp>;
    return _Sp(__r, const_cast<typename _Sp::element_type*>(__r.get()));
}

static_pointer_cast

template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
static_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
    using _Sp = shared_ptr<_Tp>;
    return _Sp(__r, static_cast<typename _Sp::element_type*>(__r.get()));
}

reinterpret_pointer_cast

template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
reinterpret_pointer_cast(const shared_ptr<_Up>& __r) noexcept
{
    using _Sp = shared_ptr<_Tp>;
    return _Sp(__r, reinterpret_cast<typename _Sp::element_type*>(__r.get()));
}

代码

rtti1.h

#pragma once
#include <iostream>
using std::cout;
#ifndef _RTTI1_H_
#define _RTTI1_H_
class Grand
{
private:
    int hold;

public:
    Grand(int h = 0) : hold(h) {}
    virtual void Speak() const { cout << "I am a grand class!\n"; }
    virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
    Superb(int h = 0) : Grand(h) {}
    void Speak() const { cout << "I am a superb class!!\n"; }
    virtual void Say() const
    {
        cout << "I hold the superb value of " << Value() << "!\n";
    }
};
class Magnificent : public Superb
{
private:
    char ch;

public:
    Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
    void Speak() const { cout << "I am a magnificent class!!!\n"; }
    void Say() const
    {
        cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";
    }
};
#endif

main.cpp

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "rtti1.h"
#include <Memory>
using std::cout;
using std::endl;

std::shared_ptr<Grand> GetOne();
void change(const std::shared_ptr<int> pt, int n);
int main()
{
    cout << "dynamic_pointer_cast***************************************************************" << endl;
    std::srand(std::time(0));
    std::shared_ptr<Grand> pg = nullptr;
    std::shared_ptr<Superb> ps = nullptr;
    for (int i = 0; i < 5; i++)
    {
        pg = GetOne();
        pg->Speak();
        if (ps = std::dynamic_pointer_cast<Superb>(pg)) // 如果可以转换,则if条件判断为1,则执行ps->Say();反之,不执行
            ps->Say();
    }
    cout << "const_pointer_cast***************************************************************" << endl;
    int pop1 = 38383;
    const int pop2 = 2000;
    std::shared_ptr<int> po1(&pop1);
    std::shared_ptr<const int> po2(&pop2);
    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
    int n = -103;
    // 转换成功
    std::shared_ptr<int> pc1 = nullptr;
    pc1 = std::const_pointer_cast<int>(po1);
    *pc1 += n;
    // 转换失败
    std::shared_ptr<int> pc2 = nullptr;
    pc2 = std::const_pointer_cast<int>(po2);
    *pc2 += n;
    cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
    cout << "static_pointer_cast***************************************************************" << endl;
    Grand x = Grand(10);   // 基类对象
    std::shared_ptr<Grand> w(&x);         // 基类指针
    Superb z = Superb(11); // 继承类对象
    std::shared_ptr<Superb> pointer1(&z);
    std::shared_ptr<Superb> y = std::static_pointer_cast<Superb>(w);      // 将基类指针转换为继承类指针 允许
    y->Speak();                                // 只是允许但是不推荐,因为转换为继承类指针后可以调用继承类的成员方法,而基类没有该成员方法,会引发异常
    std::shared_ptr<Grand> a = std::static_pointer_cast<Grand>(pointer1); // 将继承类指针转换为基类指针
    a->Speak();
    cout << "reinterpret_pointer_cast***************************************************************" << endl;
    struct dat
    {
        short a;
        short b;
    };
    // 这样转换其实是不安全的,程序员需要主动保证程序的安全性。
    // 本程序运行结束程序的返回值非0,是错的
    long value = 0xA224B118;
    std::shared_ptr<long> data(&value);
    std::shared_ptr<dat> pd = std::reinterpret_pointer_cast<dat>(data);
    std::cout << std::hex << pd->a; // display first 2 bytes of value
//    std::cout << std::hex << pd->b; // display first 2 bytes of value

    return 0;
}
std::shared_ptr<Grand> GetOne() // generate one of three kinds of objects randomly
{
    std::shared_ptr<Grand> p = nullptr;
    switch (std::rand() % 3)
    {
        case 0:
            p.reset(new Grand(std::rand() % 100));
            break;
        case 1:
            p.reset(new Superb(std::rand() % 100));
            break;
        case 2:
            p.reset(new Magnificent(std::rand() % 100,
                                'A' + std::rand() % 26));
            break;
    }
    return p;
}

运行结果:

C:\Users\15495\Documents\Jasmine\Work\coding\cmake-build-debug\coding.exe
I hold the character O and the integer 66!
I am a superb class!!
I hold the superb value of 49!
I am a magnificent class!!!
I hold the character V and the integer 33!
const_pointer_cast***************************************************************
pop1, pop2: 38383, 2000
pop1, pop2: 38280, 2000
static_pointer_cast***************************************************************
I am a grand class!
I am a superb class!!
reinterpret_pointer_cast***************************************************************
b118
Process finished with exit code -1073740940 (0xC0000374)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jasmine-Lily

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值