39 votes

Nouveau (std::nothrow) vs. Nouveau dans un bloc try/catch

J'ai fait des recherches après avoir appris new contrairement à malloc() dont j'ai l'habitude, ne renvoie pas NULL en cas d'échec des allocations, et j'ai découvert qu'il y a deux façons distinctes de vérifier si une nouvelle allocation a réussi ou non. Ces deux façons sont :

try
{
    ptr = new int[1024];
}
catch(std::bad_alloc& exc)
{
    assert();
};

y

ptr = new (std::nothrow) int[1024];
if(ptr == NULL) 
    assert();

Je pense que les deux méthodes permettent d'atteindre le même objectif (corrigez-moi si je me trompe, bien sûr !), et ma question est donc la suivante :

qui est la meilleure option pour vérifier si new a réussi, en se basant entièrement sur la lisibilité, la maintenabilité et les performances, tout en ignorant les conventions de programmation de facto du c++.

49voto

Nicol Bolas Points 133791

Réfléchissez à ce que vous faites. Vous allouez de la mémoire. Et si pour une raison quelconque l'allocation de mémoire ne peut pas fonctionner, vous assert . Ce qui est plus ou moins exactement ce qui se passera si vous laissez juste la std::bad_alloc se propagent jusqu'à main . Dans un build de version, où assert est un no-op, votre programme se plantera lorsqu'il essaiera d'accéder à la mémoire. Donc c'est la même chose que de laisser l'exception remonter : arrêter le programme.

Alors posez-vous une question : Est-ce que vous realmente besoin de se soucier de ce qui se passe si vous manquez de mémoire ? Si tout ce que vous faites est d'affirmer, alors la méthode des exceptions est meilleure, parce qu'elle n'encombre pas votre code avec des erreurs aléatoires. assert s. Vous laissez simplement l'exception retomber sur main .

Si vous avez effectivement un chemin de code spécial dans le cas où vous ne pouvez pas allouer de la mémoire (c'est-à-dire que vous pouvez continuer à fonctionner), les exceptions peuvent ou non être une solution, en fonction de ce que le chemin de code est. Si le codepath n'est qu'un interrupteur défini par le fait qu'un pointeur est nul, alors la fonction nothrow sera plus simple. Si, au contraire, vous avez besoin de faire quelque chose d'assez différent (extraire d'un tampon statique, ou supprimer quelque chose, ou quoi que ce soit d'autre), alors le fait d'attraper std::bad_alloc est assez bon.

12voto

Praetorian Points 47122

Cela dépend du contexte dans lequel l'allocation a lieu. Si votre programme peut continuer même si l'allocation échoue (peut-être en renvoyant un code d'erreur à l'appelant), alors utilisez l'option std::nothrow et vérifier s'il y a NULL. Sinon, vous utiliseriez des exceptions pour le flux de contrôle, ce qui n'est pas une bonne pratique.

D'un autre côté, si votre programme a absolument besoin que cette mémoire soit allouée avec succès pour pouvoir fonctionner, utilisez try-catch à attraper (pas nécessairement dans le voisinage immédiat de la new ) une exception et sortir gracieusement du programme.

7voto

Chad Points 9700

Du point de vue de la performance pure, cela importe peu. Il y a une surcharge inhérente à la gestion des exceptions, bien que cette surcharge vaille généralement la peine d'être compensée par la lisibilité et la maintenance de l'application. Les échecs d'allocation de mémoire de cette nature ne devraient pas être dans le cas de 99% de votre application, donc cela devrait se produire peu fréquemment.

Du point de vue des performances, il est généralement préférable d'éviter l'allocateur standard en raison de ses performances relativement faibles.

Cela dit, j'accepte généralement la version qui lève des exceptions, car nos applications sont généralement dans un état où, en cas d'échec de l'allocation de mémoire, nous ne pouvons pas faire grand-chose d'autre que de nous retirer gracieusement avec un message d'erreur approprié. NULL vérifier nos ressources nouvellement allouées car, par définition, un échec d'allocation déplacera la portée hors de l'endroit où cela compte.

-3voto

drivebycoder Points 1

new est utilisé pour créer des objets, et non pour allouer de la mémoire. Votre exemple est donc quelque peu artificiel.

Les constructeurs d'objets lancent généralement un message s'ils échouent. Après avoir parcouru les new dans Visual Studio plus d'une fois, je ne pense pas que le code attrape des exceptions. Il est donc généralement judicieux de rechercher les exceptions lors de la création d'objets.

I pensez à std::bad_alloc n'est lancée que si la partie allocation de mémoire échoue. Je ne suis pas sûr de ce qui se passe si vous passez la commande std::nothrow a new mais le constructeur de l'objet lance - il y a une ambiguïté dans les documents que j'ai lus.

La différence de performance entre les 2 approches n'est probablement pas pertinente puisque la plupart du temps du processeur peut facilement être passé dans le constructeur de l'objet ou à chercher dans le tas.

Une règle empirique n'est pas toujours appropriée. Par exemple, les systèmes en temps réel limitent généralement les allocations de mémoire dynamique, de sorte que new si elle est présente, sera probablement surchargée. Dans ce cas, il pourrait utiliser un pointeur nul retourné et gérer l'échec localement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X