std:: unique_ptr
|
Défini dans l'en-tête
<memory>
|
||
|
template
<
class
T,
|
(1) | (depuis C++11) |
|
template
<
class
T,
|
(2) | (depuis C++11) |
std::unique_ptr
est un pointeur intelligent qui possède (est responsable de) et gère un autre objet via un pointeur, et élimine ensuite cet objet lorsque le
unique_ptr
sort de la portée.
L'objet est éliminé, en utilisant le suppresseur associé, lorsque l'une des situations suivantes se produit :
-
l'objet
unique_ptrgestionnaire est détruit. -
l'objet
unique_ptrgestionnaire se voit attribuer un autre pointeur via operator= ou reset() .
L'objet est éliminé, en utilisant un suppresseur potentiellement fourni par l'utilisateur, en appelant
get_deleter
(
)
(
ptr
)
. Le suppresseur par défaut (
std::default_delete
) utilise l'opérateur
delete
, qui détruit l'objet et désalloue la mémoire.
Un
unique_ptr
peut également ne posséder aucun objet, auquel cas il est décrit comme
vide
.
Il existe deux versions de
unique_ptr
:
- Gère un seul objet (par exemple, alloué avec new ).
- Gère un tableau d'objets alloué dynamiquement (par exemple, alloué avec new [ ] ).
La classe satisfait aux exigences de MoveConstructible et de MoveAssignable , mais ni de CopyConstructible ni de CopyAssignable .
Si
T*
n'était pas un type valide (par exemple,
T
est un type référence), un programme qui instancie la définition de
std
::
unique_ptr
<
T, Deleter
>
est mal formé.
| Exigences de type | ||
-
Deleter
doit être un
FunctionObject
ou une référence lvalue à un
FunctionObject
ou une référence lvalue à une fonction, pouvant être appelée avec un argument de type
unique_ptr
<
T, Deleter
>
::
pointer
.
|
Table des matières |
Notes
Seul un
unique_ptr
non constant peut transférer la propriété de l'objet géré à un autre
unique_ptr
. Si la durée de vie d'un objet est gérée par un
const
std
::
unique_ptr
, elle est limitée à la portée dans laquelle le pointeur a été créé.
unique_ptr
est couramment utilisé pour gérer la durée de vie des objets, notamment :
- fournir une garantie d'exception aux classes et fonctions qui manipulent des objets à durée de vie dynamique, en assurant leur suppression à la fois lors d'une sortie normale et lors d'une sortie par exception.
- transfert de propriété d'objets à propriétaire unique avec durée de vie dynamique vers des fonctions.
- acquisition de la propriété d'objets à durée de vie dynamique et à propriété unique depuis des fonctions.
- comme type d'élément dans les conteneurs compatibles avec le déplacement, tels que std::vector , qui contiennent des pointeurs vers des objets alloués dynamiquement (par exemple si un comportement polymorphe est souhaité).
unique_ptr
peut être construit pour un
type incomplet
T
, par exemple pour faciliter son utilisation comme handle dans l'
idiome pImpl
. Si le suppresseur par défaut est utilisé,
T
doit être complet au point du code où le suppresseur est invoqué, ce qui se produit dans le destructeur, l'opérateur d'affectation de déplacement, et la fonction membre
reset
de
unique_ptr
. (En revanche,
std::shared_ptr
ne peut pas être construit à partir d'un pointeur brut vers un type incomplet, mais peut être détruit là où
T
est incomplet). Notez que si
T
est une spécialisation de modèle de classe, l'utilisation de
unique_ptr
comme opérande, par exemple
!
p
nécessite que les paramètres de
T
soient complets en raison de l'
ADL
.
Si
T
est une
classe dérivée
d'une classe de base
B
, alors
unique_ptr
<
T
>
est
implicitement convertible
en
unique_ptr
<
B
>
. Le suppresseur par défaut du
unique_ptr
<
B
>
résultant utilisera
operator delete
pour
B
, conduisant à un
comportement indéfini
sauf si le destructeur de
B
est
virtuel
. Notez que
std::shared_ptr
se comporte différemment :
std::
shared_ptr
<
B
>
utilisera
operator delete
pour le type
T
et l'objet détenu sera correctement supprimé même si le destructeur de
B
n'est pas
virtuel
.
Contrairement à
std::shared_ptr
,
unique_ptr
peut gérer un objet via tout type de handle personnalisé satisfaisant
NullablePointer
. Cela permet, par exemple, de gérer des objets situés en mémoire partagée, en fournissant un
Deleter
qui définit
typedef
boost::offset_ptr
pointer;
ou un autre
pointeur sophistiqué
.
| Macro de test de fonctionnalité | Valeur | Norme | Fonctionnalité |
|---|---|---|---|
__cpp_lib_constexpr_memory
|
202202L
|
(C++23) |
constexpr
std::unique_ptr
|
Types imbriqués
| Type | Définition |
| pointer |
std::
remove_reference
<
Deleter
>
::
type
::
pointer
si ce type existe, sinon
T*
. Doit satisfaire
NullablePointer
|
| element_type |
T
, le type de l'objet géré par ce
unique_ptr
|
| deleter_type |
Deleter
, l'objet fonction ou la référence lvalue vers une fonction ou un objet fonction, à appeler depuis le destructeur
|
Fonctions membres
construit un nouveau
unique_ptr
(fonction membre publique) |
|
|
détruit l'objet géré s'il est présent
(fonction membre publique) |
|
assigne le
unique_ptr
(fonction membre publique) |
|
Modificateurs |
|
|
retourne un pointeur vers l'objet géré et libère la propriété
(fonction membre publique) |
|
|
remplace l'objet géré
(fonction membre publique) |
|
|
échange les objets gérés
(fonction membre publique) |
|
Observateurs |
|
|
retourne un pointeur vers l'objet géré
(fonction membre publique) |
|
|
retourne le suppresseur utilisé pour la destruction de l'objet géré
(fonction membre publique) |
|
|
vérifie s'il existe un objet géré associé
(fonction membre publique) |
|
Version objet unique,
|
|
|
déréférence le pointeur vers l'objet géré
(fonction membre publique) |
|
Version tableau,
|
|
|
fournit un accès indexé au tableau géré
(fonction membre publique) |
|
Fonctions non membres
|
(C++14)
(C++20)
|
crée un pointeur unique qui gère un nouvel objet
(modèle de fonction) |
|
(supprimé en C++20)
(C++20)
|
compare avec un autre
unique_ptr
ou avec
nullptr
(modèle de fonction) |
|
(C++20)
|
affiche la valeur du pointeur géré vers un flux de sortie
(modèle de fonction) |
|
(C++11)
|
spécialise l'algorithme
std::swap
(modèle de fonction) |
Classes d'assistance
|
(C++11)
|
Prise en charge du hachage pour
std::unique_ptr
(spécialisation de modèle de classe) |
Exemple
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // classe d'assistance pour la démonstration de polymorphisme à l'exécution ci-dessous struct B { virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; } }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // une fonction consommant un unique_ptr peut le prendre par valeur ou par référence rvalue std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // fonction d'assistance pour la démonstration de suppresseur personnalisé ci-dessous void close_file(std::FILE* fp) { std::fclose(fp); } // Démonstration de liste chaînée basée sur unique_ptr struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // détruire les nœuds de la liste séquentiellement dans une boucle, le destructeur par défaut // aurait invoqué récursivement le destructeur de son « next », ce qui aurait // provoque un débordement de pile pour des listes suffisamment grandes. while (head) { auto next = std::move(head->next); head = std::move(next); } } void push(int data) { head = std::unique_ptr<Node>(new Node{data, std::move(head)}); } }; int main() { std::cout << "1) Démonstration des sémantiques de propriété exclusive\n"; { // Créer une ressource (en propriété exclusive) std::unique_ptr<D> p = std::make_unique<D>(); // Transférer la propriété à « pass_through », // qui à son tour transfère la propriété via la valeur de retour std::unique_ptr<D> q = pass_through(std::move(p)); // « p » est maintenant dans un état 'vide' après déplacement, égal à nullptr assert(!p); } std::cout << "\n" "2) Démonstration du polymorphisme à l'exécution\n"; { // Créer une ressource dérivée et y pointer via le type de base std::unique_ptr<B> p = std::make_unique<D>(); // La répartition dynamique fonctionne comme prévu p->bar(); } std::cout << "\n" "3) Démonstration de suppresseur personnalisé\n"; std::ofstream("demo.txt") << 'x'; // préparer le fichier à lire { using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; unique_file_t fp(std::fopen("demo.txt", "r"), &close_file); if (fp) std::cout << char(std::fgetc(fp.get())) << '\n'; } // “close_file()” appelé ici (si “fp” n'est pas null) std::cout << "\n" "4) Démonstration de sécurité des exceptions et de suppresseur d'expression lambda personnalisé\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) { std::cout << "destruction à partir d'un suppresseur personnalisé...\n"; delete ptr; }); throw std::runtime_error(""); // “p” fuiterait ici s'il s'agissait d'un pointeur brut } catch (const std::exception&) { std::cout << "Exception capturée\n"; } std::cout << "\n" "5) Démonstration de unique_ptr sous forme de tableau\n"; { std::unique_ptr<D[]> p(new D[3]); } // “D::~D()” est appelé 3 fois std::cout << "\n" "6) Démonstration de liste chaînée\n"; { List wall; const int enough{1'000'000}; for (int beer = 0; beer != enough; ++beer) wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8")); std::cout << enough << " bouteilles de bière sur le mur...\n"; } // détruit toutes les bières }
Sortie possible :
1) Démonstration de sémantique de propriété exclusive D::D D::bar D::~D 2) Démonstration de polymorphisme à l'exécution D::D D::bar D::~D 3) Démonstration de suppresseur personnalisé x 4) Démonstration de sécurité d'exception et suppresseur par expression lambda D::D destruction depuis un suppresseur personnalisé... D::~D Exception capturée 5) Démonstration de forme tableau de unique_ptr D::D D::D D::D D::~D D::~D D::~D 6) Démonstration de liste chaînée 1,000,000 bouteilles de bière sur le mur...
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement correct |
|---|---|---|---|
| LWG 4144 | C++11 |
T*
n'était pas requis pour former un type valide
|
requis |
Voir aussi
|
(C++11)
|
pointeur intelligent avec sémantique de propriété partagée d'objet
(modèle de classe) |
|
(C++11)
|
référence faible à un objet géré par
std::shared_ptr
(modèle de classe) |
|
(C++26)
|
un wrapper contenant un objet alloué dynamiquement avec sémantique de type valeur
(modèle de classe) |
|
(C++17)
|
objets qui contiennent des instances de tout type
CopyConstructible
(classe) |