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

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

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


当前回答

虽然其他答案很好地描述了C++转换之间的所有差异,但我想补充一点,为什么不应该使用C样式转换(Type)var和Type(var)。

对于C++初学者来说,C风格的强制转换看起来像是C++强制转换的超集操作(static_cast<>()、dynamic_cast<<()、const_cast<>()、reinterpret_cast<>(。事实上,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++编程语言》第四版第302页的一段简短引用。

这种C样式转换比命名的转换运算符更危险因为在大型程序中很难发现这种符号,而且程序员想要进行的转换也不明确。

其他回答

这能回答你的问题吗?

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

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

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

静态铸造

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。此强制转换没有运行时开销。

reinterpret_cast的好特性(其他答案中没有提到)是它允许我们为函数类型创建一种void*指针。通常,对于对象类型,使用static_cast检索存储在void*中的指针的原始类型:

  int i = 13;
  void *p = &i;
  auto *pi = static_cast<int*>(p);

对于函数,我们必须使用reinterpret_cast两次:

#include<iostream>

using any_fcn_ptr_t = void(*)();


void print(int i)
{
   std::cout << i <<std::endl;
}

int main()
{     
  //Create type-erased pointer to function:
  auto any_ptr = reinterpret_cast<any_fcn_ptr_t>(&print);
  
  //Retrieve the original pointer:
  auto ptr = reinterpret_cast< void(*)(int) >(any_ptr);
  
  ptr(7);
}

使用reinterpret_cast,我们甚至可以为指向成员函数的指针获得类似类型的void*指针。

与普通的void*和static_cast一样,C++保证ptr指向print函数(只要我们将正确的类型传递给reinterpret_cast)。

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

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++转换:

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