'New' e 'delete' vengono deprecati in C ++?


68

Mi sono imbattuto in un quiz che riguardava la dichiarazione di array con dimensioni diverse. La prima cosa che mi è venuta in mente è che avrei bisogno di usare l'allocazione dinamica con il newcomando, in questo modo:

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

Tuttavia, ho visto che una delle soluzioni ha consentito il seguente caso:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

Dopo un po 'di ricerche ho letto che g ++ lo consente, ma mi ha fatto pensare, in quali casi è quindi necessario utilizzare l'allocazione dinamica? O è che il compilatore traduce questo come allocazione dinamica?

La funzione di eliminazione è inclusa. Si noti, tuttavia, che la domanda qui non riguarda le perdite di memoria.


54
Il secondo esempio utilizza un array di lunghezza variabile che non ha mai fatto parte di C ++. Per questo caso utilizzare std::vectorinvece ( std::vector<int> array(N);).
Qualche programmatore, amico, il

7
La risposta diretta alla tua domanda dovrebbe essere: no, non viene deprecata. Anche se le versioni moderne di C ++ offrono molte funzionalità che semplificano la gestione della proprietà della memoria (puntatori intelligenti), è ancora pratica comune allocare oggetti invocando new OBJdirettamente.
pptaszni,

8
Per le altre persone che sono confuse sul perché le persone parlano di perdite di memoria, la domanda è stata modificata per correggere un bug che non era materiale alla domanda
Mike Caron,

4
@Mannoj preferisce usare i termini Dinamico e Automatico per accumulare e impilare. È raro ma è possibile implementare C ++ senza cumuli e pile.
user4581301

1
Nulla è mai stato deprecato in C ++ e niente lo sarà mai. Fa parte del significato di C ++.
JoelFan

Risposte:


114

Nessuno dei due frammenti che mostri è un codice C ++ moderno e idiomatico.

newe delete(e new[]e delete[]) non sono deprecati in C ++ e non lo saranno mai. Sono ancora il modo di creare un'istanza di oggetti allocati dinamicamente. Tuttavia, poiché devi sempre abbinare a newa delete(e a new[]a a delete[]), sono meglio conservati all'interno delle classi (libreria) che ti garantiscono questo. Vedi Perché i programmatori C ++ dovrebbero ridurre al minimo l'uso di "nuovo"? .

Il tuo primo frammento utilizza un "nudo" new[]e quindi non delete[]è mai l'array creato. Questo é un problema. std::vectorfa tutto il necessario qui bene. Userà una qualche forma di newdietro le quinte (non mi immergerò nei dettagli dell'implementazione), ma per quello che devi preoccuparti, è un array dinamico ma migliore e più sicuro.

Il tuo secondo frammento utilizza "array a lunghezza variabile" (VLA), una funzionalità C che alcuni compilatori consentono anche in C ++ come estensione. Diversamente new, i VLA sono essenzialmente allocati nello stack (una risorsa molto limitata). Ma soprattutto, non sono una funzionalità C ++ standard e dovrebbero essere evitati perché non sono portatili. Certamente non sostituiscono l'allocazione dinamica (cioè heap).


3
Vorrei aggiungere che sebbene i VLA non siano ufficialmente nello standard, sono supportati da tutti i principali compilatori, e quindi la decisione se evitarli o meno è più una questione di stile / preferenza che una preoccupazione realistica per la portabilità.
Stack Tracer

4
Ricorda inoltre che non puoi restituire un array o archiviarlo altrove, quindi VLA non sopravviverà mai al tempo di esecuzione della funzione
Ruslan,

16
@StackTracer Per quanto ne so, MSVC non supporta i VLA. E MSVC è sicuramente un "compilatore importante".
Max Langhof,

2
"devi sempre abbinare un nuovo a un elimina" - non se lavori Qt, dato che le sue classi base hanno tutti i garbage collector, quindi devi solo usarlo newe dimenticartene, il più delle volte. Per gli elementi della GUI, quando il widget padre viene chiuso, i figli escono dall'ambito e vengono automaticamente raccolti.
vsz

6
@vsz Anche in Qt, ognuno newha ancora una corrispondenza delete; è solo che gli deletes vengono eseguiti dal widget padre anziché nello stesso blocco di codice degli news.
jjramsey,

22

Beh, per cominciare, new/ deletenon sono sempre deprecato.

Nel tuo caso specifico, tuttavia, non sono l'unica soluzione. Quello che scegli dipende da ciò che è stato nascosto sotto il tuo commento "fai qualcosa con l'array".

Il secondo esempio utilizza un'estensione VLA non standard che tenta di adattare l'array nello stack. Ciò ha alcune limitazioni - vale a dire dimensioni limitate e l'impossibilità di utilizzare questa memoria dopo che l'array non rientra nell'ambito. Non puoi spostarlo fuori, "scomparirà" dopo che la pila si sarà srotolata.

Quindi, se il tuo unico obiettivo è fare un calcolo locale e poi buttare via i dati, potrebbe effettivamente funzionare bene. Tuttavia, un approccio più solido sarebbe quello di allocare la memoria in modo dinamico, preferibilmente con std::vector. In questo modo hai la possibilità di creare spazio esattamente per tutti gli elementi di cui hai bisogno basandoti su un valore di runtime (che è quello che stiamo facendo per tutto il tempo), ma si pulirà anche bene e puoi spostarlo fuori di questo ambito se si desidera mantenere la memoria in uso per dopo.

Circling indietro all'inizio, vector sarà probabilmente usare newun paio di strati più profondi, ma non dovrebbe essere interessato in questo, come l'interfaccia si presenta è molto superiore. In tal senso, l'utilizzo newe deletepuò essere considerato scoraggiato.


