2012-04-29 7 views
6

So che è un dibattito molto vecchio che è già stato discusso molte volte in tutto il mondo. Ma attualmente sto avendo problemi a decidere quale metodo dovrei usare piuttosto che un altro tra array statici e dinamici in un caso particolare. In realtà, non avrei usato C++ 11, avrei usato array statici. Ma ora sono confuso poiché potrebbero esserci benefici equivalenti con entrambi.Matrici statiche VS. array dinamici in C++ 11

Prima soluzione:

template<size_t N> 
class Foo 
{ 
    private: 
     int array[N]; 

    public: 
     // Some functions 
} 

Seconda soluzione:

template<size_t N> 
class Foo 
{ 
    private: 
     int* array; 

    public: 
     // Some functions 
} 

non posso capita di scegliere dal momento che i due hanno i loro vantaggi:

  • array statici sono più veloci, e non ci interessa affatto la gestione della memoria.
  • Gli array dinamici non occupano nulla finché la memoria non viene allocata. Dopodiché, sono meno pratici da usare rispetto agli array statici. Ma dal momento che C++ 11, possiamo ottenere grandi benefici dalla semantica del movimento, che non possiamo usare con gli array statici.

Non penso ci sia una buona soluzione, ma mi piacerebbe avere qualche consiglio o semplicemente sapere cosa ne pensi di tutto questo.

+2

Le tue due soluzioni dovrebbero essere: il tuo primo modello, o un vettore e nessun modello size_t.Evitare l'uso di puntatori grezzi in cui un contenitore STL può essere utilizzato senza problemi. – mfontanini

+13

@fontanini: Se si dovesse modificare per C++ 11, le due soluzioni dovrebbero essere 'std :: array ' o 'std :: vector '. –

+1

@ DavidRodríguez-dribeas si signore! – mfontanini

risposta

5

In realtà non sono d'accordo con "dipende". Non utilizzare mai l'opzione 2. Se si desidera utilizzare una costante di traduzione, utilizzare sempre l'opzione 1 o std :: array. L'unico vantaggio che hai elencato, che gli array dinamici non pesano nulla fino a quando sono assegnati, è in realtà un orribile, enorme svantaggio, e uno che deve essere sottolineato con grande enfasi.

Non disporre mai di oggetti con più di una fase di costruzione. Mai e poi mai. Questa dovrebbe essere una regola dedicata alla memoria attraverso qualche grande tatuaggio. Non farlo mai.

