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

的正确用法是:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C 样式转换(type)value
  • 函数样式转换type(value)

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

答案

static_cast是您应该尝试使用的第一个演员表。它的作用类似于类型之间的隐式转换(例如intfloat或指向void*指针),它还可以调用显式转换函数(或隐式函数)。在许多情况下,无需明确声明static_cast ,但必须注意, T(something)语法与(T)something等效,应避免使用(稍后会详细介绍)。 T(something, something_else)是安全的,并且保证可以调用构造函数。

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


const_cast可用于将const删除或添加到变量;没有其他 C ++ 强制转换能够将其删除(甚至不能reinterpret_cast )。重要的是要注意,仅当原始变量为const ,才可以修改以前的const值;如果使用它来使const脱离对未使用const声明的内容的引用,则这是安全的。例如,这在基于const重载成员函数时很有用。它还可以用于向对象添加const ,例如调用成员函数重载。

const_castvolatile上也类似地工作,尽管这种情况不太常见。


dynamic_cast仅用于处理多态。您可以将任何多态类型的指针或引用强制转换为任何其他类类型(多态类型至少具有一个声明或继承的虚函数)。您不仅可以向下投射,还可以使用它–您可以侧向投射,甚至向上投射另一个链。 dynamic_cast将查找所需的对象,并在可能的情况下将其返回。如果不能,则在使用指针的情况下将返回nullptr ,在使用引用的情况下将返回std::bad_cast

dynamic_cast有一些限制。如果继承层次结构中有多个相同类型的对象(所谓的 “可怕的钻石”),并且您没有使用virtual继承,则该方法将无效。它也只能进行公共继承 - 它将始终无法通过protected继承或private继承。但是,这很少有问题,因为这种继承形式很少。


reinterpret_cast是最危险的强制类型转换,应谨慎使用。它将一种类型直接转换为另一种类型 - 例如将值从一个指针转换为另一种指针,或将指针存储在int或其他各种讨厌的东西中。在很大程度上,只有保证你得到reinterpret_cast是,通常,如果你把结果返回到原来的类型,你会得到的值完全相同( 如果中间型比原来的类型更小)。 reinterpret_cast也无法完成许多转换。它主要用于特别奇怪的转换和位操作,例如将原始数据流转换为实际数据,或将数据存储在指向对齐数据的指针的低位。


C 样式转换和函数样式转换分别是使用(type)objecttype(object)进行的转换,并且在功能上等效。它们被定义为以下成功的第一个:

  • const_cast
  • static_cast (尽管忽略访问限制)
  • static_cast (请参见上文),然后是const_cast
  • reinterpret_cast
  • reinterpret_cast ,然后是const_cast

因此,它可以在某些情况下替代其他强制类型转换,但由于具有降级到reinterpret_cast的能力,因此可能非常危险,并且在需要显式强制类型转换时应首选后者,除非您确定static_cast将成功执行或reinterpret_cast将失败。即使这样,也可以考虑使用更长,更明确的选项。

C 样式强制转换在执行static_cast时也会忽略访问控制,这意味着它们具有执行其他强制转换无法执行的操作的能力。但是,这主要是一种争执,在我看来,这是避免使用 C 样式强制转换的另一个原因。

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

使用static_cast进行普通类型转换。

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

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

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

下面是当我使用static_castdynamic_castconst_castreinterpret_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);
}

如果您不了解内部原理,可能会有所帮助...

static_cast

  • C ++ 编译器已经知道如何在定标器类型之间进行转换,例如 float 到 int。对他们使用static_cast
  • 当您要求编译器将类型A转换为Bstatic_cast调用B的构造函数,并将A作为参数传递。或者, A可以具有转换运算符(即A::operator B() )。如果B没有这样的构造函数,或者A没有转换运算符,那么您将得到编译时错误。
  • 如果 A 和 B 在继承层次结构(或 void)中,则从A*强制转换为B*总是成功的,否则会出现编译错误。
  • 陷阱 :如果将基本指针转换为派生指针,但如果实际对象不是真正派生类型,则不会出错。您会得到错误的指针,并且很可能在运行时出现段错误。 A&B&
  • 疑难杂症 :从衍生角色转换为基础角色,反之亦然,创建副本!对于使用 C#/ Java 的人来说,这可能是一个巨大的惊喜,因为其结果基本上是从 Derived 创建的对象。

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 删除常数。
  • 另一个示例是当您要实现T& SomeClass::foo()以及const T& SomeClass::foo() const 。为了避免代码重复,可以将 const_cast 应用于从另一个函数返回的值。

reinterpret_cast

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

这能回答您的问题吗?

我从来没有使用过reinterpret_cast ,并且想知道是否遇到需要它的情况不会引起不良设计的味道。在我处理dynamic_cast的代码库中,使用了很多代码。与static_cast的区别在于, dynamic_cast会进行运行时检查,这可能(更安全)或可能(更多开销)不是您想要的(请参阅msdn )。

除了到目前为止的其他答案之外,这是一个显而易见的示例,其中static_cast不足,因此需要reinterpret_cast 。假设有一个函数,该函数在输出参数中返回指向不同类(不共享公共基类)的对象的指针。此类函数的真实示例是CoCreateInstance() (请参阅最后一个参数,实际上是void** )。假设您从此函数请求特定的对象类,因此您预先知道了指针的类型(通常对 COM 对象执行此操作)。在这种情况下,您无法使用static_cast将指针转换为void** :您需要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);

尽管其他答案很好地描述了 C ++ 强制转换之间的所有差异,但我想简短地说明一下为什么不应该使用 C 样式强制转换(Type) varType(var)

对于 C ++ 初学者而言,C 样式强制转换看起来像是 C ++ 强制转换的超集操作(static_cast <>(),dynamic_cast <>(),const_cast <>(),reinterpret_cast <>()),并且有人可能会更喜欢 C ++ 强制转换。实际上,C 样式强制转换是父集,编写起来比较短。

C 样式强制转换的主要问题是它们隐藏了开发人员的强制转换意图。 C 样式转换几乎可以执行所有类型的转换,从由 static_cast <>()和 dynamic_cast <>()完成的通常安全的转换到具有潜在危险的转换,例如 const_cast <>(),可以删除 const 修饰符,以便 const 变量可以被修改并重新解释_cast <>(),甚至可以将整数值重新解释为指针。

这是示例。

int a=rand(); // Random number.

int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.

int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.

int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.

*pa4=5; // Program crashes.

将 C ++ 强制转换添加到语言中的主要原因是允许开发人员阐明其意图 - 为什么他要执行该强制转换。通过使用在 C ++ 中完全有效的 C 样式强制转换,您使代码的可读性降低,并且更容易出错,尤其是对于其他未创建代码的开发人员而言。因此,为了使您的代码更具可读性和明确性,您应该始终喜欢 C ++ 强制转换而不是 C 样式强制转换。

这是 Bjarne Stroustrup(C ++ 的作者)《 C ++ 编程语言》第 4 版 - 第 302 页的简短引用。

这种 C 样式的转换比命名的转换运算符要危险得多,因为在大型程序中很难发现这种表示法,并且程序员打算进行的转换类型也不明确。

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

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 ++ cast

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