Jakie są potencjalne zagrożenia podczas korzystania z trybu boost:: shared PST?

Jakie są sposoby na strzelanie sobie w stopę podczas używania boost::shared_ptr? Innymi słowy, jakich pułapek muszę unikać, gdy używam boost::shared_ptr?

Author: ThiefMaster, 2009-03-31

13 answers

Odniesienia cykliczne: a {[2] } do czegoś, co ma {[2] } do pierwotnego obiektu. Możesz oczywiście użyć weak_ptr<>, Aby przerwać ten cykl.


Jako przykład tego, o czym mówię w komentarzach dodaję następujący tekst.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        if (parent_) parent_->frob();
    }

private :
    void do_frob();
    shared_ptr<node> parent_;
    vector< shared_ptr<node> > children_;
};

W tym przykładzie masz drzewo węzłów, z których każdy zawiera wskaźnik do swojego rodzica. Funkcja członka frob (), z jakiegokolwiek powodu, faluje w górę przez drzewo. (Nie jest to całkowicie dziwaczne; niektóre frameworki GUI działają tak sposób).

Problem polega na tym, że jeśli stracisz odniesienie do najwyższego węzła, wtedy najwyższy węzeł nadal będzie miał silne odniesienia do swoich dzieci, a wszystkie jego dzieci również będą miały silne odniesienia do swoich rodziców. Oznacza to, że istnieją okrągłe odniesienia, które uniemożliwiają Czyszczenie się wszystkich instancji, podczas gdy nie ma możliwości dotarcia do drzewa z kodu, ta pamięć przecieka.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
        if (parent) parent->frob();
    }

private :
    void do_frob();
    weak_ptr<node> parent_; // Note: now a weak_ptr<>
    vector< shared_ptr<node> > children_;
};

Tutaj węzeł nadrzędny został zastąpiony słabym wskaźnikiem. Już nie ma wpływ na żywotność węzła, do którego się odnosi. Tak więc, jeśli najwyższy węzeł wychodzi poza zakres, jak w poprzednim przykładzie, to chociaż posiada silne odniesienia do swoich dzieci, jego dzieci nie mają mocnych odniesień do swoich rodziców. W ten sposób nie ma mocnych odniesień do obiektu, a on sam się oczyszcza. To z kolei powoduje, że dzieci tracą swoją jedną mocną referencję, co powoduje, że sprzątają i tak dalej. Krótko mówiąc, to nie przecieka. I tylko strategicznie Zamiana shared_ptr na weak_ptr.

Uwaga: powyższe odnosi się również do std::shared_ptr i STD::weak_ptr, jak to ma miejsce w przypadku boost::shared_ptr i boost::weak_ptr.

 41
Author: Kaz Dragon,
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
2017-03-30 09:17:16

Tworzenie wielu niepowiązanych shared_ptr ' s z tym samym obiektem:

#include <stdio.h>
#include "boost/shared_ptr.hpp"

class foo
{
public:
    foo() { printf( "foo()\n"); }

    ~foo() { printf( "~foo()\n"); }
};

typedef boost::shared_ptr<foo> pFoo_t;

void doSomething( pFoo_t p)
{
    printf( "doing something...\n");
}

void doSomethingElse( pFoo_t p)
{
    printf( "doing something else...\n");
}

int main() {
    foo* pFoo = new foo;

    doSomething( pFoo_t( pFoo));
    doSomethingElse( pFoo_t( pFoo));

    return 0;
}
 24
Author: Michael Burr,
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
2009-03-31 15:28:03

Konstruowanie anonimowego tymczasowego wskaźnika współdzielonego, na przykład wewnątrz argumentów wywołania funkcji:

f(shared_ptr<Foo>(new Foo()), g());

Jest to dozwolone, ponieważ new Foo() może być wykonane, następnie g() wywołane I g() rzucać wyjątek, Bez shared_ptr nigdy nie jest ustawione, więc shared_ptr nie ma szans na oczyszczenie Foo obiektu.

 18
Author: Brian Campbell,
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
2009-04-04 00:33:31

Bądź ostrożny tworząc dwa wskaźniki do tego samego obiektu.

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer

b->doSomething(); // crashes

Zamiast tego użyj tego

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d = 
    boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--

b->doSomething(); // no crash

Również klasy posiadające shared_ptrs powinny definiować konstruktory kopiujące i operatory przypisań.

Nie próbuj używać shared_from_this() w konstruktorze--to nie zadziała. Zamiast tego utwórz statyczną metodę tworzenia klasy i zwróć shared_ptr.

