Namespaces
Variants

new expression

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
new expression
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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)
**Anmerkung:** Da der Text hauptsächlich aus C++-Syntax und HTML-Tags besteht, wurden nur die wenigen beschreibenden Textelemente übersetzt: - "(optional)" wurde zu "(optional)" belassen (da es sich um einen technischen Begriff handelt) - Die Nummern (1)-(4) bleiben unverändert - Alle C++-Schlüsselwörter (::, new, type, new-initializer, placement-args) bleiben unübersetzt - HTML-Tags und Attribute wurden vollständig erhalten
1,2) Versucht, ein Objekt des Typs zu erstellen, bezeichnet durch die type-id type , die ein Array-Typ sein kann , und kann einen Platzhalter-Typspezifizierer enthalten (seit C++11) , oder einen Klassentemplate-Namen enthalten, dessen Argument durch Klassentemplate-Argumentdeduktion abgeleitet werden soll (seit C++17) .
3,4) Gleich wie (1,2) , stellt jedoch zusätzliche Argumente für die Allokationsfunktion bereit, siehe placement new .

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

(seit C++11)
  • ein Klassentemplate wird im Typ verwendet, dessen Argumente abgeleitet werden müssen.
(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:

  • der Ausdruck ist von nicht-Klassentyp und sein Wert vor der Konvertierung zu std::size_t ist negativ;
  • der Ausdruck ist von Klassentyp und sein Wert nach der benutzerdefinierten Konvertierungsfunktion und vor der zweiten Standardkonvertierung ist negativ;
  • der Wert des Ausdrucks ist größer als ein implementierungsdefiniertes Limit;
  • der Wert ist kleiner als die Anzahl der Array-Elemente, die in der geschweiften Initialisiererliste bereitgestellt werden (einschließlich des abschließenden ' \0 ' bei einem String-Literal ).

Wenn der Wert in der ersten Dimension aus einem dieser Gründe ungültig ist,

  • falls nach der Konvertierung zu std::size_t die erste Dimension ein Core Constant Expression ist und potenziell ausgewertet wird, ist das Programm fehlerhaft,
  • andernfalls, wenn die allokierende Funktion, die aufgerufen worden wäre, nicht-werfend ist (einschließlich std::nothrow Überladungen, die nicht als noexcept deklariert sind), gibt der new -Ausdruck den Nullzeiger des erforderlichen Ergebnistyps zurück,
  • andernfalls ruft der new -Ausdruck die allokierende Funktion nicht auf und wirft stattdessen eine Ausnahme eines Typs, der zu einem Handler vom Typ std::bad_array_new_length passen würde.
(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 new-initializer eine in geschweifte Klammern eingeschlossene Initialisiererliste ist, wird das Objekt list-initialized .
(seit C++11)

Wenn type ein Array-Typ ist, wird ein Array von Objekten initialisiert:

  • Selbst wenn die erste Dimension null ist, müssen die semantischen Anforderungen für die Standardinitialisierung eines hypothetischen Elements dennoch erfüllt werden.
  • Selbst wenn die erste Dimension null ist, müssen die semantischen Anforderungen der Wertinitialisierung eines hypothetischen Elements dennoch erfüllt werden.
  • Wenn der new-initializer eine in geschweifte Klammern eingeschlossene Initialisierungsliste ist, wird das Array aggregate-initialized .
(seit C++11)
  • Wenn der new-initializer eine in runde Klammern eingeschlossene, nicht-leere Ausdrucksliste ist, wird das Array aggregate-initialized .
(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 Klassentyp T oder ein Array von Klassentyp T ist, wird nach dem Namen der Freigabefunktion im Klassenbereich von T gesucht.
  • Andernfalls, oder falls nichts im Klassenbereich von T gefunden 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

new

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

Siehe auch