Freigeben über


Die Operatoren new und delete

C++ unterstützt die dynamische Zuordnung und Freigabe von Objekten mit den Operatoren new und delete. Diese Operatoren weisen Speicher für Objekte aus einem Pool zu, der als kostenloser Speicher bezeichnet wird (auch als Heap bezeichnet). Der new-Operator ruft die spezielle Funktion operator new auf, und der delete-Operator ruft die spezielle Funktion operator delete auf.

Eine Liste der Bibliotheksdateien in der C-Runtime-Bibliothek und der C++-Standardbibliothek finden Sie unter CRT Library Features.

Der new-Operator

Der Compiler übersetzt eine Anweisung wie diese in einen Aufruf der Funktion operator new:

char *pch = new char[BUFFER_SIZE];

Wenn die Anforderung null Bytes Speicherplatz hat, gibt operator new einen Zeiger auf ein anderes Objekt zurück. Das heißt, wiederholte Aufrufe an operator new geben unterschiedliche Zeiger zurück.

Wenn für die Zuordnungsanforderung nicht genügend Arbeitsspeicher vorhanden ist, löst operator new eine std::bad_alloc-Ausnahme aus. Oder es wird nullptr zurückgegeben, wenn Sie das Platzierung-Formular new(std::nothrow) verwendet haben oder wenn Sie eine Verknüpfung mit nicht ausgelöster operator new-Unterstützung erstellt haben. Weitere Informationen finden Sie unter Zuordnungsfehlerverhalten.

Die beiden Bereiche für operator new-Funktionen werden in der folgenden Tabelle beschrieben.

Bereich für operator new-Funktionen

Operator Bereich
::operator new Global
class-name ::operator new Klasse

Das erste Argument von operator new muss vom Typ size_t sein, und der Rückgabetyp ist immer void*.

Die globale operator new-Funktion wird aufgerufen, wenn der new-Operator verwendet wird, um Objekte integrierter Typen, Objekte vom Klassentyp, die keine benutzerdefinierten operator new-Funktionen enthalten, und Arrays eines beliebigen Typs zuzuordnen. Wenn der new-Operator verwendet wird, um Objekte eines Klassentyps zuzuordnen, in dem eine operator new-Klasse definiert ist, wird die operator new dieser Klasse aufgerufen.

Eine für eine Klasse definierte operator new-Funktion ist eine statische Memberfunktion (die nicht virtuell sein kann), welche die globale operator new-Funktion für Objekte dieses Klassentyps ausblendet. Betrachten Sie den Fall, in dem new verwendet wird, um Arbeitsspeicher zuzuweisen und auf einen angegebenen Wert festzulegen:

#include <malloc.h>
#include <memory.h>

class Blanks
{
public:
    Blanks(){}
    void *operator new( size_t stAllocateBlock, char chInit );
};
void *Blanks::operator new( size_t stAllocateBlock, char chInit )
{
    void *pvTemp = malloc( stAllocateBlock );
    if( pvTemp != 0 )
        memset( pvTemp, chInit, stAllocateBlock );
    return pvTemp;
}
// For discrete objects of type Blanks, the global operator new function
// is hidden. Therefore, the following code allocates an object of type
// Blanks and initializes it to 0xa5
int main()
{
   Blanks *a5 = new(0xa5) Blanks;
   return a5 != 0;
}

Das Argument, das in Klammern für new bereitgestellt wird, wird an Blanks::operator new als chInit-Argument übergeben. Allerdings wird die globale operator new-Funktion ausgeblendet und bewirkt, dass Code wie der Folgende einen Fehler generiert:

Blanks *SomeBlanks = new Blanks;

Der Compiler unterstützt die Memberarrayoperatoren new und delete in einer Klassendeklaration. Zum Beispiel:

class MyClass
{
public:
   void * operator new[] (size_t)
   {
      return 0;
   }
   void   operator delete[] (void*)
   {
   }
};

