c++类型转换(static_cast、const_cast 、dynamic_cast、reinterpret_cast)使用方法

为什么要使用c++风格的类型转换

我们都知道c++的类型转换既可以使用c风格的类型转换,如(type)expression,也可以使用c++风格的类型转换,c风格转换基本上是万能转换,互不想干的类型可以转换,基类指针到子类指针也可以转换,基本上是换汤又换药啊,我记得在某处看到个例子,将一个结构体指针直接转为int指针就输出了,当时我人都傻了,居然有这种操作:

struct Node {
    int x; // 
    Node* next;
    Node() : x(-1), next(nullptr){}
};
int main()
{
    Node* testNode = new Node;
    int* p = (int*)testNode;
    delete testNode;
    return 0;
}

当然编译运行都没有任何问题,并且解引用p可以得到值-1,但是我只要将结构体的第一行变量string str, 那么这样转换就不能得到节点的数据了,所以我们写代码还是老老实实按照规范来,不要投机取巧,不然出bug时真的叫天天不应,叫地地不灵了,这种转换太粗鲁了,风马牛不相及都可以转,并且使用(type)形式,在代码中难以识别,使用代码分析工具的时候相当难识别是否使用了类型转换。

4种转换的作用

  • static_cast:功能基本和c风格转换一致,但也有例外,不能把 struct 转换成 int 类型或者把 double 类型转换成指针类型,也不能去除表达式的 const 属性。
  • const_cast:唯一的作用就是去除表达式的const 或 volatileness 属性。
  • dynamic_cast: 把基类指针或者引用转为子类指针、者兄弟指针或引用,沿着继承关系向下转换。如果转换的不是该派生类类型,那么转换失败并返回返回空指针(用于指针转换时候)或者抛出异常(用于引用转换时候)。只能用于有虚函数的对象上,否则编译报错。
  • reinterpret_cast:这真的是一个脱缰的野马,除非万不得已,否则引火烧身,这是一个强大的类型转换,可以用于任何类型的指针或者引用的转换,也可以用于指针到整形、整数到指针的转换。例如:可以把一个函数指针int (*fptr)()转换为void (*fptr)(),去掉返回值,或者将一个int类型转为int(*fptr)()。C++之父Bjarne Stroustrup的FAQ网页和MSDN的Visual C++也都指出:错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

火速实操

#include<iostream>
#include<string>
class Poultry { // 自定义测试类型 家禽类
public:
    Poultry(const std::string& dogName) : m_name(dogName) {}
    virtual const std::string& GetName() const = 0; // 一般来说家禽类不给实例化,哈哈
    virtual ~Poultry() {};
protected:
    std::string m_name;
};

// dog也是家禽的一种,但我们得把野狗排除一下,这儿没有野狗
class Dog :public Poultry {
    // ...
public:
    Dog(const std::string &dogName) : Poultry(dogName) {}
    virtual const std::string& GetName() const override
    { 
        return m_name;
    }
};

class SpotDog : public Dog { // 这是一种带斑点得家禽狗,俗称家禽斑点狗
public:
    SpotDog(const std::string& dogName) : Dog(dogName) {}
    virtual const std::string& GetName() const override
    { 
        return m_name; 
    }
};

void FooDog(Poultry *poultry) // 测试函数
{
    // 传入指针,使其发生动态绑定
    // ... do something
}

void FooSpotDog(SpotDog* spotDog)
{
    // ... do something
}

void Foo() { /* ...*/ } // 函数
typedef void(*FooPtr)(); // 函数指针
int FooInt() { return 0; } // 函数
typedef int(*FooIntPtr)(); // 又一个函数指针

int main(int argc, char *agrv[])
{
    Poultry *dog = new Dog("小黑"); // 一只狗
    // 某些狗出生就有名字了,比如总是小黑小黑的叫,某一天你叫它小白,它就听不懂了
    const Poultry* constDog = dog; 
    FooDog(constDog); // 错误 函数接收一个Pooultry*
    FooDog(static_cast<Poultry*>(constDog)); // 错误  static_cast 只能去掉常量属性
    FooDog(dynamic_cast<Poultry*>(constDog)); // 错误 不能去掉常量属性,仅仅使用于继承类链的向下转换
    FooDog(const_cast<Poultry*>(constDog)); // 正确 const_cast 专门用来去掉常量属性的
    Dog* spotDog = new SpotDog("斑点狗");
    FooSpotDog(const_cast<SpotDog*>(spotDog)); // 失败,只能去掉const属性,再无别的用途
    FooSpotDog(static_cast<SpotDog*>(spotDog)); // 成功,但是不好,没有转换失败
    FooSpotDog(reinterpret_cast<SpotDog*>(spotDog)); // 成功,但是不好,没有提示转换失败,而且这个转换很野
    FooSpotDog(dynamic_cast<SpotDog*>(spotDog)); // 成功,最佳选择,如果转换失败是会返回空指针的,
    int b = 0;
    double d = dynamic_cast<double>(b) / 5.0; // 错误,double没有虚函数 没有继承系统
    // 最后来看看reinterpret_cast的用法,一个字,野
    FooIntPtr funFooInt = &FooInt;
    FooPtr funFoo = reinterpret_cast<FooPtr>(funFooInt); // ok 任何指针都可以转换
    funFooInt = reinterpret_cast<FooIntPtr>(funFoo); // ok 
    int* fun = reinterpret_cast<int*>(funFoo); // ok
    int* funInt = reinterpret_cast<int*>(funFooInt); // ok
    funFooInt = reinterpret_cast<FooIntPtr>(fun); // ok
    funFoo = reinterpret_cast<FooPtr>(funInt); // 统统都ok,一个字,干就完了。
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值