2010-08-03 3 views
27

Мне нужно зарегистрировать объект в контейнере при его создании. Без умных указателей Я бы использовать что-то вроде этого:shared_from_this вызывается из конструктора

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

с помощью смарт-указатели, я должен использовать shared_from_this, но я не могу использовать, что в конструкторе.

Есть clean Способ решения этой проблемы? Что бы вы сделали в подобной ситуации? Я думаю о введении в init метод для вызова только после создания и поставить все в функции фабрики, как это:

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

ли это нормально или есть стандартная процедура, чтобы следовать в таких случаях?

EDIT: На самом деле мое дело сложнее. У меня есть 2 объекта, которые должны поддерживать указатели друг на друга. Поэтому я не «регистрирую», а создаю еще один объект (скажем, b_class), который в качестве параметра должен содержать this. b_class получает this как слабый указатель и сохраняет его.

Я добавляю это, потому что, так как вы даете мне дизайн советы (которые очень ценятся) по крайней мере, вы можете знать, что я делаю:

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

В моей программе a_class является юридическим лицом и b_class является одним из конкретных классов, представляющих состояние (в конструкторе это только начальное состояние). a_class нуждается в указателе на текущее состояние и b_class нуждается в управлении объектом.

a_class отвечает за создание и уничтожение b_class экземпляров и, таким образом, поддерживает shared_ptr к ним, но b_class нужно манипулировать a_class и, таким образом, поддерживает слабый указатель. a_class экземпляр «выживает» b_class экземпляров.

Вы предлагаете избегать использования интеллектуальных указателей в этом случае?

+1

Я не могу насмотреться вашего код, но очевидный вопрос в том, почему 'register_somewhere' нуждается в умном указателе? Обычно он чище, если объект не управляет своей собственной жизнью (это то, что предполагает или требует, чтобы его удерживал умный указатель - это тонкий конец клина). Независимо от того, что создает объект, следует определить стратегию распределения, статическую, автоматическую, динамическую, управляемую интеллектуальным указателем и т. Д. Если вы можете отделить регистрацию и право собственности, то тем лучше. –

+1

Простое решение: sourceforge.net/projects/shared-from-this-ctor/ –

ответ

7

a_class отвечает за создание и уничтожение b_class экземпляров

...

a_class экземпляр "выживает" b_class экземпляров.

Учитывая эти два факта, что не должно быть никакой опасности, что b_class экземпляр может попытаться получить доступ к a_class экземпляр после того, как экземпляр a_class был разрушен, как экземпляр a_class несет ответственность за уничтожение b_class экземпляров.

b_class может содержать только указатель на его ассоциированный a_class экземпляр. Необработанный указатель не выражает никакой собственности, которая подходит для этого случая.

В данном примере это не имеет значения, как a_class создается динамически, часть агрегированного объекта и т.д. Все, что создает a_class управляет своей жизнью так же, как a_class управляет время жизни b_class которой он создает экземпляр.

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; 
}; 

Обратите внимание, в этом игрушечном примере нет необходимости в shared_ptr, член объекта будет работать так же хорошо (при условии, что вы не скопировать класс сущностей).

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

Если вам нужно использовать shared_ptr во время построения, лучше иметь функцию «init». На самом деле, это единственный достойный подход, о котором я могу думать. Вероятно, у вас должна быть специальная функция, которая создает объекты этого типа, чтобы вызывать init(), если вы выберете этот путь.

Однако, в зависимости от того, за что вы регистрируетесь, может быть лучше дать любой объект, который вы регистрируете, с простым указателем на объект в конструкторе, а не с shared_ptr. Затем в деструкторе вы можете просто отменить регистрацию объекта у менеджера.

+0

Если менеджер асинхронной регистрации не гарантирует, что ваш обратный вызов не будет называться миллисекундой после уничтожения вашего объекта. Сочетание общих и слабых указателей может решить такую ​​гонку. – SnakE

1

Почему бы вам не использовать 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: здесь полный рабочий пример:

#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()); 
} 

Примечание завод функция a_class :: Create(). Его задача - убедиться, что создан только один счетчик ссылок.Потому что

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

Результаты создания двух опорных счетчиков и двойного удаления объекта.

+2

Я не думаю, что этот пример работает: вы можете положить 2 разных счетчика для одного и того же указателя ... – Emiliano

+0

Отправленный полный рабочий пример. –

+0

правильный заголовок для включения: '' not '' –

0

Для этого я написал свой собственный дроп-ин для замены shared_ptr, weak_ptr и enable_shared_from_this. Вы можете проверить это на Sourceforge.

Это позволяет вызов shared_from_this()в конструкторе и деструкторе без вспомогательных функций без пространства над головой по сравнению со стандартным enable_shared_from_this.

Примечание: создание shared_ptr's в dtors допускается только при условии, что они созданы только временно. То есть, они разрушаются до возвращения dtor.

1

Там нет никакой необходимости для shared_ptr в коде (как вы показать и объяснить). A shared_ptr является только подходит для совместного использования.

b_class Ваш не владеет это a_class, на самом деле даже пережив ею, поэтому он должен просто держать наблюдение указателя.

Если b_class полиморфные и манипуляции a_class связаны изменения его b_class указателя, вы должны использовать 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

Я придумал вспомогательный класс для этой задачи:

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); 

Немного уродливый, но он работает.

0

Вот мое решение:

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(); 

(Конструктор должен быть открытым, чтобы быть отозваны из статической функции-члена, даже внутренний один ...)

Смежные вопросы