代码之家  ›  专栏  ›  技术社区  ›  Graeme Perrow

常规铸造vs.静态铸造vs.动态铸造[复制]

  •  1567
  • Graeme Perrow  · 技术社区  · 16 年前

    我写C和C++代码已经将近二十年了,但是这些语言的一个方面我从来没有真正理解过。很明显,我用的是普通的石膏。

    MyClass *m = (MyClass *)ptr;
    

    到处都是,但似乎还有另外两种类型的石膏,我不知道有什么区别。以下几行代码有什么区别?

    MyClass *m = (MyClass *)ptr;
    MyClass *m = static_cast<MyClass *>(ptr);
    MyClass *m = dynamic_cast<MyClass *>(ptr);
    
    8 回复  |  直到 7 年前
        1
  •  1473
  •   proski    7 年前

    静态铸造

    static_cast 用于基本上要反转隐式转换的情况,只需要一些限制和添加。 静态铸造 不执行运行时检查。如果您知道您所引用的对象是特定类型的,那么应该使用此选项,这样就不需要进行检查。例子:

    void func(void *data) {
      // Conversion from MyClass* -> void* is implicit
      MyClass *c = static_cast<MyClass*>(data);
      ...
    }
    
    int main() {
      MyClass c;
      start_thread(&func, &c)  // func(&c) will be called
          .join();
    }
    

    在这个例子中,您知道您通过了 MyClass 对象,因此不需要运行时检查来确保这一点。

    动态铸件

    dynamic_cast 当您不知道对象的动态类型时非常有用。如果引用的对象不包含作为基类强制转换为的类型,则返回空指针(当强制转换为引用时, bad_cast 在这种情况下会引发异常)。

    if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
      ...
    } else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
      ...
    }
    

    不能使用 动态铸件 如果向下强制转换(强制转换到派生类),并且参数类型不是多态的。例如,以下代码无效,因为 Base 不包含任何虚拟函数:

    struct Base { };
    struct Derived : Base { };
    int main() {
      Derived d; Base *b = &d;
      dynamic_cast<Derived*>(b); // Invalid
    }
    

    “向上强制转换”(强制转换为基类)始终对两者都有效 静态铸造 动态铸件 也没有任何强制转换,因为“上强制转换”是一种隐式转换。

    规则铸件

    这些铸件也称为C型铸件。C样式的CAST与尝试C++系列的一系列序列基本相同,并且采用第一个C++模型,不需要考虑。 动态铸件 . 不用说,这是更强大的,因为它结合了所有 const_cast , 静态铸造 reinterpret_cast 但它也不安全,因为它不使用 动态铸件 .

    此外,C样式的强制转换不仅允许您这样做,而且还允许您安全地强制转换到私有基类,而“等效的” 静态铸造 序列会给您一个编译时错误。

    有些人喜欢C型的石膏,因为它们很短。我只使用它们进行数值转换,当涉及到用户定义的类型时,使用适当的C++版本,因为它们提供了更严格的检查。

        2
  •  138
  •   community wiki Breeze    11 年前

    静态浇铸

    静态强制转换执行兼容类型之间的转换。它类似于C样式的强制转换,但更具限制性。例如,C样式的转换将允许一个整数指针指向一个字符。

    char c = 10;       // 1 byte
    int *p = (int*)&c; // 4 bytes
    

    由于这会导致一个4字节的指针指向已分配内存的1字节,因此写入该指针将导致运行时错误或覆盖某些相邻内存。

    *p = 5; // run-time error: stack corruption
    

    与C样式转换不同,静态转换将允许编译器检查指针和指针数据类型是否兼容,这允许程序员在编译期间捕获这种不正确的指针分配。

    int *q = static_cast<int*>(&c); // compile-time error
    

    重新解释铸造

    为了强制进行指针转换,与C样式转换在后台的方式相同,将使用reinterpret转换。

    int *r = reinterpret_cast<int*>(&c); // forced conversion
    

    此强制转换处理某些不相关类型之间的转换,例如从一个指针类型到另一个不兼容的指针类型。它只执行数据的二进制拷贝,而不改变底层的位模式。请注意,这种低级操作的结果是特定于系统的,因此不可移植。如果不能完全避免,应谨慎使用。

    动态铸件

    此类型仅用于将对象指针和对象引用转换为继承层次结构中的其他指针或引用类型。通过执行运行时检查指针是否引用目标类型的完整对象,它是唯一确保可以转换指向的对象的强制转换。为了使这个运行时检查成为可能,对象必须是多态的。也就是说,类必须定义或继承至少一个虚拟函数。这是因为编译器将只为这些对象生成所需的运行时类型信息。

    动态铸造示例

    在下面的示例中,使用动态强制转换将mychild指针转换为mybase指针。派生到基的转换成功,因为子对象包含完整的基对象。

    class MyBase 
    { 
      public:
      virtual void test() {}
    };
    class MyChild : public MyBase {};
    
    
    
    int main()
    {
      MyChild *child = new MyChild();
      MyBase  *base = dynamic_cast<MyBase*>(child); // ok
    }
    

    下一个示例尝试将MyBase指针转换为MyChild指针。由于基对象不包含完整的子对象,因此指针转换将失败。为了表明这一点,动态强制转换将返回一个空指针。这为在运行时检查转换是否成功提供了一种方便的方法。

    MyBase  *base = new MyBase();
    MyChild *child = dynamic_cast<MyChild*>(base);
    
    
    if (child == 0) 
    std::cout << "Null pointer returned";
    

    如果转换引用而不是指针,则动态强制转换将通过引发错误的强制转换异常而失败。这需要使用try catch语句来处理。

    #include <exception>
    // …  
    try
    { 
      MyChild &child = dynamic_cast<MyChild&>(*base);
    }
    catch(std::bad_cast &e) 
    { 
      std::cout << e.what(); // bad dynamic_cast
    }
    

    动态或静态铸造

    使用动态强制转换的好处在于,它允许程序员在运行时检查转换是否成功。缺点是,执行此检查会带来性能开销。因此,在第一个示例中,使用静态强制转换更可取,因为派生到基的转换永远不会失败。

    MyBase *base = static_cast<MyBase*>(child); // ok
    

    但是,在第二个示例中,转换可能成功或失败。如果MyBase对象包含MyBase实例,它将失败;如果它包含MyChild实例,它将成功。在某些情况下,直到运行时才知道这一点。在这种情况下,动态强制转换比静态强制转换更好。

    // Succeeds for a MyChild object
    MyChild *child = dynamic_cast<MyChild*>(base);
    

    如果使用静态强制转换而不是动态强制转换来执行基到派生的转换,则转换不会失败。它会返回一个指向不完整对象的指针。取消对此类指针的引用可能会导致运行时错误。

    // Allowed, but invalid
    MyChild *child = static_cast<MyChild*>(base);
    
    // Incomplete MyChild object dereferenced
    (*child);
    

    连续铸造

    这一个主要用于添加或删除变量的const修饰符。

    const int myConst = 5;
    int *nonConst = const_cast<int*>(&myConst); // removes const
    

    尽管const cast允许更改常量的值,但这样做仍然是可能导致运行时错误的无效代码。例如,如果常量位于只读内存的一部分,则可能发生这种情况。

    *nonConst = 10; // potential run-time error
    

    const cast主要在函数接受非常量指针参数时使用,即使它不修改指针。

    void print(int *p) 
    {
       std::cout << *p;
    }
    

    然后,可以使用const cast向函数传递常量变量。

    print(&myConst); // error: cannot convert 
                     // const int* to int*
    
    print(nonConst); // allowed
    

    Source and More Explanations

        3
  •  75
  •   HardcoreBro    10 年前

    你应该看看这篇文章 C++ Programming/Type Casting .

    它包含对所有不同类型的转换的良好描述。以下内容摘自上述链接:

    康斯特卡斯特

    const_cast(表达式)const_cast用于添加/删除 变量的常数(或易变性)。

    静态铸造

    静态强制转换(表达式)静态强制转换用于在 整数类型。例如,char->long、int->short等。

    静态强制转换还用于将指针强制转换为相关类型,用于 示例将void*铸造到适当的类型。

    动态铸件

    动态强制转换用于在运行时转换指针和引用, 通常用于向上或向下投射指针或引用 继承链(继承层次结构)。

    动态投射(表达式)

    目标类型必须是指针或引用类型,并且 表达式的计算结果必须是指针或引用。动态铸造工程 仅当表达式引用的对象类型为 与目标类型兼容,并且基类至少有一个 虚拟成员函数。如果不是,则转换表达式的类型 是指针,如果对引用进行动态强制转换,则返回空值 失败,引发错误的强制转换异常。如果没有失败,动态 CAST将目标类型的指针或引用返回到对象 引用的表达式。

    重新解释铸模

    reinterpret cast只是将一种类型按位强制转换为另一种类型。任何指针 或整体式,可浇铸到任何其他与重新解释铸造, 容易误用。例如,使用reinterpret cast one 不安全的是,可能会将整数指针强制转换为字符串指针。

        4
  •  25
  •   Peter Mortensen TravisEz13    11 年前

    避免使用C型铸造。

    C风格的强制转换是const和reinterpret强制转换的混合,在代码中很难找到和替换。C++应用程序程序员应该避免C风格的CAST。

        5
  •  25
  •   Atul Kumar    9 年前

    仅供参考,我相信bjarne stroustrup是引用说,C风格的强制转换是要避免的,如果可能的话,你应该使用静态强制转换或动态强制转换。

    Barne Stroustrup's C++ style FAQ

    就你所要的接受这个建议。我远非C++大师。

        6
  •  11
  •   DrPizza    16 年前

    C样式的强制转换将const-cast、static-cast和reinterpret-cast合并在一起。

    我希望C++没有C样式的转换。C++正确地(如它们应该)显示出突出;Casts通常表示做坏事)并且正确地区分不同类型的转换。它们还允许编写类似的外观函数,例如boost::lexical_cast,从一致性的角度来看,这非常好。

        7
  •  10
  •   Peter Mortensen TravisEz13    11 年前

    dynamic_cast 具有运行时类型检查,并且只与引用和指针一起使用,而 static_cast 不提供运行时类型检查。有关完整信息,请参阅msdn文章 static_cast Operator .

        8
  •  9
  •   larsmoa    8 年前

    dynamic_cast 仅支持指针和引用类型。它返回 NULL 如果类型是指针,则不可能进行强制转换;如果类型是引用类型,则引发异常。因此, 动态铸件 可用于检查对象是否属于给定类型, static_cast 不能(您只会得到一个无效的值)。

    其他答案中包含了C型(和其他)铸件。