int main()
{
   MyClass *pMyClass = new MyClass[5];
   delete [] pMyClass;
}

Zuordnungsfehlerverhalten

Die new-Funktion in der C++-Standardbibliothek unterstützt das im C++-Standard seit C++98 angegebene Verhalten. Wenn für eine Zuordnungsanforderung nicht genügend Arbeitsspeicher vorhanden ist, löst operator new eine std::bad_alloc-Ausnahme aus.

Älterer C++-Code hat einen NULL-Zeiger für eine fehlgeschlagene Zuordnung zurückgegeben. Wenn Sie Code haben, der die nicht ausgelöste Version von new erwartet, verknüpfen Sie Ihr Programm mit nothrownew.obj. Die nothrownew.obj-Datei ersetzt globale operator new durch eine Version, die nullptr zurückgibt, wenn eine Zuordnung fehlschlägt. operator new löst std::bad_alloc nicht mehr aus. Weitere Informationen zu nothrownew.obj und anderen Linkeroptionsdateien finden Sie unter Linkoptionen.

Sie können keinen Code kombinieren, der auf Ausnahmen von globalen operator new mit Code prüft, der auf Nullzeiger in derselben Anwendung überprüft. Sie können jedoch weiterhin class-local operator new erstellen, die sich anders verhalten. Diese Möglichkeit bedeutet, dass der Compiler standardmäßig defensiv agieren muss und Überprüfungen auf NULL-Zeigerrückgaben in new-Aufrufen einschließen muss. Weitere Informationen zur Optimierung dieser Compilerprüfungen finden Sie unter /Zc:throwingnew.

Behandeln von ungenügendem Arbeitsspeicher

Die Art und Weise, wie Sie eine fehlgeschlagene Zuordnung aus einem new-Ausdruck testen, hängt davon ab, ob Sie den Standardausnahmemechanismus oder eine nullptr-Rückgabe verwenden. Standard C++ erwartet, dass ein Allocator entweder std::bad_alloc oder eine von std::bad_alloc abgeleitete Klasse auslöst. Sie können eine solche Ausnahme behandeln, wie in diesem Beispiel gezeigt:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   try {
      int *pI = new int[BIG_NUMBER];
   }
   catch (bad_alloc& ex) {
      cout << "Caught bad_alloc: " << ex.what() << endl;
      return -1;
   }
}

Wenn Sie die nothrow-Form von new verwenden, können Sie wie in diesem Beispiel gezeigt auf einen Zuordnungsfehler testen:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new(nothrow) int[BIG_NUMBER];
   if ( pI == nullptr ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

Sie können eine fehlerhafte Speicherzuweisung testen, wenn Sie nothrownew.obj-Datei verwendet haben, um globale operator new zu ersetzen, wie hier gezeigt:

#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
   int *pI = new int[BIG_NUMBER];
   if ( !pI ) {
      cout << "Insufficient memory" << endl;
      return -1;
   }
}

Sie können einen Handler für fehlgeschlagene Speicherzuweisungsanforderungen bereitstellen. Es ist möglich, eine benutzerdefinierte Wiederherstellungsroutine zu schreiben, um einen solchen Fehler zu behandeln. Sie könnte z. B. einen reservierten Speicher freigeben und die Zuweisung dann erneut ausführen lassen. Weitere Informationen finden Sie unter _set_new_handler.

Der delete-Operator

Speicher, der dynamisch mithilfe des new-Operators zugewiesen wird, kann über den delete-Operator freigegeben werden. Der Löschoperator ruft die operator delete-Funktion auf, wodurch Arbeitsspeicher wieder in den verfügbaren Pool freigegeben wird. Die Verwendung des delete-Operators bewirkt auch, dass der Klassendestruktor (sofern vorhanden) aufgerufen wird.

Es gibt globale und klassenbezogene operator delete-Funktionen. Nur eine operator delete-Funktion kann für eine bestimmte Klasse definiert werden. Wenn definiert, blendet sie die globale operator delete-Funktion aus. Die globale operator delete-Funktion wird immer für Arrays eines beliebigen Typs aufgerufen.

