Может ли указатель на базовую точку указывать на массив производных объектов?
Сегодня я отправился на собеседование и получил этот интересный вопрос.
Помимо утечки памяти и того факта, что виртуального 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
Вы не можете индексировать это. Вы выделили массив Rectangles
и сохранили указатель на первый в shapes
. Когда вы выполняете shapes[1]
, вы разыгрываете (shapes + 1)
. Это не даст вам указателя на следующий Rectangle
, но указатель на то, что будет следующим Shape
в предполагаемом массиве Shape
. Конечно, это поведение undefined. В вашем случае вам повезет и вы получите крах.
Использование указателя на Rectangle
делает работу индексации корректной.
int main()
{
Rectangle * shapes = new Rectangle[10];
for (int i = 0; i < 10; ++i) shapes[i].draw();
}
Если вы хотите иметь разные типы Shape
в массиве и использовать их полиморфно, вам нужен массив указателей на Shape.
Ответ 2
Как сказал Мартиньо Фернандес, индексирование неверно. Если бы вы захотели сохранить массив 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, поскольку инициализация массива задает только указатели, а не сами объекты.
Ответ 3
При индексировании указателя компилятор добавит соответствующую сумму в зависимости от размера того, что находится внутри массива. Так скажите, что sizeof (Shape) = 4 (поскольку он не имеет переменных-членов). Но sizeof (Rectangle) = 12 (точные числа, скорее всего, неверны).
Итак, когда вы начинаете индексировать, начиная с say... 0x0 для первого элемента, тогда, когда вы пытаетесь получить доступ к 10-му элементу, вы пытаетесь перейти к недопустимому адресу или местоположению, а не к началу объекта.