Korzystanie z inteligentnych wskaźników dla członków klasy
Mam problem ze zrozumieniem użycia inteligentnych wskaźników jako członków klasy w C++11. Dużo czytałem o inteligentnych wskaźnikach i myślę, że rozumiem, jak unique_ptr
i shared_ptr
/weak_ptr
Praca w ogóle. To czego nie rozumiem to prawdziwe użycie. Wygląda na to, że każdy zaleca używanie unique_ptr
jako drogi prawie cały czas. Ale jak bym coś takiego zaimplementował:
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 wskaźnikami. A unique_ptr
nie zadziała z powodu getDevice()
, prawda? Więc to jest czas, kiedy używam shared_ptr
i weak_ptr
? Nie można użyć unique_ptr
? Wydaje mi się, że w większości przypadków shared_ptr
ma więcej sensu, chyba że używam wskaźnika w naprawdę małym zakresie?
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 sposób, aby przejść? Dziękuję bardzo!
2 answers
Nie, niekoniecznie. Ważne jest tutaj określenie odpowiedniej polityki własności dla twojegoA
unique_ptr
nie zadziała z powodugetDevice()
, prawda?
Device
obiektu, tzn. kto będzie właścicielem obiektu wskazywanego przez twój (inteligentny) wskaźnik.
Czy będzie to instancja Settings
obiektu samego ? Czy obiekt Device
musi zostać zniszczony automatycznie, gdy obiekt Settings
zostanie zniszczony, czy powinien przetrwać ten obiekt?
W pierwszym przypadku std::unique_ptr
jest tym, czego potrzebujesz, ponieważ sprawia, że Settings
jest jedynym (unikalnym) właścicielem wskazanego obiektu i jedynym obiektem odpowiedzialnym za jego zniszczenie.
Zgodnie z tym założeniem, {[3] } powinien zwrócić prosty Obserwujący wskaźnik (wskaźniki obserwacyjne są wskaźnikami, które nie utrzymują wskazanego obiektu przy życiu). Najprostszym rodzajem wskaźnika obserwacyjnego jest wskaźnik surowy:
#include <memory>
class Device {
};
class Settings {
std::unique_ptr<Device> device;
public:
Settings(std::unique_ptr<Device> d) {
device = std::move(d);
}
Device* getDevice() {
return device.get();
}
};
int main() {
std::unique_ptr<Device> device(new Device());
Settings settings(std::move(device));
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
[Uwaga 1: możesz się zastanawiać dlaczego używam surowych wskaźników tutaj, kiedy wszyscy mówią, że surowe wskaźniki są złe, niebezpieczne i niebezpieczne. W rzeczywistości jest to cenne Ostrzeżenie, ale ważne jest, aby umieścić je we właściwym kontekście: surowe wskaźniki są złe , gdy są używane do ręcznego zarządzania pamięcią, tj. przydzielania i dealokacji obiektów przez new
i delete
. Kiedy jest używany wyłącznie jako środek do osiągnięcia semantyki referencyjnej i przekazywania nieposiadających, obserwujących wskaźników, nie ma nic z natury niebezpieczne w surowych wskaźnikach, może z wyjątkiem tego, że należy uważać, aby nie dereferować zwisającego wskaźnika. - UWAGA KOŃCOWA 1]
[Uwaga 2: Jak się okazało w komentarzach, w tym konkretnym przypadku, gdy własność jest unikalna i obiekt będący własnością jest zawsze gwarantowany (tzn. Wewnętrzny element danych device
nigdy nie będzie nullptr
), Funkcja getDevice()
może (i może powinna) zwrócić referencję, a nie wskaźnik. While to prawda, zdecydowałem się zwrócić surowy wskaźnik, ponieważ miałem na myśli, że jest to krótka odpowiedź, którą można uogólnić do przypadku, w którym {[13] } może być nullptr
, i pokazać, że surowe wskaźniki są w porządku, o ile nie używa się ich do ręcznego zarządzania pamięcią. - UWAGA KOŃCOWA 2]
Sytuacja jest radykalnie inna, oczywiście, jeśli twój Settings
obiekt powiniennie mieć wyłączną własność urządzenia. Może tak być na przykład, jeśli zniszczenie Settings
obiektu nie powinno oznaczać również zniszczenia spiczastego Device
obiektu.
Jest to coś, co tylko Ty jako projektant Twojego programu możesz powiedzieć; z przykładu, który podajesz, trudno mi powiedzieć, czy tak jest, czy nie.
Aby pomóc ci to zrozumieć, możesz zadać sobie pytanie, Czy istnieją inne obiekty poza Settings
, które mają prawo utrzymać obiekt przy życiu, o ile trzymają wskaźnik do niego, zamiast być tylko bierni obserwatorzy. Jeśli tak jest, to potrzebujesz wspólnej polityki własności, która jest tym, co oferuje std::shared_ptr
: {[31]]}
#include <memory>
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> const& d) {
device = d;
}
std::shared_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device = std::make_shared<Device>();
Settings settings(device);
// ...
std::shared_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
Zauważ, że weak_ptr
jest obserwującym wskaźnikiem, a nie wskaźnikiem posiadającym - innymi słowy, nie utrzymuje wskazanego obiektu przy życiu, jeśli wszystkie inne wskaźniki posiadające do wskazanego obiektu wykraczają poza zakres.
Zaletą weak_ptr
nad zwykłym surowym wskaźnikiem jest to, że można bezpiecznie stwierdzić, czy weak_ptr
jest wiszący czy nie (tzn. czy wskazuje na ważny obiekt, czy też pierwotnie wskazywany obiekt został zniszczony). Można to zrobić poprzez wywołanie expired()
funkcji member w obiekcie weak_ptr
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-07 10:25:59
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(const std::shared_ptr<Device>& device) : device(device) {
}
const std::shared_ptr<Device>& getDevice() {
return device;
}
};
int main()
{
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::shared_ptr<Device> myDevice(settings.getDevice());
// do something with myDevice...
return 0;
}
week_ptr
jest używany tylko dla pętli odniesienia. Wykres zależności musi być wykresem acyklicznym. We współdzielonych wskaźnikach są 2 liczniki referencyjne: 1 dla shared_ptr
s, oraz 1 dla wszystkich wskaźników (shared_ptr
i weak_ptr
). Po usunięciu wszystkich shared_ptr
S, wskaźnik jest usuwany. Gdy wskaźnik jest potrzebny od weak_ptr
, lock
powinien być użyty do uzyskania wskaźnika, jeśli istnieje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-26 23:06:04