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:
unique_ptr
Au lieu d'un pointeur brut?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);
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".
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é.