我已经被编写 C 和 c + + 代码几乎二十年的时间里,但我已经永远不会真正理解这些语言的一个方面。我亦显然使用常规的强制转换

MyClass *m = (MyClass *)ptr;

四处走动,那里似乎是两种类型的强制转换,但我不知道的区别。下面行之间的区别是代码的什么?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
2008-08-26 13:20:55
问题评论:

请参阅 <stackoverflow.com/questions/28080/...;

我不把传统 C 样式强制转换"常规铸造"在 c + +,因为它呈。通常不应使用在 c + +,尤其是与类,只是很容易犯下的错误。使用它是 C 程序员已移动到 c + +,但尚未完全尚未学习 c + + 的一个信号。

如何的问题将有答案可以是问题的重复的没有解答概览更多,这个问题要求更快地再"原始"

回答:

static_cast

static_cast的情况下,基本上需要使用的一些限制和补充的隐式转换反向使用。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

dynamic_cast用于您不知道什么是对象的动态类型的情况。如果您向下转换和参数的类型不是多态,不能使用dynamic_cast示例︰

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

dynamic_cast返回 null 指针,如果引用的对象不包含强制转换为基类的类型 (当您强制转换为引用, bad_cast会引发一个异常在这种情况下)。

下面的代码是无效的因为Base不是多态 (不包含虚函数)︰

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

"向上转换"总是使用static_castdynamic_cast,有效,也没有任何强制转换,"向上强制转换"为隐式转换。

常规的铸造

这些强制转换也称为 C 样式强制转换。C 样式强制转换是试验的 c + + 转换的序列范围和执行工作,而无需不断考虑dynamic_cast第一个 c + + 转换基本上相同。不用说,这是功能强大得多,因为它结合了所有const_caststatic_castreinterpret_cast,但也是不安全的因为它不使用dynamic_cast.

此外,C 样式强制转换不只允许您这样做,但它们还使您可以安全地强制转换为私有基类,而"等效" static_cast系列将为您提供编译时错误的。

有些人由于其简洁起见而喜欢 C 样式强制转换。我将它们用于数值强制转换,并涉及相应的 c + + 使用强制转换在用户定义的类型时,为他们提供了更严格的检查。

请参阅也提升的两个其他类型转换︰ boost.org/doc/libs/1_47_0/libs/conversion/...

@JohannesSchaub-litb︰ 确实,C 样式强制转换允许您安全地强制转换为基类的私有类?我可以看到使用私有基类时只/基/,但虚拟/多个继承呢?我假设 C 样式强制转换 does 没有指针操作。

@JohannesSchaub-litb 为 true,另外,还有一些系统开销使用旧 c 样式强制转换在 c + + 转换所涉及的吗?

@Joseph︰ 它将不会执行交叉强制转换是否正确,或任何其他情况下,需要进行运行时检查 (dynamic_cast是必需的)。但是,它会执行所有相同的指针调整static_cast也一样。不错,支持多个 (非虚拟) 继承和使用正确的指针调整。

@haxpor C 样式强制转换不具有动态的开销强制转换-它可能做它本质上是只添加一个指针调整或指针中减去。尽管后一种可以任意复杂,它将调用内置 (int <>-float) 和用户定义的转换。

static_cast

static_cast进行编译时,运行时不检查所涉及的类型。在许多情况下,这会使它安全类型的强制转换,因为它能提供最小空间不意外/安全各种类型之间的转换。

但是,对此非常重要注意事项是,由于其缺乏运行时检查, static_cast将和事实上,必须允许为向下转换为指针派生的基类。这然后可用于调用未定义的行为,除非非常小心地使用。查看,例如︰应 static_cast < 衍生 * >(Base pointer) 导致编译时错误?

除非您提供了重载的转换运算符, static_cast只允许等指针或引用BaseDerived,或者基本类型,如longintintfloat之间的相关类型之间的转换.

如果您提供了一个隐式转换operator T(),然后将使用static_cast<T>(otherType)您所提供的explicit转换运算符, static_cast 需要发出信号蓄意意图强制转换。

dynamic_cast

dynamic_cast存在,以便在运行时检查/转换从基类及其派生的类,通过引用或指针,两者之一。

如果实例不能被强制转换为派生类型, dynamic_cast将引发时试图引用,在运行时异常std::bad_cast或返回nullptr时应用于指针。

正在转换下继承层次结构的用途,由于dynamic_cast工作仅当源类型是多态的。否则,编译器将给出一个错误,例如error: cannot dynamic_cast 'b' (of type 'class base*') to type 'class inh1*' (source type is not polymorphic)

Static_cast 的例子和 / vs dynamic_cast

如果我们有下面的类

class B {};

class D : B {};

然后您可以以下操作

B* b = new D();
D* d1 = static_cast<D*>b; // Valid! d1 is a valid and correct pointer to a D
D* d2 = dynamic_cast<D*>b; // Valid! d2 is a valid and correct pointer to a D

在此示例中,指针d1d2都将指向b的类型正确版本的要求。

下面的示例说明了潜在灾祸static_cast可以比dynamic_cast更不安全的︰

B* b = new B();
D* d1 = static_cast<D*>b; // works, but D component is invalid!
D* d2 = dynamic_cast<D*>b; // cast fails => d2 is now a nullptr

