Czy wskaźnik do bazy może wskazywać na tablicę obiektów pochodnych?


99

Poszedłem dziś na rozmowę kwalifikacyjną i zadano mi to interesujące pytanie.

Oprócz wycieku pamięci i faktu, że nie ma wirtualnego dtora, dlaczego ten kod się zawiesza?

#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
Masz na myśli oprócz brakującego średnika? (Byłby to jednak błąd w czasie kompilacji, a nie w czasie wykonywania)
Platinum Azure,

Czy na pewno wszystkie były wirtualne?
Yochai Timmer

8
Powinno być Shape **Wskazuje na tablicę prostokątów. Wtedy dostęp powinien mieć kształty [i] -> draw ();
RedX,

2
@Tony, powodzenia, informuj nas na bieżąco :)
Seth Carnegie,

2
@AndreyT: Kod jest teraz poprawny (i również pierwotnie był poprawny). To ->był błąd popełniony przez redaktora.
R. Martinho Fernandes,

Odpowiedzi:


150

Nie możesz tak indeksować. Przydzieliłeś tablicę Rectanglesi zapisałeś wskaźnik do pierwszego in shapes. Kiedy robisz shapes[1]wyłuskiwanie (shapes + 1). To nie da ci wskaźnika do następnego Rectangle, ale wskaźnik do tego, co będzie następne Shapew przypuszczalnej tablicy Shape. Oczywiście jest to niezdefiniowane zachowanie. W twoim przypadku masz szczęście i masz awarię.

Użycie wskaźnika, aby Rectangleindeksowanie działało poprawnie.

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

Jeśli chcesz mieć Shapew tablicy różne rodzaje znaków i używać ich polimorficznie, potrzebujesz tablicy wskaźników do Shape.


37

Jak powiedział Martinho Fernandes, indeksowanie jest nieprawidłowe. Jeśli zamiast tego chcesz przechowywać tablicę kształtów, musisz to zrobić za pomocą tablicy Shape *, na przykład:

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

Zwróć uwagę, że musisz wykonać dodatkowy krok inicjalizacji Rectangle, ponieważ inicjalizacja tablicy ustawia tylko wskaźniki, a nie same obiekty.


13

Podczas indeksowania wskaźnika kompilator doda odpowiednią ilość w oparciu o rozmiar tego, co znajduje się w tablicy. Powiedzmy, że sizeof (Shape) = 4 (ponieważ nie ma zmiennych składowych). Ale sizeof (Rectangle) = 12 (dokładne liczby są prawdopodobnie błędne).

Więc kiedy indeksujesz zaczynając od powiedzmy ... 0 x 0 dla pierwszego elementu, wtedy kiedy próbujesz uzyskać dostęp do dziesiątego elementu, próbujesz udać się pod nieprawidłowy adres lub lokalizację, która nie jest początkiem obiektu.


1
Jako osoba nie znająca się na C ++ wspomnienie o SizeOf () pomogło mi zrozumieć, co @R. Martinho mówił w swojej odpowiedzi.
Marjan Venema
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.