指向base的指针可以指向派生对象数组吗?

时间:2011-08-25 19:36:15

标签: c++ pointers

我今天去了一份面试,并得到了这个有趣的问题。

除了内存泄漏以及没有虚拟dtor之外,为什么这段代码会崩溃?

#include <iostream>

//besides the obvious mem leak, why does this code crash?

class Shape
{
public:
    virtual void draw() const = 0;
};

class Circle : public Shape
{
public:
    virtual void draw() const { }

    int radius;
};

class Rectangle : public Shape
{
public:
    virtual void draw() const { }

    int height;
    int width;
};

int main()
{
    Shape * shapes = new Rectangle[10];
    for (int i = 0; i < 10; ++i)
        shapes[i].draw();
}

3 个答案:

答案 0 :(得分:149)

你不能那样索引。您已经分配了一个Rectangles数组,并在shapes中存储了指向第一个数组的指针。执行shapes[1]后,您将取消引用(shapes + 1)。这不会给你指向下一个Rectangle的指针,而是指向假定数组Shape中下一个Shape的指针。当然,这是未定义的行为。在你的情况下,你很幸运并且崩溃了。

使用指向Rectangle的指针使索引工作正常。

int main()
{
   Rectangle * shapes = new Rectangle[10];
   for (int i = 0; i < 10; ++i) shapes[i].draw();
}

如果你想在数组中使用不同种类的Shape并以多态方式使用它们,你需要一个指针数组来形成。

答案 1 :(得分:36)

正如Martinho Fernandes所说,索引是错误的。如果你想要存储一个Shapes数组,你必须使用Shape *的数组,如下所示:

int main()
{
   Shape ** shapes = new Shape*[10];
   for (int i = 0; i < 10; ++i) shapes[i] = new Rectangle;
   for (int i = 0; i < 10; ++i) shapes[i]->draw();
}

请注意,您必须执行额外的初始化Rectangle步骤,因为初始化数组只会设置指针,而不是对象本身。

答案 2 :(得分:13)

索引指针时,编译器将根据数组内部的大小添加适当的数量。所以说sizeof(Shape)= 4(因为它没有成员变量)。但sizeof(Rectangle)= 12(确切数字可能有误)。

因此,当您从第一个元素的起始位置说起... 0x0时,那么当您尝试访问第10个元素时,您将尝试转到无效地址或不是该对象开头的位置。 / p>