Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 [ 12 ] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

Примеры использования пар

Тип pair часто встречается в стандартной библиотеке С++. Например, контейнеры тар и multimap используют его для операций с элементами, представляющими пары «ключ/значение». Контейнеры тар и multimap рассматриваются на с. 200. а на с. 103 приведен пример практического использования типа pair. Объекты типа pair также применяются в функциях стандартной библиотеки С++, возвращающих два значения (см. пример на с. 192).

Класс auto ptr

В этом разделе рассматривается тип auto ptr. Стандартная библиотека С++ предоставляет его как своего рода «умный указатель», помогающий предотвратить утечки ресурсов при исключениях. Обратите внимание на слова «своего рода». Они добавлены, потому что на практике применяется несколько разновидностей умных указателей. Класс auto ptr полезен только при решении определенного круга проблем. За его пределами тип auto ptr не поможет. Будьте внимательны и тщательно изучите материал этого раздела.

Знакомство с классом auto ptr

Функции часто работают по следующей схеме.

1. Получение ресурсов.

2. Выполнение операций.

3. Освобождение полученных ресурсов.

Если полученные ресурсы связаны с локальными объектами, они автоматически освобождаются при выходе из функции в результате вызова деструкторов локальных объектов. Но если ресурсы не связаны с объектом, оии должны освобождаться явно. Как правило, такие операции с ресурсами выполняются с применением указателей.

Характерный пример работы с ресурсами через указатели - создание и уничтожение объектов операторами new и delete:

void f() {

ClassA* ptr = new ClassA; Создание объекта

Выполнение операци11 delete ptr; Уничтожение обьекта

Объяснение основано на материале книги Скотта Мейерса (Scott Meyers) «Моге Effective С++». Общая схема была изначально представлена Бьярном Страуструпом под термином «выделение ресурсов при инициализации» в книгах «The С++ Programming Language*, 2nd Edition, и «The Design and Evolution of С++». Указатель auto ptr был включен в стандарт специально для поддержки этой методики.



Создание и инициализация auto ptr std;:auto ptr<ClassA> ptr(new ClassA);

Работа с указателем

При использовании подобных схем нередко возшткают проб.7гемы. Первая и наиболее очевидная проблема заключается в том, что программист может забыть об удалении объекта (особенно если внутри функции присутствуют команды return). Но существует и другая, менее очевидная опасность: во время работы функции может проР13ойти исключение, что приведет к немедленному выходу из функции без вьшолнения оператора delete, находящегося в конце тела функции. В результате возникает утечка памяти или, в общем случае, - ресурсов. Для ее предотвращения обычно приходится перехватывать все возможные исключения. Пример:

void f()

ClassA* ptr = new ClassA: Создание объекта try (

Работа с объектом

catch (...){ Для произвольного исключения:

delete ptr: - освободить ресурс

throw; - перезапустить исключение

delete ptr; Нормальное освобождение ресурса

Освобождение объекта при возникновении исключений приводит к усложнению программы и появлению лишнего кода. Если по этой схеме обрабатывается не один, а два объекта или имеется несколько секций catch, программа становится еще запутаннее. В подобных решениях - сложных и чреватых ошибками - проявляется плохой стиль программирования.

В данной ситуации нужен умный указатель, освобождающий данные, на которые он ссылается, при уничтожении самого указателя. Более того, поскольку такой указатель является локальной неременной, он будет уничтожаться автоматически при выходе из функции независимо от причины выхода - нормального завершения или исключения. Класс autojDtr проектировался име1пю для этих целей.

Указатель auto ptr является владельцем объекта, на который он ссылается. В результате уничтожение указателя автоматически приводит к уничтожению объекта. Для работы autojDtr необходимо, чтобы управляемый объект имел только одного владельца.

Ниже приведен предыдущий пример, переработанный с применением auto ptr:

Заголовочный файл для auto ptr finclude <ше[Т10гу>

void fO {



Команда delete и секция catch стали лишними. Интерфейс указателя auto ptr почти не отличается от интерфейса обычного указателя; оператор * производит разыменование объекта, иа который ссылается указатель, а оператор -> предоставляет доступ к членам класса или струюуры. Математические операции с указателями (такие, как ++) для auto ptr не определены. Впрочем, это скорее достоинство, нежели недостаток, потому что вычисления с указателями слиптом часто приводят к неприятностям.

Учтите, что тип auto ptr не позволяет инициализировать объект обычным указателем в конструкции присваивания. Инициализация auto ptr должна производиться напрямую по значению:

std::auto ptr<ClassA> ptrl(new ClassA): OK

std::autoj)tr<ClassA> ptr2 = new ClassA; ОШИБКА

Передача прав владения в auto ptr

Тип auto ptr поддерживает семантику строгой принадлежности. Иначе говоря, поскольку тип auto ptr удаляет объект, на который он ссылается, этот объект не может «принадлежать» другим объектам. Два и более экземпляра auto ptr не должны одновременно быть владельцами одного объекта. К сожалению, в программе такая ситуация не исключена (например, если два экземпляра auto ptr инициализируются одним и тем же объектом). Программист обязан позаботиться о том, чтобы этого не случилось.

Возникает вопрос: как работают копирующий конструктор и оператор присваивания типа auto ptr? В обычном варианте эти операции копируют данные из одного объекта auto ptr в другой, но в нашем случае это создает ситуацию, при которой один объект принадлежит сразу двум экземплярам auto ptr. Проблема решается просто, но у этого решения есть одно важное следствие: копирующий конструктор и оператор присваивания передают -вправо владения» тем объектом, на который ссылается auto ptr.

Рассмотрим следующий пример использования копирующего конструктора:

Инициализация auto ptr новым объектом std::autoj)tr<ClassA> ptrl(new ClassA):

Копирование auto ptr

- право владения объектом передается от ptrl к ptr2 std:;autoj)tr<ClassA> ptr2(ptrl);

После выполнения первой команды объект, созданный оператором new, принадлежит ptrl. Вторая команда передает право владения от ptrl к ptr2. Следова-

Между следующими двумя фрагментами существует тонкое различие;

X x;

Y у(х): Явное преобразование и

X х;

Y у = х; Неявное преобразование



0 1 2 3 4 5 6 7 8 9 10 11 [ 12 ] 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239