C++
Gestione della memoria
Ricerca…
Sintassi
- : :( opt ) nuovo ( espressione-elenco ) ( opt ) nuovo-tipo-id nuovo-inizializzatore ( opt )
- : :( opt ) nuovo ( espressione-elenco ) ( opt ) ( type-id ) new-initializer ( opt )
- : :( opt ) elimina cast-expression
- : :( opt ) delete [] cast-expression
- std :: unique_ptr < id-tipo > var_name (nuovo tipo-id ( opt )); // C ++ 11
- std :: shared_ptr < id-tipo > var_name (nuovo tipo-id ( opt )); // C ++ 11
- std :: shared_ptr < id-tipo > var_name = std :: make_shared < id-tipo > ( opt ); // C ++ 11
- std :: unique_ptr < id-tipo > var_name = std :: make_unique < id-tipo > ( opt ); // C ++ 14
Osservazioni
A leading ::
forza l'operatore new o delete a cercare in ambito globale, ignorando qualsiasi operatore new o delete sovraccarico specifico della classe.
Gli argomenti facoltativi che seguono la new
parola chiave vengono in genere utilizzati per chiamare il posizionamento nuovo , ma possono anche essere utilizzati per passare ulteriori informazioni all'allocatore, ad esempio un tag che richiede che la memoria venga allocata da un pool selezionato.
Il tipo allocato viene in genere specificato esplicitamente, ad esempio, new Foo
, ma può anche essere scritto come auto
(dal C ++ 11) o decltype(auto)
(dal C ++ 14) per dedurlo dall'inizializzatore.
L'inizializzazione dell'oggetto assegnato avviene secondo le stesse regole dell'inizializzazione delle variabili locali. In particolare, l'oggetto verrà inizializzato di default se l'inizializzatore è omesso e, quando si assegna dinamicamente un tipo scalare o un array di tipo scalare, non si garantisce che la memoria venga azzerata.
Un oggetto array creato da una nuova espressione deve essere distrutto usando delete[]
, indipendentemente dal fatto che la nuova espressione sia stata scritta con []
o meno. Per esempio:
using IA = int[4];
int* pIA = new IA;
delete[] pIA; // right
// delete pIA; // wrong
Pila
Lo stack è una piccola regione di memoria in cui vengono inseriti valori temporanei durante l'esecuzione. L'allocazione dei dati nello stack è molto veloce rispetto all'allocazione dell'heap, poiché tutta la memoria è già stata assegnata per questo scopo.
int main() {
int a = 0; //Stored on the stack
return a;
}
Lo stack è chiamato perché le catene di chiamate di funzione avranno la memoria temporanea "impilata" l'una sull'altra, ognuna utilizzando una piccola sezione separata di memoria.
float bar() {
//f will be placed on the stack after anything else
float f = 2;
return f;
}
double foo() {
//d will be placed just after anything within main()
double d = bar();
return d;
}
int main() {
//The stack has no user variables stored in it until foo() is called
return (int)foo();
}
I dati memorizzati nello stack sono validi solo finché l'ambito che ha allocato la variabile è ancora attivo.
int* pA = nullptr;
void foo() {
int b = *pA;
pA = &b;
}
int main() {
int a = 5;
pA = &a;
foo();
//Undefined behavior, the value pointed to by pA is no longer in scope
a = *pA;
}
Archiviazione libera (heap, allocazione dinamica ...)
Il termine 'heap' è un termine di calcolo generale che indica un'area di memoria da cui le parti possono essere allocate e deallocate indipendentemente dalla memoria fornita dallo stack .
In C++
lo standard si riferisce a quest'area come al negozio gratuito che è considerato un termine più accurato.
Le aree di memoria allocate dall'archivio gratuito potrebbero vivere più a lungo dell'ambito originale in cui è stato allocato. Anche i dati troppo grandi per essere memorizzati nello stack possono essere allocati dal Free Store .
La memoria grezza può essere allocata e deallocata dalle parole chiave new ed delete .
float *foo = nullptr;
{
*foo = new float; // Allocates memory for a float
float bar; // Stack allocated
} // End lifetime of bar, while foo still alive
delete foo; // Deletes the memory for the float at pF, invalidating the pointer
foo = nullptr; // Setting the pointer to nullptr after delete is often considered good practice
È anche possibile allocare array di dimensioni fisse con nuovi e cancella , con una sintassi leggermente diversa. L'allocazione delle matrici non è compatibile con l'allocazione di non matrice e il mixaggio dei due porterà alla corruzione dell'heap. L'allocazione di una matrice alloca anche la memoria per tenere traccia della dimensione della matrice per l'eliminazione successiva in un modo definito dall'implementazione.
// Allocates memory for an array of 256 ints
int *foo = new int[256];
// Deletes an array of 256 ints at foo
delete[] foo;
Quando si utilizza new e delete invece malloc e free , il costruttore e il distruttore verranno eseguiti (simile agli oggetti basati sullo stack). Questo è il motivo per cui new e delete sono preferiti su malloc e gratuiti .
struct ComplexType {
int a = 0;
ComplexType() { std::cout << "Ctor" << std::endl; }
~ComplexType() { std::cout << "Dtor" << std::endl; }
};
// Allocates memory for a ComplexType, and calls its constructor
ComplexType *foo = new ComplexType();
//Calls the destructor for ComplexType() and deletes memory for a Complextype at pC
delete foo;
Da C ++ 11 in poi, si consiglia l'uso di puntatori intelligenti per indicare la proprietà.
C ++ 14 ha aggiunto std::make_unique
, cambiando la raccomandazione per favorire std::make_unique
o std::make_shared
invece di usare naked new e delete .
Posizionamento nuovo
Ci sono situazioni in cui non vogliamo fare affidamento su Free Store per allocare memoria e vogliamo usare allocazioni di memoria personalizzate usando new
.
Per queste situazioni possiamo usare Placement New
, dove possiamo dire all'operatore 'new' di allocare memoria da una locazione di memoria pre-allocata
Per esempio
int a4byteInteger;
char *a4byteChar = new (&a4byteInteger) char[4];
In questo esempio, la memoria puntata da a4byteChar
è di 4 byte allocata a 'stack' tramite la variabile intera a4byteInteger
.
Il vantaggio di questo tipo di allocazione di memoria è il fatto che i programmatori controllano l'allocazione. Nell'esempio sopra, poiché a4byteInteger
è allocato sullo stack, non è necessario effettuare una chiamata esplicita a "delete a4byteChar`.
Lo stesso comportamento può essere raggiunto anche per la memoria allocata dinamica. Per esempio
int *a8byteDynamicInteger = new int[2];
char *a8byteChar = new (a8byteDynamicInteger) char[8];
In questo caso, il puntatore di memoria di a8byteChar
si a8byteChar
alla memoria dinamica allocata da a8byteDynamicInteger
. In questo caso, tuttavia, è necessario chiamare esplicitamente delete a8byteDynamicInteger
per rilasciare la memoria
Un altro esempio per la classe C ++
struct ComplexType {
int a;
ComplexType() : a(0) {}
~ComplexType() {}
};
int main() {
char* dynArray = new char[256];
//Calls ComplexType's constructor to initialize memory as a ComplexType
new((void*)dynArray) ComplexType();
//Clean up memory once we're done
reinterpret_cast<ComplexType*>(dynArray)->~ComplexType();
delete[] dynArray;
//Stack memory can also be used with placement new
alignas(ComplexType) char localArray[256]; //alignas() available since C++11
new((void*)localArray) ComplexType();
//Only need to call the destructor for stack memory
reinterpret_cast<ComplexType*>(localArray)->~ComplexType();
return 0;
}