2009-03-18 13 views

risposta

14

Gli oggetti creati con new[] devono utilizzare delete[]. L'utilizzo di delete non è definito sugli array.

Con malloc e gratuito si ha una situazione più semplice. Esiste solo una funzione che libera i dati che si allocano, non esiste nemmeno il concetto di un distruttore chiamato. La confusione arriva solo perché delete[] e delete sembrano simili. In realtà sono 2 funzioni completamente diverse.

L'utilizzo di eliminazione non chiama la funzione corretta per eliminare la memoria. Dovrebbe chiamare lo delete[](void*), ma chiama lo delete(void*). Per questo motivo non si può contare sull'utilizzo di delete per la memoria allocata con new[]

See this C++ FAQ

[16.13] Posso cadere il [] quando serie deleteing di qualche tipo built-in (char, int , eccetera)?

No!

volte programmatori pensare che il [] nella delete[] p esiste solo modo il compilatore chiamerà appropriati distruttori per tutti gli elementi dell'array . A causa di questo ragionamento, essi assumono che un array di alcuni tipi di integrato come char o int può essere delete d senza il []. Per esempio, hanno assumono il seguente codice valido è:

void userCode(int n) { 
    char* p = new char[n]; 
    ... 
    delete p; // ← ERROR! Should be delete[] p ! 
} 

Ma il codice di cui sopra è sbagliato, e può causare un disastro in fase di esecuzione. In particolare il codice che si chiama per delete p è operator delete(void*), ma il codice che si chiama per delete[] p è operator delete[](void*). Il comportamento predefinito per quest'ultimo è chiamare il primo, ma gli utenti possono sostituire quest'ultima con un comportamento diverso (in qual caso normalmente anche sostituire il nuovo codice corrispondente operatore new[](size_t)). Se sostituito il codice delete[] quindi non era compatibile con il codice di eliminazione , e si chiama quella sbagliata (vale a dire, se lei ha detto delete p piuttosto di delete[] p), si potrebbe finire con un disastro in fase di esecuzione .

Perché esiste delete[] in primo luogo?

Se fate xoy:

char * x = new char[100]; 
char * y = new char; 

Entrambi sono memorizzati in char * variabili tipizzate.

Penso che il motivo della decisione di delete e delete[] accompagni un lungo elenco di decisioni che sono a favore dell'efficienza in C++. È così che non vi è alcun prezzo forzato per fare una ricerca di quanto deve essere eliminato per una normale operazione di cancellazione.

Avere 2 new e new[] sembra solo logico avere delete e delete[] comunque per simmetria.

+0

forse è possibile aggiungere alla risposta le informazioni/i collegamenti per sovrascrivere i vari nuovi/cancella per rispondere alla domanda successiva. –

+0

@Brian Fuori dalla curiosità: perché la necessità di distinguere tra i due? Come in, perché delete [] esiste in primo luogo? – FreeMemory

11

La differenza è che delete eliminerà solo l'intero intervallo di memoria, ma chiamerà solo il distruttore per 1 oggetto. delete[] elimineranno entrambi la memoria e chiameranno il distruttore per ogni singolo oggetto. Se non si utilizza delete[] per gli array, è solo una questione di tempo prima di introdurre una perdita di risorse nell'applicazione.

EDIT Aggiorna

Secondo lo standard, passando un oggetto allocato con new[] per delete è indefinito. Il comportamento dello è probabilmente il comportamento di che agirà come descritto.

+0

Allora, perché è buona pratica eliminare [] i char array allora? Solo per mantenere l'abitudine? –

+0

Ummm, sono abbastanza sicuro che chiamare delete sul risultato da new [] (o viceversa) sia un comportamento indefinito. Quindi, può portare a molto peggio di una semplice perdita di memoria. – derobert

+0

@derobert, non sono sicuro al 100% se è definito o meno, ma sicuramente crea un comportamento scorretto. – JaredPar

-1

Quando si utilizza new [] per allocare un array, si sta effettivamente dicendo C++ della dimensione dell'array. Quando usi malloc, stai invece dicendo a quanta memoria è allocata. Nel primo caso, liberare in base alla dimensione dell'array non avrebbe senso. In questo caso, lo fa. Ma poiché non esiste alcuna differenza tra un puntatore per un array e un singolo oggetto, è necessaria una funzione separata.

0

