C++基础 强制转换

目录

static_cast:static_cast(expression)

const_cast

dynamic_cast

reinterpret_cast


C++ 提供以下几类转换

static_cast:static_cast<type-id>(expression)

tatic_cast 主要用于以下几种情况:

  1. 用于显式地将一个表达式转换为另一种类型,包括基本类型、指针类型和引用类型。
  2. 用于将基类指针或引用转换为派生类指针或引用(向下转型)。
  3. 用于将 void* 指针转换为任意类型的指针。
  4. 用于将任意类型的指针转换为 void* 指针。

需要注意的是tatic_cast 不会在运行时检查该对象的运行时类型,安全的向下转换可以用dynamic_cast 执行

示例

#include <vector>
#include <iostream>
 
struct B
{
    int m = 0;
    void hello() const
    {
        std::cout << "Hello world,这里是 B!\n";
    }
};
 
struct D : B
{
    void hello() const
    {
        std::cout << "Hello world,这里是 D!\n";
    }
};
 
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
 
int main()
{
    // 1: 静态向下转型
    D d;
    B& br = d; // 通过隐式转换向上转型
    br.hello();
    D& another_d = static_cast<D&>(br); // 向下转型
    another_d.hello();
 
    // 2: 左值到右值
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
    std::cout << "移动后,v.size() = " << v.size() << '\n';
 
    // 3: 初始化转换
    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';
 
    // 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);
}

输出 

Hello world,这里是 B!
Hello world,这里是 D!
移动后,v.size() = 0
n = 3
v.size() = 10
*ni = 3
1
0

const_cast

类型转换运算符,可以将 const 和 volatile 限定符从指针或引用类型中去除。它可以用于将常量对象变成非常量对象,从而允许对其进行修改,函数指针和成员函数指针无法用于 const_cast

 示例

#include <iostream>
 
struct type
{
    int i;
 
    type(): i(3) {}
 
    void f(int v) const {
        // this->i = v;                 // 编译错误:this 是指向 const 的指针
        const_cast<type*>(this)->i = v; // 只要该对象不是 const 就 OK
    }
};
 
int main() 
{
    int i = 3;                 // 不声明 i 为 const
    const int& rci = i; 
    const_cast<int&>(rci) = 4; // OK:修改 i
    std::cout << "i = " << i << '\n';
 
    type t; // 如果这是 const type t,那么 t.f(4) 会是未定义行为
    t.f(4);
    std::cout << "type::i = " << t.i << '\n';
 
    const int j = 3; // 声明 j 为 const
    [[maybe_unused]]    //可能是有意不使用,编译器不会发出警告
    int* pj = const_cast<int*>(&j);
    // *pj = 4;      // 未定义行为
 
    [[maybe_unused]]
    void (type::* pmf)(int) const = &type::f; // 指向成员函数的指针
    // const_cast<void(type::*)(int)>(pmf);   // 编译错误:const_cast 不能用于成员函数指针
}

dynamic_cast

沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用,失败会返回空指针

#include <iostream>
 
struct V
{
    virtual void f() {} // 必须为多态以使用运行时检查的 dynamic_cast
};
 
struct A : virtual V {};
 
struct B : virtual V
{
    B(V* v, A* a)
    {
        // 构造中转型(见后述 D 的构造函数中的调用)
        dynamic_cast<B*>(v); // 良好定义:v 有类型 V*,V 是 B 的基类,产生 B*
        dynamic_cast<B*>(a); // 未定义行为:a 有类型 A*,A 不是 B 的基类
    }
};
 
struct D : A, B
{
    D() : B(static_cast<A*>(this), this) {}
};
 
struct Base
{
    virtual ~Base() {}
};
 
struct Derived: Base
{
    virtual void name() {}
};
 
int main()
{
    D d; // 最终派生对象
    A& a = d; // 向上转型,可以用 dynamic_cast,但不是必须的
 
    [[maybe_unused]]
    D& new_d = dynamic_cast<D&>(a); // 向下转型
    [[maybe_unused]]
    B& new_b = dynamic_cast<B&>(a); // 侧向转型
 
    Base* b1 = new Base;
    if (Derived* d = dynamic_cast<Derived*>(b1); d != nullptr)
    {
        std::cout << "成功从 b1 向下转型到 d\n";
        d->name(); // 可以安全调用
    }
 
    Base* b2 = new Derived;
    if (Derived* d = dynamic_cast<Derived*>(b2); d != nullptr)
    {
        std::cout << "成功从 b2 向下转型到 d\n";
        d->name(); // 可以安全调用
    }
 
    delete b1;
    delete b2;
}

reinterpret_cast

重新解释底层位模式在类型间任意转换,实际上转换比static_cast更不安全,

static_cast就是利用C++类型之间的继承关系图和聚合关系图(编译器必须知道),根据一个子对象地址计算另一个子对象的地址。 

reinterpret_cast不关心继承关系,直接把数据类型A的地址解释成另一个数据类型B的地址

 示例

#include <cstdint>
#include <cassert>
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int i = 7;
 
    // 指针到整数并转回
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // 不能误用 static_cast
    std::cout << "&i 的值是 " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
 
    // 到另一函数指针并转回
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); 未定义行为
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // 安全
 
    // 通过指针的类型别名化
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "本系统是小端的\n"
                                 : "本系统是大端的\n");
 
    // 通过引用的类型别名化
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
 
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(const_iref); // 编译错误——不能去除 const
    // 必须用 const_cast 代替:int &iref = const_cast<int&>(const_iref);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

路奇怪

有钱出钱,没钱多出编程主意啊

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

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

打赏作者

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

抵扣说明:

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

余额充值