In che modo l’associazione di nuovi con l’eliminazione può portare solo alla perdita di memoria?

Prima di tutto, usare delete per qualsiasi cosa allocata con new[] è un comportamento non definito secondo lo standard C ++.

In Visual C ++ 7 tale abbinamento può portare a una delle due conseguenze.

Se il tipo new [] ‘ed ha un banale costruttore e distruttore VC ++ usa semplicemente new invece di new[] e l’uso di delete per quel blocco funziona bene – le new chiamate appena “allocano memoria”, delete solo le chiamate “free memory”.

Se il tipo new [] ‘ed ha un costruttore o distruttore non banale, il trucco di cui sopra non può essere fatto – VC ++ 7 deve invocare esattamente il giusto numero di distruttori. Quindi antepone la matrice con una dimensione_t che memorizza il numero di elementi. Ora l’indirizzo è ritornato con new[] punti sul primo elemento, non sull’inizio del blocco. Quindi, se si usa delete , chiama solo il distruttore per il primo elemento e le chiamate “memoria libera” con l’indirizzo diverso da quello restituito da “allocate memoria” e questo porta a qualche indicazione di errore all’interno di HeapFree () che a mio avviso si riferisce a corruzione dell’heap.

Eppure ogni qui e là si possono leggere affermazioni false che usando delete after new[] porta a una perdita di memoria. Sospetto che qualsiasi dimensione della corruzione dell’heap sia molto più importante di un fatto che il distruttore è chiamato solo per il primo elemento e che probabilmente i distruttori non chiamati non hanno liberato oggetti secondari allocati all’heap.

In che modo l’utilizzo di delete after new[] potrebbe portare solo a una perdita di memoria in alcune implementazioni C ++?

Supponiamo che io sia un compilatore C ++ e implemento la gestione della memoria in questo modo: antepone ogni blocco di memoria riservata alla dimensione della memoria, in byte. Qualcosa come questo;

 | size | data ... | ^ pointer returned by new and new[] 

Si noti che, in termini di allocazione della memoria, non c’è differenza tra new e new[] : entrambi allocano solo un blocco di memoria di una certa dimensione.

Ora come delete[] conoscere la dimensione dell’array, per chiamare il numero giusto di distruttori? Basta dividere la size del blocco di memoria per sizeof(T) , dove T è il tipo di elementi dell’array.

Ora supponiamo di implementare delete come una sola chiamata al distruttore, seguita dalla liberazione dei byte delle size , quindi i distruttori degli elementi successivi non saranno mai chiamati. Ciò si traduce in perdite di risorse allocate dagli elementi successivi. Tuttavia, poiché sizeof(T) byte di size libera (non i byte sizeof(T) ), non si verifica alcun danneggiamento dell’heap.

La fiaba sul mixare new[] e delete presumibilmente causando una perdita di memoria è proprio questo: una fiaba. Non ha assolutamente alcun fondamento nella realtà. Non so da dove venisse, ma ormai ha acquisito una vita propria e sopravvive come un virus, propagandosi per passaparola da un principiante all’altro.

La logica più probabile dietro questa assurdità della “perdita di memoria” è che dal punto di vista innocente e ingenuo la differenza tra delete ed delete[] è che l’ delete è usata per distruggere un solo object, mentre delete[] distrugge una serie di oggetti (” molti “oggetti”). Una conclusione ingenua che di solito deriva da questo è che il primo elemento dell’array sarà distrutto da delete , mentre il resto persisterà, creando così la presunta “perdita di memoria”. Naturalmente, qualsiasi programmatore che abbia almeno una conoscenza di base delle tipiche implementazioni dell’heap comprenderebbe immediatamente che la conseguenza più probabile è la corruzione dell’heap, non una “perdita di memoria”.

Un’altra spiegazione popolare per l’ingenua teoria della “perdita di memoria” è che dal momento in cui viene chiamato il numero sbagliato di distruttori, la memoria secondaria di proprietà degli oggetti nell’array non viene deallocata. Questo potrebbe essere vero, ma è ovviamente una spiegazione molto forzata, che ha poca rilevanza di fronte a un problema molto più serio con la corruzione dell’heap.

In breve, la miscelazione di diverse funzioni di allocazione è uno di quegli errori che portano a un comportamento indefinito solido, imprevedibile e molto pratico. Qualsiasi tentativo di imporre dei limiti concreti alle manifestazioni di questo comportamento indefinito è solo una perdita di tempo e un segno sicuro della mancanza di comprensione di base.

Inutile aggiungere, new/delete e new[]/delete[] sono in realtà due meccanismi indipendenti di gestione della memoria, che sono personalizzabili indipendentemente. Una volta che vengono personalizzati (sostituendo le funzioni di gestione della memoria non elaborata) non c’è assolutamente modo di iniziare a prevedere cosa potrebbe accadere se si mischiano.

