Come inizializzare la memoria con il nuovo operatore in C ++?


176

Sto appena iniziando a entrare in C ++ e voglio prendere alcune buone abitudini. Se ho appena assegnato una matrice di tipo intcon l' newoperatore, come posso inizializzarli tutti su 0 senza eseguirli tutti? Dovrei solo usare memset? Esiste un modo "C ++" per farlo?


19
Se vuoi prendere una buona abitudine C ++, evita di usare direttamente le matrici e usa invece vector. Vector inizializzerà tutti gli elementi indipendentemente dal tipo, quindi non è necessario ricordare di chiamare l'operatore delete [].
Brianegge,

@brianegge: cosa succede se devo passare un array a una funzione C esterna, posso semplicemente dargli il vettore?
dreamlax,

12
Puoi passare &vector[0].
jamesdlin,

Naturalmente, quando si passano gli array alle funzioni C, in genere è necessario specificare il puntatore al primo elemento, & vector [0] come ha detto @jamesdlin e le dimensioni dell'array, fornite in questo caso da vector.size ().
Trebor Rude,

Correlato (richiede tipi non array): stackoverflow.com/questions/7546620/…
Aconcagua

Risposte:


392

È una funzionalità sorprendentemente poco nota di C ++ (come dimostra il fatto che nessuno ha ancora fornito questa risposta), ma in realtà ha una sintassi speciale per l'inizializzazione del valore di un array:

new int[10]();

Si noti che è necessario utilizzare le parentesi vuote, ad esempio non è possibile utilizzare (0)o altro (motivo per cui ciò è utile solo per l'inizializzazione del valore).

Ciò è esplicitamente consentito dalla ISO C ++ 03 5.3.4 [expr.new] / 15, che dice:

Una nuova espressione che crea un oggetto di tipo Tinizializza l'oggetto nel modo seguente:

...

  • Se il nuovo inizializzatore è nel modulo (), l'elemento viene inizializzato in base al valore (8.5);

e non limita i tipi per i quali ciò è consentito, mentre il (expression-list)modulo è esplicitamente limitato da ulteriori regole nella stessa sezione in modo tale da non consentire tipi di array.


1
Anche se sono d'accordo sul fatto che questo è poco noto, non posso (del tutto) concordare sul fatto che sia davvero molto sorprendente - è stato aggiunto in C ++ 03, che la maggior parte delle persone sembra aver quasi ignorato (poiché questa era una delle poche cose nuove ha aggiunto).
Jerry Coffin,

2
@Jerry: devo ammettere che non lo sapevo ancora (probabilmente perché quando sono arrivato a leggere lo standard, era già C ++ 03). Detto questo, è notevole che tutte le implementazioni che conosco supportino questo (suppongo sia perché è così banale da implementare).
Pavel Minaev,

2
Sì, è abbastanza banale da implementare. Per quanto nuovo, tutta l ' "inizializzazione del valore" era nuova in C ++ 03.
Jerry Coffin,

34
In C ++ 11 è possibile utilizzare initializtion uniforme pure: new int[10] {}. Puoi anche fornire valori da inizializzare con:new int[10] {1,2,3}
bames53

Non confondere l'inizializzazione predefinita con l'inizializzazione del valore: sono entrambi chiaramente definiti nello standard e sono inizializzazioni diverse.
Deduplicatore,

25

Supponendo che tu voglia davvero un array e non uno std :: vector, il "modo C ++" sarebbe questo

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

Tuttavia, tieni presente che questo è in realtà solo un ciclo che assegna ogni elemento a 0 (non c'è davvero un altro modo per farlo, escludendo un'architettura speciale con supporto a livello hardware).


Non mi importa se il loop è implementato al di sotto di una funzione, volevo solo sapere se dovevo implementare o meno un loop del genere. Grazie per il consiglio.
dreamlax,

4
Potresti essere sorpreso. Ero. Sul mio STL (sia GCC che Dinkumware), std :: copy si trasforma in realtà in un memcpy se rileva che viene chiamato con tipi predefiniti. Non sarei sorpreso se std :: fill_n usasse memset.
Brian Neal,

2
No. Utilizza "Inizializzazione valore" per impostare tutti i membri su 0.
Martin York,

24

Esiste un numero di metodi per allocare un array di tipo intrinseco e tutti questi metodi sono corretti, sebbene quale scegliere, dipende ...

Inizializzazione manuale di tutti gli elementi in loop

int* p = new int[10];
for (int i = 0; i < 10; i++)
{
    p[i] = 0;
}

Utilizzando la std::memsetfunzione da<cstring>

int* p = new int[10];
std::memset(p, 0, sizeof(int) * 10);

Utilizzando l' std::fill_nalgoritmo di<algorithm>

int* p = new int[10];
std::fill_n(p, 10, 0);

Usando il std::vectorcontenitore

std::vector<int> v(10); // elements zero'ed

Se C ++ 0x disponibile, utilizza le funzionalità dell'elenco di inizializzatori

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime

1
dovrebbe essere vector <int> Se hai aggiunto p = new int [10] () hai un elenco completo.
Karsten,

@mloskot, nel primo caso in cui è stato inizializzato un array utilizzando "nuovo", come verrà eseguito il passaggio per riferimento? Se avessi usato la int array[SIZE] ={1,2,3,4,5,6,7};notazione, potrei usare void rotateArray(int (& input)[SIZE], unsigned int k);sarebbe la mia dichiarazione di funzione, cosa sarebbe quando uso la prima convenzione? qualche suggerimento?
Anu,

1
Temo che l'esempio std::memsetsia sbagliato: si passa 10, sembra aspettarsi il numero di byte, vedere en.cppreference.com/w/cpp/string/byte/memset . (Penso che ciò mostri bene perché si dovrebbe evitare un tale costrutto di basso livello quando possibile.)
Suma,

@Suma Grande cattura! Fisso. Questo sembra essere un candidato per un bug di dieci anni :-) Sì, sono d'accordo con il tuo commento.
mloskot,

