C++
Speicherverwaltung
Suche…
Syntax
- : :( opt ) new ( Ausdrucksliste ) ( opt ) new-type-id new-initializer ( opt )
- : :( opt ) new ( Ausdrucksliste ) ( opt ) ( Typ-ID ) new-initializer ( opt )
- : :( ( opt ) löscht den Cast-Ausdruck
- : :( ( opt ) delete [] Cast-Ausdruck
- std :: unique_ptr < Typ-ID > Variablenname (neue Typ-ID ( Opt )); // C ++ 11
- std :: shared_ptr < Typ-ID > Variablenname (neue Typ-ID ( Opt )); // C ++ 11
- std :: shared_ptr < typ-id > var_name = std :: make_shared < typ-id > ( opt ); // C ++ 11
- std :: unique_ptr < typ-id > var_name = std :: make_unique < typ-id > ( opt ); // C ++ 14
Bemerkungen
Ein Lead ::
zwingt den New- oder Delete-Operator dazu, im globalen Gültigkeitsbereich nachzuschlagen, wobei alle überlasteten klassenspezifischen New- oder Delete-Operatoren überschrieben werden.
Die optionalen Argumente nach dem new
Schlüsselwort werden normalerweise verwendet, um Platzierung neu aufzurufen, können jedoch auch verwendet werden, um zusätzliche Informationen an den Zuweiser zu übergeben, z. B. ein Tag, das anfordert, dass Speicher aus einem ausgewählten Pool zugewiesen wird.
Der zugewiesene Typ wird normalerweise explizit angegeben, z. B. new Foo
, kann aber auch als auto
(seit C ++ 11) oder decltype(auto)
(seit C ++ 14) geschrieben werden, um ihn vom Initialisierer abzuleiten.
Die Initialisierung des zugeordneten Objekts erfolgt nach den gleichen Regeln wie die Initialisierung lokaler Variablen. Insbesondere wird das Objekt standardmäßig initialisiert, wenn der Initialisierer nicht angegeben wird, und wenn ein Skalar-Typ oder ein Array mit einem Skalar-Typ dynamisch zugewiesen wird, kann nicht garantiert werden, dass der Speicher auf Null gesetzt wird.
Ein mit einem neuen Ausdruck erstelltes Array-Objekt muss mit delete[]
, unabhängig davon, ob der neue Ausdruck mit []
oder nicht. Zum Beispiel:
using IA = int[4];
int* pIA = new IA;
delete[] pIA; // right
// delete pIA; // wrong
Stapel
Der Stack ist ein kleiner Speicherbereich, in den temporäre Werte während der Ausführung eingefügt werden. Die Zuordnung von Daten in den Stapel ist im Vergleich zur Heap-Zuordnung sehr schnell, da der gesamte Speicher bereits für diesen Zweck zugewiesen wurde.
int main() {
int a = 0; //Stored on the stack
return a;
}
Der Stack wird benannt, weil Ketten von Funktionsaufrufen ihren temporären Speicher übereinander "stapeln", wobei jeder einen separaten kleinen Speicherbereich verwendet.
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();
}
Auf dem Stack gespeicherte Daten sind nur gültig, solange der Bereich, der die Variable zugewiesen hat, noch aktiv ist.
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;
}
Freier Speicher (Heap, dynamische Zuordnung ...)
Der Begriff "Heap" ist ein allgemeiner Berechnungsausdruck, der einen Speicherbereich bedeutet, aus dem Teile zugewiesen und freigegeben werden können, unabhängig von dem vom Stapel bereitgestellten Speicher.
In C++
der Standard diesen Bereich als Free Store, was als genauerer Begriff angesehen wird.
Vom Free Store zugewiesene Speicherbereiche leben möglicherweise länger als der ursprüngliche Umfang, in dem sie zugewiesen wurden. Daten, die zu groß sind, um auf dem Stack gespeichert zu werden, können auch aus dem Free Store zugewiesen werden.
Rohspeicher kann durch die neuen und Löschschlüsselwörter zugewiesen und freigegeben werden.
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
Es ist auch möglich, Arrays fester Größe mit new und delete mit einer etwas anderen Syntax zuzuordnen. Die Arrayzuweisung ist nicht kompatibel mit der Nicht-Arrayzuordnung. Das Mischen der beiden führt zu Heap-Beschädigungen. Durch die Zuweisung eines Arrays wird auch Speicher zugewiesen, um die Größe des Arrays für eine spätere Löschung in einer implementierungsdefinierten Weise zu verfolgen.
// Allocates memory for an array of 256 ints
int *foo = new int[256];
// Deletes an array of 256 ints at foo
delete[] foo;
Wenn Sie new und delete statt malloc und free verwenden , werden Konstruktor und Destruktor ausgeführt (ähnlich wie bei stapelbasierten Objekten). Deshalb werden Neu und Löschen gegenüber Malloc und kostenlos bevorzugt.
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;
Ab C ++ 11 wird die Verwendung von intelligenten Zeigern für die Angabe des Eigentums empfohlen.
C ++ 14 fügte std::make_unique
zur STL hinzu und änderte die Empfehlung, um std::make_unique
oder std::make_shared
zu std::make_shared
anstatt nacktes new und delete zu verwenden .
Platzierung neu
Es gibt Situationen, in denen wir uns beim Zuweisen von Speicher nicht auf Free Store verlassen möchten und benutzerdefinierte Speicherzuordnungen mit new
möchten.
Für diese Situationen können wir Placement New
, wo wir dem 'new'-Operator mitteilen können, dass er Speicherplatz von einem zuvor zugewiesenen Speicherplatz zuweisen soll
Zum Beispiel
int a4byteInteger;
char *a4byteChar = new (&a4byteInteger) char[4];
In diesem Beispiel ist der Speicher, auf den a4byteChar
zeigt, 4 Byte, die über die Integer-Variable a4byteInteger
dem 'Stack' a4byteInteger
.
Der Vorteil dieser Art der Speicherzuordnung ist die Tatsache, dass Programmierer die Zuweisung steuern. Da im a4byteInteger
Beispiel a4byteInteger
auf Stack zugewiesen wird, müssen Sie nicht explizit "delete a4byteChar" aufrufen.
Dasselbe Verhalten kann auch für dynamisch zugewiesenen Speicher erreicht werden. Zum Beispiel
int *a8byteDynamicInteger = new int[2];
char *a8byteChar = new (a8byteDynamicInteger) char[8];
In diesem Fall verweist der Speicherzeiger von a8byteChar
auf den dynamischen Speicher, der von a8byteDynamicInteger
zugewiesen wird. In diesem Fall müssen Sie jedoch delete a8byteDynamicInteger
explizit aufrufen, um den Speicher freizugeben
Ein weiteres Beispiel für die C ++ - Klasse
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;
}