Die Operatoren new und delete

C++ unterstützt die dynamische Zuordnung und Zuordnung von Objekten mithilfe der new Und delete Operatoren. 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 newauf, und der delete Operator ruft die spezielle Funktion operator deleteauf.

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, operator new wird ein Zeiger auf ein anderes Objekt zurückgegeben. Das heißt, wiederholte Aufrufe, um operator new unterschiedliche Zeiger zurückzugeben.

Wenn für die Zuordnungsanforderung nicht genügend Arbeitsspeicher vorhanden ist, operator new wird eine std::bad_alloc Ausnahme ausgelöst. Oder es wird zurückgegeben nullptr , wenn Sie das Platzierungsformularnew(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 `Scope`
::operator new Global
Klassenname::operator new Klasse

Das erste Argument muss operator new vom Typ size_tsein, 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 diese Klasse operator new aufgerufen.

Eine operator new für eine Klasse definierte Funktion ist eine statische Memberfunktion (die nicht virtuell sein kann), die die globale operator new Funktion für Objekte dieses Klassentyps ausblendet. Berücksichtigen Sie den Fall, in dem new speicherweise einem bestimmten Wert zugewiesen und festgelegt wird:

#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 new in Klammern angegeben wird, wird als chInit Argument übergebenBlanks::operator new. Die globale operator new Funktion ist jedoch ausgeblendet, was dazu führt, dass Code wie folgt einen Fehler generiert:

Blanks *SomeBlanks = new Blanks;

Der Compiler unterstützt Memberarray new und delete Operatoren in einer Klassendeklaration. 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, operator new wird eine std::bad_alloc Ausnahme ausgelöst.

Ä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 newerwartet, verknüpfen Sie Ihr Programm mit nothrownew.obj. Die nothrownew.obj Datei ersetzt global operator new durch eine Version, die zurückgegeben nullptr wird, wenn eine Zuordnung fehlschlägt. operator new wird nicht mehr ausgelöst std::bad_alloc. Weitere Informationen und nothrownew.obj andere Linkeroptionsdateien finden Sie unter Linkoptionen.

Sie können keinen Code kombinieren, der auf Ausnahmen von global operator new mit Code überprüft, der auf Nullzeiger in derselben Anwendung überprüft. Sie können jedoch weiterhin Klassenlokale operator new erstellen, die sich anders verhalten. Diese Möglichkeit bedeutet, dass der Compiler standardmäßig defensiv agieren muss und Überprüfungen auf NULL-Zeiger-Rü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 verwenden oder eine nullptr Rückgabe verwenden. Standard C++ erwartet, dass ein Zuweisungsmodul entweder std::bad_alloc oder eine von der Klasse abgeleitete Klasse ausgelöst wird std::bad_alloc. 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 verwenden new, können Sie einen Zuordnungsfehler testen, wie in diesem Beispiel gezeigt:

#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 die Datei verwendet nothrownew.obj haben, um global 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 Operators zugewiesen wird, kann über den newdelete Operator freigegeben werden. Der Löschoperator ruft die operator delete Funktion auf, die Arbeitsspeicher wieder in den verfügbaren Pool freigibt. 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 Klassenmememmfunktionen operator delete gibt es zwei Formulare:

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

Für eine bestimmte Klasse kann nur eine der beiden vorhergehenden Formulare vorhanden sein. Das erste Formular verwendet ein einzelnes Argument vom Typ void *, das einen Zeiger auf das Objekt zum Deallocate enthält. Das zweite Formular mit der Größe des Deallocation-Vorgangs verwendet zwei Argumente: Der erste ist ein Zeiger auf den Speicherblock, um die Zuordnung zu behandeln, und die zweite ist die Anzahl der Bytes, die zuordnungen. 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 sie nicht virtuell sein kann. Die operator delete Funktion gehorcht der Zugriffssteuerung, wie in der Member-Access-Steuerung beschrieben.

Das folgende Beispiel zeigt benutzerdefinierte operator new Und operator delete Funktionen, die zum Protokollieren von Zuordnungen und Deallocations 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;
}

Der vorangehende Code kann verwendet werden, um "Speicherlecks" zu erkennen, d. h. Speicher, der im kostenlosen Speicher zugewiesen ist, aber nie freigegeben wurde. Um Lecks zu erkennen, werden die globalen new und delete Operatoren neu definiert, um die Zuordnung und die Zuordnung des Speichers zu zählen.

Der Compiler unterstützt Memberarray new und delete Operatoren in einer Klassendeklaration. 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;
}