|
Программирование >> Оптимизация возвращаемого значения
WINDOW HANDLEw(createWindow{)) ; здесь отображаем информацию в окне с дескриптором w; destroyWindowCw); Многие оконные системы имеют интерфейс, подобный тому, что создается с помощью языка С. Такой интерфейс использует функции типа createWindow и releaseWindow для захвата и освобождения ресурсов окна. Если исключение возникнет в процессе отображения данных info в окне w, то ресурсы этого окна будут потеряны так же, как и любые другие динамически выделяемые ресурсы. Решение проблемы остается прежним. Надо создать класс, конструктор и деструктор которого захватывают и освобождают необходимый ресурс: / / Класс для захвата и освобождения дескриптора окна. classWindowHandle{ public: WindowHandle{WINDOW HANDLEhandle) :w(handle) {} -WindowHandle{) { destroyWindow{w) ; } operatorWINDOW HANDLE{) {returnw; } См. ниже, private: WINDOW HANDLEw; Нижеприведенные функции объявлены как private, чтобы / / запретить создание нескольких копий WINDOW HANDLE. / / См. правило 2 8 , где описан более гибкий подход. WindowHandle(const WindowHandle&); WindowHandle&operator={constWindowHandle&) ; Bee это очень похоже на шаблон auto j)tr, но с тем отличием, что операторы присваивания и копирования явно запрещены, а для преобразования WindowHandle в WINDOW HANDLE определен оператор неявного преобразования типа. Этот оператор имеет серьезное практическое значение, потому что теперь везде, где раньше использовался обьганый WlNDOW bIANDLE, можно вместо него включать в код WindowHandle. (Как вы помните, с операторами неявного преобразования типа нужно обращаться очень осторожно - см. правило 5.) С помощью класса WindowHandle можно переписать функцию display Info следующим образом: Эта функция предотвращает утечку ресурсов при возникновении исключения, voiddisplaylnfo(constInformation& info) { WindowHandlew(createWindow()) ; отображаем данные info в окне w; Даже при возникновении исключения в теле функции display Info окно, созданное функцией createWindow, будет удалено. Итак, даже используя исключения, можно избежать утечек ресурсов, если следовать правилу размещения ресурсов внутри объектов. Но что произойдет, если исключение возникнет в тот момент, когда программа как раз находится в процессе захвата ресурса, то есть внутри конструктора класса, который требует выделения ресурсов? А что произойдет, если исключение будет возбуждено во время автоматического освобождения таких ресурсов? Может быть, конструкторы и деструкторы требуют специального обращения? Ответам на эти вопросы посвящены правила 10 и 11. Правило 10. Не допускайте утечки ресурсов в конструкторах Представьте, что вы разрабатываете программу для мультимедийной адресной книги. Наряду с обычной текстовой информацией: полным именем, адресом и телефонным номером - книга могла бы содержать фотографии людей и образцы их речи (например, в виде правильного произношения имени). Ваша программа могла бы иметь следующий вид: class Image { Изображение. public: Image(const stringsImageDataFileName); classAudioClip{ Звуковые данные, public: AudioClip (const str ing& audioDataFileName) ; class PhoneNumber{...}; Телефонныйномер, class BookEntry { / / Единичная запись в адресной public: книге. BookEntry(const strings name, const strings address = , const stringsImageFileName = , const strings audioClipFileName = ) ; -BookEntry(); / / Эта функция добавляет / / телефонный номер. voidaddPhoneNumber(const PhoneNumberSnumber); private: string theName; Полное имя человека. stringtheAddress; Адрес. list<PhoneNumber> thePhones; Телефонные номера. Image*thelmage; Изображение. AudioClip *theAudioClip; Аудиоклип. Каждая запись BookEntry должна содержать полное имя человека. Следовательно, это обязательный аргумент конструктора (см. правило 4), а все остальные данные - адрес человека, имена файлов, содержащих видео- и аудиоинформацию - не обязательны. Обратите внимание на использование класса list для хранения телефонных номеров, который является одним из контейнерных классов стандартной библиотеки языка С++ (см. правило 35). Простейший вариант конструктора BookEntry выглядит так: BookEntry: : BookEntry (const string&name, const string& address = , const string& imageFileName = , const string&audioClipFileName= ) : theName(name),theAddress(address), thelmage(O),theAudioClip(0) if (imagefileName!= ) { thelmage = new Image (imageFileName) ; if(audioClipFileName! = ) { theAudioClip = newAudioClip (audioClipFileName) ; BookEntry::-BookEntry() { delete thelmage; delete theAudioClip; Конструктор обнуляет указатели thelmage и theAudioClip, a затем, если соответствующие аргументы содержат нормальные имена файлов, создает для них реальные объекты. Деструктор удаляет эти объекты, не допуская тем самым утечек памяти в объекте BookEntry. Язык С++ гарантирует безопасность удаления нулевых указателей, поэтому деструктор BookEntry не проверяет, ссылаются ли указатели на реальные объекты. Пока все выглядит хорошо, и при нормальных условиях все бы прекрасно работало, но при исключительных ситуациях появятся некоторые проблемы. Посмотрим, что произойдет, если исключение возникнет во время выполнения следующей части конструктора BookEntry: if (audioClipFileName!= ) { theAudioClip = new AudioClip(audioClipFileName); Исключение может возникнуть, например, потому что operator new (см. правило 8) не может выделить достаточно памяти для объекта AudioClip. Оно может также возникнуть в самом конструкторе AudioClip. Но, независимо от причины появления, исключение, возникшее в конструкторе BookEntry, распространится в точку, где создается объект BookEntry. Итак, если исключение возникает во время создания объекта, на который должен указывать theAudioClip (передавая управление в точку, внешнюю по
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |