C++类型转换 释义
dynamic_cast 沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用
static_cast 也能用来进行向下转型,它不会有运行时检查的开销,但只有在程序(通过某些其他逻辑)能够保证 表达式 所指向的对象肯定是 Derived 时才是安全的
reinterpret_cast 通过重新解释底层位模式在类型间转换
const_cast 在有不同 cv 限定的类型间转换,可以用来移除常量性或易变性
duration_cast 转换 std::chrono::duration 为不同类型 ToDuration 的时长
time_point_cast 将 std::chrono::time_point 从一个时长转换到另一个
clock_cast(C++20) 转换时钟 Source 的时间点 t 为时钟 Dest 的等价时间点
bit_cast(C++20) 通过转译 from 的对象表示获得 To 类型值

bad_cast: 在 dynamic_cast 对引用类型运行时检查失败(例如因为类型并非以继承关联)时,还有若请求的刻面不存在于本地环境时从 std::use_facet 抛出此类型异常


bad_cast

bad_cast:

#include <iostream>
#include <typeinfo>
 
struct Foo { virtual ~Foo() {} };
struct Bar { virtual ~Bar() {} };
 
int main() {
    Bar b;
    try {
        Foo& f = dynamic_cast<Foo&>(b);
    } catch(const std::bad_cast& e) {
        std::cout << e.what() << '\n';
    }
}

结果:

Bad dynamic cast

bit_cast

#include <cstdint>
#include <bit>
#include <iostream>
 
constexpr double f64v = 19880124.0;
constexpr auto u64v = std::bit_cast<std::uint64_t>(f64v);
 
constexpr std::uint64_t u64v2 = 0x3fe9000000000000ull;
constexpr auto f64v2 = std::bit_cast<double>(u64v2);
 
int main() {
    std::cout
        << std::fixed <<f64v << "f64.to_bits() == 0x"
        << std::hex << u64v << "u64\n";
 
    std::cout
        << "f64::from_bits(0x" << std::hex << u64v2 << "u64) == "
        << std::fixed << f64v2 << "f64\n";
}

结果:

19880124.000000f64.to_bits() == 0x4172f58bc0000000u64
f64::from_bits(0x3fe9000000000000u64) == 0.781250f64

clock_cast

#include <chrono> 
#include <iostream>
 
int main() {
  
}

reinterpret_cast

通过重新解释底层位模式在类型间转换。

#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);
}

结果:

&i 的值是 0x7fff352c3580
42
本系统是小端的
42

static_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

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;
}

结果:

成功从 b2 向下转型到 d
posted on 2022-12-14 14:40  txt1994  阅读(48)  评论(0编辑  收藏  举报
返回顶端