Przekazałem referencje do shared_ptrs bez problemów. Po prostu upewnij się, że jest skopiowany, zanim zostanie zapisany (tzn. nie referencje jako członkowie klasy).

 13
Author: ,
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
2009-04-03 23:47:20

Oto dwie rzeczy, których należy unikać:

  • Wywołanie funkcji get(), aby pobrać surowy wskaźnik i użyć go po tym, jak obiekt wskazywany do wyjdzie poza zakres.

  • Przekazywanie odniesienia lub surowego wskaźnika do shared_ptr powinno być również niebezpieczne, ponieważ nie zwiększy wewnętrznej liczby, która pomaga utrzymać obiekt przy życiu.

 12
Author: Frank,
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
2009-03-31 15:07:44

Debugujemy kilka tygodni dziwne zachowanie.

Powodem było:
przekazaliśmy' this 'niektórym pracownikom wątków zamiast 'shared_from_this'.

 9
Author: Mykola Golubyev,
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
2009-03-31 15:21:09

Nie jest to dokładnie pistolet, ale z pewnością źródło frustracji, dopóki nie zawiniesz głowy, jak to zrobić w C++0x sposób: większość predykatów, które znasz i kochasz z <functional> nie baw się ładnie shared_ptr. Na szczęście, std::tr1::mem_fn działa z obiektami, wskaźnikami i shared_ptrs, zastępując std::mem_fun, ale jeśli chcesz użyć std::negate, std::not1, std::plus albo któryś z tych starych znajomych z shared_ptr, Przygotuj się na przytulanie się z std::tr1::bind i prawdopodobnie również argument zastępczy. W praktyce jest to o wiele bardziej ogólne, ponieważ teraz w zasadzie kończysz używać bind dla każdego adaptera obiektu funkcji, ale trzeba się przyzwyczaić, jeśli znasz już funkcje wygody STL.

Ten artykuł DDJ porusza ten temat, z dużą ilością przykładowego kodu. Ja również blogowałem o tym kilka lat temu, kiedy po raz pierwszy musiałem wymyślić, jak to zrobić.

 4
Author: Meredith L. Patterson,
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
2009-08-07 08:53:11

Używanie shared_ptr dla naprawdę małych obiektów (jak char short) może to być nad głową, jeśli masz wiele małych obiektów na stercie, ale nie są one tak naprawdę "wspólne". boost::shared_ptr przydziela 16 bajtów dla każdej nowej liczby referencji, którą tworzy w g++ 4.4.3 i VS2008 z Boostem 1.42. std::tr1::shared_ptr przydziela 20 bajtów. Jeśli masz milion odrębnych shared_ptr<char>, oznacza to, że 20 milionów bajtów Twojej pamięci zniknęło w trzymaniu just count=1. Nie wspominając o kosztach pośrednich i fragmentacji pamięci. Spróbuj wykonać następujące czynności na Twojej ulubionej platformie.

void * operator new (size_t size) {
  std::cout << "size = " << size << std::endl;
  void *ptr = malloc(size);
  if(!ptr) throw std::bad_alloc();
  return ptr;
}
void operator delete (void *p) {
  free(p);
}
 3
Author: Sumant,
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
2010-03-22 23:46:43

Podanie shared_ptr do tego wewnątrz definicji klasy jest również niebezpieczne. Zamiast tego użyj enabled_shared_from_this.

Zobacz następujący post TUTAJ

 1
Author: George Godik,
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
2017-05-23 10:30:58

Musisz być ostrożny, gdy używasz shared_ptr w kodzie wielowątkowym. Wtedy stosunkowo łatwo staje się przypadkiem, gdy kilka shared_ptr s, wskazujących na tę samą pamięć, jest używanych przez różne wątki.

 1
Author: oo_olo_oo,
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
2009-04-04 10:21:43

Powszechne użycie shared_ptr niemal nieuchronnie spowoduje niechciane i niewidoczne zajęcie pamięci.

Odniesienia cykliczne są dobrze znaną przyczyną, a niektóre z nich mogą być pośrednie i trudne do wykrycia, szczególnie w złożonym kodzie, nad którym pracuje więcej niż jeden programista; programista może zdecydować, że jeden obiekt potrzebuje odniesienia do innego jako szybkiej poprawki i nie ma czasu na sprawdzenie całego kodu, aby sprawdzić, czy zamyka cykl. To zagrożenie jest ogromnie niedoceniany.

Mniej dobrze rozumiany jest problem niepublikowanych odniesień. Jeśli obiekt jest współdzielony z wieloma shared_ptrs, to nie zostanie zniszczony, dopóki każdy z nich nie zostanie wyzerowany lub nie wyjdzie poza zakres. Bardzo łatwo jest przeoczyć jedną z tych referencji i skończyć z przedmiotami czającymi się niewidocznie w pamięci, z którymi myślałeś, że skończyłeś.

