- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
Инициализация указателя p1 неявно требует, чтобы компилятор создал указатель типа shared_ptr из указателя int*, возвращенного оператором new. Поскольку нельзя неявно преобразовать обычный указатель в интеллектуальный, такая инициализация ошибочна. По той же причине функция, возвращающая указатель shared_ptr, не может неявно преобразовать простой указатель в своем операторе return:
shared_ptr<int> clone(int p) {
return new int(p); // ошибка: неявное преобразование
// в shared_ptr<int>
}
Следует явно связать указатель shared_ptr с указателем, который предстоит возвратить:
shared_ptr<int> clone (int p) {
// ok: явное создание shared_ptr<int> из int*
return shared_ptr<int>(new int(p));
}
По умолчанию указатель, используемый для инициализации интеллектуального указателя, должен указывать на область динамической памяти, поскольку по умолчанию интеллектуальные указатели используют оператор delete для освобождения связанного с ним объекта. Интеллектуальные указатели можно связать с указателями на другие виды ресурсов. Но для этого необходимо предоставить собственную функцию, используемую вместо оператора delete. Предоставление собственного кода удаления рассматривается в разделе 12.1.4.
Не смешивайте обычные указатели с интеллектуальнымиУказатель shared_ptr может координировать удаление только с другими указателями shared_ptr, которые являются его копиями. Действительно, этот факт — одна из причин, по которой рекомендуется использовать функцию make_shared(), а не оператор new. Это связывает указатель shared_ptr с объектом одновременно с его резервированием. При этом нет никакого способа по неосторожности связать ту же область памяти с несколькими независимо созданными указателями shared_ptr.
Рассмотрим следующую функцию, работающую с указателем shared_ptr:
// ptr создается и инициализируется при вызове process()
void process(shared_ptr<int> ptr) {
// использование ptr
} // ptr выходит из области видимости и удаляется
Параметр функции process() передается по значению, поэтому аргумент копируется в параметр ptr. Копирование указателя shared_ptr осуществляет инкремент его счетчика ссылок. Таким образом, в функции process() значение счетчика не меньше 2. По завершении функции process() осуществляется декремент счетчика ссылок указателя ptr, но он не может достигнуть нуля. Поэтому, когда локальная переменная ptr удаляется, память, на которую она указывает, не освобождается.
Правильный способ использования этой функции подразумевает передачу ей указателя shared_ptr:
shared_ptr<int> p(new int (42)); // счетчик ссылок = 1
process(p); // копирование p увеличивает счетчик;
// в функции process() счетчик = 2
int i = *p; // ok: счетчик ссылок = 1
Хотя функции process() нельзя передать встроенный указатель, ей можно передать временный указатель shared_ptr, явно созданный из встроенного указателя. Но это, вероятно, будет ошибкой:
int *x(new int(1024)); // опасно: x - обычный указатель, a
// не интеллектуальный process(x);
// ошибка: нельзя преобразовать int* в shared_ptr<int>
process(shared_ptr<int>(x)); // допустимо, но память будет освобождена!
int j = *x; // непредсказуемо: x - потерянный указатель!
В этом вызове функции process() передан временный указатель shared_ptr. Этот временный указатель удаляется, когда завершается выражение, в котором присутствует вызов. Удаление временного объекта приводит к декременту счетчика ссылок, доводя его до нуля. Память, на которую указывает временный указатель, освобождается при удалении временного указателя.
Но указатель x продолжает указывать на эту (освобожденную) область памяти; теперь x — потерянный указатель. Результат попытки использования значения, на которое указывает указатель x, непредсказуем.
При связывании указателя shared_ptr с простым указателем ответственность за эту память передается указателю shared_ptr. Как только ответственность за область памяти встроенного указателя передается указателю shared_ptr, больше нельзя использовать встроенный указатель для доступа к памяти, на которую теперь указывает указатель shared_ptr.
Опасно использовать встроенный указатель для доступа к объекту, принадлежащему интеллектуальному указателю, поскольку нельзя быть уверенным в том, что этот объект еще не удален.
Другие операции с указателем shared_ptrКласс shared_ptr предоставляет также несколько других операций, перечисленных в табл. 12.2 и табл. 12.3. Чтобы присвоить новый указатель указателю shared_ptr, можно использовать функцию reset():
p = new int(1024); // нельзя присвоить обычный указатель
// указателю shared_ptr
p.reset(new int(1024)); // ok: p указывает на новый объект
Подобно оператору присвоения, функция reset() модифицирует счетчики ссылок, а если нужно, удаляет объект, на который указывает указатель p. Функцию-член reset() зачастую используют вместе с функцией unique() для контроля совместного использования объекта несколькими указателями shared_ptr. Прежде чем изменять базовый объект, проверяем, является ли владелец единственным. В противном случае перед изменением создается новая копия:
if (!p.unique())
p.reset(new string(*p)); // владелец не один; резервируем новую копию
*p += newVal; // теперь, когда известно, что указатель единственный,
// можно изменить объект
Упражнения раздела 12.1.3Упражнение 12.10. Укажите, правилен ли следующий вызов функции process(), определенной в текущем разделе. В противном случае укажите, как его исправить?
shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p));
Упражнение 12.11. Что будет, если вызвать функцию process() следующим образом?
process(shared_ptr<int>(p.get()));
Упражнение 12.12. Используя объявления указателей p и sp, объясните каждый из следующих вызовов функции process(). Если вызов корректен, объясните, что он делает. Если вызов некорректен, объясните почему:
auto p = new int();
auto sp = make_shared<int>();
(a) process(sp);
(b) process(new int());
(c) process(p);
(d) process(shared_ptr<int>(p));
Упражнение 12.13. Что будет при выполнении следующего кода?
auto sp = make_shared<int>();
auto p = sp.get();
delete p;
12.1.4. Интеллектуальные указатели и исключения
В разделе 5.6.2 упоминалось, что программы, использующие обработку исключений для продолжения работы после того, как произошло исключение, нуждаются в способе правильного освобождения ресурсов в случае исключения. Самый простой из них подразумевает использование интеллектуальных указателей.
При использовании интеллектуального указателя его класс гарантирует освобождение памяти, когда в ней больше нет необходимости, даже при преждевременном выходе из блока:
void f() {
shared_ptr<int> sp(new int(42)); // зарезервировать новый объект
// код, передающий исключение, не обрабатываемое в функции f()
} // shared_ptr освобождает память автоматически по завершении функции
При выходе из функции, обычном или в связи с исключением, удаляются все ее локальные объекты. В данном случае указатель sp имеет тип shared_ptr, поэтому при удалении проверяется его счетчик ссылок. В данном случае sp — единственный указатель на контролируемую им область памяти, поэтому она освобождается в ходе удаления указателя sp.
Память, контролируемая непосредственно, напротив, не освобождается автоматически, когда происходит исключение. Если для управления памятью используются встроенные указатели и исключение происходит после оператора new, но перед оператором delete, то контролируемая память не будет освобождена:
void f() {
int *ip = new int(42); // динамически зарезервировать новый объект
// код, передающий исключение, не обрабатываемое в функции f()

