题 什么时候应该使用static_cast,dynamic_cast,const_cast和reinterpret_cast?


有什么用途:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C风格演员 (type)value
  • 功能式演员 type(value)

如何确定在哪些特定情况下使用哪个?


2052
2017-12-01 20:11


起源


也许这是一个很好的参考:您如何解释static_cast,reinterpret_cast,const_cast和dynamic_cast与新C ++程序员之间的差异?。 - Nan Xiao
对于使用不同类型的演员表的一些有用的具体示例,您可以查看类似问题的第一个答案 这个话题。 - TeaMonkie


答案:


static_cast 是你应该尝试使用的第一个演员。它执行类型之间的隐式转换(例如 int 至 float或指向 void*),它也可以调用显式转换函数(或隐式转换函数)。在许多情况下,明确说明 static_cast 没有必要,但重要的是要注意到 T(something) 语法相当于 (T)something 应该避免(稍后再详述)。一个 T(something, something_else) 然而,是安全的,并保证调用构造函数。

static_cast 也可以通过继承层次结构进行转换。向上施放(朝向基类)是不必要的,但是当向下施放时,只要不向下施放它就可以使用 virtual 遗产。但是,它没有进行检查,并且它是未定义的行为 static_cast 将层次结构下移到实际上不是对象类型的类型。


const_cast 可用于删除或添加 const 变量;没有其他C ++演员能够删除它(甚至没有 reinterpret_cast)。重要的是要注意修改以前的 const 如果原始变量是,则仅定义值 const;如果你用它来拿走 const 关于对未声明的内容的引用 const,这很安全。这在基于的方法重载成员函数时非常有用 const, 例如。它也可以用来添加 const 到一个对象,比如调用成员函数重载。

const_cast 也类似的工作 volatile虽然那不太常见。


dynamic_cast 几乎专门用于处理多态性。您可以将指向任何多态类型的指针或引用转换为任何其他类类型(多态类型至少有一个虚函数,声明或继承)。您可以使用它而不仅仅是向下投射 - 您可以侧向投射甚至向上投射另一条链。该 dynamic_cast 将寻找所需的物体并在可能的情况下将其归还。如果不能,它将返回 nullptr 在指针的情况下,或扔 std::bad_cast 在参考的情况下。

dynamic_cast 但是有一些限制。如果继承层次结构中存在多个相同类型的对象(所谓的“可怕的钻石”)并且您没有使用它,则它不起作用 virtual 遗产。它也只能通过公共继承 - 它总是无法通过 protected 要么 private 遗产。然而,这很少是一个问题,因为这种形式的遗传很少见。


reinterpret_cast 是最危险的演员,应该非常谨慎地使用。它将一种类型直接转换为另一种类型 - 例如将值从一个指针转换为另一个指针,或将指针存储在一个指针中 int或者各种其他讨厌的东西。很大程度上,这是你唯一的保证 reinterpret_cast 通常,如果您将结果转换回原始类型,您将获得完全相同的值(但是  如果中间类型小于原始类型)。有很多转换 reinterpret_cast 也做不到。它主要用于特别奇怪的转换和位操作,例如将原始数据流转换为实际数据,或将数据存储在对齐指针的低位中。


C风格演员 和 功能式演员 是演员使用 (type)object 要么 type(object), 分别。 C样式转换定义为以下第一个成功:

  • const_cast
  • static_cast (虽然忽略了访问限制)
  • static_cast (见上文),然后 const_cast
  • reinterpret_cast
  • reinterpret_cast, 然后 const_cast

因此,它可以在某些情况下用作其他演员阵容的替代品,但由于能够转换成其他演员阵容,因此可能非常危险。 reinterpret_cast除非你确定,否则当需要显式铸造时,后者应该是首选 static_cast 会成功还是 reinterpret_cast 将失败。即使这样,也要考虑更长,更明确的选择。

执行a时,C风格的强制转换也会忽略访问控制 static_cast,这意味着他们有能力执行其他演员无法进行的操作。不过,这主要是一个kludge,在我看来,这是避免C风格演员阵容的另一个原因。


2211
2017-12-01 20:22



dynamic_cast仅适用于多态类型。你只需要在转换为派生类时使用它。 static_cast肯定是第一个选项,除非你特别需要dynamic_cast的functinoality。一般而言,这不是一些神奇的银弹“类型检查演员”。 - jalf
很棒的答案!一个快速评论:如果你有一个Derived *&to cast into Base *&,则可能需要static_cast来构建层次结构,因为双指针/引用不会自动构建层次结构。两分钟前我遇到过这种(坦白,不常见)的情况。 ;-) - bartgol
*“没有其他C ++演员能够删除 const (甚至不 reinterpret_cast)“......真的吗?怎么样? reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))? - Mehrdad
我认为上面缺少的一个重要细节是,与static或reinterpret_cast相比,dynamic_cast具有运行时性能损失。这很重要,例如在实时软件中。 - jfritz42
值得一提的是 reinterpret_cast 在处理API的一组不透明数据类型时,它通常是首选的武器 - camelCase


使用 dynamic_cast 用于转换继承层次结构中的指针/引用。

使用 static_cast 用于普通类型的转换。

使用 reinterpret_cast 用于低级重新解释位模式。使用时要格外小心。

使用 const_cast 扔掉 const/volatile。除非你使用const不正确的API,否则请避免这种情况。


284
2018-01-21 04:53





(上面已经给出了很多理论和概念上的解释) 

