Можно ли вызвать `delete` для вектора указателей в C ++ через for_each <algorithm>? - PullRequest
3 голосов
/ 17 августа 2010

Предположим, у меня есть std::vector<Obj *> objs (по соображениям производительности у меня нет актуальных указателей Obj с).

Я постоянно заполняю его obj.push_back(new Obj(...));.

После того, как я закончуЯ должен delete оттесненные элементы.Один из способов сделать это:

for (std::vector<Obj *>::iterator it = objs.begin(); it != objs.end(); ++it) {
    delete *it;
}

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

#include <algorithm>
...
for_each(objs.begin(), objs.end(), delete);

Что вы думаете?

Ответы [ 7 ]

15 голосов
/ 17 августа 2010

Ваша проблема в том, что delete - это не функция, а ключевое слово, и поэтому вы не можете получить его адрес.

В C ++ 0x будет класс std::default_delete (используемый std::unique_ptr), который вы можете использовать или, как все говорят, написать один самостоятельно будет тривиально (стандартный также вызывает компиляцию ошибка, если вы пытаетесь удалить неполный тип).

#include <vector>
#include <algorithm>
#include <memory>

int main()
{
    std::vector<int*> vec;
    std::for_each(vec.begin(), vec.end(), std::default_delete<int>());
}
9 голосов
/ 17 августа 2010

Да, но вам нужен функтор:

struct delete_ptr
{
    template <typename T>
    void operator()(T* pPtr)
    {
        delete pPtr;
    }
};

std::for_each(objs.begin(), objs.end(), delete_ptr());

В C ++ 0x, lambda поможет вам создать функторы на месте:

std::for_each(objs.begin(), objs.end(), [](Obj* pPtr){ delete pPtr; });

Однако этоопасно, перед лицом исключений. sbi показал решение.

4 голосов
/ 17 августа 2010

Хотя вы можете сделать это ( GMan показал решение ), наличие контейнеров с открытыми указателями на собственные ресурсы имеет запах сильный кода. Например, в этом коде:

  void foo()
  {
    std::vector<Obj *> bar;
    fill(bar);
    use(bar);
    std::for_each(objs.begin(), objs.end(), delete_ptr()); // as GMan suggests
  }

если use() бросит, вы будете пропускать предметы.

Так что для этого лучше использовать умные указатели:

std::vector< std::shared_ptr<Obj> > bar;
2 голосов
/ 17 августа 2010

Вместо того, чтобы пытаться решить проблему удаления, вы можете полностью убрать ее, сохраняя shared_ptr s в векторе или используя ptr_vector boost (см. http://www.boost.org/doc/libs/1_39_0/libs/ptr_container/doc/tutorial.html).

).
2 голосов
/ 17 августа 2010

Не совсем; for_each требует функции или объекта, которые могут быть вызваны с помощью (), а delete не является ни функцией, ни объектом. Вам нужно будет обернуть его в функцию (или функциональный объект), например:

struct Deleter
{
    void operator()(Obj* obj) {delete obj;}
};

std::for_each(objs.begin(), objs.end(), Deleter());

Но вы должны быть очень осторожны, управляя временем жизни объектов с помощью необработанных указателей, особенно если вы их передаете. Вам нужно будет помнить, чтобы удалить их, если вы удалите их из vector или переназначите их, или если вы очистите vector, или если исключение, разрыв или возврат функции могут привести к разрушению вектора. В общем, всегда лучше разделять обязанности по управлению ресурсами и их использованию.

Вам лучше использовать вектор объектов, если Obj не является полиморфным базовым классом, или объекты действительно большие или достаточно сложные, чтобы их копирование оказало заметное влияние на производительность. Если это так (и вы его профилировали, чтобы убедиться, что это так), вы должны рассмотреть вектор умных указателей (shared_ptr или unique_ptr, если ваш компилятор поддерживает это), или Boost's ptr_vector .

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

1 голос
/ 17 августа 2010

Да.Заполните его умными указателями и используйте vector.clear () - самый простой способ.

1 голос
/ 17 августа 2010

for_each нужен указатель функции или функциональный объект.Для освобождения памяти вы можете попробовать &::operator delete, который получит адрес функции, которая освобождает память.Тем не менее, когда вы используете оператор delete, компилятор вызывает деструктор перед вызовом operator delete(void*), поэтому очистка фактически не является частью функции operator delete(void*).

Используйте функтор в виде ответа GMan.

...