Odpowiedzi:
To proste, gdy masz właściwości, które możesz przypisać do każdego inteligentnego wskaźnika. Istnieją trzy ważne właściwości.
Pierwszy oznacza, że inteligentny wskaźnik nie może usunąć obiektu, ponieważ nie jest on jego właścicielem. Drugi oznacza, że tylko jeden inteligentny wskaźnik może kiedykolwiek wskazywać ten sam obiekt w tym samym czasie. Jeśli inteligentny wskaźnik ma zostać zwrócony z funkcji, własność jest na przykład przenoszona do zwróconego inteligentnego wskaźnika.
Trzeci oznacza, że wiele inteligentnych wskaźników może wskazywać na ten sam obiekt w tym samym czasie. Odnosi się to również do surowego wskaźnika , jednak surowe wskaźniki nie mają ważnej cechy: nie określają, czy są właścicielami, czy nie. Inteligentny wskaźnik udziału własności usunie obiekt, jeśli każdy właściciel zrezygnuje z obiektu. Takie zachowanie jest często potrzebne, więc inteligentne wskaźniki będące własnością wspólną są szeroko rozpowszechnione.
Niektóre posiadające inteligentne wskaźniki nie obsługują ani drugiego, ani trzeciego. Dlatego nie można ich zwrócić z funkcji ani przekazać gdzie indziej. Który jest najbardziej odpowiedni do RAII
celów, w których inteligentny wskaźnik jest przechowywany lokalnie i jest właśnie tworzony, aby uwalniał obiekt po przekroczeniu zakresu.
Udział własności można zrealizować, mając konstruktor kopii. To oczywiście kopiuje inteligentny wskaźnik, a zarówno kopia, jak i oryginał będą odnosić się do tego samego obiektu. Przeniesienia własności nie można obecnie tak naprawdę zrealizować w C ++, ponieważ nie ma możliwości przeniesienia czegoś z jednego obiektu do drugiego obsługiwanego przez język: Jeśli spróbujesz zwrócić obiekt z funkcji, dzieje się tak, że obiekt jest kopiowany. Tak więc inteligentny wskaźnik, który realizuje przeniesienie własności, musi użyć konstruktora kopiowania, aby zrealizować to przeniesienie własności. To z kolei przerywa jego użycie w kontenerach, ponieważ wymagania określają pewne zachowanie konstruktora kopiującego elementów kontenerów, które jest niezgodne z tym tak zwanym zachowaniem „inteligentnego konstruktora” tych inteligentnych wskaźników.
C ++ 1x zapewnia natywne wsparcie dla przenoszenia własności, wprowadzając tak zwane „konstruktory ruchów” i „operatory przypisania ruchów”. Jest również wyposażony w taki inteligentny wskaźnik przeniesienia własności o nazwie unique_ptr
.
scoped_ptr
to inteligentny wskaźnik, którego nie można przenieść ani udostępnić. Jest to użyteczne, jeśli lokalnie potrzebujesz przydzielić pamięć, ale upewnij się, że zostanie ponownie zwolniona, gdy wyjdzie poza zakres. Ale nadal możesz go wymienić na inny scoped_ptr, jeśli chcesz.
shared_ptr
to inteligentny wskaźnik, który dzieli własność (trzeci rodzaj powyżej). Jest liczony jako odniesienie, dzięki czemu może zobaczyć, kiedy ostatnia jego kopia wykracza poza zakres, a następnie uwalnia zarządzany obiekt.
weak_ptr
jest nie posiadającym inteligentnego wskaźnika. Służy do odwoływania się do zarządzanego obiektu (zarządzanego przez shared_ptr) bez dodawania liczby referencji. Zwykle trzeba wyciągnąć surowy wskaźnik z shared_ptr i skopiować go. Nie byłoby to jednak bezpieczne, ponieważ nie można sprawdzić, kiedy obiekt został faktycznie usunięty. Zatem słaby_ptr zapewnia środki poprzez odwołanie się do obiektu zarządzanego przez shared_ptr. Jeśli potrzebujesz uzyskać dostęp do obiektu, możesz zablokować zarządzanie nim (aby uniknąć tego w innym wątku, shared_ptr uwalnia go podczas korzystania z obiektu), a następnie użyj go. Jeśli słaby_ptr wskazuje na obiekt już usunięty, zauważy cię, zgłaszając wyjątek. Korzystanie ze słaby_ptr jest najbardziej korzystne, gdy masz cykliczne odwołanie: liczenie odniesień nie może łatwo poradzić sobie z taką sytuacją.
intrusive_ptr
jest podobny do shared_ptr, ale nie zachowuje liczby referencji w shared_ptr, ale pozostawia zwiększenie / zmniejszenie liczby do niektórych funkcji pomocniczych, które muszą być zdefiniowane przez zarządzany obiekt. Ma to tę zaletę, że już przywoływany obiekt (który ma liczbę referencji zwiększoną przez zewnętrzny mechanizm liczenia referencji) może być upchnięty w intrusive_ptr - ponieważ liczba referencji nie jest już wewnętrzna względem inteligentnego wskaźnika, ale inteligentny wskaźnik wykorzystuje istniejący referencyjny mechanizm zliczania.
unique_ptr
jest wskaźnikiem przeniesienia własności. Nie możesz go skopiować, ale możesz go przenieść za pomocą konstruktorów ruchów C ++ 1x:
unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!
Jest to semantyka, której przestrzega std :: auto_ptr, ale z powodu braku natywnej obsługi ruchu, nie zapewnia ich bez pułapek. Unique_ptr automatycznie wykradnie zasoby z tymczasowego innego pliku Unique_ptr, który jest jedną z kluczowych cech semantyki ruchu. auto_ptr zostanie wycofane w następnej wersji C ++ Standard na korzyść Unique_ptr. C ++ 1x zezwoli również na upychanie obiektów, które są ruchome, ale nie można ich kopiować do kontenerów. Możesz na przykład włożyć unikatowe elementy do wektora. Zatrzymam się tutaj i odsyłam do świetnego artykułu na ten temat, jeśli chcesz przeczytać więcej na ten temat.
auto_ptr
jest już przestarzałe (C ++ 11).
intrusive_ptr
może to być lepsze niż shared_ptr
dla lepszej spójności pamięci podręcznej. Najwyraźniej pamięć podręczna działa lepiej, jeśli przechowujesz liczbę referencji jako część pamięci samego zarządzanego obiektu zamiast osobnego obiektu. Można to zaimplementować w szablonie lub nadklasie zarządzanego obiektu.
scoped_ptr jest najprostszy. Kiedy wychodzi poza zakres, zostaje zniszczony. Poniższy kod jest nielegalny (scoped_ptrs nie można kopiować), ale zilustruje pewną kwestię:
std::vector< scoped_ptr<T> > tPtrVec;
{
scoped_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory
shared_ptr jest liczony jako referencja. Za każdym razem, gdy nastąpi kopia lub przypisanie, liczba referencji jest zwiększana. Za każdym razem, gdy uruchamiany jest destruktor instancji, liczba referencyjna surowego T * jest zmniejszana. Gdy wynosi 0, wskaźnik jest zwalniany.
std::vector< shared_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
// This copy to tPtrVec.push_back and ultimately to the vector storage
// causes the reference count to go from 1->2
tPtrVec.push_back(tPtr);
// num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe
słaby_ptr jest słabym odniesieniem do współdzielonego wskaźnika, który wymaga sprawdzenia, czy wskazany na współdzielony_ptr nadal istnieje
std::vector< weak_ptr<T> > tPtrVec;
{
shared_ptr<T> tPtr(new T());
tPtrVec.push_back(tPtr);
// num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed = tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
cout << "Raw T* was freed, can't access it"
}
else
{
tPtrVec[0]->DoSomething(); // raw
}
intrusive_ptr jest zwykle używany, gdy istnieje ptr inteligentnego ptr innej firmy, którego musisz użyć. Wywoła funkcję bezpłatną w celu dodania i zmniejszenia liczby referencji. Aby uzyskać więcej informacji, zobacz link do ulepszenia dokumentacji.
if (tPtrAccessed[0].get() == 0)
ma tak być if (tPtrAccessed.get() == 0)
?
Nie pomijaj boost::ptr_container
w żadnej ankiecie dotyczącej inteligentnych wskaźników. Mogą być bezcenne w sytuacjach, w których np. std::vector<boost::shared_ptr<T> >
Byłoby zbyt wolne.
Popieram radę dotyczącą przeglądania dokumentacji. To nie jest tak przerażające, jak się wydaje. I kilka krótkich wskazówek:
scoped_ptr
- wskaźnik automatycznie usuwany, gdy wykracza poza zakres. Uwaga - przypisanie nie jest możliwe, ale nie nakłada kosztów ogólnychintrusive_ptr
- wskaźnik zliczania odniesienia bez narzutu smart_ptr
. Jednak sam obiekt przechowuje liczbę referencjiweak_ptr
- współpracuje z shared_ptr
sytuacjami powodującymi zależności cykliczne (przeczytaj dokumentację i wyszukaj w Google fajny obraz;)shared_ptr
- ogólny, najmocniejszy (i najcięższy) spośród inteligentnych wskaźników (od tych oferowanych przez boost)auto_ptr
, który zapewnia, że obiekt, na który wskazuje, jest automatycznie niszczony, gdy kontrola opuszcza zakres. Ma jednak inną semantykę kopiowania niż reszta facetów.unique_ptr
- przyjdzie z C ++ 0xOdpowiedź na edycję: Tak