Чтение онлайн

на главную

Жанры

Эффективное использование STL
Шрифт:

• Второй параметр не имеет имени. Он относится к типу указателя на функцию, которая вызывается без параметров и возвращает

istream_iterator<int>
.

Любопытно, не правда ли? Однако такая интерпретация соответствует одному из основных правил C++: все, что может интерпретироваться как указатель на функцию, должно интерпретироваться именно так. Каждый программист с опытом работы на C++ встречался с теми или иными воплощениями этого правила. Сколько раз вы встречались с такой ошибкой:

class Widget{...}; // Предполагается, что у Widget

// имеется
конструктор по умолчанию

Widget w; // Какая неприятность...

Вместо объекта класса

Widget
с именем
w
в этом фрагменте объявляется функция
w
, которая вызывается без параметров и возвращает
Widget
. Умение распознавать подобные «ляпы» — признак хорошей квалификации программиста C++.

Все это по-своему интересно, однако мы нисколько не приблизились к поставленной цели: инициализировать объект

list<int>
содержимым файла. Зато теперь мы знаем, в чем заключается суть проблемы, и легко справимся с ней. Объявления формальных параметров не могут заключаться в круглые скобки, но никто не запрещает заключить в круглые скобки аргумент при вызове функции, поэтому простое добавление круглых скобок поможет компилятору увидеть происходящее под нужным углом зрения:

list<int> data((istream_iterator<int>(dataFile)), // Обратите внимание

 istream_iterator<int>); // на круглые скобки

// вокруг первого аргумента

// конструктора list

Именно так следует объявлять данные. Учитывая практическую полезность

istream_iterator
и интервальных конструкторов (совет 5), этот прием стоит запомнить.

К сожалению, не все компиляторы знают об этом. Из нескольких протестированных компиляторов почти половина соглашалась только на неправильное объявление

data
без дополнительных круглых скобок! Чтобы умиротворить такие компиляторы, можно закатить глаза и воспользоваться неверным, как было показано выше, объявлением
data
, но это недальновидное и плохо переносимое решение.

Более грамотный выход заключается в том, чтобы отказаться от модного использования анонимных объектов

istream_iterator
при объявлении
data
и просто присвоить этим итераторам имена. Следующий фрагмент работает всегда:

ifstream dataFile("ints.dat");

istream_iterator<int> dataBegin(dataFile);

istream_iterator<int> dataEnd;

list<int> data(dataBegin.dataEnd);

Именованные объекты итераторов противоречат стандартному стилю программирования STL, но зато ваша программа будет однозначно восприниматься как компиляторами, так и людьми, которые с ними работают.

Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера

Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в обратном направлении (

begin
,
end
,
rbegin
и т.д.); они могут сообщить тип хранящихся в них объектов (
value_type
); они выполняют все необходимые операции управления памятью при вставке и удалении; они сообщают текущее количество элементов и максимальную вместимость (
size
и
max_size
соответственно); и, конечно же, они автоматически уничтожают все хранящиеся в них объекты при уничтожении самого контейнера.

Работая с такими интеллектуальными контейнерами, многие программисты вообще забывают о необходимости «прибрать за собой» и надеются, что контейнер выполнит за них всю грязную работу. Нередко их ожидания оправдываются, но если контейнер содержит указатели на объекты, созданные оператором

new
, этого не происходит. Разумеется, контейнер указателей уничтожает все хранящиеся в нем элементы при уничтожении самого контейнера, но «деструктор» указателя ничего не делает! Он не вызывает
delete
.

В результате при выполнении следующего фрагмента возникает утечка ресурсов:

void doSomething {

vector<Widget*> vwp;

for (int i=0; i<SOME_MAGIC_NUMBER; ++i) vwp.push_back(new Widget);

 … // Использовать vwp

} // Здесь происходит утечка Widget!

Все элементы

vwp
уничтожаются при выходе
vwp
из области видимости, но это не изменяет того факта, что
delete
не вызывается для объектов, созданных оператором
new
. За удаление таких элементов отвечает программист, а не контейнер. Так было задумано. Только программист знает, нужно ли вызывать
delete
для этих указателей.

Обычно это делать нужно. На первый взгляд решение выглядит довольно просто:

void doSomethng {

 vector<Widget*> vwp;

 ... // Как прежде

 for (vector<Widget*>::iterator = vwp.begin; i != vwp.end; ++i)

delete *i;

}

Такое решение работает, если не проявлять особой разборчивости в трактовке этого понятия. Во-первых, новый цикл

for
делает примерно то же, что и
for_each
, но он не столь нагляден (совет 43). Во-вторых, этот код небезопасен по отношению к исключениям. Если между заполнением
vwp
указателями и вызовом
delete
произойдет исключение, это снова приведет к утечке ресурсов. К счастью, с обеими проблемами можно справиться.

Чтобы от

for_each
– подобного цикла перейти непосредственно к
for_each
, необходимо преобразовать
delete
в объект функции. С этим справится даже ребенок — если, конечно, вы найдете ребенка, который захочет возиться с STL:

template <typename T>

struct DeleteObject: // В совете 40 показано,

 public unary_function<const T*, void> { // зачем нужно наследование

 void operator(const T* ptr) const {

Поделиться:
Популярные книги

Последний попаданец 2

Зубов Константин
2. Последний попаданец
Фантастика:
юмористическая фантастика
попаданцы
рпг
7.50
рейтинг книги
Последний попаданец 2

"Фантастика 2023-123". Компиляция. Книги 1-25

Харников Александр Петрович
Фантастика 2023. Компиляция
Фантастика:
боевая фантастика
альтернативная история
5.00
рейтинг книги
Фантастика 2023-123. Компиляция. Книги 1-25

Мама из другого мира. Дела семейные и не только

Рыжая Ехидна
4. Королевский приют имени графа Тадеуса Оберона
Любовные романы:
любовно-фантастические романы
9.34
рейтинг книги
Мама из другого мира. Дела семейные и не только

Восход. Солнцев. Книга IX

Скабер Артемий
9. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга IX

Идущий в тени 3

Амврелий Марк
3. Идущий в тени
Фантастика:
боевая фантастика
6.36
рейтинг книги
Идущий в тени 3

С Новым Гадом

Юнина Наталья
Любовные романы:
современные любовные романы
эро литература
7.14
рейтинг книги
С Новым Гадом

Флеш Рояль

Тоцка Тала
Детективы:
триллеры
7.11
рейтинг книги
Флеш Рояль

Мастер Разума

Кронос Александр
1. Мастер Разума
Фантастика:
героическая фантастика
попаданцы
аниме
6.20
рейтинг книги
Мастер Разума

Неожиданный наследник

Яманов Александр
1. Царь Иоанн Кровавый
Приключения:
исторические приключения
5.00
рейтинг книги
Неожиданный наследник

Восход. Солнцев. Книга XI

Скабер Артемий
11. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга XI

Система Возвышения. (цикл 1-8) - Николай Раздоров

Раздоров Николай
Система Возвышения
Фантастика:
боевая фантастика
4.65
рейтинг книги
Система Возвышения. (цикл 1-8) - Николай Раздоров

Таблеточку, Ваше Темнейшество?

Алая Лира
Любовные романы:
любовно-фантастические романы
6.30
рейтинг книги
Таблеточку, Ваше Темнейшество?

Пенсия для морского дьявола

Чиркунов Игорь
1. Первый в касте бездны
Фантастика:
попаданцы
5.29
рейтинг книги
Пенсия для морского дьявола

Герой

Бубела Олег Николаевич
4. Совсем не герой
Фантастика:
фэнтези
попаданцы
9.26
рейтинг книги
Герой