Концепция управления жизненным циклом ресурсов (в данном случае — мьютексов) при помощи специализированных классов вроде Lock
рассматривается в любом серьезном учебнике С++. Попробуйте начать с книги Страуструпа (Stroustrup) «The С++ Programming Language» [7], поскольку именно Страуструп популяризировал эту идиому, однако информацию также можно найти в совете 9 «More Effective С++». Каким бы источником вы ни воспользовались, помните, что приведенный выше класс Lock урезан до абсолютного минимума. Полноценная версия содержала бы многочисленные дополнения, не имеющие никакого отношения к STL. Впрочем, несмотря на минимализм, приведенная версия Lock вполне может использоваться в рассматриваемом примере:
vector
...
{// Создание нового блока
Lock
vector
if (first5 != v.end()) {
*first5 - 0:
}
}// Закрытие блока с автоматическим
// освобождением мьютекса
Поскольку мьютекс контейнера освобождается в деструкторе Lock, важно обеспечить уничтожение Lock сразу же после освобождения мьютекса. Для этого мы создаем новый блок, в котором определяется объект Lock, и закрываем его, как только надобность в мьютексе отпадает. На первый взгляд кажется, что вызов releaseMutexFor
попросту заменен необходимостью закрыть блок, но это не совсем так. Если мы забудем создать новый блок для Lock, мьютекс все равно будет освобожден, но это может произойти позднее положенного момента — при выходе из внешнего блока. Если забыть о вызове releaseMutexFor
Более того, решение, основанное на классе Lock, лучше защищено от исключений. С++ гарантирует уничтожение локальных объектов при возникновении исключения, поэтому Lock освободит мьютекс, даже если исключение произойдет при использовании объекта Lock. При использовании парных вызовов getMutexFo
мьютекс не будет освобожден, если исключение происходит после вызова getMutexFor
releaseMutexFor
.
Исключения и управление ресурсами важны, но данный совет посвящен другой теме — потоковой безопасности в STL. Как говорилось выше, вы можете
Контейнеры vector и string
Все контейнеры STL по-своему полезны, однако большинство программистов С++ работает с vector и string чаще, чем с их собратьями, и это вполне понятно. Ведь контейнеры vector и string разрабатывались как замена массивов, а массивы настолько полезны и удобны, что встречаются во всех коммерческих языках программирования от COBOL до Java.
В этой главе контейнеры vector и string рассматриваются с нескольких точек зрения. Сначала мы разберемся, чем они превосходят классические массивы STL, затем рассмотрим пути повышения быстродействия vector и string, познакомимся с различными вариантами реализации string, изучим способы передачи string и vector функциям API, принимающим данные в формате С. Далее будет показано, как избежать лишних операций выделения памяти. Глава завершается анализом поучительной аномалии, vector
Совет 13. Используйте vector и string вместо динамических массивов
Принимая решение о динамическом выделении памяти оператором new, вы берете на себя ряд обязательств.
1.Выделенная память в дальнейшем должна быть освобождена оператором delete. Вызов new без последующего delete приводит к утечке ресурсов.
2.Освобождение должно выполняться соответствующей формой оператора delete. Одиночный объект освобождается простым вызовом delete, а для массивов требуется форма delete []. Ошибка в выборе формы delete приводит к непредсказуемым последствиям. На одних платформах программа «зависает» во время выполнения, а на других она продолжает работать с ошибками, приводящими к утечке ресурсов и порче содержимого памяти.
3. Оператор delete для освобождаемого объекта должен вызываться ровно один раз. Повторное освобождение памяти также приводит к непредсказуемым последствиям.
Итак, динамическое выделение памяти сопряжено с немалой ответственностью, и я не понимаю, зачем брать на себя лишние обязательства. При использовании vector и string необходимость в динамическом выделении памяти возникает значительно реже.