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

на главную - закладки

Жанры

Эффективное использование 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. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 2

Потомок бога

Решетов Евгений Валерьевич
1. Локки
Фантастика:
попаданцы
альтернативная история
аниме
сказочная фантастика
5.00
рейтинг книги
Потомок бога

Сапер. Том II

Вязовский Алексей
2. Сапер
Фантастика:
альтернативная история
4.25
рейтинг книги
Сапер. Том II

Убивать чтобы жить 4

Бор Жорж
4. УЧЖ
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 4

Я еще не князь. Книга XIV

Дрейк Сириус
14. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я еще не князь. Книга XIV

Лекарь

Первухин Андрей Евгеньевич
1. Лекарь
Фантастика:
фэнтези
попаданцы
альтернативная история
7.50
рейтинг книги
Лекарь

История московских кладбищ. Под кровом вечной тишины

Рябинин Юрий Валерьевич
Научно-образовательная:
история
культурология
5.75
рейтинг книги
История московских кладбищ. Под кровом вечной тишины

Девочка из прошлого

Тоцка Тала
3. Айдаровы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Девочка из прошлого

Я не бог. Книга XXXIV

Дрейк Сириус
34. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Я не бог. Книга XXXIV

Последний Герой. Том 3

Дамиров Рафаэль
3. Последний герой
Фантастика:
попаданцы
альтернативная история
фантастика: прочее
5.00
рейтинг книги
Последний Герой. Том 3

Шайтан Иван

Тен Эдуард
1. Шайтан Иван
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Шайтан Иван

Война

Валериев Игорь
7. Ермак
Фантастика:
боевая фантастика
альтернативная история
5.25
рейтинг книги
Война

Дважды одаренный. Том VII

Тарс Элиан
7. Дважды одаренный
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Дважды одаренный. Том VII

Настольная книга по теологии. Библейский комментарий АСД Том 12

Церковь христиан адвентистов седьмого дня
Научно-образовательная:
религиоведение
5.00
рейтинг книги
Настольная книга по теологии. Библейский комментарий АСД Том 12