Chociaż ściśle mówiąc nie są to wycieki pamięci (wszystkie zostaną zwolnione przed zakończeniem programu) są tak samo szkodliwe i trudniejsze do wykrycia.

Te problemy są konsekwencją celowych fałszywych deklaracji: 1. Deklarowanie tego, co naprawdę chcesz być jedną własnością jako shared_ptr. scoped_ptr byłby poprawny, ale wszelkie inne odniesienia do tego obiektu będą musiały być surowym wskaźnikiem, który może pozostać zwisający. 2. Deklarowanie tego, co naprawdę chcesz, jako pasywne odniesienie obserwacyjne jako shared_ptr. weak_ptr byłby poprawny, ale wtedy masz problem z konwersją go na share_ptr za każdym razem chcesz go użyć.

Podejrzewam, że twój projekt jest doskonałym przykładem tego rodzaju kłopotów, w które ta praktyka może cię wplątać.

Jeśli masz aplikację o dużej pamięci, naprawdę potrzebujesz jednej własności, aby twój projekt mógł jawnie kontrolować żywotność obiektów.

Z pojedynczą własnością opObject = NULL; na pewno usunie obiekt i zrobi to teraz.

With shared ownership spObject=NULL; ........kto wie?......

 0
Author: John Morrison,
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
2009-08-07 08:21:33

Jeśli masz rejestr udostępnionych obiektów (na przykład listę wszystkich aktywnych instancji), obiekty nigdy nie zostaną zwolnione. Rozwiązanie: podobnie jak w przypadku kołowych struktur zależności (zob. odpowiedź Kaz Dragona), należy odpowiednio użyć weak_ptr.

 -1
Author: Mr Fooz,
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
2009-04-04 00:20:50

Inteligentne Wskaźniki nie są do wszystkiego, a surowych wskaźników nie można wyeliminować

Prawdopodobnie największym niebezpieczeństwem jest to, że ponieważ shared_ptr jest użytecznym narzędziem, ludzie zaczną go umieszczać wszędzie. Ponieważ zwykłe wskaźniki mogą być niewłaściwie używane, ci sami ludzie będą polować na surowe wskaźniki i próbować zastąpić je ciągami, kontenerami lub inteligentnymi wskaźnikami, nawet jeśli nie ma to sensu. Uzasadnione użycie surowych wskaźników stanie się podejrzane. Będzie pointer police.

To nie tylko prawdopodobnie najgorsze niebezpieczeństwo, to może być jedyne poważne niebezpieczeństwo. Wszystkie najgorsze nadużycia shared_ptr będą bezpośrednią konsekwencją idei, że inteligentne wskaźniki są lepsze od surowego wskaźnika (cokolwiek to znaczy), a umieszczenie inteligentnych wskaźników wszędzie sprawi, że programowanie C++ będzie "bezpieczniejsze".

Oczywiście sam fakt, że inteligentny wskaźnik musi być przekonwertowany na surowy wskaźnik, który ma być użyty, obala to twierdzenie sekty inteligentnych wskaźników, ale fakt, że dostęp do surowego wskaźnika jest "niejawny" w operator*, operator-> (lub explicit in get()), ale nie implicit w implicit conversion, wystarcza, aby wywołać wrażenie, że nie jest to konwersja, a surowy wskaźnik wytworzony przez tę Nie-konwersję jest nieszkodliwym tymczasowym.

C++ nie może być "bezpiecznym językiem", a żaden użyteczny podzbiór C++ nie jest "bezpiecznym"

Oczywiście dążenie do bezpiecznego podzbioru ("bezpieczny "w ścisłym znaczeniu" pamięć bezpieczna", jak LISP, Haskell, Java...) C++ jest skazany na nieskończoność i niesatysfakcjonowanie, jako bezpieczny podzbiór C++ jest malutki i prawie bezużyteczny, ponieważ niebezpieczne prymitywy są regułą, a nie wyjątkiem. Ścisłe bezpieczeństwo pamięci w C++ oznaczałoby brak wskaźników i tylko odwołania z klasą automatic storage. Jednak w języku, w którym z definicji zaufany jest programista, niektórzy ludzie będą nalegać na użycie jakiegoś (w zasadzie) idiotycznego "inteligentnego wskaźnika", nawet tam, gdzie nie ma innej przewagi nad surowymi wskaźnikami, które jeden szczególny sposób do wkręcenia stanu programu jest / align = "left" /

 -5
Author: curiousguy,
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
2011-10-25 07:02:15