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

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

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


当前回答

如果你了解一点内部情况,可能会有所帮助。。。

静态铸造

C++编译器已经知道如何在浮点到整型等缩放器类型之间进行转换。请对它们使用static_cast。当您要求编译器从类型A转换为B时,static_cast调用B的构造函数,将A作为参数传递。或者,A可以有一个转换运算符(即A::运算符B())。如果B没有这样的构造函数,或者A没有转换运算符,那么就会出现编译时错误。如果A和B位于继承层次结构(或void)中,则从A*到B*的强制转换总是成功的,否则会出现编译错误。Gotcha:若将基指针强制转换为派生指针,但若实际对象不是真正的派生类型,那个么就不会出现错误。你得到了错误的指针,很可能在运行时出现了segfault。A&到B&也是如此。Gotcha:从派生到基础或反之亦然,创建新副本!对于来自C#/Java的人来说,这可能是一个巨大的惊喜,因为结果基本上是从Derived创建的一个截断对象。

动态铸造

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

常量_成本

虽然static_cast可以从非常量转换为常量,但它不能反过来。const_cast可以实现这两种方式。其中一个方便的例子是迭代一些容器,比如set<T>,它只将元素作为常量返回,以确保不更改其键。然而,若您的意图是修改对象的非键成员,那个么应该可以。您可以使用const_cast来删除常量。另一个例子是当您想要实现T&SomeClass::foo()以及常量T&SomeClass::foo)const时。为了避免代码重复,可以将const_cast应用于从一个函数返回另一个函数的值。

重新解释(_C)

这基本上意味着在这个内存位置取这些字节,并将其视为给定的对象。例如,您可以将4字节的float加载到4字节的int,以查看float中的位的外观。显然,如果数据不适合该类型,您可能会得到segfault。此强制转换没有运行时开销。

其他回答

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

下面是我使用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和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指针之间转换时,两个强制转换都返回相同的结果,因为它们恰好在相同的位置开始,顺便说一句,标准无论如何也不能保证这一点。

这能回答你的问题吗?

我从来没有使用过reinterpret_cast,我想知道遇到一个需要它的案例是否不是糟糕设计的味道。在代码库中,我使用了很多dynamic_cast。与static_cast的区别在于dynamic_cast执行运行时检查,这可能(更安全),也可能(开销更大)是您想要的(请参见msdn)。

如果你了解一点内部情况,可能会有所帮助。。。

静态铸造

C++编译器已经知道如何在浮点到整型等缩放器类型之间进行转换。请对它们使用static_cast。当您要求编译器从类型A转换为B时,static_cast调用B的构造函数,将A作为参数传递。或者,A可以有一个转换运算符(即A::运算符B())。如果B没有这样的构造函数,或者A没有转换运算符,那么就会出现编译时错误。如果A和B位于继承层次结构(或void)中,则从A*到B*的强制转换总是成功的,否则会出现编译错误。Gotcha:若将基指针强制转换为派生指针,但若实际对象不是真正的派生类型,那个么就不会出现错误。你得到了错误的指针,很可能在运行时出现了segfault。A&到B&也是如此。Gotcha:从派生到基础或反之亦然,创建新副本!对于来自C#/Java的人来说,这可能是一个巨大的惊喜,因为结果基本上是从Derived创建的一个截断对象。

动态铸造

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

常量_成本

虽然static_cast可以从非常量转换为常量,但它不能反过来。const_cast可以实现这两种方式。其中一个方便的例子是迭代一些容器,比如set<T>,它只将元素作为常量返回,以确保不更改其键。然而,若您的意图是修改对象的非键成员,那个么应该可以。您可以使用const_cast来删除常量。另一个例子是当您想要实现T&SomeClass::foo()以及常量T&SomeClass::foo)const时。为了避免代码重复,可以将const_cast应用于从一个函数返回另一个函数的值。

重新解释(_C)

这基本上意味着在这个内存位置取这些字节,并将其视为给定的对象。例如,您可以将4字节的float加载到4字节的int,以查看float中的位的外观。显然,如果数据不适合该类型,您可能会得到segfault。此强制转换没有运行时开销。

使用dynamic_cast转换继承层次结构中的指针/引用。对普通类型转换使用static_cast。使用reinterpret_cast对位模式进行低级重新解释。使用时要格外小心。使用const_cast丢弃const/vatile。避免这种情况,除非您使用的是常量错误的API。