Adoptée à partir d' ici.
La plupart des modèles dans la norme C++ de la bibliothèque doivent être instancié avec une complète types. Toutefois shared_ptr
et unique_ptr
sont partielle des exceptions. Certains, mais pas tous de leurs membres peut être instancié avec incomplètes types. La motivation pour ce est de soutenir les expressions idiomatiques telles que pimpl à l'aide de pointeurs intelligents, et sans risque d'un comportement indéfini.
Un comportement indéfini peut se produire lorsque vous avez un type incomplète et que vous appelez delete
sur:
class A;
A* a = ...;
delete a;
Le ci-dessus est le code légal. Compiler. Votre compilateur peut ou ne peut pas émettre une alerte de code ci-dessus comme ci-dessus. Lorsqu'il s'exécute, les mauvaises choses qui va probablement se produire. Si vous êtes très chanceux, votre programme va planter. Cependant, plus la probabilité d'un résultat, c'est que votre programme en silence à une fuite de mémoire en tant que ~A()
ne sera pas appelé.
À l'aide de auto_ptr<A>
dans l'exemple ci-dessus n'aide pas. Vous obtenez toujours le même comportement indéfini comme si vous aviez utilisé un pointeur brut.
Néanmoins, l'utilisation incomplète des classes dans certains endroits est très utile! C'est là que shared_ptr
et unique_ptr
de l'aide. L'utilisation de l'un de ces pointeurs intelligents permettra de vous sortir avec un type incomplète, sauf lorsqu'il est nécessaire d'avoir un type complètes. Et le plus important, quand il est nécessaire d'avoir un type complètes, vous obtiendrez une erreur de compilation si vous essayez d'utiliser le pointeur intelligent avec un type incomplète à ce point.
Pas plus d'un comportement indéfini:
Si votre code compile, alors vous avez utilisé un type complètes partout où vous en avez besoin.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptr
et unique_ptr
exiger un type dans des endroits différents. Les raisons sont obscures, d'avoir à faire avec une dynamique deleter vs statique deleter. Les raisons précises ne sont pas importantes. En fait, dans la plupart de code, il n'est pas vraiment important pour vous de savoir exactement où une complète est requise. Juste le code, et si vous vous trompez, le compilateur va vous le dire.
Toutefois, dans le cas où il est utile pour vous, voici un tableau qui fait état de plusieurs membres de l' shared_ptr
et unique_ptr
à l'égard de l'exhaustivité des exigences. Si le membre exige une complète type, puis l'entrée a un "C", sinon l'entrée de la table est remplie avec "je".
Complete type requirements for unique_ptr and shared_ptr
unique_ptr shared_ptr
+------------------------+---------------+---------------+
| P() | I | I |
| default constructor | | |
+------------------------+---------------+---------------+
| P(const P&) | N/A | I |
| copy constructor | | |
+------------------------+---------------+---------------+
| P(P&&) | I | I |
| move constructor | | |
+------------------------+---------------+---------------+
| ~P() | C | I |
| destructor | | |
+------------------------+---------------+---------------+
| P(A*) | I | C |
+------------------------+---------------+---------------+
| operator=(const P&) | N/A | I |
| copy assignment | | |
+------------------------+---------------+---------------+
| operator=(P&&) | C | I |
| move assignment | | |
+------------------------+---------------+---------------+
| reset() | C | I |
+------------------------+---------------+---------------+
| reset(A*) | C | C |
+------------------------+---------------+---------------+
Toutes les opérations nécessitant pointeur conversions doivent types pour les deux unique_ptr
et shared_ptr
.