Przyjęto stąd .
Większość szablonów w standardowej bibliotece C ++ wymaga, aby były tworzone z kompletnymi typami. Jednak shared_ptr
i unique_ptr
to częściowe wyjątki. Niektóre, ale nie wszyscy ich członkowie, mogą być tworzone z niekompletnymi typami. Motywacją do tego jest wspieranie idiomów takich jak pimpl przy użyciu inteligentnych wskaźników i bez ryzyka nieokreślonego zachowania.
Nieokreślone zachowanie może wystąpić, gdy masz niekompletny typ i wywołujesz delete
go:
class A;
A* a = ...;
delete a;
Powyżej jest kodeks prawny. To się skompiluje. Twój kompilator może emitować ostrzeżenie o powyższym kodzie, jak wyżej. Kiedy to nastąpi, prawdopodobnie zdarzą się złe rzeczy. Jeśli masz szczęście, Twój program się zawiesi. Jednak bardziej prawdopodobne jest to, że twój program po cichu wyciek pamięci, ponieważ ~A()
nie zostanie wywołany.
Użycie auto_ptr<A>
w powyższym przykładzie nie pomaga. Nadal masz takie samo niezdefiniowane zachowanie, jakbyś używał surowego wskaźnika.
Niemniej jednak używanie niekompletnych klas w niektórych miejscach jest bardzo przydatne! To gdzie shared_ptr
i unique_ptr
pomoc. Użycie jednego z tych inteligentnych wskaźników pozwoli ci uciec z niekompletnym typem, z wyjątkiem przypadków, gdy konieczne jest posiadanie pełnego typu. A co najważniejsze, gdy konieczne jest posiadanie pełnego typu, pojawia się błąd czasu kompilacji, jeśli spróbujesz użyć inteligentnego wskaźnika z niekompletnym typem w tym momencie.
Nigdy więcej nieokreślonego zachowania:
Jeśli Twój kod się kompiluje, to wszędzie tam, gdzie potrzebujesz, używałeś pełnego typu.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptr
i unique_ptr
wymagają pełnego typu w różnych miejscach. Przyczyny są niejasne, związane z dynamicznym usuwaniem w porównaniu z usuwaniem statycznym. Dokładne powody nie są ważne. W rzeczywistości w większości kodów nie jest tak naprawdę ważne, aby wiedzieć dokładnie, gdzie wymagany jest pełny typ. Po prostu koduj, a jeśli się pomylisz, kompilator powie ci.
Jednak w przypadku jest to pomocne dla Ciebie, tutaj znajduje się tabela, która dokumentuje kilku członków shared_ptr
, a unique_ptr
w odniesieniu do wymogów kompletności. Jeśli element wymaga pełnego typu, wówczas wpis ma „C”, w przeciwnym razie wpis w tabeli jest wypełniony „I”.
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 |
+------------------------+---------------+---------------+
Wszelkie operacje wymagające konwersji wskaźnika wymagają kompletnych typów dla obu unique_ptr
i shared_ptr
.
unique_ptr<A>{A*}
Konstruktor może uciec z niekompletną A
tylko wtedy, gdy kompilator nie jest wymagane, aby skonfigurować połączenie do ~unique_ptr<A>()
. Na przykład, jeśli umieścisz unique_ptr
stos na stosie, możesz uciec z niekompletnym A
. Więcej szczegółów na ten temat można znaleźć w odpowiedzi BarryTheHatchet tutaj .
shared_ptr
unique_ptr
Howarda Hinnanta i / ” . Tabela na końcu powinna odpowiedzieć na twoje pytanie.