【问题标题】:dynamic_cast segfault with RTTI enabled启用 RTTI 的 dynamic_cast 段错误
【发布时间】:2014-06-12 14:46:21
【问题描述】:

我很好奇 dynamic_cast 如何将数组的元素转换为更大的类(它会转移所有其他元素吗?)。所以我写了一个小代码来尝试一下。但是当它编译但第一行出现段错误时,我感到很惊讶。为什么?

#include <iostream>

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

class B : public A
{
public:
    int x;
};

class C : public A
{
public:
    int x;
    int y;
};

int main()
{
    A* aArray = new B[2];
    (dynamic_cast<B&>(aArray[0])).x = 1; //segfault here
    (dynamic_cast<B&>(aArray[1])).x = 2;

    (dynamic_cast<C&>(aArray[0])).y = 3;

    std::cout << (dynamic_cast<B&>(aArray[1])).x << std::endl;

    return 0;
}

【问题讨论】:

  • Mine 在(dynamic_cast&lt;B&amp;&gt;(aArray[1])).x = 2; 崩溃,这是有道理的,因为 &aArray1[1]=aArray+sizeof(A)... 但是 sizeof(A)
  • 不要使用多态数组!
  • 不要使用多态类型的数组。指针运算会产生不正确的行为。
  • @jsantander:按照这个推理,您可以将其发布为答案。

标签: c++ rtti dynamic-cast


【解决方案1】:

我的第一个猜测是(dynamic_cast&lt;B&amp;&gt;(aArray[1])).x = 2; 将/应该抛出异常,因为aArray[1]不能被向下转换....

假设我会毫无例外地工作,否则你会使用static_cast&lt;&gt;,你肯定会破坏你的记忆!

想象一下:sizeof(A) = 4 字节; sizeof(B) = 8 字节

之后的记忆

A* aArray = new B[2];

[-----B------|-----B-----] 内存

[-----0------|------1-----] 索引

您正在将其保存到 A*。那么如果你这样做会发生什么

(static_cast<B&>(aArray[1])).x = 2; 

[[--A--]B[--A--]]|----B--]

[-------x--------|--- 你开始在这个位置写内存。

【讨论】:

    【解决方案2】:

    我来了。我用 gdb 编译运行

    首先我设置打印对象选项:

    (gdb) set print object
    

    检查数组的地址

    (gdb) print aArray
    $1 = (B *) 0x8003a404
    

    检查A和B的大小

    (gdb) print sizeof(B)
    $2 = 8
    (gdb) print sizeof(A)
    $3 = 4
    

    获取aArray[0]的地址

    (gdb) print &aArray[0]
    $4 = (B *) 0x8003a404
    

    获取aArray[1]的地址

    (gdb) print &aArray[1]
    $5 = (A *) 0x8003a408
    

    引用answer in linked question

    如果您查看表达式p[1]pBase*(Base 是完全定义的类型)并且 1 是 int,因此根据 ISO/IEC 14882:2003 5.2.1 [expr .sub] 此表达式有效且等同于*((p)+(1))

    从 5.7 [expr.add] / 5 开始,当一个整数被加到一个指针上时,只有当指针指向一个数组对象的元素并且指针运算的结果也指向一个该数组对象的元素或数组末尾的元素。然而,p 并不指向数组对象的元素,它指向派生对象的基类子对象。数组成员是 Derived 对象,而不是 Base 子对象。

    在这种特殊情况下,指针算法的实现是将指针内存增加指针类型的大小(但请参阅此answer 了解更多细微差别)。

    aArray + 1 的作用是指向 A 类型对象数组的第二个元素

    匹配:

    (gdb) print (A*)(((char *)aArray) + sizeof(A))
    $6 = (A *) 0x8003a408
    

    ...然而aArray 实际上是一个 B 类型的对象数组。

    所以数组的第二个元素是:

    (gdb) print &((B *)aArray)[1]
    $6 = (B *) 0x8003a40c
    

    所以你最终指向了第一个 B 对象中间的某个地方......并且该访问导致了分段错误。

    有关另一种解释,请参阅示例:[http://www.parashift.com/c++-faq/array-derived-vs-base.html]

    【讨论】:

      猜你喜欢
      • 2011-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-16
      • 1970-01-01
      相关资源
      最近更新 更多