- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Шрифт:
Интервал:
Закладка:
19.5.1. Потенциальные проблемы управления ресурсами
Рассмотрим одну из опасностей, таящуюся в следующем, казалось бы, безвредном присваивании указателей:
int* p = new int[s]; // занимаем память
Она заключается в трудности проверки того, что данному оператору new соответствует оператор delete. В функции suspicious() есть инструкция delete[] p, которая могла бы освободить память, но представим себе несколько причин, по которым это может и не произойти. Какие инструкции можно было бы вставить в часть, отмеченную многоточием, ..., чтобы вызвать утечку памяти? Примеры, которые мы подобрали для иллюстрации возникающих проблем, должны натолкнуть вас на размышления и вызвать подозрения относительно такого кода. Кроме того, благодаря этим примерам вы оцените простоту и мощь альтернативного решения.
Возможно, указатель p больше не ссылается на объект, который мы хотим уничтожить с помощью оператора delete.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
// ...
if (x) p = q; // устанавливаем указатель p на другой объект
// ...
delete[] p; // освобождаем память
}
Мы включили в программу инструкцию if (x), чтобы гарантировать, что вы не будете знать заранее, изменилось ли значение указателя p или нет. Возможно, программа никогда не выполнит оператор delete.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
// ...
if (x) return;
// ...
delete[] p; // освобождаем память
}
Возможно, программа никогда не выполнит оператор delete, потому что сгенерирует исключение.
void suspicious(int s, int x)
{
int* p = new int[s]; // занимаем память
vector<int> v;
// ...
if (x) p[x] = v.at(x);
// ...
delete[] p; // освобождаем память
}
Последняя возможность беспокоит нас больше всего. Когда люди впервые сталкиваются с такой проблемой, они считают, что она связана с исключениями, а не с управлением ресурсами. Не понимая истинных причин проблемы, они пытаются перехватывать исключения.
void suspicious(int s, int x) // плохой код
{
int* p = new int[s]; // занимаем память
vector<int> v;
// ...
try {
if (x) p[x] = v.at(x);
// ...
} catch (...) { // перехватываем все исключения
delete[] p; // освобождаем память
throw; // генерируем исключение повторно
}
// ...
delete[] p; // освобождаем память
}
Этот код решает проблему за счет дополнительных инструкций и дублирования кода, освобождающего ресурсы (в данном случае инструкции delete[] p;). Иначе говоря, это некрасивое решение; что еще хуже — его сложно обобщить. Представим, что мы задействовали несколько ресурсов.
void suspicious(vector<int>& v, int s)
{
int* p = new int[s];
vector<int>v1;
// ...
int* q = new int[s];
vector<double> v2;
// ...
delete[] p;
delete[] q;
}
Обратите внимание на то, что, если оператор new не сможет выделить свободную память, он сгенерирует стандартное исключение bad_alloc. Прием try ... catch в этом примере также успешно работает, но нам потребуется несколько блоков try, и код станет повторяющимся и ужасным. Мы не любим повторяющиеся и запутанные программы, потому что повторяющийся код сложно сопровождать, а запутанный код не только сложно сопровождать, но и вообще трудно понять.
ПОПРОБУЙТЕ
Добавьте блоки try в последний пример и убедитесь, что все ресурсы будут правильно освобождаться при любых исключениях.
19.5.2. Получение ресурсов — это инициализация
К счастью, нам не обязательно копировать инструкции try...catch, чтобы предотвратить утечку ресурсов. Рассмотрим следующий пример:
void f(vector<int>& v, int s)
{
vector<int> p(s);
vector<int> q(s);
// ...
}
Это уже лучше. Что еще более важно, это очевидно лучше. Ресурс (в данном случае свободная память) занимается конструктором и освобождается соответствующим деструктором. Теперь мы действительно решили нашу конкретную задачу, связанную с исключениями. Это решение носит универсальный характер; его можно применить ко всем видам ресурсов: конструктор получает ресурсы для объекта, который ими управляет, а соответствующий деструктор их возвращает. Такой подход лучше всего зарекомендовал себя при работе с блокировками баз данных (database locks), сокетами (sockets) и буферами ввода-вывода (I/O buffers) (эту работу делают объекты класса iostream). Соответствующий принцип обычно формулируется довольно неуклюже: “Получение ресурса есть инициализация” (“Resource Acquisition Is Initialization” — RAII).
Рассмотрим предыдущий пример. Как только мы выйдем из функции f(), будут вызваны деструкторы векторов p и q: поскольку переменные p и q не являются указателями, мы не можем присвоить им новые значения, инструкция return не может предотвратить вызов деструкторов и никакие исключения не генерируются.
Это универсальное правило: когда поток управления покидает область видимости, вызываются деструкторы для каждого полностью созданного объекта и активизированного подобъекта. Объект считается полностью созданным, если его конструктор закончил свою работу. Исследование всех следствий, вытекающих из этих двух утверждений, может вызвать головную боль. Будем считать просто, что конструкторы и деструкторы вызываются, когда надо и где надо.
В частности, если хотите выделить в области видимости свободную память переменного размера, мы рекомендуем использовать класс vector, а не “голые” операторы new и delete.
19.5.3. Гарантии
Что делать, если вектор невозможно ограничить только одной областью (или подобластью) видимости? Рассмотрим пример.
vector<int>* make_vec() // создает заполненный вектор
{
vector<int>* p = new vector<int>; // выделяем свободную память
// ...заполняем вектор данными;
// возможна генерация исключения...
return p;
}
Это довольно распространенный пример: мы вызываем функцию, чтобы создать сложную структуру данных, и возвращаем эту структуру как результат. Однако, если при заполнении вектора возникнет исключение, функция make_vec() потеряет этот объект класса vector.

