Gli array dovrebbero essere usati in C ++?


95

Dal momento std::listche std::vectoresistono, c'è una ragione per usare gli array C tradizionali in C ++, o dovrebbero essere evitati, proprio come malloc?



18
@Als: Questa domanda riguarda la differenza tra due contenitori specifici, mentre questa domanda riguarda la differenza tra array non elaborati e contenitori standard in generale.
Jon Purdy,

Risposte:


109

In C ++ 11, dove std::arrayè disponibile, la risposta è "sì, gli array dovrebbero essere evitati". Prima di C ++ 11, potrebbe essere necessario utilizzare array C per allocare array nell'archiviazione automatica (cioè nello stack).


3
Tuttavia, molti compilatori non supportano ancora C ++ 11. Dovrai decidere quando è meglio usare uno piuttosto che l'altro vista la mancanza di std :: array
Nowayz

4
std :: array è un modello, che influisce su grandi progetti in termini di tempo di compilazione e possibilmente dimensione del codice poiché per ogni combinazione di T, N il modello viene istanziato di nuovo.
zvrba

std :: vector garantisce l'allineamento dei dati per standard, quindi può essere utilizzato quasi ovunque. Con C ++ 11 non c'è davvero alcun motivo per usare gli array C.
Nils

16
Anche gli array @Nils garantiscono l'allineamento. Inoltre, l'allocazione automatica della memoria ("lo stack") è molto più veloce dell'allocazione dinamica della memoria. Se so di avere esattamente 3 elementi [ad esempio, le coordinate di un triangolo], non c'è motivo di usare il vettore.
zvrba

9
@zvrba: controlla l'assembly generato quando si utilizzano std :: array rispetto a array C. Nessuna differenza.
Nemanja Trifunovic

85

Sicuramente, anche se con std::arrayin C ++ 11, praticamente solo per dati statici. Gli array in stile C hanno tre importanti vantaggi rispetto a std::vector:

  • Non richiedono allocazione dinamica. Per questo motivo, gli array in stile C sono da preferire dove è probabile che ci siano molti array molto piccoli. Di 'qualcosa come un punto a dimensione n:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };
    

    Tipicamente, si potrebbe immaginare un che dimssarà molto piccolo (2 o 3), Tun tipo incorporato ( double) e che si potrebbe finire std::vector<Point>con milioni di elementi. Sicuramente non vuoi milioni di allocazioni dinamiche di 3 doppie.

  • L'inizializzazione statica del supporto. Questo è solo un problema per i dati statici, dove qualcosa come:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };
    

    Questo è spesso preferibile all'utilizzo di un vettore (e std::string), poiché evita tutti i problemi di ordine di inizializzazione; i dati vengono precaricati, prima che qualsiasi codice effettivo possa essere eseguito.

  • Infine, in relazione a quanto sopra, il compilatore può calcolare la dimensione effettiva dell'array dagli inizializzatori. Non devi contarli.

Se hai accesso a C ++ 11, std::arrayrisolve i primi due problemi e nel primo caso dovrebbe essere sicuramente utilizzato in preferenza agli array in stile C. Non si occupa del terzo, tuttavia, e avere il compilatore che dimensiona l'array in base al numero di inizializzatori è ancora un motivo valido per preferire gli array in stile C.


11
L'inizializzazione dell'array in stile C elimina anche la necessità di ripetersi. int i[] = { 1, 2, 3 };continua a lavorare con int i[] = { 1, 2, 3, 4 };. array<int, 3>deve essere modificato manualmente in array<int, 4>.

10
@JoeWreschnig Un cambiamento che puoi facilmente dimenticare. Se aggiungi un elemento, il compilatore dovrebbe lamentarsi, ma se ne rimuovi uno, ti ritroverai con un elemento inizializzato 0 in più alla fine. Uso ancora ampiamente gli array in stile C per questo tipo di dati statici.
James Kanze

3
La prima frase non ha senso.
Konrad Rudolph

4
Il terzo punto può essere risolto in modo piuttosto elegante utilizzando una make_arrayfunzione , simile a make_pairecc. Hat-tip a @R. Martinho Fernandes .
Konrad Rudolph

@KonradRudolph: Certo che lo fa. Andreas chiede "Gli array dovrebbero essere usati in C ++?", A cui James risponde "Sicuramente, anche se std::arrayin C ++ 11, [dovrebbero essere usati] praticamente solo per dati statici".
Jon Purdy,

15

Non dire mai "mai", ma sarei d'accordo sul fatto che il loro ruolo è notevolmente ridotto dalle vere strutture di dati da STL.

Direi anche che l'incapsulamento all'interno di oggetti dovrebbe ridurre al minimo l'impatto di scelte come questa. Se l'array è un membro di dati privati, puoi scambiarlo dentro o fuori senza influenzare i client della tua classe.


11

Ho lavorato su sistemi critici per la sicurezza in cui non è possibile utilizzare l'allocazione dinamica della memoria. La memoria deve essere sempre in pila. Pertanto in questo caso usereste gli array poiché la dimensione è fissata in fase di compilazione.


8
Prima di C ++ 11 avrei accettato, ma std::array<T>alloca sugli stack e fondamentalmente non ha overhead su un array grezzo.
111111

5
@ 111111 - D'accordo. Ma so che alcune persone in quel settore non sono ancora passate a C ++ 11
Ed Heal

