Вопрос

Я перебежал через enable_shared_from_this читая Boost.Примеры Asio и после прочтения документации я все еще не понимаю, как это следует правильно использовать.Может кто-нибудь, пожалуйста, привести мне пример и / или объяснение того, когда использование этого класса имеет смысл.

Это было полезно?

Решение

Это позволяет вам получить действительный shared_ptr экземпляр для this, когда все , что у вас есть , это this.Без этого у вас не было бы никакого способа получить shared_ptr Для this, если только у вас уже не было одного из них в качестве участника.Этот пример из расширьте документацию для enable_shared_from_this:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

Метод f() возвращает допустимое shared_ptr, даже несмотря на то, что у него не было экземпляра member .Обратите внимание, что вы не можете просто сделать это:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

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

enable_shared_from_this стал частью стандарта C ++ 11.Вы также можете получить его оттуда, а также из boost.

Другие советы

из статьи доктора Доббса о слабых указателях, я думаю, что этот пример легче понять (источник: http://drdobbs.com/cpp/184402026):

...подобный код не будет работать корректно:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

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

Аналогично, если функции-члену требуется shared_ptr объект, которому принадлежит объект, к которому он вызывается, он не может просто создать объект "на лету":

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

В этом коде та же проблема, что и в предыдущем примере, хотя и в более тонкой форме.Когда он будет сконструирован, shared_ptобъект r sp1 владеет вновь выделенным ресурсом.Код внутри функции-члена S::dangerous не знает об этом shared_ptr объект, так что shared_ptr объект, который он возвращает, отличается от sp1.Копирование нового shared_ptr возражать против sp2 это не помогает;когда sp2 выходит за пределы области видимости, это освободит ресурс, и когда sp1 если он выйдет за пределы области видимости, он снова освободит ресурс.

Способ избежать этой проблемы - использовать шаблон класса enable_shared_from_this.Шаблон принимает один аргумент типа шаблона, который является именем класса, определяющего управляемый ресурс.Этот класс, в свою очередь, должен быть общедоступным производным от шаблона;вот так:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

Когда вы делаете это, имейте в виду, что объект, на который вы вызываете shared_from_this должен принадлежать shared_ptr объект.Это не сработает:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}

Вот мое объяснение, с точки зрения гаек и болтов (лучший ответ меня не "зацепил").* Обратите внимание, что это результат исследования исходного кода для shared_ptr и enable_shared_from_this, который поставляется с Visual Studio 2012.Возможно, другие компиляторы реализуют enable_shared_from_this по-другому...*

enable_shared_from_this<T> добавляет приватный weak_ptr<T> экземпляр для T который удерживает 'одно истинное количество ссылок" для примера T.

Итак, когда вы впервые создаете shared_ptr<T> при переходе к новому T * внутренний weak_ptr этого T * инициализируется значением refcount равным 1.Новый shared_ptr в основном опирается на это weak_ptr.

T затем может в своих методах вызвать shared_from_this чтобы получить экземпляр shared_ptr<T> это возвращается к тому же самому внутренне сохраненному количеству ссылок.Таким образом, у вас всегда есть одно место, где T*количество ссылок сохраняется вместо того, чтобы иметь несколько shared_ptr экземпляры, которые не знают друг о друге, и каждый думает, что они являются shared_ptr который отвечает за подсчет ссылок T и удаляем его, когда их количество ссылок достигает нуля.

Обратите внимание, что использование boost::intrusive_ptr не страдает от этой проблемы.Часто это более удобный способ обойти эту проблему.

Это точно то же самое в c ++ 11 и более поздних версиях:Это делается для того, чтобы обеспечить возможность возврата this в качестве общего указателя , поскольку this дает вам необработанный указатель.

другими словами, это позволяет вам превращать код следующим образом

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

в это:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           

Другой способ - добавить weak_ptr<Y> m_stub член в class Y.Затем напишите:

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

Полезно, когда вы не можете изменить класс, из которого вы производите (напримеррасширение библиотеки других людей).Не забудьте инициализировать участника, напримерАвтор: m_stub = shared_ptr<Y>(this), он действителен даже во время работы конструктора.

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

Редактировать: Как правильно указал пользователь nobar, код уничтожит объект Y, когда назначение будет завершено и временные переменные будут уничтожены.Поэтому мой ответ неверен.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top