2010-08-03 8 views
27

Muszę zarejestrować obiekt w kontenerze po jego utworzeniu. Bez inteligentnych wskaźników użyję coś takiego:shared_from_this wywoływany z konstruktora

a_class::a_class() 
{ 
    register_somewhere(this); 
} 

z inteligentnymi wskaźników należy używać shared_from_this ale nie mogę użyć tego w konstruktorze.

Czy istnieje clean sposób rozwiązania tego problemu? Co zrobiłbyś w podobnej sytuacji? myślę o wprowadzenie metody init zadzwonić zaraz po stworzeniu i umieścić wszystko w funkcji fabrycznego jak ten:

boost::shared_ptr<a_class> create_a() 
{ 
    boost::shared_ptr<a_class> ptr(new a_class); 
    ptr->init(); 
    return ptr; 
} 

czy to dobrze czy nie jest to standardowa procedura, aby postępować w takich przypadkach?

EDIT: W rzeczywistości mój przypadek jest bardziej złożony. Mam 2 obiekty, które będą utrzymywać wskaźniki wzajemnie. Prawda jest taka, że ​​nie "rejestruję", ale tworzę kolejny obiekt (powiedzmy b_class), który wymaga this jako parametru. b_class odbiera this jako słaby wskaźnik i zapisuje go.

Dodaję to dlatego, ponieważ dajesz mi zaprojektować rad (które są bardzo cenione) przynajmniej można wiedzieć, co robię:

a_class::a_class() 
{ 
    b = new b_class(this); 
} 

W moim programie a_class jest podmiotem i b_class jest jedną z konkretnych klas reprezentujących stan (w konstruktorze jest to tylko stan początkowy). a_class potrzebuje wskaźnika do bieżącego stanu i b_class musi manipulować encją.

a_class jest odpowiedzialny za tworzenie i niszczenie b_class przypadków, a tym samym utrzymuje shared_ptr do nich, ale b_class trzeba manipulować a_class a zatem zachowuje słabą wskaźnik. a_class instancja "przeżywa" instancje b_class.

Czy w tym przypadku sugerujesz unikanie używania inteligentnych wskaźników?

+1

nie widzę dość twojej kod, ale oczywiste pytanie brzmi: dlaczego 'register_somewhere' potrzebuje inteligentnego wskaźnika? Zwykle jest to czystsze, jeśli obiekt nie zarządza własnym czasem życia (co oznacza, że ​​zakładanie lub wymaganie, że będzie trzymane przez inteligentny wskaźnik, jest cienkim końcem klina). Cokolwiek tworzy obiekt, powinno decydować o strategii alokacji, statycznej, automatycznej, dynamicznej zarządzanej przez inteligentny wskaźnik itp. Jeśli możesz oddzielić rejestrację i własność, to tym lepiej. –

+1

Proste rozwiązanie: sourceforge.net/projects/shared-from-this-ctor/ –

Odpowiedz

7

a_class jest odpowiedzialny za tworzenie i niszczenie b_class instancje

...

a_class instancja "przeżyje" b_class instancji.

Biorąc pod uwagę te dwa fakty, nie powinno być żadnego niebezpieczeństwa, że ​​instancja b_class może próbować uzyskać dostęp do instancji a_class po instancja a_class został zniszczony jako instancja a_class jest odpowiedzialny za niszczenie b_class instancji.

b_class może po prostu przytrzymać wskaźnik do powiązanej instancji a_class. Surowy wskaźnik nie wyraża żadnej własności właściwej dla tego przypadku.

W tym przykładzie nie ma znaczenia, w jaki sposób a_class jest tworzona dynamicznie, część zagregowanego obiektu itp Cokolwiek tworzy a_class zarządza jej żywotność jak a_class zarządza żywotność b_class której instancję.

E.g.

class a_class; 

class b_class 
{ 
public: 
    b_class(a_class* a_) : a(a_) {} 
private: 
    a_class* a; 
}; 

