什么时候应该使用 static_cast、dynamic_cast、const_cast 和 reinterpret_cast?

新手上路,请多包涵
阅读 1k
2 个回答

static_cast 是您应该尝试使用的第一个演员表。它执行诸如类型之间的隐式转换(例如 intfloat 或指向 void* 的指针)之类的事情,并且它还可以调用显式函数转换(或隐式转换)那些)。在许多情况下,不需要明确声明 static_cast ,但重要的是要注意 T(something) 语法等同于 (T)something 并且应该避免后来)。然而, T(something, something_else) 是安全的,并保证调用构造函数。

static_cast 也可以通过继承层次结构进行转换。向上投射(朝向基类)时没有必要,但向下投射时,只要不通过 virtual 继承就可以使用。但是,它不进行检查,并且 static_cast 将层次结构向下到实际上不是对象类型的类型是未定义的行为。


const_cast 可用于删除或添加 const 到变量;没有其他 C++ 演员能够删除它(甚至 reinterpret_cast )。需要注意的是,只有当原始变量为 const 时,修改以前的 const 值才是未定义的;如果你用它来 const 对没有用 const 声明的东西的引用,它是安全的。例如,当基于 const 重载成员函数时,这可能很有用。也可以用来给对象添加 const ,比如调用成员函数重载。

const_cast 也适用于 volatile ,尽管这种情况不太常见。


dynamic_cast 专门用于处理多态性。您可以将指向任何多态类型的指针或引用强制转换为任何其他类类型(多态类型至少有一个声明或继承的虚函数)。您不仅可以将其用于向下投射 - 您还可以向侧面投射,甚至可以向上投射另一条链。 dynamic_cast 将寻找所需的对象并在可能的情况下返回它。如果不能,它将返回 nullptr 在指针的情况下,或抛出 std::bad_cast 在引用的情况下。

dynamic_cast 但是有一些限制。如果继承层次结构中有多个相同类型的对象(所谓的“可怕的菱形”)并且您没有使用 virtual 继承,则它不起作用。它也只能通过公共继承 - 它总是无法通过 protectedprivate 继承。然而,这很少成为问题,因为这种继承形式很少见。


reinterpret_cast 是最危险的演员,应该非常谨慎地使用。它将一种类型直接转换为另一种类型——例如将值从一个指针转换为另一种,或将指针存储在 int 或各种其他讨厌的事情中。很大程度上,您使用 reinterpret_cast 获得的唯一保证是,通常如果您将结果转换回原始类型,您将获得完全相同的值(但如果中间类型小于原始类型,则 不会) .有许多转换是 reinterpret_cast 也不能做的。它主要用于特别奇怪的转换和位操作,例如将原始数据流转换为实际数据,或将数据存储在指向对齐数据的指针的低位中。


C 风格 转换和 函数风格 转换分别使用 (type)objecttype(object) 进行转换,并且在功能上是等效的。它们被定义为以下成功的第一个:

  • const_cast
  • static_cast (尽管忽略访问限制)
  • static_cast (见上文),然后 const_cast
  • reinterpret_cast
  • reinterpret_cast ,然后 const_cast

因此,在某些情况下,它可以用作其他演员表的替代品,但由于能够演变为 reinterpret_cast ,因此可能非常危险,并且在需要显式转换时应首选后者,除非您确定 static_cast 会成功或 reinterpret_cast 会失败。即便如此,考虑更长、更明确的选项。

在执行 static_cast 时,C 样式转换也会忽略访问控制,这意味着它们能够执行其他转换无法执行的操作。不过,这主要是一个杂物,在我看来,这只是避免 C 风格转换的另一个原因。

原文由 coppro 发布,翻译遵循 CC BY-SA 4.0 许可协议

让我们看看 reinterpret_caststatic_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

注意输出 12 是不同的,以及 34 。这是为什么?其中一个是 static_cast 而另一个是 reinterpret_cast 在这两种情况下都是相同类型的相同输入。

情况可以用下图形象化:

可视化

C 包含一个 B 但 --- 的起始地址与 B C static_cast 正确计算 BC —内的地址。但是 reinterpret_cast 返回我们作为输入提供的相同地址,这对于这种情况是不正确的:在该地址没有 B

但是,当在 AC 指针之间进行转换时,两种转换都返回相同的结果,因为它们恰好从相同的位置开始,顺便说一句,标准并不能保证。

原文由 Özgür Murat Sağdıçoğlu 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题