Как правильно удалить boost :: shared_ptr из списка? - PullRequest
4 голосов
/ 08 апреля 2010

У меня есть std::list из boost::shared_ptr<T>, и я хочу удалить из него элемент, но у меня есть только указатель типа T *, который соответствует одному из элементов в списке.

Однако я не могу использовать myList.remove( tPtr ) Я предполагаю, что shared_ptr не реализует == для своего типа аргумента шаблона.

Моей непосредственной мыслью было попытаться myList.remove( shared_ptr<T>(tPtr) ), что синтаксически правильно, но оно вылетит при двойном удалении, так как временный shared_ptr имеет отдельный use_count.

std::list< boost::shared_ptr<T> > myList;

T* tThisPtr = new T(); // This is wrong; only done for example code.
                       // stand-in for actual code in T using 
                       // T's actual "this" pointer from within T
{
   boost::shared_ptr<T> toAdd( tThisPtr ); // typically would be new T()
   myList.push_back( toAdd );
}

{
   //T has pointer to myList so that upon a certain action, 
   // it will remove itself romt the list

   //myList.remove( tThisPtr);                      //doesn't compile
   myList.remove( boost::shared_ptr<T>(tThisPtr) ); // compiles, but causes
                                                    // double delete
}  

Единственные варианты, которые я вижу, - это использовать std :: find с пользовательским сравнением или циклически перебирать список методом перебора и находить его самостоятельно, но, похоже, должен быть лучший способ.

Я что-то упускаю из виду, или это просто слишком нестандартное использование, чтобы делать чистые / нормальные операции?

Ответы [ 4 ]

5 голосов
/ 08 апреля 2010

Вы правы, мы не можем напрямую сравнивать указатели.Но существует remove_if, и мы можем указать наш собственный предикат.Решение:

template <typename T>
struct ptr_contains_predicate
{
    ptr_contains_predicate(T* pPtr) :
    mPtr(pPtr)
    {}

    template <typename P>
    bool operator()(const p& pPtr) const
    {
        return pPtr.get() == mPtr;
    }

    T* mPtr;
};

template <typename T>
ptr_contains_predicate<T> ptr_contains(T* pPtr)
{
    return ptr_contains_predicate<T>(pPtr);
}

Просто сохраните вышеупомянутый предикат в заголовке, и вы сможете использовать его где угодно.

myList.remove_if(ptr_contains(tThisPtr));

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

4 голосов
/ 08 апреля 2010

std :: list remove_if - это то, что вам нужно:

Определите предикат

template <typename T> struct shared_equals_raw
{
  shared_equals_raw(T* raw)
    :_raw(raw)
    {}
  bool operator()(const boost::shared_ptr<T>& ptr) const
    {
      return (ptr.get()==_raw);
    }
private:
  T*const _raw;
};

, тогда вы можете позвонить

myList.remove_if(shared_equals_raw(tThisPtr));

иметь список для очистки узлов, которые имеют shared_ptrs для tThisPtr.

(не проверено, поэтому, возможно, некоторые синтаксические вещи нуждаются в исправлении).

Хотя совет Майкла Барра относительно enable_shared_from_this хорош;было бы лучше вообще не использовать необработанный tThisPtr.

3 голосов
/ 08 апреля 2010

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

Если тип включает эту функцию, вы можете получить общий указатель от самого объекта, вызвав shared_from_this().

0 голосов
/ 08 апреля 2010

Можете ли вы использовать общий указатель для его удаления?

std::list< boost::shared_ptr<T> > myList;

boost::shared_ptr<T> tThisPtr = new T(); 

{
    myList.push_back(tThisPtr);
}

{
    myList.remove(tThisPtr);                     
}
...