以下是一些 实际例子 我用的时候 的static_cast的dynamic_castconst_cast会reinterpret_cast的

(也可以参考这一点来理解解释: http://www.cplusplus.com/doc/tutorial/typecasting/

static_cast:

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( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

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

152
2017-12-11 02:05



其他一些答案的理论很好,但仍然令人困惑,在阅读其他答案之后看到这些例子确实使它们都有意义。这是没有例子的,我仍然不确定,但有了它们,我现在肯定其他答案意味着什么。 - Solx
关于reinterpret_cast的最后一次使用:这与使用不一样 static_cast<char*>(&val) ? - Lorenzo Belli
@LorenzoBelli当然不是。你试过吗?后者不是有效的C ++并阻止编译。 static_cast 仅适用于具有已定义转换的类型,通过继承的可见关系或来自/来自 void *。对于其他一切,还有其他演员阵容。 reinterpret cast 去任何 char * 允许类型允许读取任何对象的表示 - 并且该关键字有用的唯一情况之一,而不是实现/未定义行为的猖獗生成器。但这不被视为“正常”转换,因此(通常)非常保守不允许 static_cast。 - underscore_d
当您使用数据库等系统软件时,reinterpret_cast很常见。大多数情况下,您编写自己的页面管理器,它不知道页面中存储的数据类型是什么,只返回一个void指针。它可以达到更高的水平来重新解释演员表并将其推断为他们想要的任何东西。 - Sohaib


如果你对内部有点了解,它可能会有所帮助......

的static_cast

  • C ++编译器已经知道如何将scaler类型转换为int。为他们使用static_cast。
  • 通常,在将类型A转换为B时,static_cast会调用B的构造函数传递它A.如果B没有这样的构造函数,则会出现编译时错误。
  • 演员来自 A* 至 B* 如果A和B在继承层次结构(或void)中,则总是成功,否则会出现编译错误。
  • 疑难杂症:如果您将基指针强制转换为派生指针,但如果实际对象不是派生类型,那么您  得到错误。你得到错误的指针,一旦你尝试访问派生指针的成员,你就会在运行时得到段错误。
  • 同样的 A& 至 B&
  • 疑难杂症:从Derived转换为Base或反之亦然创建新副本!对于来自C#/ Java的人来说,上面的许多都是一个巨大的惊喜。

的dynamic_cast

  • dynamic_cast使用运行时类型信息来确定强制转换是否有效。例如, (Base*) 至 (Derived*)如果指针实际上不是派生类型,则可能会失败。
  • 这意味着,与static_cast相比,dynamic_cast非常昂贵!
  • 对于 A* 至 B*,如果强制转换无效,那么dynamic_cast将返回nullptr。
  • 对于 A& 至 B& 如果强制转换无效,则dynamic_cast将抛出bad_cast异常。
  • 与其他演员表不同,存在运行时开销。

const_cast会

  • 虽然static_cast可以对const执行非const,但它不能反过来。 const_cast可以做到两种方式。
  • 这个方便的一个例子就是迭代一些容器 set<T> 它仅将其元素作为const返回,以确保您不更改其键。但是,如果您的目的是修改对象的非关键成员,那么它应该没问题。您可以使用const_cast删除constness。
  • 另一个例子是你想要实现的时候 T& foo() 以及 const T& foo()。为避免代码重复,可以应用const_cast从一个函数返回另一个函数的值。

reinterpret_cast的

  • 这基本上表示将这些字节放在此内存位置并将其视为给定对象。
  • 例如,您可以将4个字节的float加载到4个字节的int中,以查看float中的位是什么样的。
  • 显然,如果数据类型不正确,您可能会遇到段错误。
  • 此强制转换没有运行时开销。

52
2017-12-01 20:20



关于最后一点 const_cast 是不正确的:只有 dynamic_cast 实际上有运行时开销。看起来你可能已经改变了顺序而忘了移动并改写那个。 - rubenvb
你说的没错!固定。 - ShitalShah


是否 这个 回答你的问题?

我从来没用过 reinterpret_cast并且想知道是否遇到需要它的情况并不是一种糟糕的设计气味。在我的代码库中工作 dynamic_cast 经常使用。与...的区别 static_cast 是那个 dynamic_cast 做运行时检查可能(更安全)或不可能(更多开销)是你想要的(见 MSDN)。


11
2018-05-31 14:16



我已经将reintrepret_cast用于一个目的 - 从双平面中获取位数(在我的平台上使用相同的大小)。 - Joshua
需要reinterpret_cast,例如用于处理COM对象。 CoCreateInstance()具有void **类型的输出参数(最后一个参数),您将在其中传递声明为“INetFwPolicy2 * pNetFwPolicy2”。为此,您需要编写类似reinterpret_cast <void **>(&pNetFwPolicy2)的内容。 - Serge Rogatch


除了到目前为止的其他答案,这里是不明显的例子 static_cast 是不够的 reinterpret_cast 需要。假设有一个函数在输出参数中返回指向不同类的对象(不共享公共基类)的指针。这种功能的一个真实例子是 CoCreateInstance() (参见最后一个参数,实际上是这个参数 void**)。假设您从此函数请求特定的对象类,因此您事先知道指针的类型(您经常为COM对象执行此操作)。在这种情况下,您无法将指针指向您的指针 void** 同 static_cast: 你需要 reinterpret_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 适用于简单指针(不是指针指针),因此可以重写上面的代码以避免 reinterpret_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);

9