Foo* set = new Foo[100];
// ...
delete [] set;
Non si superano i limiti dell'array delete[]
. Ma dove sono archiviate queste informazioni? È standardizzato?
Foo* set = new Foo[100];
// ...
delete [] set;
Non si superano i limiti dell'array delete[]
. Ma dove sono archiviate queste informazioni? È standardizzato?
Risposte:
Quando si alloca memoria sull'heap, l'allocatore terrà traccia di quanta memoria è stata allocata. Questo è di solito memorizzato in un segmento "head" appena prima della memoria che viene allocata. In questo modo quando è il momento di liberare la memoria, il de-allocatore sa esattamente quanta memoria liberare.
free
sa quanta memoria da deallocare". Sì, la dimensione del blocco di memoria viene memorizzata "da qualche parte" malloc
(normalmente nel blocco stesso), quindi è così che free
sa. Tuttavia, new[]
/ delete[]
è una storia diversa. Quest'ultimo funziona fondamentalmente su malloc
/ free
. new[]
memorizza anche il numero di elementi che ha creato nel blocco di memoria (indipendentemente da malloc
), in modo che in seguito delete[]
possa recuperare e utilizzare quel numero per chiamare il numero corretto di distruttori.
malloc
) e conteggio degli elementi (per new[]
). Si noti che il primo non può essere utilizzato per calcolare il secondo, poiché in generale la dimensione del blocco di memoria può essere maggiore del necessario per l'array della dimensione richiesta. Si noti inoltre che il contatore degli elementi dell'array è necessario solo per i tipi con distruttore non banale. Per i tipi con banale distruttore il contatore non viene memorizzato da new[]
e, ovviamente, non recuperato da delete[]
.
Uno degli approcci per i compilatori è quello di allocare un po 'più di memoria e memorizzare un conteggio di elementi in un elemento head.
Esempio come potrebbe essere fatto:
Qui
int* i = new int[4];
il compilatore assegnerà i sizeof(int)*5
byte.
int *temp = malloc(sizeof(int)*5)
Memorizzerà "4" nei primi sizeof(int)
byte
*temp = 4;
e impostare i
i = temp + 1;
Quindi i
punta a una matrice di 4 elementi, non 5.
E cancellazione
delete[] i;
verrà elaborato nel modo seguente:
int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Le informazioni non sono standardizzate. Tuttavia nelle piattaforme su cui ho lavorato queste informazioni sono archiviate in memoria appena prima del primo elemento. Pertanto si potrebbe teoricamente accedervi e ispezionarlo, tuttavia non ne vale la pena.
Anche per questo è necessario utilizzare delete [] quando è stata allocata memoria con new [], poiché la versione array di delete sa che (e dove) deve cercare di liberare la giusta quantità di memoria e chiamare il numero appropriato di distruttori per gli oggetti.
Fondamentalmente è organizzato in memoria come:
[info] [mem che hai chiesto ...]
Dove info è la struttura utilizzata dal compilatore per archiviare la quantità di memoria allocata e cosa no.
Questo dipende dall'implementazione però.
È definito nello standard C ++ per essere specifico del compilatore. Il che significa magia del compilatore. Può rompersi con restrizioni di allineamento non banali su almeno una piattaforma principale.
Puoi pensare a possibili implementazioni realizzando che delete[]
è definito solo per i puntatori restituiti da new[]
, che potrebbe non essere lo stesso puntatore restituito da operator new[]
. Un'implementazione allo stato brado è quella di memorizzare il conteggio degli array nel primo int restituito da operator new[]
e new[]
restituire un offset del puntatore oltre quello. (Ecco perché gli allineamenti non banali possono rompersi new[]
.)
Tieni presente che operator new[]/operator delete[]
! = new[]/delete[]
.
Inoltre, questo è ortogonale al modo in cui C conosce la dimensione della memoria allocata da malloc
.
Perché l'array da "cancellare" avrebbe dovuto essere creato con un singolo utilizzo del "nuovo" operatore. La "nuova" operazione avrebbe dovuto mettere tali informazioni nell'heap. Altrimenti, come potrebbero gli usi aggiuntivi di new sapere dove finisce l'heap?
Non è standardizzato. Nel runtime di Microsoft il nuovo operatore utilizza malloc () e l'operatore delete utilizza free (). Quindi, in questa impostazione la tua domanda è equivalente alla seguente: Come fa free () a conoscere la dimensione del blocco?
C'è un po 'di contabilità in corso dietro le quinte, vale a dire in fase di esecuzione.
Questo è un problema più interessante di quanto potresti pensare all'inizio. Questa risposta riguarda una possibile implementazione.
In primo luogo, mentre a un certo livello il tuo sistema deve sapere come "liberare" il blocco di memoria, il malloc / free sottostante (che generalmente chiama new / delete / new [] / delete []) non ricorda sempre esattamente quanta memoria se lo chiedi, può essere arrotondato per eccesso (ad esempio, una volta sopra 4K è spesso arrotondato per eccesso al blocco di dimensioni 4K successivo).
Pertanto, anche se è possibile ottenere la dimensione del blocco di memoria, ciò non ci dice quanti valori ci sono nella nuova memoria [], in quanto può essere più piccola. Pertanto, dobbiamo memorizzare un numero intero in più che ci dica quanti valori ci sono.
SALVO, se il tipo in costruzione non ha un distruttore, allora delete [] non deve fare altro che liberare il blocco di memoria, e quindi non deve memorizzare nulla!