std :: shared_ptr tego


101

Obecnie próbuję nauczyć się korzystać z inteligentnych wskaźników. Jednak podczas niektórych eksperymentów odkryłem następującą sytuację, dla której nie mogłem znaleźć satysfakcjonującego rozwiązania:

Wyobraź sobie, że masz obiekt klasy A będący rodzicem obiektu klasy B (dziecko), ale oboje powinni się znać:

class A;
class B;

class A
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children->push_back(child);

        // How to do pass the pointer correctly?
        // child->setParent(this);  // wrong
        //                  ^^^^
    }

private:        
    std::list<std::shared_ptr<B>> children;
};

class B
{
public:
    setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    };

private:
    std::shared_ptr<A> parent;
};

Powstaje pytanie, w jaki sposób obiekt klasy A może przekazać std::shared_ptrsiebie ( this) swojemu dziecku?

Istnieją rozwiązania dotyczące wspólnych wskaźników Boost ( Getting a boost::shared_ptrforthis ), ale jak sobie z tym poradzić za pomocą std::inteligentnych wskaźników?


2
Podobnie jak w przypadku każdego innego narzędzia, musisz go użyć, gdy jest to właściwe. Używanie inteligentnych wskaźników do tego, co robisz, nie
YePhIcK,

Podobnie do wzmocnienia. Zobacz tutaj .
juanchopanza

1
To jest problem na tym poziomie abstrakcji. Nawet nie wiesz, że „to” wskazuje na pamięć na stercie.
Vaughn Cato

Język nie, ale ty tak. Tak długo, jak będziesz śledzić, co gdzie jest, wszystko będzie dobrze.
Alex,

Odpowiedzi:


168

Jest std::enable_shared_from_thistylko w tym celu. Dziedziczysz to i możesz dzwonić .shared_from_this()z poziomu klasy. Ponadto tworzysz w tym miejscu zależności cykliczne, które mogą prowadzić do wycieków zasobów. Można to rozwiązać za pomocą std::weak_ptr. Twój kod może więc wyglądać tak (zakładając, że dzieci polegają na istnieniu rodzica, a nie na odwrót):

class A;
class B;

class A
    : public std::enable_shared_from_this<A>
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children.push_back(child);

        // like this
        child->setParent(shared_from_this());  // ok
        //               ^^^^^^^^^^^^^^^^^^
    }

private:     
    // note weak_ptr   
    std::list<std::weak_ptr<B>> children;
    //             ^^^^^^^^
};

class B
{
public:
    void setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    }

private:
    std::shared_ptr<A> parent;
};

Należy jednak pamiętać, że powołanie .shared_from_this()wymaga, thisjest własnością std::shared_ptrw momencie wywołania. Oznacza to, że nie możesz już tworzyć takiego obiektu na stosie i generalnie nie możesz wywoływać .shared_from_this()z poziomu konstruktora lub destruktora.


1
Dziękuję za wyjaśnienie i za zwrócenie uwagi na mój problem z zależnością kołową.
Icarus

@Deduplicator co masz na myśli?
yuri kilochek

Spróbuj skonstruować shared_ptropartą na domyślnej konstrukcji shared_ptri czymkolwiek chcesz go wskazać ...
Deduplicator

1
@Deduplicator to, przepraszam za mój kalambur, raczej bezcelowy wspólny wskaźnik. Ten konstruktor jest przeznaczony do użycia ze wskaźnikami do elementów członkowskich zarządzanego obiektu lub jego baz. W każdym razie o co ci chodzi (przepraszam)? Te nieposiadające shared_ptrsą nieistotne dla tego pytania. shared_from_thisWarunki wstępne jasno określają, że obiekt musi być własnością (a nie tylko wskazywać) przez niektórych shared_ptrw momencie wywołania.
yuri kilochek

1
@kazarey Posiadanie przez a shared_ptrjest wymagane w momencie wywołania, ale w typowym schemacie użytkowania, czyli czymś podobnym shared_ptr<Foo> p(new Foo());, shared_ptrzakłada się własność obiektu dopiero po jego pełnej budowie. Można to obejść, tworząc shared_ptrw konstruktorze zainicjowanym przez thisi przechowując go gdzieś nielokalnie (np. W argumencie referencyjnym), aby nie umarł po zakończeniu konstruktora. Ale ten skomplikowany scenariusz raczej nie będzie potrzebny.
yuri kilochek

9

Masz kilka problemów w swoim projektowaniu, które wydają się wynikać z niezrozumienia inteligentnych wskazówek.

Do deklarowania własności używane są inteligentne wskaźniki. Łamiesz to oświadczając, że oboje rodzice są właścicielami wszystkich dzieci, ale także, że każde dziecko posiada swojego rodzica. Obie nie mogą być prawdą.

Ponadto zwracasz słaby wskaźnik w getChild(). W ten sposób deklarujesz, że dzwoniący nie powinien przejmować się własnością. Teraz może to być bardzo ograniczające, ale robiąc to, musisz upewnić się, że dane dziecko nie zostanie zniszczone, gdy nadal trzymane są słabe wskaźniki, jeśli użyjesz inteligentnego wskaźnika, zostanie ono rozwiązane samo .

I ostatnia sprawa. Zwykle, gdy akceptujesz nowe jednostki, powinieneś zwykle akceptować surowe wskaźniki. Inteligentny wskaźnik może mieć własne znaczenie przy zamianie dzieci między rodzicami, ale do ogólnego użytku należy akceptować surowe wskaźniki.


Wygląda na to, że naprawdę muszę wyjaśnić moje rozumienie inteligentnych wskazówek. Dziękuję za zwrócenie uwagi.
Icarus
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.