web-dev-qa-db-fra.com

Mauvaise pratique pour retourner unique_ptr pour un pointeur brut comme la sémantique de propriété?

J'ai écrit une méthode d'usine statique qui renvoie un nouvel objet Foobar rempli à partir d'un autre objet de données. J'ai récemment été obsédé par la sémantique de propriété et je me demande si je transmets le bon message en demandant à cette méthode d'usine de renvoyer un unique_ptr.

class Foobar {
public:
    static unique_ptr<Foobar> factory(DataObject data);
}

Mon intention est de dire au code client qu'il possède le pointeur. Sans pointeur intelligent, je retournerais simplement Foobar*. Je voudrais cependant imposer que cette mémoire soit supprimée pour éviter les bugs potentiels, donc unique_ptr Semblait être une solution appropriée. Si le client souhaite prolonger la durée de vie du pointeur, il suffit d'appeler .release() une fois qu'il obtient le unique_ptr.

Foobar* myFoo = Foobar::factory(data).release();

Ma question se décompose en deux parties:

  1. Cette approche transmet-elle la sémantique de propriété correcte?
  2. Est-ce une "mauvaise pratique" de retourner unique_ptr Au lieu d'un pointeur brut?
49
Bret Kuhns

Renvoyer un std::unique_ptr d'une méthode d'usine est très bien et devrait être une pratique recommandée. Le message qu'il véhicule est (IMO): Vous êtes désormais le seul propriétaire de cet objet. De plus, pour votre commodité, l'objet sait se détruire.

Je pense que c'est beaucoup mieux que de renvoyer un pointeur brut (où le client doit se rappeler comment et si disposer de ce pointeur).

Cependant, je ne comprends pas votre commentaire sur la libération du pointeur pour prolonger sa durée de vie. En général, je vois rarement une raison d'appeler release sur un smartpointer, car je pense que les pointeurs devraient toujours être gérés par une sorte de structure RAII (à peu près la seule situation où j'appelle release est de placez le pointeur dans une autre infrastructure de gestion de données, par exemple un unique_ptr avec un deleter différent, après avoir fait quelque chose pour justifier un nettoyage supplémentaire).

Par conséquent, le client peut (et doit) simplement stocker le unique_ptr quelque part (comme un autre unique_ptr, qui a été construit à partir de celui renvoyé) tant qu'ils ont besoin de l'objet (ou d'un shared_ptr, s'ils ont besoin de plusieurs copies du pointeur). Le code côté client devrait donc ressembler davantage à ceci:

std::unique_ptr<FooBar> myFoo = Foobar::factory(data);
//or:
std::shared_ptr<FooBar> myFoo = Foobar::factory(data);

Personnellement, j'ajouterais également un typedef pour le type de pointeur renvoyé (dans ce cas std::unique_ptr<Foobar>) et ou le deleter utilisé (dans ce cas std :: default_deleter) vers votre objet d'usine. Cela le rend plus facile si vous décidez plus tard de modifier l'allocation de votre pointeur (et avez donc besoin d'une méthode différente pour la destruction du pointeur, qui sera visible en tant que deuxième paramètre de modèle de std::unique_ptr). Je ferais donc quelque chose comme ceci:

class Foobar {
public:  
    typedef std::default_deleter<Foobar>     deleter;
    typedef std::unique_ptr<Foobar, deleter> unique_ptr;

    static unique_ptr factory(DataObject data);
}

Foobar::unique_ptr myFoo = Foobar::factory(data);
//or:
std::shared_ptr<Foobar> myFoo = Foobar::factory(data);
60
Grizzly

UNE std::unique_ptr possède uniquement l'objet vers lequel il pointe. Il dit "je possède cet objet, et personne d'autre ne le fait."

C'est exactement ce que vous essayez d'exprimer: vous dites "appelant à cette fonction: vous êtes désormais le seul propriétaire de cet objet; faites-en comme bon vous semble, sa durée de vie est à votre charge".

17
James McNellis

Il transmet exactement la sémantique correcte et est la façon dont je pense que toutes les usines en C++ devraient fonctionner: std::unique_ptr<T> n'impose aucune sorte de sémantique de propriété et c'est extrêmement bon marché.

6
Dietmar Kühl