7

Se la memoria che stai allocando è una classe con un costruttore che fa qualcosa di utile, l'operatore new chiamerà quel costruttore e lascerà il tuo oggetto inizializzato.

Ma se stai allocando un POD o qualcosa che non ha un costruttore che inizializza lo stato dell'oggetto, allora non puoi allocare memoria e inizializzare quella memoria con l'operatore nuovo in una sola operazione. Tuttavia, hai diverse opzioni:

1) Utilizzare invece una variabile di stack. È possibile allocare e inizializzare per impostazione predefinita in un solo passaggio, in questo modo:

int vals[100] = {0};  // first element is a matter of style

2) uso memset(). Nota che se l'oggetto che stai allocando non è un POD , memorizzarlo è una cattiva idea. Un esempio specifico è se memorizzi una classe con funzioni virtuali, spazzerai via la vtable e lascerai il tuo oggetto in uno stato inutilizzabile.

3) Molti sistemi operativi hanno chiamate che fanno quello che vuoi: allocare su un heap e inizializzare i dati su qualcosa. Un esempio di Windows sarebbeVirtualAlloc()

4) Questa è di solito l'opzione migliore. Evita di dover gestire la memoria da solo. Puoi usare i contenitori STL per fare praticamente tutto ciò che faresti con la memoria grezza, inclusa l'allocazione e l'inizializzazione in un colpo solo:

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero

6

Si C'è:

std::vector<int> vec(SIZE, 0);

Utilizzare un vettore anziché un array allocato dinamicamente. I vantaggi includono la necessità di non doversi preoccupare di eliminare esplicitamente l'array (viene eliminato quando il vettore esce dall'ambito) e anche che la memoria viene automaticamente eliminata anche se viene generata un'eccezione.

Modifica: per evitare ulteriori riduzioni del drive-by da parte di persone che non si preoccupano di leggere i commenti qui sotto, dovrei chiarire che questa risposta non dice che il vettore è sempre la risposta giusta. Ma è sicuramente un modo più C ++ che "manualmente" assicurandosi di eliminare un array.

Ora con C ++ 11, c'è anche std :: array che modella un array di dimensioni costanti (rispetto al vettore che è in grado di crescere). C'è anche std :: unique_ptr che gestisce un array allocato dinamicamente (che può essere combinato con l'inizializzazione come risposta in altre risposte a questa domanda). Ognuno di questi è un modo più C ++ rispetto alla gestione manuale del puntatore alla matrice, IMHO.


11
questo non risponde davvero alla domanda che è stata posta.
John Knoeller,

1
Devo usare sempre std::vectorinvece di array allocati dinamicamente? Quali sono i vantaggi dell'utilizzo di un array su un vettore e viceversa?
dreamlax,

1
@ John Knoller: L'OP ha chiesto un modo C ++ per farlo, direi che il vettore è il modo c ++ per farlo. Naturalmente, hai ragione nel dire che potrebbero esserci situazioni che richiederebbero ancora un array semplice e non conoscendo la situazione del PO, questa potrebbe essere una. Immagino di no, poiché sembra plausibile che l'OP non sia a conoscenza dei vettori.
villintehaspam,

1
@villintehaspam: Sebbene questa soluzione non risponda alla mia domanda, è il percorso che sto per prendere. Tyler McHenry risponde alla mia domanda in modo più diretto e dovrebbe aiutare soprattutto le persone che non possono - per qualsiasi motivo - utilizzare std::vector.
dreamlax,

2
@villintehaspam: No, non è un modo C ++ per farlo. È il modo Java per farlo. Sticking vectorovunque indipendentemente dal contesto è chiamato "Scrivere codice Java in C ++".
AnT

2

std::fillè un modo. Richiede due iteratori e un valore per riempire la regione. Quello, o il ciclo for, sarebbe (suppongo) il modo più C ++.

Per impostare una matrice di tipi interi primitivi su 0 in modo specifico, memsetva bene, anche se può sollevare le sopracciglia. Considera anche calloc, sebbene sia un po 'scomodo usare da C ++ a causa del cast.

Da parte mia, uso quasi sempre un loop.

(Non mi piace indovinare le intenzioni delle persone, ma è vero che std::vector, a parità di condizioni, è preferibile usarle new[].)


1

puoi sempre usare memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));

Capisco che posso usare memset, ma non ero sicuro se questo fosse il modo C ++ per affrontare il problema.
dreamlax,

1
In realtà non è il "modo C ++", ma nemmeno gli array grezzi.
Pete Kirkham,

1
@gbrandt: Vale a dire che non funziona molto bene in C o C ++. Funziona per la maggior parte dei valori del tipo char o unsigned char. Funziona per la maggior parte dei tipi di valore pari a 0 (almeno nella maggior parte delle implementazioni). Altrimenti, è generalmente inutile.
Jerry Coffin,

1
10 * sizeof( *myArray )è più documentato e a prova di modifica di 10 * sizeof( int ).
Kevin Reid,

1
In ogni caso, l'OP ha un array grezzo e memset è il modo più rapido e semplice per azzerare quell'array.
Gregor Brandt,

-1

In genere per gli elenchi dinamici di elementi, si utilizza a std::vector.

In genere utilizzo memset o un ciclo per l'allocazione dinamica della memoria non elaborata, a seconda della variabile che prevedo in futuro per quell'area di codice.

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.