Какова полезность `включить общий доступ из этого"?


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

6 287

6 ответов:

это позволяет вам получить действительный shared_ptr экземпляр this, когда у вас есть this. Без него у вас не было бы возможности получить shared_ptr до this, Если вы уже участник. Этот пример из boost документация для 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, хотя у него не было экземпляра члена. Обратите внимание, что вы не можете просто сделать это:

class Y: public enable_shared_from_this<Y>
{
public:

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

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

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

из статьи доктора Доббса о слабых указателях, я думаю, что этот пример легче понять (источник: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;
}

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

вот мое объяснение, с точки зрения гаек и болтов (верхний ответ не "щелкнул" со мной). * Обратите внимание, что это результат исследования источника для 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*, что T * ' S внутренний weak_ptr инициализируется с refcount 1. Новый shared_ptr в основном спиной к этому weak_ptr.

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

обратите внимание, что использование 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), он действителен даже во время конструктора.

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

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