我今天去了一份面试,并得到了这个有趣的问题。
除了内存泄漏以及没有虚拟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();
}
答案 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>