2011-11-24 14 views
7

Rozważ poniższy program. Zostało uproszczone ze złożonego przypadku. Nie powiedzie się podczas usuwania poprzednio przydzielonej pamięci, chyba że usunę wirtualny destruktor z klasy Obj. Nie rozumiem, dlaczego dwa adresy z wyjścia programu różnią się, tylko jeśli obecny jest wirtualny destruktor.Co jest nie tak z użyciem nowego miejsca docelowego []? do

// GCC 4.4 
#include <iostream> 

using namespace std; 

class Arena { 
public: 
    void* alloc(size_t s) { 
     char* p = new char[s]; 
     cout << "Allocated memory address starts at: " << (void*)p << '\n'; 
     return p; 
    } 

    void free(void* p) { 
     cout << "The memory to be deallocated starts at: " << p << '\n'; 
     delete [] static_cast<char*> (p); // the program fails here 
    } 
}; 

struct Obj { 
    void* operator new[](size_t s, Arena& a) { 
     return a.alloc(s); 
    } 

    virtual ~Obj() {} // if I remove this everything works as expected 

    void destroy(size_t n, Arena* a) { 
     for (size_t i = 0; i < n; i++) 
      this[n - i - 1].~Obj(); 
     if (a) 
      a->free(this); 
    } 
}; 


int main(int argc, char** argv) { 
    Arena a; 

    Obj* p = new(a) Obj[5](); 
    p->destroy(5, &a); 

    return 0; 
} 

Jest to wyjście z programu w mojej realizacji, gdy wirtualny destruktor jest obecny:

Przydzielony adres pamięci zaczyna się na: 0x8895008 Pamięć być zwalniane rozpoczyna się pod adresem: 0x889500c

URUCHOMIONA URUCHOMIENIE (wartość wyjściowa 1)

Proszę nie pytać, jaki program powinien mieć o. Jak powiedziałem, pochodzi z bardziej złożonego przypadku, w którym Arena jest interfejsem dla różnych typów pamięci. W tym przykładzie pamięć jest po prostu przydzielana i zwalniana ze sterty.

+0

pamiętać, że * do * potrzebują umieszczenie tablicy dopasowanie usunąć w przypadku nowej ekspresji zgłasza wyjątek. –

Odpowiedz

5

this nie jest wskaźnik zwracany przez new na linii char* p = new char[s]; Można zobaczyć, że rozmiar s jest większy niż 5 Obj przypadki. Różnica (która powinna być sizeof (std::size_t)) jest w dodatkowej pamięci, zawierającej długość tablicy, 5, bezpośrednio przed adresem zawartym w this.

OK, spec jasno:

http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies

2,7 Array operator new Cookies

Kiedy operator new jest używany do tworzenia nowej tablicy, plik cookie jest zwykle przechowywany pamiętać przydzielona długość (liczba elementów tablicy), aby mogła zostać poprawnie przydzielona.

szczególności:

nr ciastko jest wymagana, jeśli typ elementu tablica T ma trywialne destructor (12,4 [class.dtor]) i zwykłe (array) Funkcja dealokacji (3.7.3.2 [basic.stc .dynamic.deallocation]) nie bierze dwóch argumentów.

Tak, wirtualny -ness z destruktora ma znaczenia, liczy się, że destruktor nie jest trywialne, które można łatwo sprawdzić, usuwając słowa kluczowego virtual przed destruktora i obserwuj awarię programu.

+0

Tak, nie obchodzi mnie rozmiar. Dbam o powód, dla którego to nie pasuje do wskaźnika alloc() podanego dla umieszczenia obiektu. – Martin

+0

@Martin, ponieważ 'alloc()' przydzielił dodatkową przestrzeń dla długości tablicy i elementów tablicy. Naprawdę nie wiem teraz, dlaczego nie przechowuje długości tablicy, jeśli nie ma wirtualnego destruktora, trzeba sprawdzić specyfikację ABI AB. – chill

+0

Co to jest spec? Moja specyfikacja C++ nie wspomina o plikach cookie. –

0

podstawie odpowiedzi dreszcze, jeśli chcesz, aby to «bezpieczny»:

#include <type_traits> 

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0)); 
+1

Nie, niezupełnie, może 'a-> free ((std :: size_t *) this - (std :: has_trivial_destructor :: value? 1: 0));', ale to ściśle mówiąc UD i jestem pewien rozbije się w niejasnym rogu i/lub innym kompilatorze;) – chill