Die globale operator delete-Funktion. Für die globalen operator delete und Klassenmember-operator delete-Funktionen gibt es zwei Formulare:

void operator delete( void * );
void operator delete( void *, size_t );

Für eine bestimmte Klasse kann nur eines der beiden voranstehenden Formulare vorhanden sein. Das erste Formular verwendet ein einzelnes Argument vom Typ void *, das einen Zeiger auf das Objekt zum Aufheben enthält. Das zweite Formular mit der Größe des Aufhebungsvorgangs verwendet zwei Argumente: Das erste ist ein Zeiger auf den Speicherblock, um die Zuordnung zu behandeln, und das zweite ist die Anzahl der Bytes, die aufgehoben werden sollen. Der Rückgabetyp beider Formulare ist void (operator delete kann keinen Wert zurückgeben).

Die Absicht des zweiten Formulars besteht darin, die Suche nach der richtigen Größenkategorie des zu löschenden Objekts zu beschleunigen. Diese Informationen werden häufig nicht in der Nähe der Zuordnung selbst gespeichert und wahrscheinlich nicht zwischengespeichert. Das zweite Formular ist nützlich, wenn eine operator delete-Funktion aus einer Basisklasse verwendet wird, um ein Objekt einer abgeleiteten Klasse zu löschen.

Die operator delete-Funktion ist statisch, sodass es nicht virtuell sein kann. Die operator delete-Funktion folgt der Zugriffssteuerung, wie in Member-Access Control beschrieben.

Das folgende Beispiel zeigt benutzerdefinierte operator new- und operator delete-Funktionen, die zum Protokollieren von Zuordnungen und Aufhebungen des Arbeitsspeichers entwickelt wurden:

#include <iostream>
using namespace std;

int fLogMemory = 0;      // Perform logging (0=no; nonzero=yes)?
int cBlocksAllocated = 0;  // Count of blocks allocated.

// User-defined operator new.
void *operator new( size_t stAllocateBlock ) {
   static int fInOpNew = 0;   // Guard flag.

   if ( fLogMemory && !fInOpNew ) {
      fInOpNew = 1;
      clog << "Memory block " << ++cBlocksAllocated
          << " allocated for " << stAllocateBlock
          << " bytes\n";
      fInOpNew = 0;
   }
   return malloc( stAllocateBlock );
}

// User-defined operator delete.
void operator delete( void *pvMem ) {
   static int fInOpDelete = 0;   // Guard flag.
   if ( fLogMemory && !fInOpDelete ) {
      fInOpDelete = 1;
      clog << "Memory block " << cBlocksAllocated--
          << " deallocated\n";
      fInOpDelete = 0;
   }

   free( pvMem );
}

int main( int argc, char *argv[] ) {
   fLogMemory = 1;   // Turn logging on
   if( argc > 1 )
      for( int i = 0; i < atoi( argv[1] ); ++i ) {
         char *pMem = new char[10];
         delete[] pMem;
      }
   fLogMemory = 0;  // Turn logging off.
   return cBlocksAllocated;
}

Mit dem vorangehenden Code kann „Arbeitsspeicherverlust“ erkannt werden, also Arbeitsspeicher, der im freien Speicher zugeordnet, jedoch nicht freigegeben wurde. Um Verluste zu erkennen, werden die globalen Operatoren new und delete neu definiert, um die Zuordnung und Aufhebung des Speichers zu zählen.

Der Compiler unterstützt die Memberarrayoperatoren new und delete in einer Klassendeklaration. Zum Beispiel:

// spec1_the_operator_delete_function2.cpp
// compile with: /c
class X  {
public:
   void * operator new[] (size_t) {
      return 0;
   }
   void operator delete[] (void*) {}
};

void f() {
   X *pX = new X[5];
   delete [] pX;
}