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