¿Puede un puntero a la base apuntar a una matriz de objetos derivados?

99

Fui a una entrevista de trabajo hoy y me hicieron esta interesante pregunta.

Además de la pérdida de memoria y el hecho de que no existe un dtor virtual, ¿por qué falla este código?

#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();
}
Tony el león
fuente
1
Además del punto y coma que falta, ¿quieres decir? (Sin embargo, eso sería un error en tiempo de compilación, no en tiempo de ejecución)
Platinum Azure
¿Estás seguro de que eran todos virtuales?
Yochai Timmer
8
Debería estar Shape **apuntando a una matriz de rectángulos. Entonces el acceso debería haber sido formas [i] -> dibujar ();
RedX
2
@Tony buena suerte entonces, mantennos informados :)
Seth Carnegie
2
@AndreyT: El código es correcto ahora (y también era correcto originalmente). El ->fue un error cometido por un editor.
R. Martinho Fernandes

Respuestas:

150

No se puede indexar así. Ha asignado una matriz de Rectanglesy almacenado un puntero al primero en shapes. Cuando lo hace shapes[1], está desreferenciando (shapes + 1). Esto no le dará un puntero al siguiente Rectangle, sino un puntero a lo que sería el siguiente Shapeen una supuesta matriz de Shape. Por supuesto, este es un comportamiento indefinido. En tu caso, estás teniendo suerte y tienes un accidente.

El uso de un puntero Rectanglehace que la indexación funcione correctamente.

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

Si desea tener diferentes tipos de Shapes en la matriz y usarlos polimórficamente, necesita una matriz de punteros a Shape.

R. Martinho Fernandes
fuente
37

Como dijo Martinho Fernandes, la indexación está mal. Si, en cambio, quisiera almacenar una matriz de formas, tendría que hacerlo utilizando una matriz de formas *, así:

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();
}

Tenga en cuenta que debe realizar un paso adicional para inicializar el rectángulo, ya que al inicializar la matriz solo se configuran los punteros y no los objetos en sí.

Patrick Costello
fuente
13

Al indexar un puntero, el compilador agregará la cantidad adecuada según el tamaño de lo que se encuentra dentro de la matriz. Entonces, digamos que sizeof (Shape) = 4 (ya que no tiene variables miembro). Pero sizeof (Rectángulo) = 12 (es probable que los números exactos sean incorrectos).

Entonces, cuando indexa comenzando en digamos ... 0x0 para el primer elemento, cuando intenta acceder al décimo elemento, está tratando de ir a una dirección no válida o una ubicación que no es el comienzo del objeto.

Jonathan Sternberg
fuente
1
Como no adepto a C ++, mencionar SizeOf () me ayudó a entender qué es @R. Martinho decía en su respuesta.
Marjan Venema