web-dev-qa-db-fra.com

Comment attribuer une adresse d'un objet existant à un pointeur intelligent?

#include <memory>

class bar{};

void foo(bar &object){
    std::unique_ptr<bar> pointer = &object;
}

Je veux assigner une adresse de l'objet au pointeur. Le code ci-dessus ne sera évidemment pas compilé, car le côté droit de l'opérateur d'affectation doit être std :: unique_ptr. J'ai déjà essayé ceci:

pointer = std::make_unique<bar>(object)

Mais cela génère de nombreuses erreurs lors de la compilation. Comment puis je faire ça?

Mettre à jour
Comme indiqué dans les réponses - l’utilisation de la méthode std::unique_ptr::reset a entraîné un comportement indéfini. Maintenant, je sais que, dans de tels cas, je devrais utiliser un pointeur standard.

18
Tomasz Kasperczyk

Essayez std :: unique_ptr :: reset

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

Mais sachez que ceci n'est pas recommandé, vous ne devez pas créer un unique_ptr à une référence transmise à une fonction. À la fin de la fonction, lorsque pointer est en cours de destruction, il essaiera également de détruire object et ne sera pas disponible en dehors de l'appel de la fonction, ce qui entraînera une erreur de mémoire d'accès.

Exemple: Ceci compilera, mais donnera une erreur d'exécution.

struct bar{ int num;};

void foo(bar &object){
    std::unique_ptr<bar> pointer;
    pointer.reset(&object);
}

int main()
{
    bar obj;
    foo(obj);
    obj.num; // obj is not a valid reference any more.
    return 0;
}

D'autre part, vous pouvez envisager d'utiliser shared_ptr ceci peut vous aider à décider: unique_ptr ou shared_ptr? .

12
Raydel Miranda

Vous pouvez uniquement affecter un autre unique_ptr ou la nullptr. Si vous y réfléchissez, cela a également un sens (bien que reset vous permette de faire ce que vous voulez, mais je pense qu’il s’agit en fait d’un bogue ou d’un défaut de unique_ptr).

Un unique_ptr est le propriétaire exclusif de l'objet pointé. Lorsqu'il sort de la portée, l'objet sera supprimé.
Cela signifie que votre fonction a la sémantique sink. Le pointeur que vous transmettez (ou plutôt l'objet pointé ") est consommé, c'est-à-dire qu'il" disparaît "(puits) à l'intérieur de la fonction. Vous transmettez un objet par référence (un objet qui n'est même pas nécessairement alloué en tas , préparez-vous à une surprise si vous passez dans un objet avec stockage automatique!) et soudain, il est parti.

La sémantique des puits devrait être communiquée correctement. Vous devriez passer un unique_ptr comme paramètre de fonction. Les pointeurs uniques ne peuvent pas être copiés. Cela obligera donc l'utilisateur de cette fonction à utiliser std::move, en créant une prise de conscience de ce qui se passe réellement.

Avoir un objet "disparaître" est une mauvaise surprise, cela ne devrait pas arriver par inadvertance.

4
Damon

Permettez-moi de vous décevoir, mais vous ne le faites pas. Vous avez là une référence, qui peut désigner un objet alloué dynamiquement ou un objet alloué à une pile, ou peut-être un objet dans un tableau alloué dynamiquement, qui n'a pas été alloué individuellement.

Vous souhaitez placer son adresse dans une classe qui appelle automatiquement delete sur cette adresse. Ce qui est invalide pour la plupart des situations présentées ci-dessus. C'est faux. Donc, passer une référence et mettre l'adresse de la référence dans un pointeur intelligent ne devrait jamais être fait, jamais. Surtout pas avec reset ().

Au mieux, vous voudrez peut-être initialiser le pointeur intelligent avec le nouvel objet attribué, par exemple:

auto ptr = std::unique_ptr<X>(new X(p1, p2));

Ne prenez jamais l’adresse d’une référence. Déjà. Sans raison. C'est moralement faux. Ce n'est pas parce que l'utilisation des adresses de référence n'est pas valide, car il peut s'agir d'une utilisation valide; c'est parce que deux mois plus tard, le nouveau collègue voudra utiliser cette adresse avec un pointeur intelligent, car c'est ainsi qu'il a entendu dire que c'est fait de nos jours. Ce qui est faux, douloureusement faux.

3
Dorin Lazăr

Ce que je peux voir, c'est que vous voulez avoir un objet unique_ptr, qui pointe vers un objet en mémoire, mais vous ne voulez pas transférer la propriété de la mémoire.

Vous devez créer un suppresseur personnalisé pour l'objet, qui ne le supprime pas en réalité: 

class bar{};
struct barfakedeleter
{
   void operator()(bar*){/* do nothing here*/}
};

unique_ptr est un modèle où le deuxième argument est le deleter de l'objet. Par défaut, il s'agit de default_deleter, mais vous pouvez l'échanger contre ce faux:

void foo(bar& object) 
{
unique_ptr<bar,barfakedeleter> pointer(&object);
....
}

post edit: Même chose que vous pouvez faire avec shared_ptr, mais étant donné que shared_ptr peut être converti en un autre shared_ptr dans l'arborescence d'héritage de l'objet, le suppresseur n'est pas stocké dans l'argument de modèle de classe, mais il est passé en tant que membre d'instance. Cela facilite en fait l'utilisation de l'OMI:

void foo(bar& object)
{
   shared_ptr<bar> pointer(&object,[](bar*){}); // just use anonymous function, which will not delete the object
}

Vous pouvez également créer des versions personnalisées de ressources:

shared_ptr<ObjectClass> pointer(GetObject(),[](ObjectClass* obj){ ReleaseTheObject(obj);});
1
Milan

La fonction que vous recherchez est reset() . Voici un exemple:

pointer.reset(&object);

Ici pointer est un unique_ptr. L'appel de reset() détruira l'objet précédent géré par pointer (s'il en existait un) et fera de object l'objet géré actuel de pointer.

Ou, si vous souhaitez définir object l'objet géré correctement lorsque vous initialisez le unique_ptr:

std::unique_ptr<bar> pointer(&object);

Un mot d'avertissement cependant:object devrait être alloué avec new si vous allez le gérer avec un unique_ptr, car unique_ptr peut essayer d'appeler delete dessus. Si elle n'a pas été créée avec new, ne l'enroulez pas dans un unique_ptr, ce n'est pas sa fonction.

1
emlai