1
Nota il "... pochi strati più profondi". Se dovessi implementare i tuoi contenitori, dovresti comunque evitare di usare newe delete, ma piuttosto usare puntatori intelligenti come std::unique_pointer.
Max

1
che in realtà si chiamastd::unique_ptr
user253751

2
@Max: std::unique_ptrchiamate predefinite del distruttore deleteo delete[], il che significa che l'oggetto posseduto deve essere stato allocato da newo new[]comunque, in cui le chiamate sono state nascoste std::make_uniquedal C ++ 14.
Laurent LA RIZZA,

15

I tuoi secondi esempi usano array a lunghezza variabile (VLA), che in realtà sono una funzionalità C99 ( non C ++!), Ma comunque supportati da g ++ .

Vedi anche questa risposta .

Si noti che le matrici a lunghezza variabile sono diverse da new/ deletee non le "deprecano" in alcun modo.

Ricorda inoltre che i VLA non sono ISO C ++.


13

Il moderno C ++ offre metodi più semplici per lavorare con allocazioni dinamiche. I puntatori intelligenti possono occuparsi della pulizia dopo le eccezioni (che possono accadere ovunque se consentito) e i primi ritorni, non appena le strutture di dati di riferimento escono dall'ambito, quindi può avere senso utilizzare questi invece:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

Da C ++ 14 puoi anche scrivere

auto buffer_new = std::make_unique<int[]>(size);

questo sembra ancora più bello e impedirebbe la perdita di memoria se l'allocazione fallisce. Da C ++ 20 dovresti essere in grado di fare altrettanto

auto a = std::make_shared<int[]>(size);

questo per me non viene ancora compilato al momento della scrittura con gcc 7.4.0. In questi due esempi utilizziamo anche autoanziché la dichiarazione del tipo a sinistra. In tutti i casi, utilizzare l'array come al solito:

buffer_old[0] = buffer_new[0] = 17;

Perdite di memoria da newe raddoppiate dal raddoppio deleteè qualcosa che il C ++ è stato oggetto di violenze per molti anni, essendo il "punto centrale" dell'argomentazione per passare ad altre lingue. Forse meglio evitare.


Dovresti evitare i unique/shared_ptrcostruttori a favore make_unique/shared, non solo non devi scrivere due volte il tipo costruito (usando auto) ma non rischi di perdere memoria o risorse se la costruzione fallisce parzialmente (se stai usando un tipo che può fallire)
Simon Buchan,

2
make_unique è disponibile con matrici da C ++ 14 e make_shared solo da C ++ 20. Questa è ancora raramente un'impostazione di default, quindi proporre std :: make_shared <int []> (size) mi ha cercato un po 'in anticipo.
Audrius Meskauskas il

Giusto! Non uso davvero make_shared<int[]>molto, quando quasi sempre lo desideri vector<int>, ma è buono a sapersi.
Simon Buchan,

unique_ptrPedanteria eccessiva, ma IIRC il costruttore non è nrow, quindi per Tquesto non hanno costruttori costruttori, quindi non c'è rischio di perdite con unique_ptr(new int[size])e shared_ptrha il seguente: "Se viene generata un'eccezione, delete p viene chiamato quando T non è un tipo di array, delete [ ] p altrimenti. ", quindi hai lo stesso effetto - il rischio è per unique/shared_ptr(new MyPossiblyAllocatingType[size]).
Simon Buchan,

3

nuovo ed elimina non vengono deprecati.

Gli oggetti creati dal nuovo operatore possono essere passati per riferimento. Gli oggetti possono essere cancellati usando delete.

nuovo ed elimina sono gli aspetti fondamentali della lingua. La persistenza di un oggetto può essere gestita usando new ed delete. Questi sicuramente non saranno deprecati.

L'istruzione - int array [N] è un modo per definire un array. L'array può essere utilizzato nell'ambito del blocco di codice allegato. Non può essere passato come il modo in cui un oggetto viene passato a un'altra funzione.


2

Il primo esempio ha bisogno di un delete[]alla fine, altrimenti si avrà una perdita di memoria.

Il secondo esempio usa una lunghezza di array variabile che non è supportata da C ++; esso permette solo espressione-costante per lunghezza dell'array .

In questo caso è utile utilizzare std::vector<>come soluzione; che racchiude tutte le azioni che è possibile eseguire su un array in una classe modello.


3
Cosa intendi con "fino a C ++ 11"? Sono abbastanza sicuro, i VLA non sono mai diventati parte dello standard.
Churill

guarda lo standard di c ++ 14 [standard c ++ 14] ( isocpp.org/files/papers/N3690.pdf ) a pagina 184 paragrafo 8.3.4
rasoio a zig

4
Quello non è il . Standard, ma solo una bozza e la parte di “matrici di runtime legato" non ha farne lo standard per quanto posso dire cppreference non menziona i VLA lì.
churill

1
@zigrazor cppreference.com ha un elenco di collegamenti ai progetti più vicini prima / dopo la pubblicazione di ciascuno degli standard. Gli standard pubblicati non sono disponibili gratuitamente, ma questi progetti dovrebbero essere molto vicini. Come puoi vedere dai numeri del documento, la bozza collegata è una bozza di lavoro precedente per C ++ 14.
noce

2
@learning_dude È non supportato dalle norme. La risposta è (ora) corretta (anche se breve). Funziona solo per te perché GCC lo consente come estensione non standard .
Noce

-4

La sintassi sembra C ++, ma il linguaggio è simile al semplice Algol60. Era comune avere blocchi di codice come questo:

read n;
begin
    integer array x[1:n];
    ... 
end;

L'esempio potrebbe essere scritto come:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

A volte mi manca questo nelle lingue attuali;)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.