启用RTTI的dynamic_cast段错误

时间:2014-06-12 14:46:22

标签: c++ rtti dynamic-cast

我很好奇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;
}

2 个答案:

答案 0 :(得分:3)

我的第一个猜测是(dynamic_cast<B&>(aArray[1])).x = 2; 将/应抛出异常,因为aArray[1]无法下载....

让我说我会毫无例外地工作,或者你会使用static_cast<>代替,你肯定会损害你的记忆!

想象: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 - ]] | ---- -----乙

[------- x -------- | ---你开始在这个位置上写下记忆。

答案 1 :(得分:2)

我走了。我用gdb编译并运行

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

(gdb) set print object

检查aArray的地址

(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对象的基类子对象。 Derived对象是一个数组成员,而不是Base子对象。

在这种特殊情况下,指针算法的实现是通过指针类型的大小来增加指针内存(但请参阅此answer以获得其他细微差别)。

aArray + 1的效果是指向A类对象数组的第2个元素

匹配:

(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]