|
Программирование >> Оптимизация возвращаемого значения
записи более надежно при генерации исключений, чем-явный вызов функций, поэтому привыкайте использовать классы, подобные LogEntry. Кроме того, проще создать один объект LogEntry, чем добавлять отдельные вызовы для начала и окончания записи. Создание, присваивание и уничтожение интеллектуальных указателей Создать интеллектуальный указатель легко: определяется объект, на который он должен указывать (обычно при помощи аргументов конструктора интеллектуального указателя), а затем находящемуся внутри интеллектуального указателя обычному указателю присваивается значение указателя на этот объект. Если нельзя определить исходный объект, то внутреннему указателю присваивается значение О или сообщается об ошибке (возможно, при помощи генерации исключения). Реализация конструктора копирования, оператора(ов) присваивания и деструктора интеллектуального указателя несколько осложняется вопросом о владельце объекта. Если интеллектуальный указатель является владельцем объекта, на который указывает, то он отвечает за удаление объекта после своего уничтожения. При этом предполагается, что объект, на который указывает интеллектуальный указатель, является динамическим. Такое предположение является обычным при работе с интеллектуальными указателями. (Как убедиться в том, что это предположение выполняется, см. в правиле 27.) Рассмотрим шаблон auto ptr из стандартной библиотеки С++. В правиле 9 объяснялось, что объект типа auto ptr является интеллектуальным указателем на объект в куче до тех пор, пока указатель auto ptr не будет уничтожен. После этого деструктор auto ptr уничтожает объект, на который он (auto ptr) указывал. Шаблон указателя auto ptr может быть реализован так: template<class Т> class auto ptr { public: auto ptr(T *ptr = 0) : pointee{ptr) {} --auto ptr() { delete pointee; } private: T *pointee; Такая реализация работает прекрасно, если auto ptr является владельцем объекта. Но что произойдет после копирования или присвоения auto ptr? auto ptr<TreeNode> ptnl{new TreeNode) ; auto ptr<TreeNode> ptn2 = ptnl; Вызов конструктора копирования. Что произойдет? auto ptr<TreeNode> ptn3 ; ptn3 = ptn2; Вызов operator=. Что произойдет? Если вы просто скопировали внутренний обычный указатель, то в результате два объекта auto ptr будут указывать на один и тот же объект. Это приведет к проблемам, поскольку деструктор каждого из объектов auto ptr должен удалить объект, на который указывает объект auto ptr. Следовательно, вы будете пытаться удалить объект несколько раз. Результат такого многократного удаления не известен (и часто приводит к печальным последствиям). В качестве альтернативы можно было бы создать новую копию объекта, на который указывает auto ptr, при помощи оператора new. У вас появится гарантия, что на один объект не будет указывать несколько объектов auto ptr, но от этого может снизиться производительность из-за создания (и уничтожения) нового объекта. Кроме того, тип создаваемого объекта не всегда будет известен, так как объект auto ptr<T> не обязательно должен указывать на объект типа Т; он может указывать на объект производного от Т типа. Это противоречие нетрудно устранить при помощи виртуальных конструкторов (см. правило 25), но их применение кажется неуместным в таком универсальном классе, как auto ptr. Все названные проблемы исчезнут, если запретить копирование и присвоение объектов auto ptr, но для классов auto ptr было принято более гибкое решение: владелец объекта изменяется при копировании или присвоении оператора auto ptr: template<class Т> class auto ptr { public: auto ptr {auto ptr<T>& rhs) ; Конструктор копирования. auto ptr<T>& Оператор operator={auto ptr<T>& rhs) ; присваивания. template<class T> auto ptr<T>::auto ptr(auto ptr<T>& rhs) { pointee = rhs.pointee; Владельцем объекта *pointee становится указатель *this. rhs.pointee =0; Указатель rhs больше не владеет } ничем. template<class Т> auto ptr<T>& auto ptr<T>::operator={auto ptr<T>& rhs) { if (this == &rhs) Если объект this return *this; присваивается сам себе, ничего не делать, delete pointee; Удалить объект, pointee = rhs.pointee; Владельцем объекта *pointee rhs. pointee =0; вместо указателя rhs становится *this. return *this; Обратите внимание, что оператор присваивания должен удалять объект, владельцем которого он является, перед тем как стать владельцем нового объекта. Если он не сможет этого сделать, то объект никогда не будет удален. Помните: только объект auto ptr является владельцем того, на что он указывает. Поскольку при вызове конструктора копирования auto ptr меняется владелец объекта, то передача auto ptr по значению часто является очень неудачной, и вот почему: Эта функция часто приводит к серьезньм ошибкам. void printTreeNode(ostreamSc s, auto ptr<TreeNode> p) { s *p; } int main() { auto ptr<TreeNode> ptn(new TreeNode); printTreeNode(cout, ptn); Передать auto ptr no значению. Bo время инициализации параметра p функции printTreeNode (при помощи вызова конструктора копирования auto ptr), владельцем объекта, на который указывает ptn, становится р. После завершения работы функции print TreeNode указатель р выходит из области видимости, и его деструктор удаляет то, на что р указывает (то есть то, на что раньше указывал ptn). Однако ptn больше ни на что не указывает (лежащий в его основе простой указатель равен нулю), поэтому почти всякая попытка использовать его после вызова функции printTreeNode даст непредсказуемые результаты. Следовательно, передача указателей auto ptr должна осуществляться, только если вы уверены, что вам нужно поменять владельца объекта на (переменный) параметр функции. Но такое действие требуется очень редко. Это не означает, что вы не можете передавать указатели auto ptr в качестве параметров. Просто передача по значению является не совсем подходящим способом для этого, в отличие от передачи по ссылке на const: / / Поведение этой функции намного нагляднее. void printTreeNode (ostreamSc s, const auto ptr<TreeNode>Sc p) { s *p; } В приведенной функции p является ссылкой, а не объектом, поэтому для инициализации р не вызывается конструктор. Когда этой версии функции printTreeNode передается указатель ptn, он остается владельцем объекта, на который указывает, и может безопасно использоваться после вызова функции printTreeNode. Таким образом, передача указателя auto ptr как ссылки на const позволяет избежать опасностей, возникающих при передаче по значению. Знание процедуры смены владельца при копировании и присвоении интеллектуальных указателей, несомненно, полезно, но вы можете быть не менее
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |