Qual è il punto dell'allocazione dinamica in C ++?

5

Non l'ho mai capito davvero. Posso farlo, ma non capisco perché vorrei.

Ad esempio, stavo programmando un gioco ieri e ho impostato una serie di puntatori per i nemici allocati dinamicamente nel gioco, quindi l'ho passato a una funzione che aggiorna le loro posizioni.

Quando ho eseguito il gioco, ho ricevuto uno di quegli errori di asserzione non descrittivi, qualcosa su un blocco di memoria non esistente, non lo so. Era un errore in fase di esecuzione, quindi non ha detto dove si trovava il problema. Quindi ho appena detto avvitarlo e riscriverlo con l'istanza statica, cioè:

while(n<4)
{
Enemy tempEnemy = Enemy(3, 4);
enemyVector.push_back(tempEnemy);
n++;
}
updatePositions(&enemyVector);

E ha funzionato immediatamente alla perfezione.

Ora certo, alcuni di voi potrebbero pensare qualcosa all'effetto di "Forse se sapessi cosa stavi facendo" o forse "n00b non può usare i puntatori L0L", ma francamente, non puoi davvero negarlo rendono le cose troppo complicate, quindi la maggior parte delle lingue moderne le ha eliminate completamente.

Ma per favore ... qualcuno - Cosa È il punto di allocazione dinamica? Che vantaggio offre? Perché non dovrei mai fare ciò che ho appena fatto nell'esempio sopra?

    
posta temporary_user_name 09.06.2012 - 19:08
fonte

4 risposte

25

Prima di tutto, sei utilizza l'allocazione dinamica. Non devi gestire da solo i puntatori grezzi. Ma std::vector , come quasi tutte le altre strutture dati utili, alloca internamente la memoria in modo dinamico. Questo perché le tue uniche alternative sono: Assegnazione statica (troppo limitata, devi conoscere la dimensione in tempo di compilazione ) e allocazione dello stack (troppo limitata, qualche megabyte al massimo e viene deallocata al più presto come restituisce la funzione di allocazione). Dynamic ti offre la maggiore quantità di memoria e la massima libertà di utilizzo. Un puntatore o riferimento astrae anche la dimensione di ciò che è allocato - nello stack, devi sapere al momento della compilazione (o usare alloca per allocarlo manualmente, ma poi hai bisogno di ancora più attenzione e ancora ottenere un puntatore).

In secondo luogo, molti programmatori C ++ saranno d'accordo sul fatto che i puntatori grezzi non sono molto utili la maggior parte del tempo, proprio a causa della complessità e della tendenza all'errore che citi. Ne hai bisogno sotto il cofano, e dovresti essere assolutamente in grado di usarli, ma non dovresti farlo nel 99% di tutto il codice nell'interesse di sanità mentale, correttezza, prestazioni del programmatore, sicurezza delle eccezioni , eliminazione della perdita di memoria, ecc. Le tue alternative sono alcune combinazioni di contenitori ( std::vector è solo la punta dell'iceberg), puntatori intelligenti, altre forme di RAII, semplice allocazione di vecchi stack ogni volta che puoi farla franca, e riferimenti.

Si noti inoltre che esiste una differenza tra i puntatori (e altri tipi di riferimento indiretto) e l'allocazione dinamica. Puntatori (grezzi e intelligenti, oltre a riferimenti) ti offrono (come ha sottolineato un commentatore) il polimorfismo, indipendentemente da come viene assegnato l'oggetto puntato. ti dovrai avvolgere la tua mente attorno a loro, attorno a riferimenti, intorno a puntatori intelligenti e a questioni come la proprietà che ne seguiranno l'esempio, indipendentemente da cosa / dove / come lo assegni.

    
risposta data 09.06.2012 - 20:00
fonte
2

delnan ha fornito una buona spiegazione di cosa sia l'allocazione dinamica. +1 a quello.

L'unica cosa che voglio aggiungere è che usi l'allocazione dinamica quando è necessario. Se hai piccole dimensioni fisse di oggetti, sicuramente assegnarle staticamente è abbastanza buono per ora. Se hai bisogno di oggetti di breve durata all'interno di una singola funzione, certi oggetti locali nello stack sono fantastici.

Assegna gli oggetti solo dinamicamente quando gli altri metodi non sono abbastanza buoni. Non usarlo solo perché il linguaggio lo consente. Prima o poi, avrai bisogno.

