34 votes

Comment les pointeurs intelligents choisissent-ils entre supprimer et supprimer []?

Considérer:

 delete new std :: string [2];
delete [] new std :: string;
 

Tout le monde sait que le premier est une erreur. Si le second n'était pas une erreur, nous n'aurions pas besoin de deux opérateurs distincts.

Considérez maintenant:

 std :: unique_ptr <int> x (new int [2]);
std :: unique_ptr <int> y (new int);
 

x sait-il utiliser delete[] par opposition à delete ?


Contexte: cette question m'a traversé la tête quand j'ai pensé que la qualification de type de tableau de pointeurs serait une fonctionnalité de langage pratique.

 int *[] foo = new int [2]; // OK
int *   bar = new int;     // OK
delete [] foo;             // OK
delete bar;                // OK
foo = new int;             // Compile error
bar = new int[2];          // Compile error
delete foo;                // Compile error
delete [] bar;             // Compile error
 

32voto

Armen Tsirunyan Points 59548

Malheureusement, ils ne savent pas quoi supprimer pour utiliser, par conséquent, ils utilisent delete. C'est pourquoi, pour chaque pointeur intelligent, nous avons une smart array homologue.

std::shared_ptr uses delete
std::shared_array uses delete[]

Donc, votre ligne

std :: unique_ptr <int> x (new int [2]);

en fait provoque un comportement indéfini.

D'ailleurs, si vous écrivez

std :: unique_ptr<int[]> p(new int[2]);
                     ^^

ensuite, delete[] seront utilisés étant donné que vous avez explicitement demandé. Cependant, la ligne suivante sera toujours UB.

std :: unique_ptr<int[]> p(new int);

La raison qu'ils ne peuvent pas choisir entre delete et delete[] que new int et new int[2] sont exactement du même type - int*.

Ici's un liés à la question de l'utilisation correcte des deleters en cas d' smart_ptr<void> et smart_ptr<Base> lorsque Base n'a pas de destructeur virtuel.

6voto

Matthieu M. Points 101624

Il n'est pas "magique" afin de détecter si un int* désigne:

  • un seul segment de mémoire allouée entier
  • un segment de mémoire allouée tableau
  • un entier dans un segment de mémoire allouée tableau

L'information a été perdue par le système de type et de n d'exécution de la méthode (portable) peut fixer. C'est rageant et un grave défaut de conception (*) dans le C que le C++ hérité (pour des raisons de compatibilité, certains disent).

Cependant, il y sont des manières de faire face avec des tableaux de pointeurs intelligents.

Tout d'abord, votre unique_ptr type est incorrect de traiter avec un tableau, vous devez utiliser:

std::unique_ptr<int[]> p(new int[10]);

qui est censé appeler delete[]. Je sais qu'il y est question de la mise en œuvre d'un avertissement spécifique dans Clang pour attraper évidente inadéquation avec unique_ptr: c'est une qualité de mise en œuvre de la question (de la Standard simplement dit que c'est UB), et tous les cas ne peuvent être couverts sans WPA.

Deuxièmement, une boost::shared_ptr peut avoir un personnalisé deleter, qui pourrait, si vous le concevez pour appeler le bon delete[] de l'opérateur. Cependant, il y a un boost::shared_array spécialement conçu pour cela. Une fois de plus, la détection de l'inadéquation est une qualité de mise en œuvre de problème. std::shared_ptr souffre du même problème (édité après ildjarn la remarque).

Je suis d'accord que c'est pas joli. Il semble tellement odieux qu'un défaut de conception ( * ), depuis les origines de la C nous hante encore aujourd'hui.

(*) certains diront que C penche fortement en faveur d'éviter la surcharge et cela aurait ajouté un rétroprojecteur. J'ai en partie d'accord: malloc toujours de connaître la taille du bloc, après tout.

3voto

Gob00st Points 1982

std::unique_ptr n'est pas fait pour le tableau que je cite plus tard boost document:

Normalement, un shared_ptr ne peut pas tenir correctement un pointeur vers un alloués dynamiquement un tableau. Voir shared_array pour cet usage.

Si vous voulez la gestion de la mémoire pour le tableau de pointeur, vous avez quelques options dépend de votre exigence:

  1. Utiliser boost::shared_array
  2. Utiliser std::vector de boost::shared_ptr
  3. Utilisez le boost pointeur récipient comme boost::ptr_vector

3voto

unwind Points 181987

De la documentation de Microsoft :

(Une spécialisation partielle unique_ptr<Type[]> gère les objets du tableau alloués avec new[] et a le suppresseur par défaut default_delete<Type[]> , spécialisé pour appeler delete[] _Ptr .)

J'ai ajouté les deux derniers crochets, semble être une faute de frappe car cela n'a pas de sens sans eux.

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