Как удалить элементы в векторе, если используется remove_if, а элементы являются указателями на объекты?

Я боюсь, что столкнусь с проблемами утечки памяти, выполнив следующие действия:

(Образец кода)

class myItem //random container stuff mostly. All primatives.
{
    int index;
    char* name;
    int val1;
    int val2;
};

class vecList
{

    vector< myitem* > *myVec;

    void delete()
    { 
        MyVec->erase(std::remove_if(myVec->begin(), MyVec->end(), IsMarkedToDelete), MyVec->end()); //leak here?
    }
};

Erase не освобождает память, если это указатель, верно? Если бы я не использовал remove_if, я мог бы вызвать удаление указателя перед его уничтожением. Как бы я поступил в этом случае? Умные указатели? Я бы предпочел не переделывать все с ними, и я действительно не хочу добавлять библиотеку повышения.

Спасибо!


person Jordan    schedule 01.11.2011    source источник
comment
ну, это зависит от того, как был создан myitem и что именно содержит myitem...   -  person Smash    schedule 01.11.2011
comment
Общий совет по С++: если вы используете необработанные указатели, вы делаете это неправильно. (Это не абсолютно, но те, кто знают, что делают, знают, когда игнорировать этот совет.)   -  person Kerrek SB    schedule 01.11.2011
comment
@Kerrek: с небольшим классом-оболочкой nonowning_ptr, который по сути является необработанным указателем, но с описательным именем, вы можете полностью избежать использования необработанных указателей. :)   -  person Xeo    schedule 01.11.2011
comment
@Xeo: Ура. И к тому времени, когда вы это сделаете, вы, вероятно, уже будете знать, как сделать что-то вроде того, что спрашивает ОП :-)   -  person Kerrek SB    schedule 01.11.2011
comment
Безопасное уничтожение объектов, на которые ссылается контейнер указателей STL, сложно, поскольку операции итератора STL могут вызывать исключения: stackoverflow.com/questions/7902452/   -  person Raedwald    schedule 01.11.2011
comment
вы ничего не выделяете динамически, не нужно беспокоиться об удалении вещей... кстати, почему указатели? Разве вы не можете просто сделать vector<myitem> myVec, а затем в функции удаления использовать . вместо ->?   -  person Smash    schedule 01.11.2011
comment
Связанный (но не дублирующийся) вопрос: stackoverflow.com/questions/307082/   -  person Raedwald    schedule 01.11.2011
comment
@KerrekSB, @Xeo, я думаю, что если ваши необработанные указатели владеют указателем, то правильнее будет сказать, что вы делаете это неправильно. Я не вижу особой пользы от nonowning_ptr.   -  person deft_code    schedule 01.11.2011
comment
@deft: Да, это описание подошло бы лучше. И знаете что? Вот что описывает имя nonowning_ptr. ;) Это все о выражении ваших намерений.   -  person Xeo    schedule 01.11.2011
comment
@deft_code Я согласен. На самом деле в большинстве приложений указатель не владеет объектом, и большинство указателей должны быть необработанными. Только в исключительных случаях контейнер владеет объектами, но содержит указатели. (Я не думаю, что сталкивался с этим за более чем 20 лет C++.)   -  person James Kanze    schedule 01.11.2011
comment
@Raedwald: Означает ли это, что я не должен перебирать и вызывать delete и erase() для каждого элемента в моем деструкторе?   -  person Jordan    schedule 01.11.2011
comment
@Jordan: я точно не знаю, каковы ограничения вашего кода, но если вы повторяете контейнер, вы должны учитывать возможность того, что iterator::operator++, iterator::operator* или iterator::operator-> вызовут исключение.   -  person Raedwald    schedule 01.11.2011


Ответы (4)


Вы можете просто удалить элемент в своей функции IsMarkedToDelete, когда он возвращает значение true.

person Xeo    schedule 01.11.2011
comment
На самом деле это хорошая идея. Сначала я посмотрю, могу ли я просто переработать код, чтобы не использовать указатели на элементы, но если это окажется невозможным, это может быть путь (при условии, что с этим нет проблем?) - person Jordan; 01.11.2011

