Un puntatore al punto base può puntare a un array di oggetti derivati?


99

Oggi sono andato a un colloquio di lavoro e mi è stata posta questa domanda interessante.

Oltre alla perdita di memoria e al fatto che non esiste un dtor virtuale, perché questo codice si blocca?

#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
Oltre al punto e virgola mancante, intendi? (Sarebbe un errore in fase di compilazione, però, non runtime)
Platinum Azure

Sei sicuro che fossero tutti virtuali?
Yochai Timmer

8
Dovrebbe essere Shape **che punta a una matrice di rettangoli. Quindi l'accesso avrebbe dovuto essere forme [i] -> draw ();
RedX

2
@ Tony in bocca al lupo allora, tienici informati :)
Seth Carnegie

2
@AndreyT: il codice ora è corretto (ed era corretto anche in origine). È ->stato un errore commesso da un editore.
R. Martinho Fernandes

Risposte:


150

Non puoi indicizzare in questo modo. Hai assegnato un array di Rectanglese memorizzato un puntatore al primo in shapes. Quando lo fai shapes[1], stai dereferenziando (shapes + 1). Questo non ti darà un puntatore al prossimo Rectangle, ma un puntatore a quello che sarebbe il prossimo Shapein un presunto array di Shape. Naturalmente, questo è un comportamento indefinito. Nel tuo caso, sei fortunato e stai subendo un incidente.

L'uso di un puntatore a Rectanglefa sì che l'indicizzazione funzioni correttamente.

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

Se vuoi avere diversi tipi di messaggi Shapenell'array e usarli in modo polimorfico, hai bisogno di un array di puntatori a Shape.


37

Come ha detto Martinho Fernandes, l'indicizzazione è sbagliata. Se invece volessi memorizzare un array di Shapes, dovresti farlo usando un array di Shape *, in questo modo:

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

Notare che è necessario eseguire un passaggio aggiuntivo per inizializzare il rettangolo, poiché l'inizializzazione dell'array imposta solo i puntatori e non gli oggetti stessi.


13

Durante l'indicizzazione di un puntatore, il compilatore aggiungerà la quantità appropriata in base alla dimensione di ciò che si trova all'interno dell'array. Quindi supponiamo che sizeof (Shape) = 4 (poiché non ha variabili membro). Ma sizeof (Rectangle) = 12 (i numeri esatti sono probabilmente sbagliati).

Quindi, quando indicizzi iniziando da diciamo ... 0x0 per il primo elemento, quando provi ad accedere al decimo elemento stai cercando di andare a un indirizzo non valido o a una posizione che non è l'inizio dell'oggetto.


1
In quanto non esperto di c ++, menzionare SizeOf () mi ha aiutato a capire cosa @R. Martinho stava dicendo nella sua risposta.
Marjan Venema
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.