为什么我必须在这里使用 dynamic_cast

jah*_*ber 5 c++ dynamic-cast casting multiple-inheritance reinterpret-cast

我注意到,如果我在下面的代码中使用 C 风格的转换(或 reinterpret_cast),我会得到一个分段错误异常,但如果我使用 a dynamic_cast,那就没问题了。为什么是这样?因为我确定指针 a 是 B 类型,因为该Add方法已经确保输入是 B 类型。

dynamic_cast即使我已经通过我的实现保证指针 a 是 B 类型,我是否必须在这里使用?

编辑:

我确实意识到使用 C 样式转换(或 reinterpret_cast)通常是一种不好的做法。但是对于这种特殊情况,为什么它们不起作用。

这在实践中是有应用的,因为如果类 B 是一个接口,而类 D 由于某种原因被迫存储类型 A 的指针。当实现已经保证了接口类型的类型安全时,这里强制使用动态转换。

#include <iostream>

using namespace std;

class A
{
    public:
    virtual ~A() = default;
};

class B
{
    public:
    virtual string F() = 0;
};

class C : public A, public B
{
    public:
    virtual ~C() = default;
    virtual string F() { return "C";}
};

class D
{
    public:

    D() : a(nullptr) {}

    void Add(B* b)
    {
        A* obj = dynamic_cast<A*>(b);
        if(obj != nullptr)
            a = obj;
    }

    B* Get()
    {
        return (B*)(a); // IF I USE DYNAMIC CAST HERE, IT'D BE OK
    }

    private:
    A* a;
};

int main()
{
    D d;
    d.Add(new C());

    B* b = d.Get();
    if(b != nullptr)
        cout << b->F();
}
Run Code Online (Sandbox Code Playgroud)

Gui*_*cot 4

tl;dr:c 风格的强制转换很狡猾,很容易引入错误。

那么这个表达式中发生了什么?

class A
{
    public:
    virtual ~A() = default;
};

class B
{
    public:
    virtual string F() = 0;
};

B* Get()
{
    return (B*)(a);
}
Run Code Online (Sandbox Code Playgroud)

请注意,AB是不相关的。

如果您使用适当的static_cast替代品会怎样?

B* Get()
{
    return static_cast<B*>(a);
}
Run Code Online (Sandbox Code Playgroud)

然后您将看到正确的诊断:

error: invalid 'static_cast' from type 'A*' to type 'B*'
            return static_cast<B*>(a);
                   ^~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

不好了

reinterpret_cast事实上,当静态方法无法完成时,c 风格会进行后备处理。所以你的代码相当于:

B* Get()
{
    return reinterpret_cast<B*>(a);
}
Run Code Online (Sandbox Code Playgroud)

这不是你想要的。这不是您要找的演员。

A对象与子对象有不同的地址B,主要是为了给vtable腾出地方。

到底在这里做什么reinterpret_cast

真的不多。它只是告诉编译器将发送给它的内存地址解释为另一种类型。仅当您要求的类型在该地址具有生命周期时,它才有效。在你的情况下,这不是真的,A那个地方有一个对象,B你的对象的一部分在内存的其他地方。

静态转换将调整指针以确保它指向该类型在内存中的正确偏移量,如果无法计算偏移量则无法编译。

C* c = new C();
cout << c;
cout << "\n";

A* a = dynamic_cast<A*>(c);
cout << a;
cout << "\n";

B* b = dynamic_cast<B*>(c);
cout << b;
cout << "\n";
Run Code Online (Sandbox Code Playgroud)

会给你类似的东西:

0xbe3c20
0xbe3c20
0xbe3c28
Run Code Online (Sandbox Code Playgroud)

那你能做什么呢?

如果您想使用静态强制转换,则必须执行此操作,因为这是编译器可以看到和C之间关系的唯一位置:AB

B* Get()
{
    return static_cast<B*>(static_cast<C*>(a));
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您不知道C该对象是否a指向的运行时类型,那么您必须使用dynamic_cast.

  • @jahithber 不准确。根据标准,无法保证此属性,并且可能(并且将会!)因平台而异。一般建议如下:不要使用重新解释的强制转换,也不要使用 c 风格的强制转换。C 风格只是为了兼容性,不应该使用。reinterpret 有一些用例,但通常这些用例可以通过多个静态强制转换的组合来替换。因此,不要使用强制类型转换,否则您会看到此类错误再次出现。 (2认同)