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!

Author: michaelk, 2013-03-27

2 answers

A unique_ptr nie zadziała z powodu getDevice(), prawda?

Nie, niekoniecznie. Ważne jest tutaj określenie odpowiedniej polityki własności dla twojego 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_ptrjest 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.

 166
Author: Andy Prowl,
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_ptrs, oraz 1 dla wszystkich wskaźników (shared_ptr i weak_ptr). Po usunięciu wszystkich shared_ptrS, wskaźnik jest usuwany. Gdy wskaźnik jest potrzebny od weak_ptr, lock powinien być użyty do uzyskania wskaźnika, jeśli istnieje.

 0
Author: Naszta,
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