Подтвердить что ты не робот

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 экземпляры.

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

4b9b3361

Ответ 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();

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