std::vector
для хранения указателей на размещенные в куче объекты.Например, вы размещаете в куче десяток объектов типа
Книга
(Book
) и сохраняете указатели на них в векторе:class Book {
public:
Book(int index);
...
};
...
std::vector<Book *> books;
for (int i = 0; i < 10; ++i)
books.push_back(new Book(i));
Естественно, после использования память надо освободить. Обычно стандартный прием для этого таков:for (std::vector<Book *>::iterator i = books.begin(); i != books.end(); ++i)
delete *i
В целом, с таким подходом все в порядке, разве что слегка веет от него излишней алгоритмической загруженностью. Он вынужден, навязан особенностями языка C++. Индексная переменная i
здесь абсолютно неважна для цикла, она является чисто служебной. Все это, конечно, не так страшно, как использование оператора goto
или статических переменных, но все равно хочется гармонии. И способ есть. Данный код можно переписать так:#include <algorithm>
...
class deleter {
public:
template <typename T>
void operator()(const T* p) const {
delete p;
}
};
...
std::for_each(a.begin(), a.end(), deleter());
Данный код определяет класс-функтор, у которого перегруженный оператор void operator()(...)
является шаблонным. Затем стандартный алгоритм std::for_each()
вызывает этот оператор для каждого члена вектора.Конечно, вы можете сказать, что мол битва за идею принуждает нас таскать за собой классСоглашусь, однако, что конкретно этот пример весьма тривиален и является в большинстве делом вкуса, нежели вопросом реального выигрыша простоте и тестируемости кода. Но сам прием весьма показателен в плане замены простейших алгоритмов высокоуровневыми сущностями. И еще, в защиту такого приему могу сказать, что например, вы можете переопределить алгоритмdeleter
, но тут аргумент простой данный подход ближе к декларативному подходу в программировании, нежели к прямому алгоритмическому. В декларативном подходе вы стараетесь как можно больше логики перенести из ее явного программирования базовыми конструкциями типа условий, циклов и т.д. к ее выражению через определения (декларации) сущностей и их взаимосвязей. Декларативные конструкции проще дробить на независимые куски, а значит проще тестировать. Например, вы можете протестировать алгоритмstd::for_each
в изоляции, тем самым гарантируя его корректную работу сразу во всей программе, а вот протестировать явный цикл в изоляции вряд ли получится, так как цикл "жестко вплетен" в прочую логику программы. Максимум удастся проверить данный конкретный цикл как-то вручную, и если их программе много, проверять придется каждый из них.
std::for_each
на свой, который сможет на конкретно вашей платформе выполняться гораздо быстрее, или, например, ловить исключения работы с кучей и журналировать проблемы освобождения памяти. В случае же прямого использования цикла for
вам придется переписать сам цикл. Хорошо, когда такое место одно в программе, а если их тысячи?
хороший прием.
ОтветитьУдалитьно нужно стараться придерживаться идимы RAII для обсепечения exception safety.
А потому приходится писать "умные" контейнеры, а в них уже да, можно и такой for_each намутить.
Проще использовать share_ptr
ОтветитьУдалитьВ целом да, RAII - это правильный путь.
ОтветитьУдалить