|
Программирование >> Оптимизация возвращаемого значения
отношению к конструктору BookEntry), что удалит объект, на который уже указывает the Image? Ответ, как кажется, очевиден - деструктор BookEntry, но это неверно. Деструктор BookEntry не будет вызван никогда. В языке C-I-I- удаляются только полностью сконструированные объекты, то есть такие, конструкторы которых уже завершили выполнение кода. Тем самым, если объект b типа BookEntry создается как локальный объект: void testBookEntryClass{) { BookEntryb( Addison-Wesley PublishingCompany , One JacobWay, Reading, MA 01867 ) ; и в процессе создания b возникает исключение, то деструктор для объекта b не будет вызван. Можно попытаться взять контроль в свои руки и выделить память для b динамически, а затем, при возникновении исключения, вызывать оператор delete: voidtestBookEntryClass() { BookEntry *pb= О, try { pb = new BookEnt ry b { Addison-Wesley Publishing Company , One JacobWay, Reading, MA01867 ) ; catch {... ) { Перехватываем все исключения. deletepb; Удаляем pb при появлении исключения. throw; Передаемобработку исключения вызывающемумодулю. de 1 et е pb; / / Удаляем pb обычным образом. Тем не менее, объект Image, созданный в конструкторе BookEntry, будет потерян, потому что присваивание рЬ произойдет не раньше, чем успешно закончит работу оператор new. Если исключение возникнет внутри конструктора BookEntry, то значение pb останется равным нулю, поэтому его удаление в блоке catch не вызовет никаких действий. Использование вместо BookEntry* smart-указателей, а именно класса auto ptr<BookEntry> (см. правило 9), также не даст результата, потому что присваивание pb все равно не произойдет по той же причине. Конечно, деструкторы для не полностью построенных объектов не вызываются в языке С++ вовсе не потому, чтобы усложнить жизнь программистам. Во многих случаях подобные действия не только не имели бы никакого смысла, но 3-679 и были бы потенциально опасными. Если бы деструкторы вызывались для объектов, создание которых не завершено, как деструкторы могли бы определить, что им делать? Единственный выход состоял бы в том, чтобы снабдить объекты статусными битами, показывающими, насколько продвинулось выполнение конструктора. Но это увеличило бы размеры объектов и замедлило выполнение конструкторов. Примененный в языке С++ подход позволяет избежать такой дополнительной работы, но платить приходится тем, что частично сконструированные объекты не удаляются автоматически. Поскольку в языке С++ не освобождаются ресурсы, выделенные объектам, во время создания которых возникают исключения, то необходимо проектировать конструкторы так, чтобы они делали это сами. Часто бывает достаточно просто перехватить все возможные исключения, выполнить код завершения, а затем передать исключение для дальнейшей обработки. Для объекта BookEntry это может выглядеть следующим образом: BookEntry: : BookEntry (const string& name, const string& address = , const strings imageFileName = , const strings audioClipFileName = ) :theName(name),theAddress(address) , thelmage(0),theAudioClip(0) try { Это новый блок try. if (imageFileName! = ) { thelmage = new Image (imageFileName) ; if (audioClipFileName ! = ) { theAudioClip= new AudioClip(audioClipFileName); catch (...) { Перехватываем все исключения, delete thelmage; Выполняемнеобходимые delete theAudioClip; операции очистки, throw; Вызываем исключение / / для дальнейшей обработки . Не следует волноваться по поводу элементов-данных, не являющихся указателями. Они инициализируются автоматически перед вызовом конструктора класса, поэтому, когда конструктор BookEntry начинает выполняться, элементы данных объекта theName, theAddress и thePhones уже полностью созданы. Значит, они будут удалены одновременно с объектом типа BookEntry, которому принадлежат, и вмешательство программиста здесь не нужно. Разумеется, если конструкторы этих объектов вызывают функции, способные привести к исключениям, то такие конструкторы должны побеспокоиться о перехвате исключений и выполнении операций очистки перед тем, как передать исключения далее. try { Без изменений. catch (...) { cleanup 0; Освобождаемресурсы, throw; Передаемисключение / / для дальнейшей обработки. BookEntry::-BookEntry() { cleanup(); Все это хорошо, но кое-какие проблемы еще остаются. Переделаем класс BookEntry таким образом, чтобы thelmage и theAudioClip стали константными указателями: classBookEntry { public: private: Какиранее. ,Image * const thelmage; Теперь указатели имеют AudioClip * const theAudioClip; атрибут const. Можно заметить, что код в блоке catch конструктора BookEntry почти совпадает с кодом в деструкторе BookEntry. Не стоит дублировать код ни в этом месте программы, ни где бы то ни было еще, поэтому чтобы ул5шить программу, стоит поместить общий код в закрытую функцию и вызывать ее из конструктора и деструктора: class BookEntry { public: Без изменений. private: voidcleanupO ; Общие операцииочистки. voidBookEntry::cleanup() { delete thelmage; delete theAudioClip; BookEntry: : BookEntry (const strings name, const strings address = , const stringsImageFileName = , const strings audioClipFileName = ) : theName(name) , theAddress(address) , thelmage(0),theAudioClip(0)
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |