samedi 24 mai 2014

make_unique

Le support C++14 de la bibliothèque standard s'améliore également, avec l'ajout de la fonction std::make_unique. Conceptuellement, c'est difficile de faire plus simple: c'est l'équivalent de std::make_shared, mais qui retourne un pointeur unique au lieu d'un pointeur partagé.

#include <memory>
#include <string>
#include <iostream>

int main()
{
  auto val = std::make_unique<std::string>("abcd");
  std::cout << *val << std::endl;
  return 0;
}

Si vous vous souvenez, l'intérêt de make_shared est double: d'une part, permettre l'allocation du bloc de contrôle (contenant entre autres le compteur de références) en même temps que les données, ce qui permet d'augmenter les performances à l'allocation et de réduire la fragmentation mémoire, et d'autre part de rendre le code exception safe dans ce genre de cas:

f(std::shared_ptr<A>(new A), std::shared_ptr<B>(new B)

L'ordre d'exécution des différents éléments n'est pas garanti, et il est possible que le compilateur alloue A, puis B, puis construise les shared_ptr. Dans ce cas, une exception dans le constructeur de B causerait une fuite mémoire dans A. L'utilisation de make_shared résout le problème:

f(std::make_shared<A>(), std::make_shared<B>()

Avec make_unique, la première raison ne tient plus, puisqu'il n'y a pas de structure de contrôle. La deuxième raison, bien sûr, tient encore.

Mais plus généralement, le couple std::make_shared / std::make_unique permet un nouvel idiome qui vise à se débarrasser de tous les new et de tous les delete lorsqu'ils correspondent à de la mémoire gérée par nos pointeurs intelligents. Ainsi, la présence d'un new est une indication pour le programmeur qui relit le code que la section gère la mémoire d'une manière particulière (par exemple, implémentation d'un conteneur bas niveau).

Aucun commentaire: