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

标签 c++ casting multiple-inheritance dynamic-cast reinterpret-cast

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

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

编辑:

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

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

#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();
}

最佳答案

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

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

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

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

B* Get()
{
    return (B*)(a);
}

请注意,AB 不相关。

如果您使用正确的 static_cast 会怎样?

B* Get()
{
    return static_cast<B*>(a);
}

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

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

哦不

事实上,当静态转换无法完成时,c 风格会使用 reinterpret_cast 进行回退。所以你的代码相当于:

B* Get()
{
    return reinterpret_cast<B*>(a);
}

这不是你想要的。这不是您要找的 Actor 。

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

What exactly is reinterpret_cast doing here?

确实不多。它只是告诉编译器将发送给它的内存地址解释为另一种类型。仅当您要求的类型在该地址具有生命周期时,它才有效。在您的情况下,情况并非如此,该位置有一个 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";

会给你类似的东西:

0xbe3c20
0xbe3c20
0xbe3c28

那你能做什么?

如果您想使用静态转换,则必须通过 C,因为它是编译器可以看到 A 之间关系的唯一位置B:

B* Get()
{
    return static_cast<B*>(static_cast<C*>(a));
}

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

关于c++ - 为什么我必须在这里使用dynamic_cast,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60119947/

相关文章:

python - 如何在 Python 中找到继承变量的来源?

c++ - 使用 "using declaration"扩展非类型模板参数包(模板可变参数编译时 SignalSlot 实现)

python - 如何在python中使用子类的方法调用第二个父类的方法?

c++ - 填充张量的示例代码?

c++ - 编译器找到头文件但找不到 cpp 文件?

scala - 这个流的类型是如何转换的?

java - Java 泛型的类型别名

java.lang.ClassCastException : java. lang.Double 无法转换为 java.lang.String

c++ - 替换(或重新实现?)std::function 用于一些统计和测试

c++ - 二叉搜索树 - 实现一个 "search"函数