Shared_from_this вызывается из конструктора
Мне нужно зарегистрировать объект в контейнере при его создании.
Без интеллектуальных указателей я бы использовал что-то вроде этого:
a_class::a_class()
{
register_somewhere(this);
}
С помощью интеллектуальных указателей я должен использовать shared_from_this
, но я не могу использовать это в конструкторе.
Есть ли чистый способ решить эту проблему? Что бы вы сделали в подобной ситуации?
Я собираюсь ввести метод init
для вызова сразу после создания и поместить все в функцию factory следующим образом:
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
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
который он создает.
например.
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;
};
Ответ 2
Если вам нужно использовать shared_ptr во время построения, лучше иметь функцию init. На самом деле, это единственный достойный подход, о котором я могу думать. Вероятно, у вас должна быть специальная функция, которая создает объекты этого типа, для обеспечения вызова init()
, если вы выберете этот путь.
Однако, в зависимости от того, для чего вы регистрируетесь, может быть лучше дать любой объект, который вы регистрируете, с простым указателем на объект в конструкторе, а не с shared_ptr. Затем в деструкторе вы можете просто отменить регистрацию объекта у менеджера.
Ответ 3
Почему бы вам не использовать 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);
}
};
Обновление: - это полный рабочий пример:
#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());
}
Обратите внимание на функцию factory a_class:: create(). Его задача - убедиться, что создан только один счетчик ссылок. Поскольку
boost::shared_ptr<a_class> p(new a_class);
Результаты создания двух контрольных счетчиков и двойного удаления объекта.
Ответ 4
В коде 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));
}
};
Ответ 5
Я придумал вспомогательный класс для этой проблемы:
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);
Немного уродливый, но он работает.
Ответ 6
С этой целью я написал свою собственную замену для shared_ptr
, weak_ptr
и enable_shared_from_this
. Вы можете проверить это на Sourceforge.
Он позволяет вызывать shared_from_this()
в конструкторе и деструкторе без вспомогательных функций без пробелов по сравнению со стандартным enable_shared_from_this.
Примечание. создание shared_ptr в dtors разрешено только до тех пор, пока они создаются только временно. То есть, они уничтожаются до возвращения dtor.
Ответ 7
Здесь мое решение:
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();
(Конструктор должен быть общедоступным для вызова из статической функции-члена, даже внутренней)...