new
expression
Erstellt und initialisiert Objekte mit dynamischer storage duration , also Objekte, deren Lebensdauer nicht notwendigerweise durch den Gültigkeitsbereich begrenzt ist, in dem sie erstellt wurden.
Inhaltsverzeichnis |
Syntax
::
(optional)
new
(
type
)
new-initializer
(optional)
|
(1) | ||||||||
::
(optional)
new
type
new-initializer
(optional)
|
(2) | ||||||||
::
(optional)
new
(
placement-args
)
(
type
)
new-initializer
(optional)
|
(3) | ||||||||
::
(optional)
new
(
placement-args
)
type
new-initializer
(optional)
|
(4) | ||||||||
Erklärung
| type | - | der Ziel-Typ-Bezeichner |
| new-initializer | - | eine klammerumschlossene Ausdrucksliste oder eine geschweifte Initialisiererliste (seit C++11) |
| placement-args | - | zusätzliche Platzierungsargumente |
Der
new
-Ausdruck versucht, Speicher zu allozieren und dann entweder ein einzelnes unbenanntes Objekt oder ein unbenanntes Array von Objekten im allokierten Speicher zu konstruieren und zu initialisieren. Der
new
-Ausdruck gibt einen prvalue-Zeiger auf das konstruierte Objekt zurück oder, falls ein Array von Objekten konstruiert wurde, einen Zeiger auf das Anfangselement des Arrays.
Syntax (1) oder (3) ist erforderlich, wenn type Klammern enthält:
new int(*[10])(); // Fehler: wird geparst als (new int) (*[10]) () new (int (*[10])()); // Korrekt: allokiert ein Array von 10 Zeigern auf Funktionen
Zusätzlich wird type gierig geparst: Es werden alle Tokens einbezogen, die Teil eines Deklarators sein können:
new int + 1; // okay: wird geparst als (new int) + 1, erhöht einen von new int zurückgegebenen Zeiger new int * 1; // error: wird geparst als (new int*) (1)
Der new-initializer ist nicht optional, wenn
- type ist ein Array unbekannter Größe ,
|
(seit C++11) |
|
(seit C++17) |
double* p = new double[]{1, 2, 3}; // erzeugt ein Array vom Typ double[3] auto p = new auto('c'); // erzeugt ein einzelnes Objekt vom Typ char. p ist ein char* auto q = new std::integral auto(1); // OK: q ist ein int* auto q = new std::floating_point auto(true) // FEHLER: Typ-Einschränkung nicht erfüllt auto r = new std::pair(1, true); // OK: r ist ein std::pair<int, bool>* auto r = new std::vector; // FEHLER: Elementtyp kann nicht abgeleitet werden
Dynamische Arrays
Wenn type ein Array-Typ ist, müssen alle Dimensionen außer der ersten als positive integrale Konstantenausdrücke (bis C++14) konvertierte Konstantenausdrücke vom Typ std::size_t (seit C++14) angegeben werden, aber (nur bei Verwendung der nicht geklammerten Syntaxen (2) und (4) ) darf die erste Dimension ein Ausdruck ganzzahligen Typs, Aufzählungstyps oder Klassentyps mit einer einzelnen nicht-expliziten Konvertierungsfunktion zu ganzzahligem oder Aufzählungstyp sein (bis C++14) ein beliebiger zu std::size_t konvertierbarer Ausdruck sein (seit C++14) . Dies ist die einzige Möglichkeit, direkt ein Array mit Laufzeit-gestützter Größe zu erstellen. Solche Arrays werden oft als dynamische Arrays bezeichnet:
int n = 42; double a[n][5]; // Fehler auto p1 = new double[n][5]; // OK auto p2 = new double[5][n]; // Fehler: nur die erste Dimension darf nicht konstant sein auto p3 = new (double[n][5]); // Fehler: Syntax (1) kann nicht für dynamische Arrays verwendet werden
|
Das Verhalten ist undefiniert, wenn der Wert in der ersten Dimension (bei Bedarf in einen integralen oder Aufzählungstyp konvertiert) negativ ist. |
(bis C++11) |
|
In den folgenden Fällen ist der Wert des Ausdrucks, der die erste Dimension angibt, ungültig:
Wenn der Wert in der ersten Dimension aus einem dieser Gründe ungültig ist,
|
(seit C++11) |
Die erste Dimension von Null ist akzeptabel, und die Allokationsfunktion wird aufgerufen.
|
Wenn new-initializer eine in geschweifte Klammern eingeschlossene Initialisierungsliste ist und die erste Dimension potenziell ausgewertet wird und kein Kernkonstantenausdruck ist, werden die semantischen Einschränkungen der Kopierinitialisierung eines hypothetischen Elements des Arrays aus einer leeren Initialisierungsliste geprüft. |
(seit C++11) |
Allokation
Der new -Ausdruck reserviert Speicher durch Aufruf der entsprechenden Allokierungsfunktion . Wenn type ein Nicht-Array-Typ ist, lautet der Name der Funktion operator new . Wenn type ein Array-Typ ist, lautet der Name der Funktion operator new [ ] .
Wie in der
Allokierungsfunktion
beschrieben, kann das C++-Programm globale und klassen-spezifische Ersetzungen für diese Funktionen bereitstellen. Wenn der
new
-Ausdruck mit dem optionalen
::
-Operator beginnt, wie in
::
new
T
oder
::
new
T
[
n
]
, werden klassen-spezifische Ersetzungen ignoriert (die Funktion wird im globalen
Gültigkeitsbereich
gesucht
). Andernfalls, wenn
T
ein Klassentyp ist, beginnt die Suche im Klassen-Gültigkeitsbereich von
T
.
Beim Aufruf der Allokationsfunktion übergibt der
new
-Ausdruck die Anzahl der angeforderten Bytes als ersten Parameter vom Typ
std::size_t
, was exakt
sizeof
(
T
)
für nicht-array
T
entspricht.
Die Array-Allokation kann nicht spezifizierten Overhead bereitstellen, der von einem Aufruf von new zum nächsten variieren kann, es sei denn, die ausgewählte Allokationsfunktion ist die standardmäßige nicht-allocierende Form. Der von dem new -Ausdruck zurückgegebene Zeiger wird um diesen Wert von dem von der Allokationsfunktion zurückgegebenen Zeiger versetzt sein. Viele Implementierungen verwenden den Array-Overhead, um die Anzahl der Objekte im Array zu speichern, die von dem delete [ ] -Ausdruck verwendet wird, um die korrekte Anzahl von Destruktoren aufzurufen. Darüber hinaus, wenn der new -Ausdruck verwendet wird, um ein Array von char , unsigned char , oder std::byte (since C++17) zu allokieren, kann es bei Bedarf zusätzlichen Speicher von der Allokationsfunktion anfordern, um die korrekte Ausrichtung von Objekten aller Typen, die nicht größer als die angeforderte Array-Größe sind, zu gewährleisten, falls später eines in das allokierte Array platziert wird.
|
new -Ausdrücke dürfen Zuweisungen, die durch ersetzbare Allokationsfunktionen erfolgen, weglassen oder kombinieren. Im Fall des Weglassens kann der Speicher vom Compiler bereitgestellt werden, ohne einen Aufruf einer Allokationsfunktion durchzuführen (dies erlaubt auch das Optimieren ungenutzter new -Ausdrücke). Im Fall der Kombination kann die durch einen new -Ausdruck E1 vorgenommene Allokation erweitert werden, um zusätzlichen Speicher für einen weiteren new -Ausdruck E2 bereitzustellen, wenn alle folgenden Bedingungen erfüllt sind:
1)
Die Lebensdauer des durch
E1
allokierten Objekts umfasst strikt die Lebensdauer des durch
E2
allokierten Objekts.
2)
E1
und
E2
würden dieselbe ersetzbare globale Allokationsfunktion aufrufen.
3)
Für eine werfende Allokationsfunktion würden Ausnahmen in
E1
und
E2
zuerst im selben Handler aufgefangen.
Beachten Sie, dass diese Optimierung nur zulässig ist, wenn new -Ausdrücke verwendet werden, nicht jedoch andere Methoden zum Aufruf einer ersetzbaren Allokationsfunktion: delete [ ] new int [ 10 ] ; kann wegoptimiert werden, aber operator delete ( operator new ( 10 ) ) ; kann es nicht. |
(seit C++14) |
|
Während der Auswertung eines konstanten Ausdrucks wird ein Aufruf einer Allokationsfunktion stets ausgelassen. Nur new -Ausdrücke, die andernfalls zu einem Aufruf einer ersetzbaren globalen Allokationsfunktion führen würden, können in konstanten Ausdrücken ausgewertet werden. |
(seit C++20) |
Placement new
Wenn placement-args bereitgestellt werden, werden sie der Allokierungsfunktion als zusätzliche Argumente übergeben. Solche Allokierungsfunktionen werden als "Placement new " bezeichnet, nach der standardmäßigen Allokierungsfunktion void * operator new ( std:: size_t , void * ) , die einfach ihr zweites Argument unverändert zurückgibt. Dies wird verwendet, um Objekte in allokiertem Speicher zu konstruieren:
// innerhalb eines beliebigen Blockbereichs... { // Statische Allokation des Speichers mit automatischer Speicherdauer // der groß genug für jedes Objekt vom Typ "T" ist. alignas(T) unsigned char buf[sizeof(T)]; T* tptr = new(buf) T; // Konstruiere ein "T"-Objekt, platziere es direkt in Ihren // vorallozierten Speicher an der Speicheradresse "buf". tptr->~T(); // Sie müssen den Destruktor des Objekts **manuell** aufrufen // wenn seine Nebeneffekte vom Programm benötigt werden. } // Beim Verlassen dieses Blockbereichs wird "buf" automatisch freigegeben.
Hinweis: Diese Funktionalität ist durch die Memberfunktionen der Allocator Klassen gekapselt.
|
Wenn ein Objekt mit einer Ausrichtungsanforderung, die __STDCPP_DEFAULT_NEW_ALIGNMENT__ überschreitet, oder ein Array solcher Objekte allokiert wird, übergibt der new -Ausdruck die Ausrichtungsanforderung (eingepackt in std::align_val_t ) als zweites Argument an die Allokationsfunktion (bei Placement-Formen erscheinen placement-arg nach der Ausrichtung als drittes, viertes etc. Argument). Falls die Überladungsauflösung fehlschlägt (was passiert, wenn eine klassen-spezifische Allokationsfunktion mit einer anderen Signatur definiert ist, da sie die globalen verdeckt), wird die Überladungsauflösung ein zweites Mal versucht, ohne die Ausrichtung in der Argumentliste. Dies erlaubt es ausrichtungs-unbewussten klassen-spezifischen Allokationsfunktionen, Vorrang vor den globalen ausrichtungs-bewussten Allokationsfunktionen zu nehmen. |
(since C++17) |
new T; // ruft operator new(sizeof(T)) auf // (C++17) oder operator new(sizeof(T), std::align_val_t(alignof(T)))) new T[5]; // ruft operator new[](sizeof(T)*5 + Overhead) auf // (C++17) oder operator new(sizeof(T)*5+Overhead, std::align_val_t(alignof(T)))) new(2,f) T; // ruft operator new(sizeof(T), 2, f) auf // (C++17) oder operator new(sizeof(T), std::align_val_t(alignof(T)), 2, f)
Wenn eine nicht-werfende Allokationsfunktion (z.B. diejenige, die durch new ( std:: nothrow ) T ausgewählt wird) aufgrund eines Allokationsfehlers einen Nullzeiger zurückgibt, dann kehrt der new -Ausdruck sofort zurück; er versucht nicht, ein Objekt zu initialisieren oder eine Freigabefunktion aufzurufen. Wenn ein Nullzeiger als Argument an einen nicht-allokierenden Placement- new -Ausdruck übergeben wird, was dazu führt, dass die ausgewählte standardmäßige nicht-allokierende Placement-Allokationsfunktion einen Nullzeiger zurückgibt, ist das Verhalten undefiniert.
Initialisierung
Das von einem new -Ausdruck erzeugte Objekt wird gemäß den folgenden Regeln initialisiert.
Wenn type kein Array-Typ ist, wird das einzelne Objekt im erworbenen Speicherbereich konstruiert:
- Wenn der new-initializer fehlt, wird das Objekt default-initialized .
- Wenn der new-initializer eine in Klammern eingeschlossene Ausdrucksliste ist, wird das Objekt direct-initialized .
|
(seit C++11) |
Wenn type ein Array-Typ ist, wird ein Array von Objekten initialisiert:
- Wenn der new-initializer fehlt, wird jedes Element default-initialized .
-
- Selbst wenn die erste Dimension null ist, müssen die semantischen Anforderungen für die Standardinitialisierung eines hypothetischen Elements dennoch erfüllt werden.
- Wenn der new-initializer ein Paar von Klammern ist, wird jedes Element value-initialized .
-
- Selbst wenn die erste Dimension null ist, müssen die semantischen Anforderungen der Wertinitialisierung eines hypothetischen Elements dennoch erfüllt werden.
|
(seit C++11) |
|
(seit C++20) |
Initialisierungsfehler
Wenn die Initialisierung durch das Werfen einer Ausnahme beendet wird (z.B. vom Konstruktor), sucht das Programm nach einer passenden Freigabefunktion, dann:
- Wenn eine geeignete Freigabefunktion gefunden werden kann, wird die Freigabefunktion aufgerufen, um den Speicher freizugeben, in dem das Objekt konstruiert wurde. Danach setzt sich die Ausnahme im Kontext des new -Ausdrucks fort.
- Wenn keine eindeutig passende Freigabefunktion gefunden werden kann, führt die Weitergabe der Ausnahme nicht zur Freigabe des Speichers des Objekts. Dies ist nur angemessen, wenn die aufgerufene Allokationsfunktion keinen Speicher allokiert, andernfalls führt es wahrscheinlich zu einem Speicherleck.
Der Umfang der Suche nach der passenden Freigabefunktion wird wie folgt bestimmt:
-
Wenn der
new
-Ausdruck nicht mit
::beginnt und der allozierte Typ entweder ein KlassentypToder ein Array von KlassentypTist, wird nach dem Namen der Freigabefunktion im Klassenbereich vonTgesucht. -
Andernfalls, oder falls nichts im Klassenbereich von
Tgefunden wird, wird der Name der Freigabefunktion durch Suche im globalen Bereich gesucht.
Für eine Nicht-Platzierungs-Allokationsfunktion wird die normale Deallokationsfunktionssuche verwendet, um die passende Deallokationsfunktion zu finden (siehe delete-expression ).
Für eine Platzierungs-Allokationsfunktion muss die passende Deallokationsfunktion dieselbe Anzahl an Parametern haben, und jeder Parametertyp außer dem ersten muss identisch mit dem entsprechenden Parametertyp der Allokationsfunktion sein (nach Parametertransformationen ).
- Wenn die Suche eine einzelne passende Freigabefunktion findet, wird diese Funktion aufgerufen; andernfalls wird keine Freigabefunktion aufgerufen.
- Wenn die Suche eine Nicht-Platzierungs-Freigabefunktion findet und diese Funktion, betrachtet als eine Platzierungs-Freigabefunktion, als Übereinstimmung für die Allokationsfunktion ausgewählt worden wäre, ist das Programm fehlerhaft.
In jedem Fall muss die passende Freigabefunktion (falls vorhanden) nicht gelöscht und (since C++11) von der Stelle aus zugänglich sein, an der der new -Ausdruck erscheint.
struct S { // Platzierungs-Allokationsfunktion: static void* operator new(std::size_t, std::size_t); // Nicht-Platzierungs-Deallokationsfunktion: static void operator delete(void*, std::size_t); }; S* p = new (0) S; // Fehler: Nicht-Platzierungs-Deallokationsfunktion passt // zur Platzierungs-Allokationsfunktion
Wenn eine Freigabefunktion in einem new -Ausdruck (aufgrund eines Initialisierungsfehlers) aufgerufen wird, werden die an diese Funktion übergebenen Argumente wie folgt bestimmt:
- Das erste Argument ist der Wert (vom Typ void * ), der vom Allokationsfunktionsaufruf zurückgegeben wurde.
- Andere Argumente (nur für Platzierungs-Deallokationsfunktionen) sind die placement-args , die an die Platzierungs-Allokationsfunktion übergeben wurden.
Falls es der Implementierung erlaubt ist, ein temporäres Objekt einzuführen oder eine Kopie eines beliebigen Arguments als Teil des Aufrufs der Allokationsfunktion zu erstellen, ist es nicht spezifiziert, ob dasselbe Objekt im Aufruf sowohl der Allokations- als auch der Deallokationsfunktion verwendet wird.
Speicherlecks
Die Objekte, die durch new -Ausdrücke erzeugt werden (Objekte mit dynamischer Speicherdauer), bestehen so lange fort, bis der durch den new -Ausdruck zurückgegebene Zeiger in einer passenden delete-expression verwendet wird. Wenn der ursprüngliche Wert des Zeigers verloren geht, wird das Objekt unerreichbar und kann nicht freigegeben werden: ein Speicherleck tritt auf.
Dies kann passieren, wenn der Zeiger zugewiesen wird an:
int* p = new int(7); // dynamisch allozierter int mit Wert 7 p = nullptr; // Speicherleck
oder wenn der Zeiger den Gültigkeitsbereich verlässt:
void f() { int* p = new int(7); } // Speicherleck
oder aufgrund einer Ausnahme:
void f() { int* p = new int(7); g(); // könnte eine Exception werfen delete p; // okay wenn keine Exception auftritt } // Speicherleck wenn g() eine Exception wirft
Um die Verwaltung dynamisch allokierter Objekte zu vereinfachen, wird das Ergebnis eines new -Ausdrucks oft in einem Smart Pointer gespeichert: std::auto_ptr (bis C++17) std::unique_ptr , oder std::shared_ptr (seit C++11) . Diese Pointer garantieren, dass der delete-Ausdruck in den oben gezeigten Situationen ausgeführt wird.
Hinweise
Itanium C++ ABI erfordert, dass der Array-Allokations-Overhead null beträgt, wenn der Elementtyp des erstellten Arrays trivial destruierbar ist. Ebenso verhält sich MSVC.
Einige Implementierungen (z.B. MSVC vor VS 2019 v16.7) benötigen Overhead für nicht-null Array-Allokation bei nicht-allozierendem Placement-Array new , wenn der Elementtyp nicht trivial destruierbar ist, was seit CWG issue 2382 nicht mehr konform ist.
Ein nicht-allozierender Platzierungs-Array- new -Ausdruck, der ein Array von unsigned char , oder std::byte (seit C++17) erzeugt, kann verwendet werden, um implizit Objekte in einem gegebenen Speicherbereich zu erzeugen: Er beendet die Lebensdauer von Objekten, die mit dem Array überlappen, und erzeugt dann implizit Objekte von Implizite-Lebensdauer-Typen im Array.
std::vector bietet ähnliche Funktionalität für eindimensionale dynamische Arrays.
Schlüsselwörter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 74 | C++98 | Wert in der ersten Dimension muss integralen Typ haben | Aufzählungstypen erlaubt |
| CWG 299 | C++98 |
Wert in der ersten Dimension muss
integralen oder Aufzählungstyp haben |
Klassentypen mit einer einzelnen
Konvertierungsfunktion zu integralem oder Aufzählungstyp erlaubt |
| CWG 624 | C++98 |
das Verhalten war nicht spezifiziert, wenn die
Größe des allokierten Objekts das implementierungsdefinierte Limit überschreiten würde |
in diesem Fall wird kein Speicher bezogen und eine
Exception geworfen |
| CWG 1748 | C++98 |
nicht-allokierendes Placement
new
muss
prüfen, ob das Argument null ist |
undefiniertes Verhalten für Null-Argument |
| CWG 1992 | C++11 |
new
(
std::
nothrow
)
int
[
N
]
könnte werfen std::bad_array_new_length |
geändert um einen Nullzeiger zurückzugeben |
| CWG 2102 | C++98 |
es war unklar, ob Default-/Wert-Initialisierung
wohlgeformt sein muss bei der Initialisierung leerer Arrays |
erforderlich |
| CWG 2382 | C++98 |
nicht-allokierendes Placement-Array
new
könnte Allokierungs-Overhead erfordern |
solcher Allokierungs-Overhead verboten |
| CWG 2392 | C++11 |
das Programm könnte fehlerhaft sein, selbst wenn die
erste Dimension nicht potenziell ausgewertet wird |
in diesem Fall wohlgeformt |
| P1009R2 | C++11 |
die Array-Grenze konnte nicht
in einem new Ausdruck abgeleitet werden |
Ableitung erlaubt |