Если в векторе были только указатели на объект, то у вас произошла утечка памяти, как только вы вызвали remove_if. remove_if перемещает указатели, которые вы удерживаете, но ничего не говорит о значениях, стоящих за возвращаемым им итератором. Таким образом, если у вас есть что-то вроде [a, b, c, d] (где a, b и т. д. представляют разные указатели), то после e = remove_if( v.begin(), v.end(), matches(b) ) ваш вектор может (и, вероятно, будет) выглядеть как [a, c, d, d], где e указывает на второй d, а все следы b потеряны. навсегда.

Очевидным решением было бы использовать shared_ptr в vector; это гарантирует, что любой указатель, который в конечном итоге будет удален из vector, будет удален. В противном случае вы можете использовать два прохода: первый будет for_each с чем-то вроде:

struct DeleteIfCondition
{
    void operator()( ObjectType* &ptr ) const
    {
        if ( condition( *ptr ) ) {
            ObjectType* tmp = ptr;
            ptr = NULL;
            delete tmp;
        }
    }
};

std::for_each( v.begin(), v.end(), DeleteIfCondition() );

как функциональный объект, за которым следуют:

v.erase( std::remove( v.begin(), v.end(), NULL ), v.end() );
person James Kanze    schedule 01.11.2011
comment
Ваш функтор - это то, что я бы использовал для своего ответа, просто добавив возврат логического значения. :) - person Xeo; 01.11.2011

Вы можете использовать remove_if, затем for_each от возвращаемого значения до конца, а затем стереть. Это, конечно, сделало бы ваш код немного длиннее. Другая возможность — хранить shared_ptr указателей, если ваш код с этим согласен.

Вышеупомянутое является откровенной ложью, как указал Бенджамин, поэтому у вас остается только «другая возможность».

person Michael Krelin - hacker    schedule 01.11.2011
comment
Как поможет использование for_each? - person Jordan; 01.11.2011
comment
std::for_each(rif=std::remove_if(...),YourVec->end(),[](myitem* i){delete i;}); std::erase(rif,YourVec->end()); — Я говорил о c++11 для краткости, я полагаю, вам придется ввести функцию удаления в C++, если у вас ее нет под рукой в ​​вашем проекте. - person Michael Krelin - hacker; 01.11.2011
comment
Это предполагает, что remove_if выполняет обмен, перемещая удаленные элементы в конец. Он не дает таких обещаний. - person Benjamin Lindley; 01.11.2011
comment
Итак, мне осталось использовать только shared_ptrs? Мой код довольно многопоточный. Не вызовет ли это каких-то проблем? - person Jordan; 01.11.2011
comment
Я мало знаю о вашем коде. Третья возможность - сначала просмотреть ваши элементы, чтобы удалить и обнулить, например, элементы, а затем удалить/стереть нули. - person Michael Krelin - hacker; 01.11.2011
comment
ВАУ!!, это не указано. Это сводит меня с ума. Конечно, это ошибка в стандарте. - person deft_code; 01.11.2011
comment
ловко, это не так. На самом деле, его указание сделало бы remove_if излишне неэффективным. Я бы скорее сказал, что это была моя ошибка, предполагать это. - person Michael Krelin - hacker; 01.11.2011
comment
std::partition доступен, если вы хотите разделить последовательность. - person Mike Seymour; 01.11.2011

Вы можете использовать эту функцию:

template<typename T, typename TESTFN>
void delete_if(std::vector<T*>& vec, TESTFN&& predicate)
{
   auto it = remove_if(vec.begin(), vec.end(), [&](T* item) {
      if (predicate(item)) {
         delete item;
         return true;
      }
      return false;
   });
   vec.erase(it, vec.end());
}

Ex:

delete_if(MyVec, [](T* item) { return item->index == 5; });
person Kiruahxh    schedule 27.05.2021