class a_class 
{ 
public: 
    a_class() : b(new b_class(this)) {} 
private: 
    boost::shared_ptr<b_class> b; 
}; 

Uwaga, w tym przykładzie zabawek nie ma potrzeby na shared_ptr, członek obiekt będzie działać tak samo dobrze (zakładając, że nie kopiować klasę podmiot).

class a_class 
{ 
public: 
    a_class() : b(this) {} 
private: 
    b_class b; 
}; 
5

Jeśli absolutnie potrzebujesz shared_ptr podczas budowy, najlepiej jest mieć funkcję "init". W rzeczywistości jest to jedyne przyzwoite podejście, jakie mogę wymyślić. Prawdopodobnie powinieneś mieć specjalną funkcję, która tworzy obiekty tego typu, aby zapewnić wywołanie init(), jeśli wybierzesz tę ścieżkę.

Jednak w zależności od tego, na co się rejestrujesz, lepszym rozwiązaniem może być przekazanie zarejestrowanego obiektu za pomocą zwykłego wskaźnika do obiektu w konstruktorze, a nie do pliku shared_ptr. Następnie w destruktorze można po prostu wyrejestrować obiekt z menedżera.

+0

Jeśli menedżer jest asynchronicznie wyrejestrowywany, nie gwarantuje, że Twój oddzwonień nie zostanie wywołany w milisekundach po zniszczeniu obiektu. Połączenie wspólnych i słabych wskaźników może rozwiązać taki wyścig. – SnakE

1

Dlaczego nie używacie http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/enable_shared_from_this.html

struct a_class : enable_shared_from_this<a_class> { 
    a_class() { 
     shared_ptr<a_class> ptr(this); 
     register_somewhere(ptr); 
    } 
}; 

Update: tutaj jest pełna przykładów pracy:

#include <stdio.h> 
#include <boost/smart_ptr/enable_shared_from_this.hpp> 

struct a_class; 
boost::shared_ptr<a_class> pa; 

void register_somewhere(boost::shared_ptr<a_class> p) 
{ 
    pa = p; 
}; 

struct a_class : boost::enable_shared_from_this<a_class> { 
private: 
    a_class() { 
     printf("%s\n", __PRETTY_FUNCTION__); 
     boost::shared_ptr<a_class> ptr(this); 
     register_somewhere(ptr); 
    } 

public: 
    ~a_class() { 
     printf("%s\n", __PRETTY_FUNCTION__); 
    } 

    static boost::shared_ptr<a_class> create() 
    { 
     return (new a_class)->shared_from_this(); 
    } 
}; 

int main() 
{ 
    boost::shared_ptr<a_class> p(a_class::create()); 
} 

Uwaga fabryka funkcja a_class :: create(). Jego zadaniem jest upewnienie się, że tworzony jest tylko jeden licznik referencyjny.Ponieważ

boost::shared_ptr<a_class> p(new a_class); 

Wynikiem utworzenia dwóch liczników odwołań i dwukrotnego usunięcia obiektu.

+2

Nie sądzę, aby ten przykład działał: możesz zakończyć z 2 różnymi licznikami dla tego samego wskaźnika ... – Emiliano

+0

Wysłano pełny przykład pracy. –

+0

Poprawny nagłówek do włączenia to '' not '' –

0

W tym celu napisałem własną zastąpienie drop-in dla shared_ptr, weak_ptr i enable_shared_from_this. Możesz to sprawdzić pod numerem Sourceforge.

Umożliwia wezwanie shared_from_this()w konstruktora i destruktora bez funkcji pomocniczych bez przestrzeni nad głową w porównaniu do standardowych enable_shared_from_this.

Uwaga: tworzenie shared_ptr w dtors jest dozwolone tylko tak długo, jak długo są tworzone tylko tymczasowo. Oznacza to, że są niszczone, zanim dtor powróci.

1

Jest ma potrzeby dla shared_ptr w kodzie (jak go pokazać i wyjaśnić). A shared_ptr tylko tylko odpowiednie dla współwłasności.