Sembra che la tua domanda sia davvero “perché la corruzione dell’heap non avviene?”. La risposta a questo è “perché il gestore dell’heap tiene traccia delle dimensioni dei blocchi allocate”. Torniamo a C per un minuto: se vuoi allocare un singolo int in C devi fare int* p = malloc(sizeof(int)) , se vuoi allocare array di dimensione n puoi scrivere int* p = malloc(n*sizeof(int)) o int* p = calloc(n, sizeof(int)) . Ma in ogni caso lo libererai gratuitamente free(p) , indipendentemente da come lo hai assegnato. Non si passa mai la dimensione a free (), free () solo “sa” quanto liberare, poiché la dimensione di un blocco malloc () – ed è salvata da qualche parte “davanti” al blocco. Tornando al C ++, new / delete e new [] / delete [] sono di solito implementati in termini di malloc (anche se non devono esserlo, non dovresti fare affidamento su questo). Questo è il motivo per cui la nuova combinazione [] / delete non corrompe l’heap: l’eliminazione libera la giusta quantità di memoria, ma, come spiegato da tutti prima di me, è ansible ottenere perdite non chiamando il numero corretto di distruttori.

Detto questo, ragionare sul comportamento non definito in C ++ è sempre un esercizio inutile. Perché è importante se la nuova combinazione [] / delete funziona, “solo” perdite o causa il danneggiamento dell’heap? Non dovresti scrivere un codice del genere, punto! E, in pratica, eviterei la gestione manuale della memoria ogni volta che è ansible – STL e boost ci sono per un motivo.

Se il distruttore non banale che non è chiamato per tutti tranne il primo elemento dell’array dovrebbe liberare della memoria si ottiene una perdita di memoria poiché questi oggetti non vengono ripuliti correttamente.

Ciò porterà a una perdita in TUTTE le implementazioni di C ++ in ogni caso in cui il distruttore libera la memoria, perché il distruttore non viene mai chiamato.

In alcuni casi può causare errori molto peggiori.

la perdita di memoria potrebbe accadere se l’operatore new () viene sovrascritto ma new [] non lo è. lo stesso vale per l’operatore delete / delete []

Oltre a comportare un comportamento indefinito, la causa più semplice delle perdite risiede nell’implementazione che non chiama il distruttore per tutti tranne il primo object nell’array. Ciò ovviamente causerà perdite se gli oggetti hanno risorse allocate.

Questa è la class più semplice ansible che potrei pensare di ottenere questo comportamento:

  struct A { char* ch; A(): ch( new char ){} ~A(){ delete ch; } }; A* as = new A[10]; // ten times the A::ch pointer is allocated delete as; // only one of the A::ch pointers is freed. 

PS: nota che i costruttori non riescono a essere chiamati in molti altri errori di programmazione: distruttori di base non virtuali, falsa dipendenza da puntatori intelligenti, …

In ritardo per una risposta, ma …

Se il tuo meccanismo di cancellazione è semplicemente chiamare il distruttore e mettere il puntatore libero, insieme con la dimensione implicita da sizeof , su uno stack libero, quindi chiamare delete su un blocco di memoria allocato con new [] comporterà la perdita della memoria – ma non corruzione. Strutture malloc più sofisticate potrebbero corrompere o rilevare questo comportamento.

Perché la risposta non può essere che causa entrambi?

Ovviamente la memoria è trapelata se la corruzione dell’heap si verifica o meno.

O meglio, dal momento che riesco a ri-implementare nuovi e cancellare ….. non può non causare nulla. Tecnicamente posso causare nuovi e cancellare per eseguire nuovi [] ed eliminare [].

Quindi: comportamento non definito.

Stavo rispondendo a una domanda che è stata contrassegnata come duplicata, quindi la copierò qui solo nel caso in cui si metta in crisi. È stato detto molto prima di me come funziona l’allocazione della memoria, spiegherò solo la causa e gli effetti.

Solo una piccola cosa su google: http://en.cppreference.com/w/cpp/memory/new/operator_delete

Comunque, delete è una funzione per un singolo object. Libera l’istanza dal puntatore e lascia;

delete [] è una funzione utilizzata per deallocare gli array. Ciò significa che non si limita a liberare il puntatore; Dichiara l’intero blocco di memoria di quell’array come spazzatura.

In pratica è bello, ma mi dici che la tua applicazione funziona. Probabilmente ti starai chiedendo … perché?

La soluzione è C ++ non risolve le perdite di memoria . Se userai cancella senza parentesi, cancellerà solo la matrice come object – un processo che potrebbe causare una perdita di memoria .

bella storia, perdita di memoria, perché dovrei preoccuparmi?

La perdita di memoria si verifica quando la memoria allocata non viene eliminata. Quella memoria quindi richiede uno spazio disco non necessario, che ti farà perdere memoria utile praticamente senza motivo. Questa è una ctriggers programmazione e probabilmente dovresti risolverla nei tuoi sistemi.