Главная - Литература

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 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

Проверяйте корректность указателя перед его удалением Один из лучших способов обрушить программу - вызвать функции deleteQ или freeQ для указателя, который уже был освобожден. Увы, лишь немногие языки обнаруживают такой тип ошибок.

Если вы присваиваете освобождаемым указателям пустое значение, то перед использованием или повторным удалением указателя вы сможете проверить его на равенство null Разумеется, если вы не устанавливаете в null освобождаемые указатели, у вас такой возможности не будет. В связи с этим можно предложить следующее дополнение к коду удаления указателя:

Пример проверки утверждения о неравенстве указателя null перед его удалением (С++)

ASSERT( pointer != NULL, "Attempting to delete null pointer." ); memset( pointer, GARBAGE DATA, MemoryBlockSize( pointer ) ); delete pointer; pointer = NULL;

Отслеживайте распределение памяти для указателей Ведите список указателей, для которых была выделена память. Это позволит вам проверить, находится ли указатель в этом списке перед его освобождением. Вот как для этих целей может быть изменен код удаления указателя:

Пример проверки, выделялась ли память для указателя (С++)

ASSERT( pointer != NULL, "Attempting to delete null pointer." ); if ( IsPointerInList( pointer ) ) {

memset( pointer, GARBAGE DATA, MemoryBlockSize( pointer ) );

RemovePointerFromList( pointer );

delete pointer;

pointer = NULL;

else {

ASSERT( FALSE, "Attempting to delete unallocated pointer." );

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

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

SAFE NEW Вызывает new для выделения памяти, добавляет указатель в список задействованных указателей и возвращает вновь созданный указатель вы-



зывающей стороне. Он может также проверить, что оператор new не вернул null (ошибка нехватки памяти). Поскольку это надо сделать только единожды в этом месте, упрощается процесс обработки ошибок в других частях вашей программы.

SAFEDELETE Проверяет, находится ли переданный ему указатель в списке действительных указателей. Если он там есть, метод записывает мусор в адресуемую им память, удаляет указатель из списка, вызывает С++-оператор delete для освобождения памяти и устанавливает указатель в null. Если указатель не найден в списке, SAFEDELETE выводит диагностическое сообщение и прерывает программу

Метод SAFEDELETE, реализованный в виде макроса, может выглядеть так:

Пример добавления оболочки для кода удаления указателя (С++)

ttdefine SAFE DELETE( pointer ) { \

ASSERT( pointer != NULL, "Attempting to delete null pointer."); \ if ( IsPointerInList( pointer ) ) { \

memset( pointer, GARBAGE DATA, MemoryBlockSize( pointer ) ); \

RemovePointerFromList( pointer ); \

delete pointer; \

pointer = NULL; \

else { \

ASSERT( FALSE, "Attempting to delete unallocated pointer." ); \

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

Централизовав управление памятью в этих двух методах, вы УДние отладочных средств» также сможете менять поведение SAFE NEW и SAFEJDELETE в отладочной и промышленных версиях продукта. Например, обнаружив попытку освободить пустой указатель в период разработки, SAFE DELETE может остановить программу. Но если это происходит во время эксплуатации, он может просто записать ошибку в журнал и продолжить выполнение.

Вы легко сможете адаптировать эту схему для функций calloc и free в языке С, а также для других языков, использующих указатели.

Используйте технологию, не основанную на указателях Указатели в целом сложнее для понимания, они подвержены ошибкам и приводят к созданию машинно-зависимого, непереносимого кода. Если вы можете придумать разумную альтернативу указателям, избавьте себя от головной боли и возьмите ее за основу.

Указатели в С++

Язык С++ добавил специфические тонкости при работе с указателями и ссылками. В следующих подразделах описаны основные принципы, применяемые в работе с указателями на С++.



ДополшшшньштденииМио- Осознайте различие между указателями и ссылка-

жество других советов n<j при- ми В С++ и указатели (*), и ссылки {&) косвенно ссылают-

менбнию указателей в G+f см, ся на объект. Для непосвященных единственной, чисто кос-

в «Effectrve С++», 2d ed (Meyers, метической разницей между ними будет способ обращения

(Meyers ШЩ J-fi ли objectfield. Наиболее значительным

различием является то, что ссылка обязана всегда ссылаться на объект, тогда как указатель может быть равен null Кроме того, после инициализации ссылки нельзя изменить то, куда она ссылается.

Используйте указатели для передачи параметров «по ссылке» и константные ссылки для передачи параметров то значению По умолчанию С++ передает в методы аргументы по значению, а не по ссылке. Когда объект передается по значению, С++ создает копию объекта, и при передаче объекта вызывающей программе вновь создается копия. Для больших объектов такое копирование может съедать много времени и ресурсов. Следовательно, при передаче объектов в метод вы обычно стараетесь избегать копирования объектов, а это означает, что вы хотите передавать их по ссылке, а не по значению.

Однако иногда хотелось бы использовать семантику передачи по значению (т. е. передаваемый объект должен остаться неизменным) и реализацию передачи параметра по ссылке (т. е. передавать сам объект, а не его копию).

В С++ решением этой проблемы является применение указателей для передачи по ссылке, и - как ни странно может звучать - «константных ссылок» для передачи по значению! Приведем пример:

Пример передачи параметров по значению и по ссылке (С++)

void SomeRoutine(

const LARGE OBJECT &nonmodifiableObject, LARGE OBJECT *modifiableObject

Дополнительным преимуществом этого подхода является синтаксическое различие между изменяемыми и неизменяемыми объектами в вызванном методе. В изменяемых объектах ссылка на элементы будет осуществляться с помощью нотации object->тетЬег, тогда как в неизменяемых будет использоваться нотация object.member.

Недостаток этого подхода состоит в необходимости постоянного применения константных ссылок. Хорошим тоном считается использование модификатора const везде, где это возможно (Meyers, 1998). Поэтому в своем коде вы сможете объявлять передаваемые по значению параметры как константные ссылки. В библиотечном коде и других неподконтрольных вам местах вы столкнетесь с проблемой константных параметров. Компромиссной позицией будет все же задавать параметры, предназначенные только для чтения, с помощью ссылок, но не объявлять их константными. При этом подходе вы не в полной мере реализуете преимущество проверки компилятором попыток модификации неизменяемых аргументов метода, однако по крайней мере предоставляете возможность визуального различения object-УтетЬег и object.member.



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 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294



0.0105