Est-il sûr de supprimer un pointeur NULL?

299

Est-il sûr de supprimer un pointeur NULL?

Et est-ce un bon style de codage?

qiuxiafei
la source
21
La bonne pratique consiste à écrire des programmes C ++ sans un seul appel à delete. Utilisez plutôt RAII . Autrement dit, utilisez std::vector<T> v(100);au lieu de T* p = new T[100];, utilisez des pointeurs intelligents comme unique_ptr<T>et shared_ptr<T>qui s'occupent de la suppression au lieu des pointeurs bruts, etc.
fredoverflow
8
grâce à make_shared(c ++ 11) et make_unique(c ++ 14) votre programme devrait contenir zéro de newetdelete
sp2danny
2
Il peut encore y avoir de rares cas qui nécessitent une nouvelle / suppression, par exemple atomic <T *>: atomic <unique_ptr <T>> n'est pas autorisé et atomic <shared_ptr <T>> a des frais généraux qui peuvent être inacceptables dans certains cas.
atb
2
Pour déclarer une classe avec la gestion des ressources en utilisant RAII, vous devez appeler new et delete right?
VinGarcia
2
@VinGarcia Le fait est que la plupart des codes utilisateur / client (c'est-à-dire non-bibliothèque) ne devraient jamais avoir à écrire newou delete. Les classes conçues pour gérer les ressources, où les composants standard ne peuvent pas faire le travail, peuvent bien sûr faire ce dont elles ont besoin, mais le fait est qu'elles font le truc laid avec la mémoire qu'elles gèrent, pas le code de l'utilisateur final. Donc, créez votre propre bibliothèque / classe d'assistance pour faire new/ delete, et utilisez cette classe à la place d'eux.
underscore_d

Réponses:

265

deleteeffectue le contrôle de toute façon, donc le vérifier de votre côté ajoute des frais généraux et semble plus laid. Une très bonne pratique consiste à définir le pointeur sur NULL après delete(permet d'éviter la double suppression et d'autres problèmes de corruption de mémoire similaires).

Je serais également ravi si deletepar défaut, le paramètre était défini sur NULL comme dans

#define my_delete(x) {delete x; x = NULL;}

(Je connais les valeurs R et L, mais ça ne serait pas bien?)

ruslik
la source
72
Notez qu'il peut toujours y avoir plusieurs autres pointeurs pointant vers le même objet même si vous en définissez un sur NULL lors de la suppression.
2010 à
13
Dans la plupart des cas dans mon code, le pointeur devient hors de portée une fois qu'il a été supprimé. Bien plus sûr que de simplement le mettre à NULL.
jalf
142
Une pratique très divine ne définit pas le pointeur sur NULL après la suppression. La définition d'un pointeur sur NULL après sa suppression masque les erreurs d'allocation de mémoire, ce qui est une très mauvaise chose. Un programme correct ne supprime pas deux fois un pointeur et un programme qui supprime deux fois un pointeur doit se bloquer.
Damon
15
@Alice: Peu importe ce que dit la norme à cet égard. La norme a défini la suppression d'un pointeur nul étant valide pour une raison absurde il y a 30 ans, il est donc légal (probablement un héritage C). Mais la suppression du même pointeur deux fois (même après avoir changé son modèle de bits) est toujours une grave erreur de programme. Non pas par le libellé de la norme, mais par la logique et l'appropriation du programme. Tout comme la suppression d'un pointeur nul, car le pointeur nul ne correspond à aucun objet , donc rien ne pourrait éventuellement être supprimé. Un programme doit savoir exactement si un objet est valide et à qui il appartient, et quand il peut être supprimé.
Damon
27
@Damon Cependant, malgré ces abrogations de vos règles de propriété draconiennes, les structures sans verrou sont plus robustes que celles basées sur les verrous. Et oui, mes collègues m'aiment en effet pour le profil d'exécution amélioré que ces structures fournissent et la sécurité des threads rigoureuse qu'elles maintiennent, ce qui permet de raisonner plus facilement sur le code (idéal pour la maintenance). Cependant, rien de tout cela ni votre attaque personnelle implicite n'ont à voir avec une définition de l'exactitude, la validité ou la propriété. Ce que vous proposez est une bonne règle de base, mais ce n'est pas une loi universelle ni inscrite dans la norme.
Alice
77

À partir du projet de norme C ++ 0x.

$ 5.3.5 / 2 - "[...] Dans les deux cas, la valeur de l'opérande de suppression peut être une valeur de pointeur nulle. [... '"

Bien sûr, personne ne ferait jamais la «suppression» d'un pointeur avec une valeur NULL, mais c'est sûr de le faire. Idéalement, on ne devrait pas avoir de code qui supprime un pointeur NULL. Mais cela est parfois utile lorsque la suppression de pointeurs (par exemple dans un conteneur) se produit en boucle. Comme la suppression d'une valeur de pointeur NULL est sûre, on peut vraiment écrire la logique de suppression sans vérification explicite de l'opérande NULL à supprimer.

Soit dit en passant, C Standard $ 7.20.3.2 dit également que «gratuit» sur un pointeur NULL ne fait rien.

La fonction libre provoque la désallocation de l'espace pointé par ptr, c'est-à-dire qu'il est disponible pour une allocation ultérieure. Si ptr est un pointeur nul, aucune action ne se produit.

Chubsdad
la source
2
J'aimerais vraiment cette réponse pour ses citations si elle n'a pas intentionnellement introduit l'inefficacité dans le code non optimisé. Comme l'indique la réponse acceptée, la suppression d'un pointeur nul est un non-op. Par conséquent, vérifier si un pointeur est nul avant de le supprimer est complètement superflu.
codetaku
47

Oui c'est sûr.

Il n'y a aucun mal à supprimer un pointeur nul; elle réduit souvent le nombre de tests à la fin d'une fonction si les pointeurs non alloués sont initialisés à zéro puis simplement supprimés.


Étant donné que la phrase précédente a semé la confusion, un exemple - qui ne fait pas exception à la règle - de ce qui est décrit:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Il existe toutes sortes de lentes qui peuvent être choisies avec l'exemple de code, mais le concept est (j'espère) clair. Les variables de pointeur sont initialisées à zéro afin que les deleteopérations à la fin de la fonction n'aient pas besoin de tester si elles ne sont pas nulles dans le code source; le code de bibliothèque effectue cette vérification de toute façon.

