2010-03-09 15 views
7

po dyskusji na spotkaniu oprogramowania Postanowiłem dowiedzieć się, czy usunięcie dynamicznie przydzielonej tablicy prymitywów ze zwykłym delete spowoduje wyciek pamięci.Czy usuń p gdzie p jest wskaźnikiem do tablicy zawsze wyciek pamięci?

Pisałem ten mały program i skompilowany z visual studio 2008 działa na Windows XP:

#include "stdafx.h" 
#include "Windows.h" 

const unsigned long BLOCK_SIZE = 1024*100000; 
int _tmain() 
{ 
    for (unsigned int i =0; i < 1024*1000; i++) 
    { 
     int* p = new int[1024*100000]; 
     for (int j =0;j<BLOCK_SIZE;j++) p[j]= j % 2; 
     Sleep(1000); 
     delete p; 
    } 
} 

ja niż monitorować zużycie pamięci mojej aplikacji przy użyciu menedżera zadań, zaskakująco pamięć została przydzielona i zwolniona poprawnie , przydzielona pamięć nie stale zwiększać jak oczekiwano

I zostały zmodyfikowane mój program testowy, aby przydzielić non prymitywny typ tablicy:

#include "stdafx.h" 
#include "Windows.h" 


struct aStruct 
{ 
    aStruct() : i(1), j(0) {} 

    int i; 
    char j; 
} NonePrimitive; 

const unsigned long BLOCK_SIZE = 1024*100000; 
int _tmain() 
{ 
    for (unsigned int i =0; i < 1024*100000; i++) 
    { 
     aStruct* p = new aStruct[1024*100000]; 
     Sleep(1000); 
     delete p; 

    } 
} 

po uruchomieniu przez 10 minut nie było znaczącego zwiększenia pamięci

Przygotowałem projekt z ostrzeżeniem na poziomie 4 i nie otrzymałem ostrzeżeń.

Czy możliwe jest, że czas pracy studia wizualnego będzie śledzony dla przydzielonych typów obiektów, więc nie ma różnicy między delete i delete[] w tym środowisku?

+0

Duplikat? http://stackoverflow.com/questions/1913343/how-could-pairing-new-with-delete-possibly-lead-to-memory-leak-only –

+0

@Philip Potter: Nie z tego samego pytania - ten jest specjalnie o powodowaniu wycieku pamięci. A przeciek pamięci nie jest typowy w tym przypadku. – sharptooth

+0

Wypróbuj go z tablicą 'shared_ptr ', wskazując na inną przydzieloną int, a zobaczysz, czy twoja implementacja czyni równoważnik "delete" i "delete []". Zawsze ta fascynacja C++ rzeczami, które ludzie wiedzą, są złe, są wyraźnie określone w standardzie, aby być błędnym, a jednak wyglądają tak, jakby mogły czasem po prostu zadziałać ;-) –

Odpowiedz

19

delete p, gdzie p jest tablicą nazywa się niezdefiniowanym zachowaniem.

W szczególności przy przydzielaniu tablicy typów surowych danych (int) kompilator nie musi dużo pracować, więc przekształca go w prosty malloc(), więc usuwanie p prawdopodobnie zadziała.

delete p zamierza zawieść, typowo, gdy:

  • p był złożony typ danych - Usuwanie p; nie będzie wiedział, jak wywoływać poszczególne destruktory.
  • "Użytkownik" przeładowuje operatora new [] i usuwa [], aby użyć innej sterty do zwykłej sterty.
  • środowisko wykonawcze debugowania przeciąża operatora new [] i usuń [], aby dodać dodatkowe informacje o śledzeniu dla tablicy.
  • kompilator decyduje, że musi przechowywać dodatkowe informacje RTTI wraz z obiektem, które usuwają p; nie zrozumie, ale usuń [] p; będzie.
17

Nie, to niezdefiniowane zachowanie. Nie rób tego - użyj delete[].

W VC++ od 7 do 9 zdarza się, że działa when the type in question has trivial destructor, ale może przestać działać w nowszych wersjach - zwykle z nieokreślonym zachowaniem. I tak nie rób tego.

+0

Dobre wyjaśnienie, dlaczego przypadkiem działa w przypadku @ Eli! –

2

nie, należy użyć delete[] gdy ma do czynienia z tablicami

3

Nazywa niezdefiniowane zachowanie; to może zadziałać, ale nie wiesz dlaczego, więc nie powinieneś go trzymać.

Nie sądzę, że Visual Studio śledzi sposób alokacji obiektów, jako tablice lub zwykłe obiekty, i magicznie dodaje [] do swojego usunięcia. Prawdopodobnie kompiluje on delete p; do tego samego kodu, który został przydzielony z p = new int i, jak powiedziałem, z jakiegoś powodu działa. Ale nie wiesz dlaczego.

2

Po prostu użycie delete nie wywoła destruktorów obiektów w tablicy. Chociaż będzie to działać prawidłowo, to jest ono niezdefiniowane, ponieważ istnieją pewne różnice w ich działaniu. Więc nie powinieneś go używać, even for built in types.

+0

FAQ ma bardzo mocny argument, dziękuję za wskazanie tego – Eli

1

Powodem, dla którego usuwanie nie jest zbyt duże, jest pamięć bezpłatna, która już wie, ile pamięci trzeba zwolnić. Jednak część C++ jest mało prawdopodobna, aby została poprawnie oczyszczona. Założę się, że wywoływany jest tylko destruktor pierwszego obiektu.

1

Użycie polecenia delete za pomocą [] mówi kompilatorowi, aby wywołał destruktor na każdym elemencie tablicy. Nie użyciu delete [] może spowodować wycieki pamięci, jeżeli są stosowane na tablicę obiektów, które używają dynamicznych alokacji pamięci jak następuje:

class AClass 
{ 
public: 
    AClass() 
    { 
     aString = new char[100]; 
    } 
    ~AClass() 
    { 
     delete [] aString; 
    } 
private: 
    const char *aString; 
}; 

int main() 
{ 
    AClass * p = new AClass[1000]; 
    delete p; // wrong 
    return 0; 
} 
3

Jedną z odpowiedzi jest to, że tak, może to spowodować wycieki pamięci, ponieważ nie wymaga destruktor dla każdego elementu w tablicy. Oznacza to, że każda dodatkowa pamięć należąca do elementów w tablicy wycieknie.

Im bardziej zgodna z normami jest odpowiedź na nieokreślone zachowanie. Na przykład kompilator ma pełne prawo do korzystania z różnych pul pamięci dla macierzy niż dla elementów nieszablonowych. Wykonanie nowego, ale w ten sam sposób, usunięcia drugiego może spowodować uszkodzenie sterty.

Twój kompilator może zagwarantować, że standard nie spełnia wymagań, ale pierwszy problem pozostaje. W przypadku elementów POD, które nie posiadają dodatkowej pamięci (lub zasobów, takich jak uchwyty plików), może być OK.

Nawet jeśli jest to bezpieczne dla kompilatora i elementów danych, nie rób tego tak czy inaczej - jest to również mylące dla każdego, kto próbuje przeczytać twój kod.