【问题标题】:Multiple inheritance cast not working as expected多重继承强制转换未按预期工作
【发布时间】:2016-10-18 07:40:13
【问题描述】:

我最近遇到了转换和多重继承的问题:我需要将Base* 转换为Unrelated*,因为特定的Derived 类派生了Unrelated 类。

这是一个简短的例子:

#include <iostream>

struct Base{
    virtual ~Base() = default; 
};

struct Unrelated{
    float test = 111;    
};

struct Derived : Base,Unrelated{};

int main(){
    Base* b = new Derived;
    Unrelated* u1 = (Unrelated*)b;
    std::cout << u1->test << std::endl; //outputs garbage
    Unrelated* y = dynamic_cast<Unrelated*>(b);
    std::cout << y->test << std::endl; //outputs 111
}

第一个演员显然不起作用,但第二个演员确实起作用了。 我的问题是:为什么第二个演员起作用了? dynamic_cast 不应该只适用于转换为相关的类类型吗?我认为在运行时没有关于 Unrelated 的任何信息,因为它不是多态的。

编辑:我使用 colirus gcc 作为示例。

【问题讨论】:

  • 您在 b 中保留指向 Derived 实例的指针,并将其转换为您继承的 Unrelated。有什么问题?
  • UnrelatedDerived 的基数,而您正在从Derived* 向上转换为Unrelated*,这就是它有效的原因。
  • 呃。 C 风格的演员表。不要使用它们。改用正确的 C++ 风格转换 - 总是。

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


【解决方案1】:

dynamic_cast 起作用是因为其基类的指针所指向的对象的动态类型Unrelated 相关。

请记住,dynamic_cast 需要一个虚拟表来在运行时检查对象的继承树。

C 样式转换 (Unrelated*)b 不起作用,因为 the C-style cast does const_cast, static_cast, reinterpret_cast and more, but it does not do dynamic_cast

我建议在 C++ 代码中避免 C 风格的强制转换,因为与精确的 C++ 强制转换相比,它们做了很多事情。坚持使用 C 风格转换的同事仍然偶尔会出错。

【讨论】:

    【解决方案2】:

    第一次转换 (Unrelated*)b 不起作用,因为您将 Base 类子对象(可能仅包含一个 vtable 指针)视为 Unrelated,其中包含一个 float

    相反,您可以上下投射,static_cast&lt;Unrelated*&gt;( static_cast&lt;Derived*&gt;( b ) )

    这就是dynamic_cast 为您所做的,因为Base 是一种多态类型(至少一个虚拟方法),它允许dynamic_cast 检查最派生对象的类型。

    顺便说一句,dynamic_cast&lt;void*&gt;( b ) 会给你一个指向最派生对象的指针。

    但是,由于您知道类型,因此无需调用 dynamic_cast 的轻微开销:只需执行向下和向上转换即可。


    而不是 C 样式转换 (Unrelated*)b 您应该使用相应的 C++ 命名转换 或转换,因为 C 样式指针转换可以做你意想不到的事情,因为在维护期间更改类型时效果会完全改变。

    C 风格转换最多可以执行 2 个 C++ 命名转换。在这种情况下,C 样式转换对应于reinterpret_cast。编译器将不允许在此处进行任何其他命名转换。

    这是一个警告信号。 ;-)

    相比之下,向下和向上转换是static_casts,通常是良性转换。


    话虽如此,最好的办法是使用绝密技术几乎完全避免强制转换:

    首先不要丢弃类型信息。

    即,在示例代码中,只需使用Derived* 作为指针的类型。

    【讨论】:

    • 虽然我完全同意你的最后一句话,但这也意味着不要做多态性
    【解决方案3】:

    对于多重继承,Derived 对象由两个子对象组成,一个Base 和一个Unrelated。编译器知道如何访问它需要的对象的任何部分,通常是通过向指针添加偏移量(但这是一个实现细节)。当它知道指针的实际类型时,它只有可以这样做。通过使用 C 风格的强制转换,您已经告诉编译器忽略实际类型并将该指针值视为指向其他内容的指针。它不再具有正确访问所需子对象所需的信息,因此失败了。

    dynamic_cast 允许编译器使用有关对象的运行时信息来定位其中包含的正确子对象。如果您要输出或检查指针值本身,您会发现它们是不同的。

    【讨论】:

      猜你喜欢
      • 2019-06-10
      • 1970-01-01
      • 2011-11-17
      • 2017-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多