2012-12-05 10 views
6
class Base1 
{ 
    private: 
    int testInput; 
    public: 
     Base1(); 
     virtual int GetRow(void) = 0; 
}; 

Base1::Base1() 
{ 
    testInput = 0; 
} 

class table : public Base1 
{ 
    private: 
    int row;  
    public: 
    table(); 
    virtual int GetRow(void); 
}; 

table::table() 
{ 
    //Contructor 
    row = 5; 
} 

int table::GetRow() 
{ 
    return row; 
} 

int main() 
{ 
    Base1* pBase = new table[3]; 
    pBase[0].GetRow(); 
    pBase[1].GetRow(); //when i get to this line, the compiler keep saying access 
          // violation. 
    pBase[2].GetRow(); 

    return 0; 
} 

Próbuję utworzyć tablicę 3 klas tabeli. Warunkiem jest to, że muszę użyć obiektu Base, aby to zrobić.Jak stworzyć tablicę z polimorfizmem w C++?

Base1 * pBase = new table[3]; 

dobrze mi wygląda. Ale kiedy próbowałem uzyskać dostęp do każdej tabeli, kompilator powiedział, że to naruszenie zasad dostępu. Nie wiem, co jest złego w tym kodzie. Korzystam jednak z Visual Studio 2010.

Odpowiedz

13

W języku C++ polimorfizm i macierze nie mieszają się.

Ponieważ ogólnie wielkość klasy pochodnej różni się od wielkości klasy bazowej, polimorfizm i arytmetyka wskaźnika nie współgrają ze sobą ładnie. Ponieważ dostęp do tablicy obejmuje arytmetykę wskaźników, wyrażenia takie jak pBase[1] nie działają zgodnie z oczekiwaniami.

Jedną z możliwości jest posiadanie tablicy wskaźników do twoich obiektów, być może nawet inteligentnych wskaźników w celu uproszczenia zarządzania pamięcią. Ale nie zapomnij zdefiniować wirtualnego destruktora w Base1.

3

Musisz utworzyć (z new) te wszystkie elementy tablicy, zrób to tak:

for(int i = 0; i < 3; ++i) 
    pBase[i] = new table(); 
6

dostajesz błąd, ponieważ tablica jest statycznie wpisane do Base1. Oznacza to, że ten wiersz:

pBase[1].GetRow(); 

dodaje rozmiar w bajtach Base1 do pBase i interpretuje to jako początek innym Base1 obiektu, ale to faktycznie wskazuje gdzieś w połowie pierwszego table instancji.

Jeśli potrzebujesz tablicy polimorficznych instancji, musisz je zapisać w tablicy (lub najlepiej w std::vector) za pomocą wskaźników (lub najlepiej jakiejś formy inteligentnych wskaźników).

4

Odpowiedź Agnew była natychmiastowa. Pozwól mi wyjaśnić trochę więcej. Przez zwiększając swój kod, mogę wydrukować wielkości Base1 i table obiektu, jak również adresy trzech table obiektów, jak są one tworzone przez operatora new:

A Base1 object is 8 bytes 
A table object is 12 bytes 
A table object is being constructed at 0x002977C0 
A table object is being constructed at 0x002977CC 
A table object is being constructed at 0x002977D8 

Jak widać te obiekty są rozmieszczone w pamięci 12 bajtów.

A teraz wydrukować adresy że pBase [0], pBase [1] i pBase [2] podać:

pBase[0] is at 0x002977C0 
pBase[1] is at 0x002977C8 
pBase[2] is at 0x002977D0 

Teraz spójrz na to, co się dzieje: Wskaźniki wrócimy są oddalone od 8 bajtów niezależnie. Dzieje się tak, ponieważ arytmetyka wskaźnika jest wykonywana na wskaźniku, którego typ to Base1, a ponieważ Base1 ma długość 8 bajtów, co robi kompilator, aby przetłumaczyć pBase[n] na pBase + (n * sizeof(Base1)).

Teraz powinieneś być w stanie dokładnie zrozumieć, dlaczego najpierwGetRow() działa i dlaczego awarii na drugim.

+0

i, shhh .. ale rzucenie do właściwego typu, a następnie wywołanie metody na rzuconym typie zapobiegnie katastrofie – johnathon

+0

Masz na myśli '((tabela) pBase) [1] .GetRow()'? To zadziała, ale jest to * okropne * i prawie na pewno wróci i ugryzie cię w dupę. –

+0

tak, mam na myśli właśnie to. Tak, jest to dość "okropne", ale jest to całkiem słuszny sposób, aby przekonać kompilator do uzyskania dostępu do obiektów polimorficznych pod prawidłowymi adresami. – johnathon