web-dev-qa-db-fra.com

Est-ce que nouveau retourne NULL dans tous les cas?

Je sais que selon la norme C++ au cas où le nouveau ne parviendrait pas à allouer de la mémoire, il est censé lever l'exception std :: bad_alloc. Mais j'ai entendu dire que certains compilateurs tels que VC6 (ou implémentation CRT?) N'y adhèrent pas. Est-ce vrai ? Je pose cette question car la vérification de NULL après chaque nouvelle instruction rend le code très laid.

52
Naveen

VC6 n'était pas conforme par défaut à cet égard. new de VC6 a renvoyé 0 (ou NULL).

Voici l'article de la base de connaissances de Microsoft sur ce problème ainsi que la solution de contournement suggérée à l'aide d'un gestionnaire new personnalisé:

Si vous avez un ancien code qui a été écrit pour le comportement VC6, vous pouvez obtenir ce même comportement avec les compilateurs MSVC plus récents (quelque chose comme 7.0 et versions ultérieures) en reliant un fichier objet nommé nothrownew.obj. Il y a en fait un ensemble de règles assez compliqué dans les compilateurs 7.0 et 7.1 (VS2002 et VS2003) pour déterminer s'ils sont par défaut à ne pas lancer ou à lancer new.

Il semble que MS a nettoyé cela dans la version 8.0 (VS2005) - maintenant, il s'agit toujours par défaut d'un nouveau lancement, sauf si vous liez spécifiquement à nothrownew.obj.

Notez que vous pouvez spécifier que vous souhaitez que new renvoie 0 au lieu de lancer std::bad_alloc en utilisant le std::nothrow paramètre:

SomeType *p = new(std::nothrow) SomeType;

Cela semble fonctionner dans VC6, donc cela pourrait être un moyen de corriger plus ou moins mécaniquement le code pour qu'il fonctionne de la même manière avec tous les compilateurs afin que vous n'ayez pas à retravailler la gestion des erreurs existante.

55
Michael Burr

Je voudrais ajouter l'opinion (quelque peu controversée) selon laquelle la vérification de NULL après une tentative d'allocation est à peu près un exercice futile. Si votre programme se heurte à cette situation, il est probable que vous ne puissiez pas faire beaucoup plus que de quitter rapidement. Il est très probable que toute tentative d'allocation ultérieure échouera également.

Sans vérifier NULL, votre code suivant tenterait de déréférencer un pointeur NULL, qui a tendance à quitter le programme rapidement, avec une condition de sortie relativement unique (et facilement déboguable).

Je n'essaye pas de vous dissuader de vérifier NULL, c'est certainement une programmation consciencieuse. Mais vous n'y gagnez pas beaucoup, sauf dans des cas très spécifiques où vous pouvez peut-être stocker des informations de récupération (sans allouer plus de mémoire), ou libérer de la mémoire moins importante, etc. Mais ces cas seront relativement rares pour la plupart des gens.

Compte tenu de cela, je ferais simplement confiance au compilateur pour lancer bad_alloc, personnellement - au moins dans la plupart des cas.

19
unwesen

Basé sur la spécification C++, il lancera toujours std :: bad_alloc lorsque vous utilisez simplement new sans aucun paramètre, mais bien sûr il peut y avoir des compilateurs non conformes.

Je ne voudrais pas coder pour être conforme aux compilateurs non conformes à C++. VC6 étant l'un d'entre eux à cet égard.

Il est toutefois recommandé de toujours définir votre pointeur sur NULL après les avoir supprimés. Donc à cause de cela, la vérification de NULL est toujours nécessaire.

Cela étant dit, voici quelques options pour nettoyer votre code:

Option 1: définir votre propre nouveau gestionnaire

Un moyen sûr de nettoyer votre code serait d'appeler: set_new_handler en premier.

Ensuite, vous pouvez vérifier NULL dans votre gestionnaire et y lancer std :: bad_alloc si NULL est retourné.

Si vous préférez les exceptions, c'est votre meilleur choix. Si vous souhaitez mieux retourner NULL, vous pouvez également le faire en effectuant une capture dans votre nouveau gestionnaire.

Option 2: utilisation d'un nouveau surchargé

Le fichier d'en-tête standard c ++ définit une structure nothrow qui est vide. Vous pouvez utiliser un objet de cette structure dans new pour obtenir sa version surchargée qui renvoie toujours NULL.

void* operator new (size_t size, const std::nothrow_t &);
void* operator new[] (void *v, const std::nothrow_t &nt);

Donc dans votre code:

 char *p = new(std::nothrow) char[1024];

Voici ne bonne réfraction pour une lecture plus approfondie

9
Brian R. Bondy