C++的类型转换运算符:reinterpret_cast

C++的类型转换运算符:reinterpret_cast

reinterpret_cast 是 C++ 中与 C 风格类型转换最接近的类型转换运算符。它让程序员能够将一种对象类型转换为另一种,不管它们是否相关;也就是说,它使用如下所示的语法强制重新解释类型:

Base* objBase = new Base ();
Unrelated* notRelated = reinterpret_cast<Unrelated*>(objBase);
// The code above compiles, but is not good programming!*

这种类型转换实际上是强制编译器接受 static_cast 通常不允许的类型转换,通常用于低级程序(如驱动程序),在这种程序中,需要将数据转换为 API(应用程序编程接口)能够接受的简单类型(例如,有些 OS 级 API 要求提供的数据为 BYTE 数组,即 unsigned char*):

SomeClass* object = new SomeClass();
// Need to send the object as a byte-stream...
unsigned char* bytesFoAPI = reinterpret_cast<unsigned char*>(object);

上述代码使用的类型转换并没有改变源对象的二进制表示, 但让编译器允许程序员访问 SomeClass 对象包含的各个字节。由于其他 C++类型转换运算符都不允许执行这种有悖类型安全的转换,因此除非万不得已,否则不要使用 reinterpret_cast 来执行不安全(不可移植)的转换。

应尽量避免在应用程序中使用 reinterpret_cast,因为它让编译器将类型 X 视为不相关的类型 Y,这看起来不像是优秀的设计或实现。  

解释
与 static_cast 不同,但与 const_cast 类似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针间转换,或在指针表示依赖其类型的不明架构上)。它纯粹是一个编译时指令,指示编译器将 表达式 视为如同具有 新类型 类型一样处理。

唯有下列转换能用 reinterpret_cast 进行,但若转换会转型走常量性或易变性则亦不允许。

  1. 整型、枚举、指针或成员指针类型的表达式可转换到其自身的类型。产生的值与 表达式 的相同。(C++11 起)

  2. 指针能转换成大小足以保有其类型所有值的任何整型类型(例如转换成 std::uintptr_t)

  3. 任何整型或枚举类型的值可转换到指针类型。指针转换到有足够大小的整数再转换回同一指针类型后,保证拥有其原值,否则结果指针无法安全地解引用(不保证相反方向的往返转换;相同指针可拥有多种整数表示)。不保证空指针常量 NULL 或整数零生成目标类型的空指针值;为此目的应该用 static_cast 或隐式转换。

  4. 任何 std::nullptr_t 类型的值,包含 nullptr,可转换成任何整型类型,如同它是 (void*)0 一样,但没有值能转换成 std::nullptr_t,甚至 nullptr 也不行:为该目的应该用 static_cast。(C++11 起)

  5. 任何对象指针类型 T1* 可转换成指向对象指针类型 cv T2 。这严格等价于 static_cast(static_cast(表达式))(这意味着,若 T2 的对齐要求不比 T1 的更严格,则指针值不改变,且将结果指针转换回原类型将生成其原值)。任何情况下,只有类型别名化(type aliasing)规则允许(见下文)时,结果指针才可以安全地解引用

  6. T1 类型的左值表达式可转换成到另一个类型 T2 的引用。结果是与原左值指代同一对象,但有不同类型的左值或亡值。不创建临时量,不进行复制,不调用构造函数或转换函数。只有类型别名化(type aliasing)规则允许(见下文)时,结果指针才可以安全地解引用

  7. 任何函数指针可转换成指向不同函数类型的指针。通过指向不同函数类型的指针调用函数是未定义的,但将这种指针转换回指向原函数类型的指针将生成指向原函数的指针值。

  8. 一些实现上(特别是在任何 POSIX 兼容的系统上,即基于 dlsym 的要求),函数指针可以转换成 void* 或任何其他对象指针,反之亦然。若实现支持双向的转换,则转换回原类型将生成原值,否则结果指针不能安全地解引用或调用。

  9. 任何指针类型的空指针值可转换成任何其他指针类型,产生该类型的空指针值。注意不能用 reinterpret_cast 将空指针常量 nullptr 或任何其他 std::nullptr_t 类型的值转换为指针:为此目的应该使用隐式转换或 static_cast。

  10. 成员函数指针可转换成指向不同类型的不同成员函数的指针。转换回原类型将生成原值,否则结果指针不能安全使用。

  11. 指向某类 T1 的成员对象的指针可转换成指向另一个类 T2 的另一个成员对象的指针。若 T2 的对齐不比 T1 更严格,则转换回原类型 T1 将生成原值,否则不能安全地使用结果指针。

同所有转型表达式,结果是:

左值,若 new_type 是左值引用或到函数类型的右值引用;
亡值,若 new_type 是到对象类型的右值引用;
否则为纯右值。

演示 reinterpret_cast 的一些用法:

#include 
#include 
#include 
int f() { return 42; }
int main()
{
    int i = 7;
 
    // 指针到整数并转回
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast 为错误
    std::cout << "The value of &i is 0x" << 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);
    if(p2[0] == '\x7')
        std::cout << "This system is little-endian\n";
    else
        std::cout << "This system is big-endian\n";
 
    // 通过引用的类型别名化
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
 
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast(const_iref); // 编译错误——不能去除 const
    // 必须用 const_cast 代替:int &iref = const_cast(const_iref);
}

可能的输出:

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

该文章会更新,欢迎大家批评指正。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器

你可能感兴趣的:(CC++编程要点,c++)