【问题标题】:Which one to use when static_cast and reinterpret_cast have the same effect? [duplicate]当 static_cast 和 reinterpret_cast 具有相同的效果时使用哪一个? [复制]
【发布时间】:2010-11-14 17:28:20
【问题描述】:

可能重复:
Should I use static_cast or reinterpret_cast when casting a void* to whatever

通常,尤其是在 Win32 编程中,需要从一种不透明类型转换为另一种。例如:

 HFONT font = cast_here<HFONT>( ::GetStockObject( SYSTEM_FONT ) );

static_cast 和 reinterpret_cast 在这里都适用并且具有完全相同的效果,因为 HFONT 是一个指向专门用于定义 HFONT 的虚拟结构的指针,而 GetStockObject() 返回的 HGDIOBJ 是一个 void* 指针。

哪一个 - static_cast 或 reinterpret_cast - 更可取?

【问题讨论】:

标签: c++ winapi casting


【解决方案1】:

说演员表会产生相同的效果是不正确的。演员们做了两件完全不同的事情:

  • static_cast&lt;T&gt;(x) 表示将表达式 x 转换为类型 T
  • reinterpret_cast&lt;T*&gt;(&amp;x) 表示将内存位置 '&x' 解释为 T*

考虑以下几点:

struct A1 { int a1; };
struct A2 { int a2; };
struct B : public A1, public A2 {};

void foo (A1 * a1, A2 * a2)
{
  B * b1_a1 = static_cast<B*> (a1);
  B * b2_a1 = reinterpret_cast<B*> (a1);
  B * b1_a2 = static_cast<B*> (a2);
  B * b2_a2 = reinterpret_cast<B*> (a2);

  std::cout << "b1_a1==b1_a2" << (b1_a1==b1_a2) << std::endl;
  std::cout << "b2_a1==b2_a2" << (b2_a1==b2_a2) << std::endl;
}

int main ()
{
  B b;
  foo (&b, &b);
}

此程序产生以下输出:

g++ -o t t.cc ; ./t
b1_a1==b1_a2: 1
b2_a1==b2_a2: 0

这显示了从a2b2static_cast 如何正确调整指针,使其指向b 的开头,但reinterpret_cast 没有。

【讨论】:

  • 这是一个很好的例子。
  • 是的,但问题是关于从 void* 投射。另请注意,使用 static_cast 向下转换是可以的,但向上转换(远离基类)可能更适合使用 dynamic_cast
  • @Martin:问题标题的措辞非常笼统,关于演员何时具有相同的行为。细节可能会使用 'void*' 案例作为这种情况的示例,但如果这是这个问题的主要动机,那么它应该作为 stackoverflow.com/questions/310451 的完全相同的副本关闭。
【解决方案2】:

static_cast 总是首选,除非绝对必要,否则避免使用reinterpret_casts。

reinterpret_cast 是最不安全的类型转换。

更多信息请访问this website,了解何时应使用哪些演员表。

【讨论】:

  • 我讨厌这种笼统的说法,这不是真的。 static_cast 仅在进行应该使用的强制转换时才更可取。 reinterpret_cast 应该用于所有危险的强制转换,以便您可以在代码中快速找到它们。两个指针类型之间的转换(如果它们不相关)是一个 reinterpret_cast 操作。如果存在关系,那么 static_cast 可能更合适。在这个问题的上下文中,我会使用 reinterpret_cast,因为你是从 void* 进行投射的。使用 reinterpret_cast 可以很好地指出这是一个潜在的危险演员表(确实如此)。
  • 好的,那么 static_cast 可以用于那些不安全的转换? static_cast 用于具有某种已定义含义的强制转换,这通常意味着相当安全。
【解决方案3】:

由于 reinterpret_cast 是实现定义的,所以我的经验法则是:“我可以使用 static_cast,必须使用 reinterpret_cast”。

(其他弃用)