Quando si hanno oggetti zombi che non sono ancora abbastanza vivi, anche se non del tutto morti, la complessità nella gestione della loro vita cresce esponenzialmente. Devi controllare in ogni metodo se è completamente vivo, o solo fingere di essere vivo. La sicurezza delle eccezioni richiede casi speciali nel distruttore. Invece di una semplice costruzione e distruzione automatica, ora hai aggiunto i requisiti che devono essere controllati in N posti diversi (# metodi + dtor). E al compilatore non interessa se controlli. E gli altri ingegneri non avranno questo requisito trasmesso, quindi possono regolare il codice in modi non sicuri, usando variabili senza controllo. E ora tutti questi metodi hanno comportamenti multipli a seconda dello stato dell'oggetto, quindi ogni utente dell'oggetto ha bisogno di sapere cosa aspettarsi. Gli zombi rovineranno la tua vita (codifica) .

Invece, se si hanno due diverse durate naturali nel programma, utilizzare due oggetti diversi. Ma ciò significa che hai due stati diversi nel tuo programma, quindi dovresti avere una macchina a stati, con uno stato che ha un solo oggetto e un altro stato con entrambi, separati da un evento asincrono. Se non vi è alcun evento asincrono tra i due punti, se tutti rientrano in un ambito di funzione, allora la separazione è artificiale e si dovrebbe fare la costruzione monofase.

L'unico caso in cui una dimensione del tempo di traduzione deve essere convertita in un'allocazione dinamica è quando la dimensione è troppo grande per lo stack. Questo arriva all'ottimizzazione della memoria, e dovrebbe sempre essere valutato usando strumenti di memoria e di profilazione per vedere cosa è meglio. L'opzione 2 non sarà mai la migliore (usa un puntatore nudo - quindi di nuovo perdiamo RAII e qualsiasi pulizia e gestione automatica, aggiungendo invarianti e rendendo il codice più complesso e facilmente infrangibile da altri). Vector (come suggerito da bitmask) sarebbe il primo pensiero appropriato, anche se potrebbe non piacervi i costi di allocazione dell'heap nel tempo. Altre opzioni potrebbero essere spazio statico nell'immagine dell'applicazione. Ma ancora una volta, questi dovrebbero essere considerati solo dopo aver determinato che si dispone di un vincolo di memoria e cosa fare da lì dovrebbe essere determinato da reali esigenze misurabili.

+0

Sì, il tuo impegno ha un senso. Durante la lettura del mio codice di nuovo, mi sono reso conto che in realtà non avevo mai avuto oggetti zombi, dal momento che la memoria era stata allocata in costruzione. Quindi non preoccuparti di questo. Giusto, gli unici vantaggi erano il guadagno dalle operazioni di spostamento alla fine (già fornito da std :: vector se usato). L'unico inconveniente di utilizzare un vettore sarebbe un sovraccarico rispetto a std :: array. Ma non so se std :: array abbia spostato la semantica ... – Morwenn

+0

Nello stack, in genere non sono necessarie le mosse, è necessario l'ottimizzazione del valore restituito, che sebbene sia specifico per il compilatore, è abbastanza universale. Gli altri casi in cui è possibile utilizzare le mosse possono quasi sempre essere eseguiti sul posto (nessuna copia), o quando è necessaria una copia, è effettivamente necessario (per mettere la memoria di un tipo in un posto diverso), quindi le mosse non funzionano. – ex0du5

4

Utilizzare nessuno dei due. È meglio usare std::vector quasi in ogni caso. Negli altri casi, ciò dipende in gran parte dal motivo per cui std::vector sarebbe insufficiente e quindi non si può rispondere in generale!

+0

Conosco già questa regola. Sarei per lavoro professionale, lo userei. Tuttavia, mi piace sperimentare e guadagnare esperienza provando. Inoltre, non avevo davvero bisogno di nient'altro che l'accesso casuale nel caso presente. Ecco perché ho pensato di provare la mia mano a questo. E, sì, dato che std :: vector dipende dall'implementazione, ci può essere un grande sovraccarico se utilizzo molte istanze di questa classe poiché la dimensione dell'array non è destinata a essere modificata dopo. – Morwenn

4

Attualmente sto avendo un problema per decidere quale dovrei usare più di un altro in un caso particolare.

È necessario considerare le opzioni caso per caso per determinare la soluzione ottimale per il contesto dato, ovvero non è possibile effettuare una generalizzazione. Se un contenitore fosse ideale per ogni scenario, l'altro sarebbe obsoleto.

Come già accennato, considerare l'utilizzo delle implementazioni std prima di scriverle.

Maggiori dettagli:

lunghezza fissa

  • Prestare attenzione di quanta parte della pila che si consumano.
  • Può consumare più memoria, se lo si considera come un contenitore di dimensioni dinamiche.
  • copie veloci.

lunghezza variabile

  • riallocazione e il ridimensionamento può essere costoso.
  • Può consumare più memoria del necessario.
  • Mosse veloci.

La scelta migliore richiede anche a capire la complessità della creazione, copia, assegnare, ecc dei tipi di elementi.

E se si utilizzano le implementazioni std, ricordare che le implementazioni possono variare.

Infine, è possibile creare un contenitore per questi tipi che astragga i dettagli dell'implementazione e selezioni dinamicamente un membro dati appropriato in base alla dimensione e al contesto, astraendo i dettagli dietro un'interfaccia generale. Ciò è utile anche a volte per disabilitare funzionalità o per rendere più ovvie alcune operazioni (ad esempio copie costose).

In breve, è necessario conoscere molto sui tipi e sull'utilizzo e misurare diversi aspetti del programma per determinare il tipo di contenitore ottimale per uno scenario specifico.

+1

Nel caso presente, poiché avevo bisogno delle specializzazioni di template per N <4, pensavo di poter usare matrici statiche per quelle e quelle dinamiche quando N è più grande. La memoria viene assegnata una volta e l'array non viene mai ridimensionato, non è un grande sovraccarico. Dato che mi piacciono le tue idee nell'ultima parte della tua risposta. Grazie! – Morwenn

+0

@Morwenn prego :) – justin

+1

In che modo differiscono i tempi di copia? – ex0du5