К сожалению, не все компиляторы знают об этом. Из нескольких протестированных компиляторов почти половина соглашалась только на
Более грамотный выход заключается в том, чтобы отказаться от модного использования анонимных объектов istream_iterator при объявлении data и просто присвоить этим итераторам имена. Следующий фрагмент работает всегда:
ifstream dataFile("ints.dat");
istream_iterator
istream_iterator
list
Именованные объекты итераторов противоречат стандартному стилю программирования STL, но зато ваша программа будет однозначно восприниматься как компиляторами, так и людьми, которые с ними работают.
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера
Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в обратном направлении (begin, end, rbegin и т. д.); они могут сообщить тип хранящихся в них объектов (value_type); они выполняют все необходимые операции управления памятью при вставке и удалении; они сообщают текущее количество элементов и максимальную вместимость (size и max_size соответственно); и, конечно же, они автоматически уничтожают все хранящиеся в них объекты при уничтожении самого контейнера.
Работая с такими интеллектуальными контейнерами, многие программисты вообще забывают о необходимости «прибрать за собой» и надеются, что контейнер выполнит за них всю грязную работу. Нередко их ожидания оправдываются, но если контейнер содержит
В результате при выполнении следующего фрагмента возникает утечка ресурсов:
void doSomething() {
vector
for (int i=0;i
// Использовать vwp
} // Здесь происходит утечка Widget!
Все элементы vwp уничтожаются при выходе vwp из области видимости, но это не изменяет того факта, что delete не вызывается для объектов, созданных оператором new. За удаление таких элементов отвечает программист, а не контейнер. Так было задумано. Только программист знает,
Обычно это делать нужно. На первый взгляд решение выглядит довольно просто:
void doSomethng() {
vector
... // Как прежде
for (vector
i != vwp.end();
++i)
delete *i;
}
Такое решение работает, если не проявлять особой разборчивости в трактовке этого понятия. Во-первых, новый цикл for делает примерно то же, что и foreach, но он не столь нагляден (совет 43). Во-вторых, этот код небезопасен по отношению к исключениям. Если между заполнением vwp указателями и вызовом delete произойдет исключение, это снова приведет к утечке ресурсов. К счастью, с обеими проблемами можно справиться.
Чтобы от foreach-подобного цикла перейти непосредственно к foreach, необходимо преобразовать delete в объект функции. С этим справится даже ребенок — если, конечно, вы найдете ребенка, который захочет возиться с STL:
template
struct DeleteObject:// В совете 40 показано,
public unary_function
void operator()(const Т* ptr) const
{
delete ptr;
}
};
Теперь становится возможным следующее:
void doSomething() {
//См. ранее
for_each(vwp.begin(),vwp.end(),DeleteObject
}