首页 文章

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

提问于
浏览
2146

有什么用途:

  • static_cast

  • dynamic_cast

  • const_cast

  • reinterpret_cast

  • C风格演员 (type)value

  • 函数式转换 type(value)

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

7 回答

  • 165

    this回答你的问题吗?

    我从来没有使用 reinterpret_cast ,并想知道是否遇到需要它的情况并不是一种糟糕的设计气味 . 在代码库中,我在 dynamic_cast 上工作了很多 . 与 static_cast 的区别在于 dynamic_cast 执行运行时检查,可能(更安全)或不可能(更多开销)是您想要的(请参阅msdn) .

  • 2283

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

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

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

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

  • 65

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

    static_cast

    • C编译器已经知道如何在诸如float之类的scaler类型之间进行转换 . 使用 static_cast .

    • 当你要求编译器从类型 A 转换为 B 时, static_cast 调用 B 的构造函数将 A 作为参数传递 . 或者, A 可以有一个转换运算符(即 A::operator B() ) . 如果 B 没有这样的构造函数,或者 A 没有转换运算符,则会出现编译时错误 .

    • 如果A和B在继承层次结构中(或无效),则从 A* 转换为 B* 总是成功,否则会出现编译错误 .

    • Gotcha :如果将基指针强制转换为派生指针,但如果实际对象不是真正的派生类型,则不会出现错误 . 你得到错误的指针,很可能在运行时出现段错误 . A&B& 也是如此 .

    • Gotcha :从Derived转换为Base或反之,创建新副本!对于来自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的元素,以确保你没有't change its key. However if your intent is to modify object'的非关键成员,那么它应该没问题 . 您可以使用const_cast删除constness .

    • 另一个例子是你想要实现 T& foo() 以及 const T& foo() . 为避免代码重复,可以应用const_cast从一个函数返回另一个函数的值 .

    reinterpret_cast

    • 这基本上说在这个内存位置取这些字节并将其视为给定对象 .

    • 例如,您可以将4个字节的float加载到4个字节的int中,以查看float中的位是什么样的 .

    • 显然,如果数据类型不正确,您可能会遇到段错误 .

    • 此强制转换没有运行时开销 .

  • 298

    除了到目前为止的其他答案,这里是不明显的例子 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);
    
  • 5

    虽然其他答案很好地描述了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变量可以被修改和reinterpret_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的作者)的书The C Programming Language第4版 - 第302页的简短引用 .

    这种C风格的转换比指定的转换运算符危险得多,因为在大型程序中很难发现符号,并且程序员想要的转换类型并不明确 .

  • 12

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

    当我使用 static_castdynamic_castconst_castreinterpret_cast 时,下面是一些 practical examples .

    (也可以参考这个来理解解释: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);
    }
    
  • 12

    static_cast 是您应该尝试使用的第一个演员表 . 它执行类型之间的隐式转换(例如 intfloat 或指向 void* 的指针),它还可以调用显式转换函数(或隐式转换函数) . 在许多情况下,明确指出 static_cast isn 't necessary, but it'重要的是要注意 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_castvolatile 上的工作方式也类似,但不太常见 .


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

    但是, dynamic_cast 有一些限制 . 它没有't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called '使用 virtual 继承来破坏钻石') and you aren't . 它也只能通过公共继承 - 它总是无法通过 protectedprivate 继承 . 然而,这很少是一个问题,因为这种形式的遗传很少见 .


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


    C-style castfunction-style cast 分别使用 (type)objecttype(object) 进行强制转换 . C样式转换定义为以下第一个成功:

    • const_cast

    • static_cast (虽然忽略了访问限制)

    • static_cast (见上文),然后 const_cast

    • reinterpret_cast

    • reinterpret_cast ,然后 const_cast

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

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

相关问题