2012-05-10 3 views
3

Je suis tombé sur un étrange segfault. La cause m'a conduit en fait à un bug, mais je ne comprends toujours pas pourquoi une erreur de segmentation est causée ici ... Le code est:Strange segfault avec unique_ptr et shared_ptr

#include <memory> 
int main(int argc, char **arv) 
{ 
    int *i = new int; 
    std::unique_ptr<int> u1(i); 
    std::unique_ptr<int> u2; 
    u1 = std::move(u2); // line 7 
    std::shared_ptr<int> s1(i); // line 8 
    std::shared_ptr<int> s2; 
    s2 = s1; 
} 

Je compilez avec g ++ 4.6 et -std=c++0x et obtenir un segfault.

Si je change la ligne 7 à u2 = std::move(u1); (c'était le bogue) il disparaît. Si je change la ligne 8 en std::shared_ptr<int> s1(new int(3)); (ce que bien sûr je ne veux pas) elle disparaît aussi. Si je supprime de la ligne 8 également aucun segfault.

Donc, pas de mal fait, mais je ne comprends pas pourquoi il devrait y avoir un segfault. Pour autant que je comprends,
dans la ligne 7 un pointeur vide est assigné à u1. Pas de réinitialisation(), pas de fin de portée. Néanmoins i semble être invalide à partir de là. Est-ce intentionné? Cela signifie qu'il faut être très très prudent lorsque vous déplacez un pointeur car un autre objet pourrait être détruit!

Qu'en pensez-vous? Comment puis-je me protéger de cela?

Merci, Steffen

Répondre

11

Votre ligne 8 est erroné: Une fois que vous capturez i dans le unique_ptr, vous ne devez pas encore donner une certaine autre objet propriété prise! Chaque propriétaire va tenter de supprimer *i, ce qui est faux.

Au lieu de cela, vous devez créer le pointeur partagé à partir du pointeur unique:

std::shared_ptr<int> s1(std::move(u2)); 

(De plus, vous avez u1 et u2 dans le mauvais sens.)

+1

ce qui me dérange, c'est que je peux le faire sans même un avertissement lors de la compilation avec '-pedantic -Wall -Wextra'. La réponse est-elle vraiment juste "Ne pas!"? – steffen

+2

@steffen: En effet, la réponse est "Do not". Il n'y a pas de protection contre 'int * p = new int; do_crazy_stuff (p); be_insane (p); take_ownership (p); Le compilateur ne peut pas vraiment savoir ce que vous allez faire avec le pointeur. –

+0

Vrai ... merci pour la dé-illusion :) – steffen

3

Cette ligne:

u1 = std::move(u2); 

Transforme le pointeur précédent stockées par u1 à supprimer. Par conséquent, votre pointeur i est libéré. La création d'un shared_ptr contenant le pointeur free'd est un comportement indéfini.

+0

ouais. Je pensais que. Voir mon commentaire à Kerrek: aucun avertissement. Le compilateur laisse moi me blesser ici! – steffen

+0

Il n'y a rien de mal à assigner les pointeurs uniques de toute façon. Un pointeur unique construit par défaut est null, et l'affectation dispose correctement de l'ancienne ressource. La ligne 'u1 = std :: move (u2);' supprime '* i', et puis' u1' et 'u2' sont tous deux nuls. –

+0

@KerrekSB Je n'ai jamais dit que quelque chose n'allait pas avec l'attribution d'un unique_ptr. La façon dont OP le fait, le pointeur maintenu par 'u1' est libre après l'assignation de mouvement' u2'. Le problème est qu'il crée un shared_ptr au pointeur (déjà libre). Steffen, le compilateur n'a aucune capacité de savoir ce que shared/unique_ptr fait ce que les pointeurs qu'ils stockent. – mfontanini