So che è per questo che non ti ho sottovalutato, ma penso che Boost avesse una versione ed è facile lanciarne anche una tua.
111111

6
ma nei sistemi critici per la sicurezza, non si utilizzano nuove funzionalità del compilatore (meno testate) e non si utilizza boost.
James Kanze,

3
Molti sistemi critici per la sicurezza sono costruiti su VECCHI compilatori che non hanno nemmeno le nuove funzionalità del compilatore perché la modifica delle toolchain è un processo lento e costoso che richiede tonnellate di documenti, test e certificazioni.
Brian McFarland

6

arrayin c++ti offre un'alternativa rapida a dimensioni fisse di dimensioni dinamiche std::vectore std::list. std :: array è una delle aggiunte in c++11. Fornisce il vantaggio dei contenitori std pur fornendo la semantica di tipo aggregato degli array in stile C.

Quindi c++11userei sicuramente std::array, dove è richiesto, sul vettore. Ma eviterei l'array in stile C in C++03.


4

La maggior parte di solito, no , non posso pensare a una ragione per usare le matrici prime sopra, dire vectors. Se il codice è nuovo .

Potrebbe essere necessario ricorrere all'uso di array se le librerie devono essere compatibili con il codice che prevede array e puntatori non elaborati.


1
... ma dal momento che C ++ 03 un vettore "ha effettivamente" un array, a cui puoi accedere tramite puntatore per leggere o scrivere. Quindi questo copre la maggior parte dei casi di codice che si aspettano puntatori ad array. È solo quando quel codice alloca o libera l'array che non puoi usare un vettore.
Steve Jessop

@SteveJessop puoi accedere all'array interno?
Luchian Grigore

1
@LuchianGrigore: vector.data()in C ++ 11 o &vector.front()precedente.
Mike Seymour

@Luchian: a condizione che il vettore non sia vuoto, puoi prendere un puntatore a un elemento (e se è vuoto, puoi passare un puntatore nullo e una lunghezza di 0 a qualsiasi funzione scritta in modo sensato che accetti il ​​caso limite di un buffer di dimensioni zero). Praticamente l'unico scopo della garanzia di contiguità dei vettori aggiunta in C ++ 03 era di consentire ai vettori di essere usati come buffer dal codice orientato al puntatore.
Steve Jessop

1
@SteveJessop E il fatto che molte persone pensassero che fosse comunque garantito, ed è stato considerato preferibile non deluderli.
James Kanze

4

So che molte persone indicano std :: array per l'allocazione di array sullo stack e std :: vector per l'heap. Ma nessuno dei due sembra supportare l'allineamento non nativo. Se stai eseguendo qualsiasi tipo di codice numerico su cui desideri utilizzare le istruzioni SSE o VPX (richiedendo quindi rispettivamente un allineamento di 128 o 256 byte), gli array C sembrerebbero comunque essere la soluzione migliore.


3

Direi che gli array sono ancora utili, se si memorizza una piccola quantità statica di dati perché no.


2

L'unico vantaggio di un array (ovviamente avvolto in qualcosa che gestirà automaticamente la sua deallocazione quando necessario) su std::vectorcui posso pensare è che vectornon può passare la proprietà dei suoi dati, a meno che il tuo compilatore non supporti C ++ 11 e muova i costruttori.


6
"il vettore non può passare la proprietà dei suoi dati" - sì, in C ++ 03, utilizzando swap.
Steve Jessop

2

Gli array in stile C sono una struttura dati fondamentale, quindi ci saranno casi in cui è meglio usarli. Per il caso generale, tuttavia, utilizzare le strutture di dati più avanzate che arrotondano gli angoli dei dati sottostanti. Il C ++ ti consente di fare alcune cose molto interessanti e utili con la memoria, molte delle quali funzionano con semplici array.


3
In che modo gli array in stile C sono più fondamentali di std::arrays? In molti casi, entrambi verranno compilati nello stesso assembly.
sinistra intorno

1
Più fondamentale in quanto è più basilare. Sai cosa farà un array, std :: array potrebbe avere stranezze di implementazione poiché si basa sulla libreria standard.
James Wynn

1
@ JamesWynn Non proprio. std::arrayha una semantica definita con precisione costruita su array statici.
Konrad Rudolph

1

Dovresti usare internamente i contenitori STL, ma non dovresti passare puntatori a tali contenitori tra diversi moduli, o finirai nell'inferno delle dipendenze. Esempio:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

è un'ottima soluzione ma non lo è

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

Il motivo è che std :: string può essere implementato in molti modi diversi, ma una stringa in stile c è sempre una stringa in stile c.


Penso che quello che stai cercando di dire sia: non collegare insieme codice oggetto diverso se per crearli sono stati utilizzati compilatori / implementazioni differenti della libreria standard. È certamente vero. In che modo questo si collega alla domanda originale?
jogojapan

È solo un consiglio quando utilizzare array o contenitori STL. Crea dati utilizzando un contenitore, passalo come array. Per altri dati che stringhe avresti qualcosa come myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329

Ma questi problemi sorgono solo quando si combinano diverse implementazioni della libreria standard nello stesso progetto. Questo è pazzesco. In qualsiasi normale parte di codice, è perfettamente corretto passare, ad esempio, un vettore, per riferimento (o, in C ++ 11, spostarlo) a una funzione.
jogojapan

1
Mi piacciono i plug-in
user877329
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.