Lo stesso vale per i modelli C ++. Ho visto troppe persone iniziare a plottare i modelli dappertutto solo perché. Poi le stesse persone si lamentano di come C ++ sia troppo complicato e complicato. C ++ ha molte funzionalità avanzate, ma spetta allo sviluppatore determinare quando e dove utilizzare queste funzionalità. Ma quando non ci sono altre alternative, l'allocazione dinamica (e i modelli) ti permetteranno di fare cose che altrimenti sarebbero state impossibili.

    
risposta data 09.06.2012 - 20:12
fonte
1

La principale differenza tra C ++ e un linguaggio come Java, è che in Java, fai una dichiarazione come

MyObject b = new MyObject(...);
myArrayOfObjects.add(b);

Gli oggetti sono riferimenti. Questa astrazione si è dimostrata molto potente nei linguaggi di livello superiore. Gli oggetti vengono allocati sull'heap e il conteggio dei riferimenti viene considerato, eliminando la nostra responsabilità di de-allocare quella memoria. Il garbage collector può semplicemente dire quando un oggetto non ha riferimenti ad esso e lo spazza via.

Il fatto è che il C ++ non è un livello superiore. Un vantaggio del C ++ è la possibilità di accedere alla gestione della memoria molto manuale per ottenere prestazioni migliori o ottimizzare alcune aree ad alte prestazioni del codice.

In C ++, se il tuo oggetto sarà un membro di una classe, o altrimenti passato in giro, memorizzato, e necessario da altre classi, ecc., dovresti usare un puntatore.

MyObject *myClassMemberVariable

Ora, ci sono propenenti in C ++ che sostengono mai usando puntatori grezzi come questo. Le librerie come boost e persino la libreria standard rendono classi come puntatori intelligenti e altre cose che tentano di implementare la gestione in qualche modo automatica della memoria delle risorse, "avvolgendo" i puntatori grezzi, fornendo schemi diversi per gestire la memoria.

Tutto dipende da te che devi decidere da solo quando usare i puntatori e come gestire la memoria. Questo è principalmente ciò che rende C ++ più difficile rispetto ad altri linguaggi (quello tra ... altre cose). Tuttavia, ha il suo uso e i suoi vantaggi per i progetti che lo utilizzano.

    
risposta data 09.06.2012 - 20:08
fonte
1

Bene, hai torto riguardo alle lingue moderne che non usano i puntatori. Li chiamano cose diverse, ma sono ancora puntatori sotto il cofano. Inoltre, non eliminano nemmeno le allocazioni di stack, ad esempio .NET e vediamo la differenza tra tipi di riferimento e tipi di valore.

Tuttavia non fa differenza per te. Il tuo problema è che non capisci cosa succede con le tue allocazioni.

I computer funzionano con un enorme ammasso di memoria che dividono in 2 gruppi, l'heap e lo stack. Lo stack viene utilizzato per il passaggio temporaneo di dati tra le chiamate di funzione, mentre l'heap è uno spazio di archiviazione più "globale" per gli oggetti. In C ++ ottieni lo stack gratuitamente allocando come hai fatto - Enemy e; è un'allocazione dello stack. Per utilizzare l'heap è necessario creare l'oggetto e quindi tenere traccia di dove è stato assegnato te stesso, in genere utilizzando un puntatore. Quindi Enemy* e = new Enemy(); assegnerà un oggetto di dimensioni Enemy all'heap, ma sarà un puntatore a quell'oggetto, non quell'oggetto stesso. Nota che e stesso è allocato nello stack (ogni variabile deve essere memorizzata da qualche parte dopo tutto).

Cosa succede ora quando chiami una funzione: conosci la copia per valore e copia per riferimento? Se hai allocato il tuo oggetto nello stack, otterrai una copia dell'oggetto quando chiami una funzione, ma se lo hai allocato sull'heap, quando passi il puntatore, otterrai una copia del puntatore ( che, naturalmente, punta ancora allo stesso oggetto sullo heap).

Suppongo che se sei abituato al modo in cui Java o .NET gestiscono gli oggetti basati su heap per te, allora il più vicino possibile con C ++ è utilizzare i puntatori intelligenti, shared_ptr < > è probabilmente quello che vuoi. Usa questi invece di puntatori grezzi, o impila oggetti e sarai molto più felice. Il bonus sulle lingue GC è che avrai anche una deallocazione istantanea di loro una volta che nessuno usa più l'oggetto (in modo da poter mettere le routine di morte del nemico nel distruttore e lasciare che il sistema lo chiami automaticamente quando il nemico viene eliminato )

    
risposta data 10.06.2012 - 02:05
fonte

Leggi altre domande sui tag