Jonathan Leffler
la source
J'ai dû le lire plusieurs fois pour le comprendre. Vous devez vouloir dire les initialiser à zéro en haut de la méthode, ou pendant celle-ci, pas en queue, sûrement? Sinon, vous supprimez simplement la mise à zéro et la suppression.
Marquis de Lorne, le
1
@EJP: Un aperçu ne sont pas totalement invraisemblables d'une fonction peut être: void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Je n'ai pas montré de structure de bloc ou de sauts, mais les delete[]opérations à la fin sont sûres en raison des initialisations au début. Si quelque chose sautait à la fin après avoir x1été alloué et avant y1était alloué et qu'il n'y avait pas d'initialisation de y1, alors il y aurait un comportement indéfini - et bien que le code puisse tester la nullité (de x1et y1) avant les suppressions, il n'y a pas besoin de le faire alors.
Jonathan Leffler
22

La suppression d'un pointeur nul n'a aucun effet. Ce n'est pas un bon style de codage nécessairement parce qu'il n'est pas nécessaire, mais ce n'est pas mal non plus.

Si vous recherchez de bonnes pratiques de codage, envisagez plutôt d'utiliser des pointeurs intelligents, vous n'avez donc pas besoin du deletetout.

Brian R. Bondy
la source
20
le moment où les gens veulent supprimer un pointeur NULL, c'est quand ils ne sont pas sûrs qu'il contient NULL ... s'ils savaient que c'était NULL alors ils n'envisageraient pas de supprimer et donc demanderaient ;-).
Tony Delroy
@Tony: Mon point était seulement qu'il n'aura aucun effet, et la présence d'un tel code qui supprime un pointeur qui contient parfois NULL n'est pas nécessairement mauvaise.
Brian R. Bondy
3
Les contrôles redondants de l'OMI sont certainement mauvais, pour les performances, la lisibilité et la maintenabilité.
paulm
@paulm OP ne parle certainement pas de ce genre de mauvais, plus de mauvais Seg Fault / UB.
Winter
8

Pour compléter la réponse de ruslik , en C ++ 14 vous pouvez utiliser cette construction:

delete std::exchange(heapObject, nullptr);
ashrasmun
la source
3

Il est sûr sauf si vous avez surchargé l'opérateur de suppression. si vous avez surchargé l'opérateur de suppression et ne gérez pas la condition nulle, cela n'est pas sûr du tout.


la source
Pourriez-vous ajouter une explication à votre réponse?
Marcin Nabiałek
-2

J'ai constaté qu'il n'est pas sûr (VS2010) de supprimer [] NULL (c'est-à-dire la syntaxe du tableau). Je ne sais pas si cela est conforme à la norme C ++.

Il est sûr de supprimer NULL (syntaxe scalaire).

pheest
la source
6
C'est illégal et je ne le crois pas.
Konrad Rudolph
5
Vous devriez pouvoir supprimer tout pointeur nul. Donc, si ça se casse pour vous, alors vous avez probablement un bogue dans le code qui le montre.
Mysticial
3
§5.3.2 Dans la deuxième alternative (tableau de suppression), la valeur de l'opérande de suppression peut être une valeur de pointeur nulle ou une valeur de pointeur résultant d'une nouvelle expression de tableau précédente.
sp2danny
1
@Opux Le comportement de VS2010 allégué dans la réponse. Comme expliqué dans d'autres commentaires, c'est sûr delete[] NULL.
Konrad Rudolph
1
@Opux C'est pourquoi j'ai écrit «je n'y crois pas» plutôt que «c'est faux». Mais je ne le fais toujours pas, et ce serait une violation assez scandaleuse et stupide de la norme. VC ++ est généralement assez bon pour suivre les restrictions de la norme, et les endroits où il les viole ont un sens historiquement.
Konrad Rudolph