【讨论】:

    【解决方案4】:

    每个人都注意到 reinterpret_cast 比 static_cast 更危险。
    这是因为 reinterpret_cast 忽略了所有类型信息,只是分配了一个新类型而没有任何实际处理,因此完成的处理是实现定义的(尽管通常指针的位模式是相同的)。

    每个人都没有提到的是 reinterpret_cast 是一种记录程序的方法。它告诉阅读代码的人,我们不得不妥协某些东西,结果我们得到了一个危险的演员表,当你弄乱这段代码时要小心。

    使用 reinterpret_cast 突出代码中的这些危险区域。

    从 void* 进行转换时,没有类型信息可供转换使用。
    因此,您要么进行无效转换,要么转换回之前转换为 void* 的原始类型。任何其他类型的强制转换都会以一些未定义的行为结束。

    这是使用 reinterpret_cast 作为标准的完美情况,可以保证将指针转换为 void* 并使用 reinterpret_cast 转换回其原始类型。通过使用 reinterpret_cast,您可以向后面出现的人们指出这里正在发生一些不好的事情。

    【讨论】:

    • 对于一些反点(如 Shmoopty 所指出的),请查看之前的问题 stackoverflow.com/questions/310451。我喜欢 static_cast 更具限制性的观点,因此即使在这些危险情况下也可能会发现错误。
    • @Richard:在没有类型信息的情况下如何发现错误。 void* 是一个类型所能拥有的尽可能少的信息。同样在您指定的文章中,答案中的 cmets 处理通过 void* 从已知类型 (T*) 转换为不同已知类型 (U*) 的问题。在这种情况下,我完全同意您不应该为此使用 reinterpret_cast。但是从 (T*) 到 void* 再转换回 (T*) 是 reinterpret_cast 的用途。
    • @Martin:使用 reinterpret_cast 可以转换的指针类型集合大于使用 static_cast 的集合。如果某些代码的修改改变了类型,使得 'void*' 不再是 to 或 from 类型,那么在 reinterpret_cast 情况下,代码将默默地继续编译(但行为未定义),而在 static_cast 情况下,代码将导致编译错误。恕我直言,后者更可取。
    • @Richard:我明白你的意思(我喜欢编译失败)。但是您的论点是基于代码中的更改,其中 reinterpret_cast 更改为 reinterpret_cast。这显然是错误的;当您更改类型时,您还应该更改使用的演员表。 reinterpret_cast 的重点是指出您需要(同时)更改类型和演员表并使其易于查找(使用自动化工具)。使用静态转换可以保护您免受这种潜在(但不太可能出现的问题)的影响,但隐藏了确实存在需要修复的危险代码的事实。
    • 我能看到的唯一问题是当它与模板相关联时,因此允许类型更加多变而不容易暴露演员表。但是,如果您有使用 reinterpret_cast 来回转换的模板化代码,我认为您与其他开发人员会遇到更大的问题。
    【解决方案5】:

    static_cast 是首选。

    来自 Scott Meyers 的《更有效的 C++》

    这个操作符 [reinterpret_cast] 用于执行类型 结果几乎是的转换 始终由实现定义。作为一个 结果,reinterpret_casts 很少 便携。

    reinterpret_cast 最常见的用途是在函数指针类型之间进行转换。

    【讨论】:

    • 但是,标准保证将对象从指针转换为 void*,然后再转换回相同的指针类型,以便使用 uisng reinterpret_cast。所以这点并不成立。
    • 但是在一般情况下是首选 static_cast。
    【解决方案6】:

    我认为 static_cast 会更好。这里是another stack overflow post,对此也有更多讨论。

    【讨论】:

      【解决方案7】:

      您真的应该按照 win32 文档中所说的正确做法去做。这是最有可能工作并且最有可能向前兼容的方法,除非我想它的作者完全无知。

      更新:我已经查过了。 Win32 文档使用 C 样式转换。在这种情况下,C++ 定义了一个 C 样式转换来执行 static cast。所以正确答案是:使用static_cast。或 C 风格的演员表,但通常最好避免使用。

      【讨论】:

      • 值得记住的是,MSDN 也适用于 C 开发人员,这可能是它使用 C 转换的原因。另外,MSDN 使用通常被认为有害的匈牙利符号。所以我不会仅仅因为这是 MSDN 所做的事情。
      • 这不是一个有效的类比。您使用的变量名称取决于您,因为它与接口无关(当然,除了避免它们用于宏的名称)。但是你必须以一种与他们使用的演员兼容的方式进行投射。显然,我只是在谈论 win32 函数的返回值,而不是一般情况。
      【解决方案8】:

      我的一般建议:

      reinterpret_cast&lt;&gt; 只能作为最后的手段使用。您是在告诉编译器放弃所有谨慎,“删除所有安全措施”,并相信您所说的真的没问题,无论编译器对代码了解多少。你应该只在其他方法都不起作用时才使用它。

      在某些地方它们可能会产生相同的效果如果你的代码是正确的。如果您最终进行更改并引入错误并且演员表不再正确,static_cast&lt;&gt; 可能会抓住它,但reinterpret_cast&lt;&gt; 肯定不会。

      在这个非常特殊的场景中,最后一个论点不成立:API 将语义强加于您;它们是很好理解的语义;并且您正在投射 (void *)。

      不过,我会使用static_cast&lt;&gt;,而将reinterpret_cast&lt;&gt; 留给真正特殊情况迫使我绕过类型安全的地方。由于 API 的 C 兼容特性,这是必需的“正常”转换。

      (有关 static_cast 和 reinterpret_cast 之间差异的更多详细信息,另请参阅 my post here

      【讨论】:

        猜你喜欢
        • 2011-10-14
        • 2011-05-27
        • 2021-11-10
        • 2013-10-18
        • 2010-09-24
        • 2019-03-16
        • 2015-04-07
        相关资源
        最近更新 更多