ProgrammationDéveloppeur C++, programmeur système

Parlez du mécanisme operator new/operator delete en C++. Comment se distinguent-ils de new/delete, quand et pourquoi les fonctions opérateurs sont-elles redéfinies dans les classes utilisateur ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En C++, operator new et operator delete sont des fonctions d'allocation et de désallocation de mémoire, qui sont appelées lors de la création et de la suppression d'objets via les opérateurs new et delete. Par défaut, elles utilisent l'allocation standard, mais dans une classe, elles peuvent être redéfinies pour un contrôle plus fin de l'allocation mémoire.

  • operator new alloue un segment de mémoire "brut", sans appeler le constructeur.
  • Après l'allocation de la mémoire, le constructeur de l'objet est automatiquement appelé.
  • operator delete libère la mémoire après l'exécution du destructeur de l'objet.

Utilisation (versions globales et locales) :

  • La version globale est utilisée par défaut (par exemple, lors de new int).
  • En redéfinissant un operator new spécifique dans une classe, il est possible d'optimiser le fonctionnement de la mémoire pour les objets de cette classe (par exemple, un pool d'objets, le suivi, la réutilisation des blocs).

Exemple de surcharge operator new/operator delete :

#include <iostream> class TrackAlloc { public: void* operator new(size_t size) { std::cout << "TrackAlloc::new for " << size << " bytes "; return ::operator new(size); } void operator delete(void* ptr) { std::cout << "TrackAlloc::delete "; ::operator delete(ptr); } };

Subtilités :

  • L'appel à new/delete d'objets avec des arguments (placement new) nécessite une surcharge distincte.
  • Les opérateurs redéfinis ne sont appelés que lors de la création/suppression d'objets de la classe, et non pour new[]/delete[]. Pour ceux-ci, il est possible de surcharger des operator new[]/delete[].
  • Les manipulations de mémoire sont dangereuses – il est nécessaire de gérer correctement les exceptions.

Question piège.

"Que se passe-t-il si on redéfinit l'opérateur new dans une classe, puis on crée un objet via une variable de classe dérivée ? Quelle version de operator new sera appelée ?"

Réponse : L'operator new de la classe dont l'objet est créé sera appelé. Si la classe dérivée n'a pas d'operator new implémenté, elle tentera de trouver la version appropriée dans la classe de base ou la version globale.

Exemple :

struct Base { void* operator new(size_t s) { std::cout << "Base new "; return ::operator new(s); } }; struct Derived : Base {}; Derived* p = new Derived; // Appellera Base::operator new !

Exemples d'erreurs réelles dues à un manque de connaissance des subtilités du sujet.


Histoire

Les développeurs ont surchargé operator new/delete sans prise en charge d'une gestion correcte des exceptions. Lorsqu'une exception est levée à l'intérieur du constructeur, la mémoire n'était pas libérée, entraînant des fuites.


Histoire

Ils ont mal implémenté operator new[] et operator delete[] : pour une classe qui contenait des tableaux, la nouvelle implémentation n'était pas appelée – les versions par défaut étaient utilisées, ce qui entraînait une désynchronisation de la logique d'allocation et de désallocation de la mémoire.


Histoire

La redéfinition de l'opérateur new global a affecté le fonctionnement des bibliothèques tierces : tous les objets (y compris temporaires et de la STL) ont commencé à s'allouer via l'allocateur journalisé, ce qui a considérablement ralenti le noyau de l'application.