Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 16

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 10 11 12 13 14 15 < 16 > 17 18 19 20 21 22 .. 105 >> Следующая

Правило 6. Используйте delete в деструкторах для указателей членов
В большинстве случаев классы, применяющие динамическое выделение памяти, будут использовать в конструкторах new для выделения памяти, а затем -delete в деструкторах для ее высвобождения. Нетрудно сделать это правильно при условии, что вы не забудете использовать delete для всех членов класса, которым когда-либо могла быть выделена память в любом из конструкторов.
По мере эксплуатации и усовершенствования классов ситуация усложняется, так как вносить изменения в классы могут те программисты, которые первоначально не создали их. При этом легко забыть, что добавление указателя - члена класса практически всегда требует выполнения следующих действий:
? инициализации указателя во всех конструкторах. Если в данном конструкторе для указателя выделять память не требуется, то указатель должен быть инициализирован как нулевой;
? удаления имеющейся памяти и выделения новой в операторе присваивания (см. также правило 17);
? удаления указателя в деструкторе. i
Если вы забудете инициализировать указатель в конструкторе или обработать его внутри оператора присваивания, проблема, вероятнее всего, станет очевидной очень быстро, поэтому на практике подобные ошибки не доставляют серьезных неприятностей. Если, однако, не удалить указатель в деструкторе, то можно вообще не заметить никаких симптомов. Просто начнут происходить небольшие утечки памяти, и получится нечто, напоминающее медленно растущую раковую опухоль, которая в конечном итоге «пожрет» имеющуюся память и приведет к преждевременной кончине вашей программы. Важно помнить об этой проблеме всякий раз, когда вы добавляете в объявление класса член, являющийся указателем.
Заметьте, между прочим, что удаление нулевого указателя всегда безопасно (при этом ничего не происходит). Таким образом, если конструкторы, операторы присваивания и другие члены-функции написаны так, что каждый указатель-член класса всегда либо указывает на память необходимого типа, либо равен нулю, вы можете, ни о чем не беспокоясь, удалять их посредством delete, независимо от того, применяли ли вы для них new или нет.
Не стоит, впрочем, впадать в крайности. Например, не нужно использовать delete для указателей, не инициализированных посредством new, и практически никогда не следует удалять указатель, который был вам ранее передан. Другими словами, ваш деструктор класса обычно не должен использовать delete, если в ваших членах класса вы не применяли new.
Правило 6. Правило 7 ^8¦¦¦HMUKXi
Правило 7. Будьте готовы к нехватке памяти
Когда оператор new не может выделить запрошенную память, он генерирует исключение. (Раньше он возвращал 0, и некоторые старые компиляторы до сих пор работают таким образом. При необходимости можно заставить компилятор делать это снова, но я воздержусь от обсуждения указанного момента в рамках данного правила.) В глубине души вы прекрасно знаете, что обработка исключений нехватки памяти - единственно правильный способ действий. Вместе с тем не приходится сомневаться, что это добавит вам много неприятной работы. В итоге вы станете время от времени, а возможно даже всегда, опускать подобную обработку.
При этом на душе у вас будет неспокойно. А что, если new действительно даст исключение?
Вы можете решить, что разумным способом решения данной проблемы будет возврат к давно минувшим дням, то есть к использованию препроцессора. Например, распространенный прием С - определение макроса, который не зависит от передаваемого типа, гарантирующего успех выделения памяти. В С++ такой макрос мог бы выглядеть следующим образом:
#define NEW(PTR, TYPE) \ ' ¦
try { (PTR) = new TYPE; } \
catch (std::bad_alloc&) { assert(O); }
«Подождите-ка! Что означает std: : bad_alloc?» - спросите вы. bad_alloc -это тип исключения, генерируемый оператором new, когда он не может удовлетворить запрос на выделение памяти, a std - название пространства имен (см. правило 28), где определяется bad_alloc. «Хорошо, а как же тогда быть с оператором assert?» Если вы заглянете в стандартный заголовочный файл <assert. h> (или в его эквивалент из С++, понимающий пространства имен, <cassert> -см. правило 49), то обнаружите, что assert - макрос. Он проверяет, является ли переданное ему выражение ненулевым, и, если это не так, вызывает abort. Заметим, что он делает это только тогда, когда не установлено стандартное макроопределение NDEBUG, то есть в режиме отладки. При окончательной компиляции, когда NDEBUG определено, assert расширяется ни во что, то есть в void. Таким ооразом, assert работает только при отладке.
В предлагаемом макросе NEW допущена распространенная ошибка: использование assert для тестирования состояний, возникающих в ходе эксплуатации (в конце концов, проблема нехватки памяти может проявиться в любое время). Кроме того, у assert есть характерный для С++ недостаток: он не учитывает огромное число способов, с помощью которых можно применять new. Существу-ют три стандартные формы получения новых объектов типа Т, и для каждой из них вам необходимо принять во внимание возможность исключений:
Предыдущая << 1 .. 10 11 12 13 14 15 < 16 > 17 18 19 20 21 22 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100