以下各项的正确用途是什么:

静态铸造动态铸造常量_成本重新解释(_C)(type)值(C样式转换)类型(值)(函数样式转换)

如何决定在哪些特定情况下使用哪一种?


当前回答

reinterpret_cast的好特性(其他答案中没有提到)是它允许我们为函数类型创建一种void*指针。通常,对于对象类型,使用static_cast检索存储在void*中的指针的原始类型:

  int i = 13;
  void *p = &i;
  auto *pi = static_cast<int*>(p);

对于函数,我们必须使用reinterpret_cast两次:

#include<iostream>

using any_fcn_ptr_t = void(*)();


void print(int i)
{
   std::cout << i <<std::endl;
}

int main()
{     
  //Create type-erased pointer to function:
  auto any_ptr = reinterpret_cast<any_fcn_ptr_t>(&print);
  
  //Retrieve the original pointer:
  auto ptr = reinterpret_cast< void(*)(int) >(any_ptr);
  
  ptr(7);
}

使用reinterpret_cast,我们甚至可以为指向成员函数的指针获得类似类型的void*指针。

与普通的void*和static_cast一样,C++保证ptr指向print函数(只要我们将正确的类型传递给reinterpret_cast)。

其他回答

为了理解,让我们考虑下面的代码片段:

struct Foo{};
struct Bar{};

int main(int argc, char** argv)
{
    Foo* f = new Foo;

    Bar* b1 = f;                              // (1)
    Bar* b2 = static_cast<Bar*>(f);           // (2)
    Bar* b3 = dynamic_cast<Bar*>(f);          // (3)
    Bar* b4 = reinterpret_cast<Bar*>(f);      // (4)
    Bar* b5 = const_cast<Bar*>(f);            // (5)

    return 0;
}

只有第(4)行编译没有错误。只能使用reinterpret_cast将指向对象的指针转换为指向任何无关对象类型的指针。

需要注意的一点是:dynamic_cast在运行时会失败,但在大多数编译器上,它也会编译失败,因为被转换的指针的结构中没有虚拟函数,这意味着dynamic_cast只能与多态类指针一起工作。

何时使用C++转换:

使用static_cast等效于进行值转换的C样式转换,或者当我们需要将指针从类显式上转换到其超类时。使用const_cast删除const限定符。使用reinterpret_cast执行指针类型与整数和其他指针类型之间的不安全转换。只有当我们知道自己在做什么并且了解别名问题时,才能使用此选项。

让我们在一个示例中看看reinterpret_cast和static_cast的区别:

#include <iostream>
using namespace std;

class A
{
    int a;
};

class B
{
    int b;
};

class C : public A, public B
{
    int c;
};

int main()
{
    {
        B b;
        cout << &b << endl;
        cout << static_cast<C *>(&b) << endl;      // 1
        cout << reinterpret_cast<C *>(&b) << endl; // 2
    }
    cout << endl;
    {
        C c;
        cout << &c << endl;
        cout << static_cast<B *>(&c) << endl;      // 3
        cout << reinterpret_cast<B *>(&c) << endl; // 4
    }
    cout << endl;
    {
        A a;
        cout << &a << endl;
        cout << static_cast<C *>(&a) << endl;
        cout << reinterpret_cast<C *>(&a) << endl;
    }
    cout << endl;
    {
        C c;
        cout << &c << endl;
        cout << static_cast<A *>(&c) << endl;
        cout << reinterpret_cast<A *>(&c) << endl;
    }
    return 0;
}

生成输出:

0x7ffcede34f0c
0x7ffcede34f08 // 1
0x7ffcede34f0c // 2

0x7ffcede34f0c
0x7ffcede34f10 // 3
0x7ffcede34f0c // 4

0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c

0x7ffcede34f0c
0x7ffcede34f0c
0x7ffcede34f0c

注意,输出1和2以及3和4是不同的。为什么?在这两种情况下,其中一种是static_cast,另一种是对相同类型的相同输入重新解释cast。

情况可以在下图中看到:

C包含一个B,但B的起始地址与C不同。static_cast正确地计算了C中B的地址。然而,reinterpret_cast返回了我们作为输入给出的相同地址,这在这种情况下是不正确的:该地址没有B。

然而,当在A指针和C指针之间转换时,两个强制转换都返回相同的结果,因为它们恰好在相同的位置开始,顺便说一句,标准无论如何也不能保证这一点。

除了到目前为止的其他答案之外,这里有一个不明显的例子,其中static_cast不够,因此需要重新解释cast。假设有一个函数在输出参数中返回指向不同类(不共享公共基类)对象的指针。此类函数的一个真实示例是CoCreateInstance()(请参阅最后一个参数,实际上是void**)。假设您从这个函数中请求特定的对象类,这样您就可以提前知道指针的类型(这通常是针对COM对象的)。在这种情况下,不能使用static_cast将指向指针的指针转换为void**:您需要重新解释cast<void**>(&yourPointer)。

在代码中:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

但是,static_cast适用于简单指针(而不是指向指针的指针),因此可以通过以下方式重写上述代码以避免重新解释cast(代价是额外的变量):

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

(上面给出了大量的理论和概念解释)

下面是我使用static_cast、dynamic_cast、const_cast、reinterpret_cast时的一些实际示例。

(也可参考此来理解解释:http://www.cplusplus.com/doc/tutorial/typecasting/)

静态铸造:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

常量成本(_C):

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

重新解释(_C):

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

reinterpret_cast的好特性(其他答案中没有提到)是它允许我们为函数类型创建一种void*指针。通常,对于对象类型,使用static_cast检索存储在void*中的指针的原始类型:

  int i = 13;
  void *p = &i;
  auto *pi = static_cast<int*>(p);

对于函数,我们必须使用reinterpret_cast两次:

#include<iostream>

using any_fcn_ptr_t = void(*)();


void print(int i)
{
   std::cout << i <<std::endl;
}

int main()
{     
  //Create type-erased pointer to function:
  auto any_ptr = reinterpret_cast<any_fcn_ptr_t>(&print);
  
  //Retrieve the original pointer:
  auto ptr = reinterpret_cast< void(*)(int) >(any_ptr);
  
  ptr(7);
}

使用reinterpret_cast,我们甚至可以为指向成员函数的指针获得类似类型的void*指针。

与普通的void*和static_cast一样,C++保证ptr指向print函数(只要我们将正确的类型传递给reinterpret_cast)。