nuovo e delete sono diversi da malloc e gratuiti in quel malloc e liberano solo allocano e liberano memoria; non chiamano né i medici né i medici.

3

Il motivo di questo requisito è storico e poiché new type e new type [size] restituiscono informazioni diverse che devono essere ripulite in modo diverso.

Considerate questo codice

Foo* oneEntry = new Foo; 
Foo* tenEntries = new Foo[10]; 

Questi entrambi restituiscono un puntatore Foo *, la differenza è la seconda chiamata si tradurrà nel costruttore Foo chiamato 10x, e che vi sia o meno 10 volte la quantità di memoria.

Quindi ora vuoi liberare i tuoi oggetti.

Per un singolo oggetto che chiamereste delete - ad es. delete oneEntry. Ciò chiama il distruttore di oggetti e rilascia la memoria.

Ma ecco il problema: oneEntry e tenEntries sono entrambi solo punti Foo. Il compilatore non ha idea se puntano a uno, dieci o mille elementi.

Quando si utilizza la sintassi speciale di delete [].Questo dice al compilatore "questa è una matrice di oggetti, calcola il conteggio e poi distruggili tutti".

Quello che realmente accade è che per il new type [size] il compilatore memorizza segretamente 'la dimensione' da qualche altra parte. Quando chiami cancella [] sa che questo valore segreto esiste in modo che possa scoprire quanti oggetti ci siano in quel blocco di memoria e li distrugga.

La domanda che si potrebbe chiedere è "perché il compilatore non memorizza sempre le dimensioni?"

Questa è una grande domanda e risale agli albori del C++. C'era il desiderio che per i tipi built-in (char, int, float, ecc) quanto segue sarebbe valido per C++;

int* ptr = new int; 
free(ptr); 

int* ptr = (int*)malloc(sizeof(int) * someSize); 
delete ptr; 

Il ragionamento dietro questo era l'aspettativa che la gente avrebbe fornito le librerie che hanno restituito allocata dinamicamente la memoria, e gli utenti di queste librerie non avrebbero alcun modo di sapere se usare liberi/delete.

Questo desiderio di compatibilità significava che le dimensioni di un array non potevano essere archiviate come parte dell'array stesso e dovevano essere conservate altrove. A causa di questo sovraccarico (e ricorda, questo era nei primi anni '80) si è deciso di fare questo libro mantenendo solo gli array e non i singoli elementi. Quindi gli array hanno bisogno di una sintassi di eliminazione speciale che cerca questo valore.

La ragione per cui malloc/free non ha questo problema è che si occupano semplicemente di blocchi di memoria e non devono preoccuparsi di chiamare costruttori/distruttori.

1

Per quanto riguarda il "perché" nel titolo: uno degli obiettivi di progettazione di C++ era che non ci sarebbero stati costi nascosti. Il C++ è stato anche sviluppato in un momento in cui ogni byte di memoria contava ancora molto di più rispetto a oggi. Anche ai progettisti di linguaggi piace l'ortogonalità: se si assegna la memoria con new[] (anziché new), è necessario liberarla con delete[].

Non credo che ci sia alcun tecnica ragione che new[] non poteva attaccare un "Sono una matrice" bandiera nell'intestazione del blocco di memoria per delete (non più delete[]) da guardare successivamente.

4

Stroustrup parla delle ragioni separata new/new[] e delete/ delete [] `operatori "The Design e l'evoluzione di C++" nelle sezioni 10.3 attraverso 10.5.1:

  • 10,3 Array Allocation - discute che volevano un modo per consentire l'assegnazione di matrici di oggetti utilizzando uno schema separato dall'allocazione di singoli oggetti (ad es., allocando gli array da un archivio separato). L'aggiunta delle versioni di array di new e delete era una soluzione per questo;
  • 10.5.1 Deallocating Arrays - discute come un problema con la deallocating array usando un solo operatore delete è che ci devono essere più informazioni del solo puntatore per determinare se il puntatore punta al primo elemento di un array o se punta solo a un singolo oggetto. Invece di "complicare il caso comune di allocazione e deallocazione di singoli oggetti", l'operatore delete[] viene utilizzato per gestire gli array. Questo si adatta alla filosofia generale del design C++ di "non pagare per ciò che non si usa".

Se questa decisione è stata un errore o meno è discutibile - in entrambi i casi ha buoni argomenti, ma abbiamo quello che abbiamo.

Problemi correlati