|
Программирование >> Оптимизация возвращаемого значения
classKitten: publicALA { public: virtual voidprocessAdoptionO ; Также потребуется функция, считывающая информацию из файла и создающая, в зависимости от типа записи в нем, либо объект Puppy (щенок), либо объект Kitten (котенок). С этой задачей отлично справится виртуальный конструктор, функция, описанная в правиле 25. Здесь достаточно привести только ее объявление: Считываем информацию о животном из S, затем возвращаем указатель на только что созданный объект подходящего типа. ALA * readALA( istream& s) ; Скорее всего, центральным местом вашей программы будет примерно такая функция: voidprocessAdoptions(istream&dataSource) { while (dataSource) .{ Пока есть данные, ALA*pa=readALA(dataSource); считываем следующую запись / / о животном. pa->processAdoption() ; Обработываем сведения о передаче животного, deletepa; Удаляемобъект,который } вернула функция readALA. Эта функция последовательно считывает информацию из потока data-Source и последовательно же обрабатывает каждую запись. Единственная тонкость состоит в том, чтобы не забыть удалять объект ра в конце каждой итерации цикла (это необходимо, поскольку функция readALA создает при каждом вызове новый динамический объект). В противном случае возникнет утечка ресурсов. Теперь посмотрим, что сл5ится, если во время выполнения функции pa->processAdoption возникнет исключение. Функция processAdoptions не содержит нужного обработчика, поэтому исключение распространится в модуль, вызвавший processAdoptions. В результате все операторы в теле processAdoptions, находящиеся там после вызова pa->processAdoption, будут пропущены, и объект ра не удалится. Таким образом, утечка ресурсов будет происходить каждый раз, когдаpa->processAdoption сгенерирует исключение. Прекратить утечку несложно: voidprocessAdoptions(istream&datasource) { while (dataSource) { ALA *pa = readALA(dataSource); try { pa->processAdoption(); catch{...) { Перехватываемвсе исключения, delete pa; Устраняемутечкуресурсов при возбуждении исключения. throw; Передаемисключениевызывающему модулю. delete ра; Устраняемутечку ресурсов / / в отсутствие исключительных ситуаций. Однако при этом текст программы засоряется блоками try и catch. Кроме того, приходится дублировать завершающий код, общий для нормального и аварийного выполнения программы. Как и при каждом дублировании, этот код неприятно писать и трудно поддерживать, но, что еще хуже, появляется мысль о его ошибочности. Ведь удалять объект ра нужно независимо от того, нормальным или аварийным путем вы покидаете функцию processAdoptions, зачем же делать это в нескольких местах? Повтора можно было бы избежать, если бы удалось каким-либо образом поместить завершающий код в деструктор локального по отношению к process-Adoptions объекта. Локальные объекты при выходе из функции удаляются всегда, независимо от причины выхода. (Единственное исключение из данного правила - вызов longjmp, и именно этот недостаток longjmp является основной причиной, по которой язык С++ вообще поддерживает исключения.) Итак, ваша задача состоит в том, чтобы перенести оператор delete из функции process-Adopt ions в деструктор локального для функции processAdoptions объекта. Для решения задачи необходимо заменить указатель ра на объект, который действует как указатель. Тогда при удалении (автоматическом) этого объекта, похожего на указатель, можно заставить его деструктор вызвать delete. Объекты, которые похожи на указатели, но таковыми не являются, называют smart-указателями (интеллектуальными указателями - подробнее о них см. в правиле 28). В данном случае не нужен особенно умный указатель; требуется только похожий на указатель объект, умеющий удалять объект, на который указывает, когда покидает область видимости. Самостоятельно создавать класс для таких объектов несложно, но в этом и нет особой нужды. Стандартная библиотека языка С++ содержит шаблон классов с именем auto ptr, который делает все необходимое. Каждый класс auto ptr принимает указатель на динамический объект в качестве аргумента конструктора и удаляет данный объект в деструкторе. Если ограничиться этими важными функциями, то реализация auto ptr выглядит следующим образом: template<class Т> class auto ptr { public: auto ptr(Т*р = 0) : ptr(р) {} Сохраняемуказательptr / / на объект. * Полная реализация почти стандартного шаблона auto-ptr приведена на стр. 289-292. ~auto ptr() {deleteptr; } Удаляемуказатель на объект. private: Т *ptr; Неиницализированный / / указатель на объект. Стандартная версия класса auto ptr выглядит гораздо более изощренно, а приведенная усеченная реализация не годится для практического использования*. Для этого к ней следует добавить, по крайней мере, конструктор копирования, оператор присваивания и функции, эмулирующие поведение указателя (см. правило 28). Однако основная идея ясна: если использовать объекты auto ptr вместо обычных указателей, то даже при возникновении исключений не нужно будет беспокоиться о неудаленных динамических объектах. Из-за того, что деструктору auto ptr соответствует оператор delete для единичных объектов, объекты auto ptr не могут применяться вместо указателей на массивы элементов. Шаблон, выполняющий функции auto ptr для массивов, вам придется разработать самостоятельно. Вообще же говоря, в таких случаях более удачным конструктивным решением является использование вместо массива объекта типа vector. При замене указателя на объект auto ptr функция processAdoptions приобретет следующий вид: void processAdopt ions(istreams dataSource) { while(dataSource){ auto ptr<ALA>pa(readALA(dataSource)) ; pa->processAdoption(); Эта версия функции processAdoptions имеет два отличия от предыдущей. Во-первых, ра объявлен как объект типа auto ptr<ALA>, а не как указатель на ALA*. Во-вторых, в конце тела цикла отсутствует оператор delete. Все остальное не изменилось, потому что, за исключением удаления, объекты auto ptr ведут себя как обычные указатели. Просто, не правда ли? Идея, лежащая в основе auto ptr - использовать объект для хранения ресурсов, которые должны освобождаться автоматически, и возложить функцию освобождения на деструктор этого объекта, - может применяться не только для указателей. Рассмотрим функцию визуального приложения, создающую окно для отображения некоторой информации: / / Эта функция может порождать утечку ресурсов при возбуждении исключения. void displayInfo(constInformationb info)
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |