Opérateurs new et delete

C++ prend en charge l’allocation dynamique et la désallocation d’objets à l’aide des opérateurs et delete des new opérateurs. Ces opérateurs allouent de la mémoire pour les objets d’un pool appelé magasin gratuit (également appelé tas). L’opérateur new appelle la fonction operator newspéciale, et l’opérateur delete appelle la fonction operator deletespéciale.

Pour obtenir la liste des fichiers de bibliothèque dans la bibliothèque runtime C et la bibliothèque C++ Standard, consultez fonctionnalités de la bibliothèque CRT.

Opérateur new

Le compilateur traduit une instruction telle que celle-ci en un appel à la fonction operator new:

char *pch = new char[BUFFER_SIZE];

Si la requête concerne zéro octets de stockage, operator new retourne un pointeur vers un objet distinct. Autrement dit, des appels répétés pour operator new retourner différents pointeurs.

En cas d’insuffisance de mémoire pour la demande d’allocation, operator new lève une std::bad_alloc exception. Sinon, elle retourne nullptr si vous avez utilisé le formulaire new(std::nothrow)de placement ou si vous avez lié dans la prise en charge non levéeoperator new. Pour plus d’informations, consultez Comportement d’échec d’allocation.

Les deux étendues des operator new fonctions sont décrites dans le tableau suivant.

Étendue des operator new fonctions

Opérateur Portée
::operator new Global
nom de classe::operator new Classe

Le premier argument de operator new doit être de type size_tet le type de retour est toujours void*.

La fonction globale operator new est appelée lorsque l’opérateur est utilisé pour allouer des objets de types intégrés, des objets de type de classe qui ne contiennent pas de fonctions définies par operator new l’utilisateur new et des tableaux de n’importe quel type. Lorsque l’opérateur new est utilisé pour allouer des objets d’un operator new type de classe où un operator new est défini, cette classe est appelée.

Une operator new fonction définie pour une classe est une fonction membre statique (qui ne peut pas être virtuelle) qui masque la fonction globale operator new pour les objets de ce type de classe. Considérez le cas où new est utilisé pour allouer et définir la mémoire sur une valeur donnée :

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

Argument fourni entre parenthèses à new passer en Blanks::operator new tant qu’argument chInit . Toutefois, la fonction globale operator new est masquée, ce qui provoque le code suivant pour générer une erreur :

Blanks *SomeBlanks = new Blanks;

Le compilateur prend en charge le tableau new de membres et delete les opérateurs dans une déclaration de classe. Par exemple :

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

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

Comportement d’échec d’allocation

La new fonction de la bibliothèque standard C++ prend en charge le comportement spécifié dans la norme C++ depuis C++98. En cas d’insuffisance de mémoire pour une demande d’allocation, operator new lève une std::bad_alloc exception.

Un ancien code C++ a retourné un pointeur Null pour une allocation ayant échoué. Si vous avez du code qui attend la version non levée de new, liez votre programme avec nothrownew.obj. Le nothrownew.obj fichier remplace global operator new par une version qui retourne nullptr si une allocation échoue. operator new ne lève std::bad_allocplus . Pour plus d’informations sur nothrownew.obj les autres fichiers d’options de l’éditeur de liens, consultez les options de lien.

Vous ne pouvez pas combiner de code qui case activée pour les exceptions de global operator new avec du code qui case activée s pour les pointeurs Null dans la même application. Toutefois, vous pouvez toujours créer un local operator new de classe qui se comporte différemment. Cette possibilité signifie que le compilateur doit agir de manière défensive par défaut et inclure des case activée pour les retours de pointeur Null dans new les appels. Pour plus d’informations sur un moyen d’optimiser ces case activée de compilateur, consultez /Zc:throwingnew.

Gestion d'une mémoire insuffisante

La façon dont vous testez une allocation ayant échoué à partir d’une new expression dépend de l’utilisation du mécanisme d’exception standard ou d’un nullptr retour. C++ standard s’attend à ce qu’un allocateur lève std::bad_alloc soit une classe dérivée de std::bad_alloc. Vous pouvez gérer une telle exception, comme illustré dans cet exemple :

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

Lorsque vous utilisez la nothrow forme de new, vous pouvez tester un échec d’allocation, comme indiqué dans cet exemple :

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

Vous pouvez tester une allocation de mémoire ayant échoué lorsque vous avez utilisé nothrownew.obj un fichier pour remplacer global operator new , comme indiqué ici :

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

Vous pouvez fournir un gestionnaire pour les demandes d’allocation de mémoire ayant échoué. Il est possible d’écrire une routine de récupération personnalisée pour gérer une telle défaillance. Il peut, par exemple, libérer une mémoire réservée, puis autoriser l’allocation à s’exécuter à nouveau. Pour plus d’informations, consultez _set_new_handler.

Opérateur delete

La mémoire allouée dynamiquement à l’aide de l’opérateur new peut être libérée à l’aide de l’opérateur delete . L’opérateur delete appelle la operator delete fonction, qui libère de la mémoire au pool disponible. L’utilisation de l’opérateur delete entraîne également l’appel du destructeur de classe (le cas échéant).

Il existe des fonctions globales et délimitées par des operator delete classes. operator delete Une seule fonction peut être définie pour une classe donnée ; si elle est définie, elle masque la fonction globaleoperator delete. La fonction globale operator delete est toujours appelée pour les tableaux de n’importe quel type.

Fonction globale operator delete . Deux formulaires existent pour les fonctions globales operator delete et membres operator delete de classe :

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

Un seul des deux formulaires précédents peut être présent pour une classe donnée. Le premier formulaire prend un seul argument de type void *, qui contient un pointeur vers l’objet à libérer. Le deuxième formulaire, dimensionné la désallocation, prend deux arguments : le premier est un pointeur vers le bloc de mémoire à libérer, et le deuxième est le nombre d’octets à libérer. Le type de retour des deux formulaires est void (operator delete ne peut pas retourner une valeur).

L’intention du deuxième formulaire est d’accélérer la recherche de la catégorie de taille correcte de l’objet à supprimer. Ces informations ne sont souvent pas stockées près de l’allocation elle-même et sont probablement non mises en cache. Le deuxième formulaire est utile lorsqu’une operator delete fonction d’une classe de base est utilisée pour supprimer un objet d’une classe dérivée.

La operator delete fonction est statique, de sorte qu’elle ne peut pas être virtuelle. La operator delete fonction obéit au contrôle d’accès, comme décrit dans Le contrôle d’accès aux membres.

L’exemple suivant montre les fonctions définies par operator newoperator delete l’utilisateur conçues pour journaliser les allocations et les désallocations de mémoire :

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

Le code précédent peut être utilisé pour détecter les « fuites de mémoire », c’est-à-dire la mémoire allouée sur le magasin gratuit, mais jamais libérée. Pour détecter les fuites, les opérateurs globaux et delete globaux new sont redéfinis pour compter l’allocation et la désallocation de la mémoire.

Le compilateur prend en charge le tableau new de membres et delete les opérateurs dans une déclaration de classe. Par exemple :

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