L'allocazione dinamica è richiesta solo quando il tempo di vita dell'oggetto deve essere diverso dall'ambito in cui viene creato (vale anche per rendere l'ambito più piccolo più grande) e hai un motivo specifico in cui non è memorizzato per valore lavoro.
Per esempio:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
Da C ++ 11 in poi, abbiamo std::unique_ptr
a che fare con la memoria allocata, che contiene la proprietà della memoria allocata. std::shared_ptr
è stato creato per quando devi condividere la proprietà. (ti servirà meno di quanto ti aspetteresti in un buon programma)
La creazione di un'istanza diventa davvero semplice:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
C ++ 17 aggiunge anche std::optional
che può impedire di richiedere allocazioni di memoria
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Non appena "istanza" esce dall'ambito, la memoria viene ripulita. Il trasferimento della proprietà è anche facile:
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
Quindi quando hai ancora bisogno new
? Quasi mai da C ++ 11 in poi. La maggior parte di essi viene utilizzata std::make_unique
fino a quando non si raggiunge un punto in cui si colpisce un'API che trasferisce la proprietà tramite puntatori non elaborati.
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
In C ++ 98/03, è necessario eseguire la gestione manuale della memoria. In questo caso, prova a passare a una versione più recente dello standard. Se sei bloccato:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Assicurati di monitorare correttamente la proprietà per non avere perdite di memoria! La semantica di Move non funziona ancora.
Quindi, quando avremo bisogno di malloc in C ++? L'unico motivo valido sarebbe quello di allocare memoria e inizializzarla in seguito tramite il posizionamento nuovo.
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
Anche se quanto sopra è valido, questo può essere fatto anche tramite un nuovo operatore. std::vector
è un buon esempio per questo.
Infine, abbiamo ancora l'elefante nella stanza: C
. Se devi lavorare con una libreria C in cui la memoria viene allocata nel codice C ++ e liberata nel codice C (o viceversa), sei costretto a usare malloc / free.
Se ti trovi in questo caso, dimentica le funzioni virtuali, le funzioni membro, le classi ... Sono consentite solo le strutture con POD.
Alcune eccezioni alle regole:
- Stai scrivendo una libreria standard con strutture dati avanzate in cui malloc è appropriato
- Devi allocare grandi quantità di memoria (nella copia di memoria di un file da 10 GB?)
- Hai degli strumenti che ti impediscono di usare determinati costrutti
- Devi memorizzare un tipo incompleto