现在d1将指向数据段类型D*,但实际数据是B*,因此试图访问D-特定成员是未定义的行为。d2,另一方面, nullptr和可以被检查并正确处理 (即不要尝试如果无法转换,则会将其用作D )。

由于dynamic_cast执行运行时类型检查,它会慢些,但这是否重要为您的应用程序应检查通过基准测试。

由于dynamic_cast可以带来额外的运行时开销,就可以通过指示编译器不将包含运行时类型信息 (RTTI) 关闭,但此功能是特定于编译器的并不是 c + + 标准的功能。

还有其他的强制转换运算符︰

reinterpret_cast

reinterpret_cast将忽略所有种类类型安全,以便您将尝试强制转换为任何其他值的任何内容。它允许要进行重新解释与目标类型的源类型基础的位模式。这是通过强制转换为引用或指针的变量,然后 '解释' 后者。

struct MyClass { std::uint8_t char a, b, c, d };
std::uint32_t i = 12345;
MyClass &p = reinterpret_cast<MyClass &> i;
std::uint8_t u = p.c; // Read 1 byte from 'within' i (N.B.: endianness not guaranteed)

reinterpret_cast可以是非常危险的除非您知道自己在做什么。显示潜在的危险的旧 C 样式强制转换在某些情况下,C 样式强制转换将回到reinterpret_casting 如果它找不到比较安全的选项︰

int i = 0;
void *v = 0;
int c = (int)v; // is valid
int d = static_cast<int>(v); // is not valid, different types
int e = reinterpret_cast<int>(v); // is valid, but very dangerous

const_cast

最后,我们有const_cast<T>,这将删除变量的const的性质,这样的结果传递给非const函数,等等。reinterpret_castconst_cast必须通过引用或者指针。

您忘了添加这仅适用时激活 RTTI。如果没有运行时类型信息,dynamic_cast 将不起作用。

这答案是完全误导的 static_cast <> safeness 上。Dynamic_cast <> 说明了技术方面的最窄定义的准确性。Reinterpret_cast <> 说明了基本身份验证和未提及 static_cast <>。此回答必须重写。

dynamic_cast 示例是完全错误的。D 是专用 ly 继承。其次,没有任何虚拟函数在类中;因此,不能使用 dynamic_cast 其间。GCC 的输出︰ 错误︰ 不能 (的类型类 B *') 的 'b' dynamic_cast 键入类 D * (源类型不是多态的)

它绝对再给我这有 33 投票的方式,当在注释的存在多年,而无需考虑这些异议。对不起,-1

@Vincent︰ 编译器没有 RTTI (或禁用) 不是 c + + 编译器。仍然可能会非常有用,但它不再是 c + +。

您应该查看文章c + + 编程/类型强制转换.

它包含所有不同的转换类型的很好的说明。从上面的链接下面的内容︰

const_cast

用 const_cast(expression) const_cast <> () 来添加或删除变量 const(ness) (或易失性等)。

static_cast

static_cast(expression) 的 static_cast <> () 用于整数类型之间进行强制转换。例如 char-> long 类型的值,int-> 短等。

静态的强制转换也用于强制转换为相关的类型,例如强制 void * 为适当的类型的指针。

dynamic_cast

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

dynamic_cast(expression)

目标类型必须是指针或引用类型,并且该表达式的计算结果必须为的指针或引用。仅当表达式所引用的对象的类型与目标类型兼容并该基类具有至少一个虚拟成员函数时,动态转换工作。如果不是,并被强制转换的表达式的类型是指针,则返回空值,如果上的动态类型转换引用失败,bad_cast 异常。如果它不会发生故障,动态转换表达式所引用的对象返回的指针或引用的目标类型。

reinterpret_cast

重新解释强制转换只是投射一种类型到另一个位。任何指针或整型类型可强制转换为任何其他与重新解释强制转换,方便地允许被滥用。例如,与重新解释的铸造一个可能不安全地,强制转换的字符串指针指向整数指针。

静态的强制转换

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

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

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

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

C 样式强制转换,与静态强制转换将允许编译器检查指针和 pointee 数据类型是兼容的这使得程序员能够捕获在编译期间此错误指针分配。

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

重新解释强制转换

若要强制指针转换,以相同的方式,像 C 样式强制转换在背景中,重新解释强制转换将使用。

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";

如果要引用转换而不是一个指针,动态转换再无法通过引发 bad_cast 异常。这需要使用 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 修饰符的变量。

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

尽管 const 类型转换允许改变常数的值,但这样做很可能会导致运行时错误的代码仍然无效。这可能会发生,例如常数位于只读存储器部分。

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

主要是在没有带非常量指针参数的函数,即使它不会修改 pointee 时使用 const 类型转换。

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

然后可以传递函数使用 const 强制转换的常量的变量。

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

print(nonConst); // allowed

源和详细说明

精心设计但简单而明确的答案。我尝试过类似的操作,我成功地使用此说明的 tem。

避免使用 C 样式强制转换。

C 样式强制转换常量的组合,重新解释强制转换,并会很难确定在代码中查找和替换。C + + 应用程序程序员应该避免 C 样式强制转换。

每位程序员的隐藏的议程::) 维护更换查找代码

请输入您的翻译

Regular cast vs. static_cast vs. dynamic_cast [duplicate]

确认取消