Mam problem ze zrozumieniem użycia inteligentnych wskaźników jako członków klasy w C ++ 11. Dużo czytałem o inteligentnych wskazówkach i myślę, że rozumiem, jak unique_ptr
i shared_ptr
/ weak_ptr
działa ogólnie. To, czego nie rozumiem, to prawdziwe użycie. Wygląda na to, że wszyscy zalecają używanie unique_ptr
go prawie cały czas. Ale jak bym zaimplementował coś takiego:
class Device {
};
class Settings {
Device *device;
public:
Settings(Device *device) {
this->device = device;
}
Device *getDevice() {
return device;
}
};
int main() {
Device *device = new Device();
Settings settings(device);
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
Powiedzmy, że chciałbym zastąpić wskaźniki inteligentnymi wskazówkami. A unique_ptr
nie zadziała z powodu getDevice()
, prawda? Więc to jest czas, kiedy używam shared_ptr
i weak_ptr
? Nie ma możliwości korzystania unique_ptr
? Wydaje mi się, że w większości przypadków shared_ptr
ma to większy sens, chyba że używam wskaźnika w naprawdę małym teleskopie?
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> device) {
this->device = device;
}
std::weak_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::weak_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
Czy to jest droga? Dziękuję bardzo!
źródło
device
do konstruktora programusettings
, czy nadal chcesz móc odwoływać się do niego w zakresie wywołującym, czy tylko za pośrednictwemsettings
? Jeśli to drugie,unique_ptr
jest przydatne. Ponadto, czy masz scenariusz, w którym wartość zwracanagetDevice()
tonull
. Jeśli nie, po prostu zwróć odwołanie.shared_ptr
jest poprawne w 8/10 przypadków. Pozostałe 2/10 są podzielone międzyunique_ptr
iweak_ptr
. Jest równieżweak_ptr
ogólnie używany do przerywania odwołań cyklicznych; Nie jestem pewien, czy twoje użycie zostanie uznane za prawidłowe.device
członka danych? Najpierw musisz o tym zdecydować.unique_ptr
zamiast tego użyć a i zrezygnować z własności podczas dzwonienia do konstruktora, jeśli wiem, że na razie nie będę go już potrzebować. Ale jako projektantSettings
klasy nie wiem, czy dzwoniący również chce zachować odniesienie. Może urządzenie znajdzie zastosowanie w wielu miejscach. Ok, może to jest dokładnie twój punkt widzenia. W takim przypadku nie byłbym jedynym właścicielem i chyba wtedy użyłbym shared_ptr. A więc: inteligentne punkty zastępują wskaźniki, ale nie odniesienia, prawda?Odpowiedzi:
Nie, niekoniecznie. Ważne jest tutaj określenie odpowiedniej polityki własności dla twojego
Device
obiektu, tj. Kto będzie właścicielem obiektu wskazywanego przez twój (inteligentny) wskaźnik.Czy będzie to instancja
Settings
obiektu sam ? CzyDevice
obiekt będzie musiał zostać zniszczony automatycznie, gdySettings
zostanie zniszczony, czy też powinien przeżyć ten obiekt?W pierwszym przypadku
std::unique_ptr
jest to, czego potrzebujesz, ponieważ stajeSettings
się jedynym (unikalnym) właścicielem wskazanego przedmiotu i jedynym obiektem odpowiedzialnym za jego zniszczenie.Przy takim założeniu
getDevice()
powinien zwrócić prosty wskaźnik obserwacyjny (wskaźniki obserwacyjne to wskaźniki, które nie utrzymują wskazanego obiektu przy życiu). Najprostszym rodzajem wskaźnika obserwacyjnego jest wskaźnik surowy:[ UWAGA 1: Możesz się zastanawiać, dlaczego używam tutaj wskaźników surowych, kiedy wszyscy powtarzają, że wskaźniki surowe są złe, niebezpieczne i niebezpieczne. W rzeczywistości jest to cenne ostrzeżenie, ale ważne jest, aby umieścić je w odpowiednim kontekście: surowe wskaźniki są złe, gdy są używane do wykonywania ręcznego zarządzania pamięcią , tj. Przydzielania i zwalniania obiektów za pomocą
new
idelete
. Kiedy używa się go wyłącznie jako środka do osiągnięcia semantyki referencyjnej i przekazywania nie-posiadania, obserwowania wskaźników, nie ma nic z natury niebezpiecznego w surowych wskaźnikach, może z wyjątkiem faktu, że należy uważać, aby nie wyłuskać wiszącego wskaźnika. - UWAGA KOŃCOWA 1 ][ UWAGA 2: Jak okazało się w komentarzach, w tym konkretnym przypadku, gdy własność jest unikalna, a posiadany obiekt jest zawsze obecny (tj. Wewnętrzny element danych
device
nigdy nie będzienullptr
), funkcjagetDevice()
mogłaby (i być może powinna) zwraca raczej odwołanie niż wskaźnik. Chociaż to prawda, zdecydowałem się zwrócić tutaj surowy wskaźnik, ponieważ chciałem, aby była to krótka odpowiedź, którą można uogólnić na przypadek, w którymdevice
może byćnullptr
, i pokazać, że surowe wskaźniki są w porządku, o ile nie używa się ich do ręczne zarządzanie pamięcią. - UWAGA KOŃCOWA 2 ]Sytuacja jest oczywiście radykalnie inna, jeśli twój
Settings
przedmiot nie powinien mieć wyłącznej własności urządzenia. Może tak być na przykład w przypadku, gdy zniszczenieSettings
przedmiotu nie powinno oznaczać również zniszczenia wskazanegoDevice
przedmiotu.To jest coś, co tylko Ty jako projektant swojego programu możesz powiedzieć; z podanego przez ciebie przykładu trudno mi stwierdzić, czy tak jest, czy nie.
Aby pomóc ci to zrozumieć, możesz zadać sobie pytanie, czy istnieją inne obiekty oprócz tych,
Settings
które są uprawnione do utrzymywaniaDevice
obiektu przy życiu, o ile trzymają na nim wskaźnik, zamiast być biernymi obserwatorami. Jeśli tak jest, potrzebujesz polityki współwłasności , którastd::shared_ptr
zapewnia:Zwróć uwagę, że
weak_ptr
jest to wskaźnik obserwujący , a nie wskaźnik będący właścicielem - innymi słowy, nie utrzymuje on przy życiu wskazanego obiektu, jeśli wszystkie inne wskaźniki będące właścicielami wskazanego obiektu wykraczają poza zakres.Przewaga
weak_ptr
nad regularnym surowego wskaźnik jest to, że można bezpiecznie powiedzieć, czyweak_ptr
jest zwisające czy nie (czyli czy jest wskazując ważnego obiektu, lub jeśli obiekt pierwotnie wskazał został zniszczony). Można to zrobić, wywołującexpired()
funkcję składową naweak_ptr
obiekcie.źródło
weak_ptr
jest zawsze alternatywą dla surowych wskaźników obserwacyjnych. W pewnym sensie jest to bezpieczniejsze, ponieważ przed wyłuskiwaniem można sprawdzić, czy nie zwisa, ale wiąże się to również z pewnym narzutem. Jeśli możesz łatwo zagwarantować, że nie zamierzasz wyłuskać wiszącego wskaźnika, powinieneś być w porządku z obserwowaniem surowych wskaźnikówgetDevice()
, gdybyśmy zwrócili odniesienie, prawda? Więc dzwoniący nie musiałby sprawdzaćnullptr
.auto myDevice = settings.getDevice()
utworzy nowe wystąpienie typu oDevice
nazwiemyDevice
i skopiuje je z tego, do którego odwołuje się odwołanie, któregetDevice()
zwraca. Jeśli chceszmyDevice
być punktem odniesienia, musisz to zrobićauto& myDevice = settings.getDevice()
. Więc jeśli czegoś nie brakuje, jesteśmy z powrotem w tej samej sytuacji, w której mieliśmy bez użyciaauto
.unique_ptr
klientowi elementu, który można modyfikować, otwiera możliwość, że klient się z niego przeniesie, uzyskując w ten sposób własność i pozostawiając pusty (unikalny) wskaźnik.const_cast
s), osobiście nie zrobiłbym tego. Ujawnia szczegół implementacji, tj. Fakt, że własność jest wyjątkowa i realizowana poprzezunique_ptr
. Widzę to w ten sposób: jeśli chcesz / musisz przekazać / zwrócić własność, przekaż / zwróć inteligentny wskaźnik (unique_ptr
lubshared_ptr
, w zależności od rodzaju własności). Jeśli nie chcesz / nie musisz przekazywać / zwracać własności, użyj (odpowiednioconst
zakwalifikowanego) wskaźnika lub odwołania, głównie w zależności od tego, czy argument może mieć wartość null, czy nie.week_ptr
jest używany tylko dla pętli referencyjnych. Graf zależności musi być grafem skierowanym acyklicznie. We wspólnych wskaźnikach istnieją 2 liczniki odwołań: 1 dlashared_ptr
s i 1 dla wszystkich wskaźników (shared_ptr
iweak_ptr
). Poshared_ptr
usunięciu wszystkich znaków wskaźnik jest usuwany. Gdy potrzebny jest wskaźnik fromweak_ptr
,lock
powinien zostać użyty do pobrania wskaźnika, jeśli istnieje.źródło
shared_ptr
? Czy możesz wyjaśnić dlaczego? O ile rozumiem,weak_ptr
nie musi być liczony, ponieważ po prostu tworzy nowyshared_ptr
podczas działania na obiekcie (jeśli podstawowy obiekt nadal istnieje).lock
. Wersja boost jest również bezpieczna wątkowo przy zliczaniu referencji (delete
wywoływana jest tylko raz).weak_ptr::lock()
aby stwierdzić, czy obiekt wygasł, musi sprawdzić „blok kontrolny”, który zawiera pierwszą liczbę referencji i wskaźnik do obiektu, więc blok sterujący nie może zostać zniszczony, gdy są jakieśweak_ptr
obiekty nadal w użyciu, więc liczbaweak_ptr
obiektów musi być śledzona, co robi drugi licznik odwołań. Obiekt zostaje zniszczony, gdy pierwsza liczba ref spadnie do zera, blok kontrolny zostanie zniszczony, gdy druga wartość ref spadnie do zera.