Operatori new e delete

C++ supporta l'allocazione dinamica e la deallocazione degli oggetti usando gli new operatori e delete . Questi operatori allocano memoria per gli oggetti da un pool denominato archivio gratuito (noto anche come heap). L'operatore new chiama la funzione operator newspeciale e l'operatore delete chiama la funzione operator deletespeciale .

Per un elenco dei file di libreria nella libreria di runtime C e nella libreria standard C++, vedere Funzionalità della libreria CRT.

new Operatore

Il compilatore converte un'istruzione come questa in una chiamata alla funzione operator new:

char *pch = new char[BUFFER_SIZE];

Se la richiesta è per zero byte di spazio di archiviazione, operator new restituisce un puntatore a un oggetto distinto. Vale a dire, chiamate ripetute per operator new restituire puntatori diversi.

Se la memoria per la richiesta di allocazione non è sufficiente, operator new genera un'eccezione std::bad_alloc . In alternativa, restituisce nullptr se è stato usato il modulo new(std::nothrow)di posizionamento o se è stato collegato il supporto non generanteoperator new. Per altre informazioni, vedere Comportamento degli errori di allocazione.

I due ambiti per operator new le funzioni sono descritti nella tabella seguente.

Ambito delle operator new funzioni

Operatore Ambito
::operator new Generale
class-name::operator new Classe

Il primo argomento di operator new deve essere di tipo size_te il tipo restituito è sempre void*.

La funzione globale operator new viene chiamata quando l'operatore new viene usato per allocare oggetti di tipi predefiniti, oggetti di tipo classe che non contengono funzioni definite dall'utente operator new e matrici di qualsiasi tipo. Quando l'operatore new viene usato per allocare oggetti di un tipo di classe in cui è definito un operator new oggetto , viene chiamato tale operator new classe.

Una operator new funzione definita per una classe è una funzione membro statica (che non può essere virtuale) che nasconde la funzione globale operator new per gli oggetti di tale tipo di classe. Si consideri il caso in cui new viene usato per allocare e impostare la memoria su un valore specificato:

#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;
}

L'argomento fornito tra parentesi a new viene passato come Blanks::operator newchInit argomento. Tuttavia, la funzione globale operator new è nascosta, causando il codice come il seguente per generare un errore:

Blanks *SomeBlanks = new Blanks;

Il compilatore supporta operatori e delete matrici new membro in una dichiarazione di classe. Ad esempio:

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

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

Comportamento degli errori di allocazione

La new funzione nella libreria standard C++ supporta il comportamento specificato nello standard C++ a partire da C++98. Quando la memoria per una richiesta di allocazione non è sufficiente, operator new genera un'eccezione std::bad_alloc .

Il codice C++ precedente ha restituito un puntatore Null per un'allocazione non riuscita. Se si dispone di codice che prevede la versione non generata di new, collegare il programma a nothrownew.obj. Il nothrownew.obj file sostituisce global operator new con una versione che restituisce nullptr se un'allocazione non riesce. operator new non genera std::bad_allocpiù . Per altre informazioni su nothrownew.obj e altri file di opzioni del linker, vedere Opzioni di collegamento.

Non è possibile combinare codice che verifica la presenza di eccezioni da globale operator new con codice che verifica la presenza di puntatori Null nella stessa applicazione. Tuttavia, è comunque possibile creare classi locali operator new che si comportano in modo diverso. Questa possibilità significa che il compilatore deve agire difensivamente per impostazione predefinita e includere controlli per i ritorni del puntatore Null nelle new chiamate. Per altre informazioni su un modo per ottimizzare questi controlli del compilatore, vedere /Zc:throwingnew.

Gestione della memoria insufficiente

Il modo in cui si esegue il test per un'allocazione non riuscita da un'espressione new dipende dal fatto che si usi il meccanismo di eccezione standard o si usa un nullptr valore restituito. Standard C++ prevede che un allocatore generi std::bad_alloc un'eccezione o una classe derivata da std::bad_alloc. È possibile gestire un'eccezione di questo tipo, come illustrato in questo esempio:

#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;
   }
}

Quando si usa il nothrow formato di , è possibile verificare se si verifica un errore di newallocazione, come illustrato in questo esempio:

#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;
   }
}

È possibile testare l'allocazione di memoria non riuscita quando è stato usato nothrownew.obj il file per sostituire globale operator new , come illustrato di seguito:

#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;
   }
}

È possibile fornire un gestore per le richieste di allocazione di memoria non riuscite. È possibile scrivere una routine di ripristino personalizzata per gestire un errore di questo tipo. Potrebbe, ad esempio, rilasciare una memoria riservata, quindi consentire l'esecuzione dell'allocazione di nuovo. Per ulteriori informazioni, vedere _set_new_handler.

delete Operatore

La memoria allocata dinamicamente tramite l'operatore new può essere liberata usando l'operatore delete . L'operatore delete chiama la operator delete funzione , che libera memoria al pool disponibile. L'uso dell'operatore delete fa anche in modo che venga chiamato il distruttore di classe (se esistente).

Sono disponibili funzioni globali e con ambito operator delete di classe. È possibile definire una operator delete sola funzione per una determinata classe; se definita, nasconde la funzione globale operator delete . La funzione globale operator delete viene sempre chiamata per matrici di qualsiasi tipo.

Funzione globale operator delete . Esistono due forme per le funzioni membro globali operator delete e di operator delete classe:

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

Solo una delle due forme precedenti può essere presente per una determinata classe. La prima maschera accetta un singolo argomento di tipo void *, che contiene un puntatore all'oggetto da deallocare. Il secondo formato, la deallocazione delle dimensioni, accetta due argomenti: il primo è un puntatore al blocco di memoria da deallocare e il secondo è il numero di byte da deallocare. Il tipo restituito di entrambi i moduli è void (operator delete non può restituire un valore).

La finalità del secondo modulo consiste nel velocizzare la ricerca della categoria di dimensioni corretta dell'oggetto da eliminare. Queste informazioni spesso non vengono archiviate vicino all'allocazione stessa e probabilmente non sono memorizzate nella cache. Il secondo modulo è utile quando viene usata una operator delete funzione di una classe base per eliminare un oggetto di una classe derivata.

La operator delete funzione è statica, quindi non può essere virtuale. La operator delete funzione rispetta il controllo di accesso, come descritto in Member-Controllo di accesso.

L'esempio seguente illustra le funzioni definite operator delete dall'utente operator new progettate per registrare allocazioni e deallocazione della memoria:

#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;
}

Il codice precedente può essere usato per rilevare la "perdita di memoria", ovvero la memoria allocata nell'archivio gratuito, ma mai liberata. Per rilevare le perdite, gli operatori globali new e delete vengono ridefinti in modo da contare l'allocazione e la deallocazione della memoria.

Il compilatore supporta operatori e delete matrici new membro in una dichiarazione di classe. Ad esempio:

// 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;
}