Twój b_class nie jest właścicielem to a_class, w rzeczywistości jest nawet przeżył przez nią, więc powinien jedynie mieć obserwując wskaźnik.

Jeśli b_class jest polimorficzny i manipulacje a_class dotyczyć zmiany jego b_class wskaźnik, należy użyć unique_ptr<b_class>:

class a_class; 
class b_class 
{ 
    friend class a_class; 
    a_class* mya; 
    b_class(a_class*p) 
    : mya(p) {} 
public: 
    virtual~b_class() {} // required for unique_ptr<b_class> to work 
    virtual void fiddle(); // do something to mya 
}; 

class a_class 
{ 
    std::unique_ptr<b_class> myb; 
public: 
    a_class() 
    : myb(new b_class(this)) {} 
    template<typename B> 
    void change_myb() 
    { 
    myb.reset(new B(this)); 
    } 
}; 
1

wymyśliłem klasie pomocnika dla tego problemu:

template <class Impl> 
class ImmediatelySharedFromThis : public std::enable_shared_from_this<Impl> { 
    typedef std::unique_ptr<void, std::function<void(void*)>> MallocGuard; 
    typedef std::shared_ptr<Impl> SharedPtr; 

    // disallow `new MyClass(...)` 
    static void *operator new(size_t) = delete; 
    static void *operator new[](size_t) = delete; 
    static void operator delete[](void*) = delete; 
protected: 
    typedef std::pair<MallocGuard&, SharedPtr&> SharingCookie; 

    ImmediatelySharedFromThis(SharingCookie cookie) { 
     MallocGuard &ptr = cookie.first; 
     SharedPtr &shared = cookie.second; 
     // This single line contains the actual logic: 
     shared.reset(reinterpret_cast<Impl*>(ptr.release())); 
    } 
public: 
    // Create new instance and return a shared pointer to it. 
    template <class ...Args> 
    static SharedPtr create(Args &&...args) { 
     // Make sure that the memory is free'd if ImmediatelySharedFromThis 
     // is not the first base class, and the initialization 
     // of another base class throws an exception. 
     MallocGuard ptr(aligned_alloc(alignof(Impl), sizeof(Impl)), free); 
     if (!ptr) { 
      throw std::runtime_error("OOM"); 
     } 

     SharedPtr result; 
     ::new (ptr.get()) Impl(SharingCookie(ptr, result), 
           std::forward<Args>(args)...); 
     return result; 
    } 

    static void operator delete(void *ptr) { 
     free(ptr); 
    } 
}; 

class MyClass : public ImmediatelySharedFromThis<MyClass> { 
    friend class ImmediatelySharedFromThis<MyClass>; 

    MyClass(SharingCookie cookie, int some, int arguments) : 
     ImmediatelySharedFromThis(cookie) 
     // You can pass shared_from_this() to other base classes 
    { 
     // and you can use shared_from_this() in here, too. 
    } 
public: 
    .... 
}; 

... 

std::shared_ptr<MyClass> obj = MyClass::create(47, 11); 

Trochę brzydko, ale działa.

0

Oto moje rozwiązanie:

class MyClass: enable_shared_from_this<MyClass> 
{ 
    public: 
     //If you use this, you will die. 
     MyClass(bool areYouBeingAnIdiot = true) 
     { 
      if (areYouBeingAnIdiot) 
      { 
       throw exception("Don't call this constructor! Use the Create function!"); 
      } 

      //Can't/Don't use this or shared_from_this() here. 
     } 

     static shared_ptr<MyClass> Create() 
     { 
      shared_ptr<MyClass> myClass = make_shared<MyClass>(false); 

      //Use myClass or myClass.get() here, now that it is created. 

      return myClass; 
     } 
} 

//Somewhere. 
shared_ptr<MyClass> myClass = MyClass::Create(); 

(Konstruktor musi być publiczne, aby być wymagalne od statycznej funkcji składowej, nawet jeden wewnętrzny ...)

Powiązane problemy