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


99

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

除了内存泄漏和没有虚拟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();
}

1
除了缺少分号,您是什么意思?(虽然这是一个编译时错误,但不是运行时)
Platinum Azure

您确定它们都是虚拟的吗?
Yochai Timmer

8
它应该Shape **指向矩形数组。然后访问应该是shapes [i]-> draw();。
RedX 2011年

2
@Tony祝您好运,然后通知我们:)
赛斯·卡内基

2
@AndreyT:该代码现在是正确的(并且最初也是正确的)。这->是编辑犯的一个错误。
R. Martinho Fernandes

Answers:


150

您不能那样索引。您已分配的数组Rectangles并存储了指向in中第一个的指针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在数组中具有不同种类的s并多态使用它们,则需要一个指向 Shape 的指针数组。


37

正如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,因为初始化数组仅设置指针,而不设置对象本身。


13

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

因此,当您从第一个元素的索引开始说... 0x0时,然后当您尝试访问第10个元素时,您将尝试去一个无效地址或不是对象开始的位置。


1
作为非c ++专家,提到SizeOf()有助于我理解@R。马丁尼奥在回答中说。
Marjan Venema
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.