|
Программирование >> Оптимизация возвращаемого значения
заинтересованы необычными объявлениями в конструкторе копирования и операторе присваивания. Такие функции обычно имеют параметры с атрибутом const, но в данном случае это не так. В вышеприведенном коде параметры изменяются во время копирования или присваивания. Другими словами, объекты auto ptr изменяются, если они копируются или являются источником при присваивании! Да, именно это и происходит. Не прекрасно ли, что язык С++ настолько гибок, что позволяет выполнять такие операции? Если бы требовалось, чтобы параметры конструкторов копирования и операторов присваивания имели параметры с атрибутом const, пришлось бы снимать атрибуты const или реализовывать передачу прав владельца каким-либо другим образом. Но удобнее прямо сказать, что вам нужно: объект должен измениться, если он копируется или является источником при присваивании. Это может показаться не очень наглядным, но это простой и понятный способ и в данном случае правильный. Если вас заинтересовало рассмотрение функций - членов указателя auto ptr, вы можете ознакомиться с его полной реализацией, которую найдете на страницах 289-292. Как вы увидите, шаблон auto ptr в стандартной библиотеке С++ имеет более гибкие конструкторы копирования и операторы присваивания, чем описанные выше. В стандартном шаблоне auto ptr эти функции являются шаблонами функций-членов, а не просто функциями-членами. (Шаблоны функций--членов описываются в этом правиле позже.) Деструктор интеллектуального указателя часто выглядит так: template<class Т> SmartPtr<T>::-SmartPtr{) { if {*this является владельцем *pointee) { delete pointee; Иногда не нужно выполнять такую проверку. Например, указатель auto ptr всегда является владельцем того, на что он указывает. В других случаях проверка бывает несколько более сложной. Интеллектуальный указатель, использующий счетчик ссылок (см. правило 29) должен корректировать счетчик ссылок перед тем, как определить, может ли он удалить то, на что указывает. Некоторые интеллектуальные указатели не отличаются от обычных: они не выполняют никаких действий над объектом, на который указывают при своем уничтожении. Реализация операторов разыменования Обратим теперь внимание на сердце интеллектуальных указателей, функции operator* и operator->. Вторая функция возвращает объект, на который ссылается указатель. В теории это просто: template<class Т> Т& SmartPtr<T>::operator*() const выполнить обработку интеллектульного указателя; return *pointee; Во-первых, функция осуществляет необходимые вычисления для инициализации pointee или какие-либо другие процедуры, обеспечивающие его правильность. Например, при использовании отложенной выборки (см. правило 17) этой функции может потребоваться создать новый объект, на который будет указывать pointee. Если ссылка pointee задана корректно, то функция operator* просто возвращает ссылку на указываемый объект. Обратите внимание, что возвращается ссылка. Возвращение объекта могло бы привести к ошибке, поэтому компиляторы не допускают этого. Имейте в виду, что ссылка pointee не обязательно должна указывать на объект типа Т; она может указывать на объект производного от Т класса. Если бы это было так, и функция operator* возвращала бы объект Т вместо ссылки на объект производного класса, то функция возвращала бы объект неправильного типа! (Это проблема потери данных - см. правило 13.) Виртуальные функции, которые вызываются в объекте, возвращаемом несчастным operator*, не вызывали бы функцию, соответствующую динамическому типу указываемого объекта. В сущности, такой интеллектуальный указатель не мог бы корректно поддерживать виртуальные функции, и насколько же интеллектуальным он бы был? Кроме того, возврат ссылки более эффективен, так как не требует создания временного объекта (см. правило 19). Это один из тех редких случаев, когда корректность и эффективность идут рука об руку. Если вы из тех, кто любит беспокоиться по различным поводам, вы можете поинтересоваться, как надо поступить, если кто-то вызовет operator* для нулевого интеллектуального указателя, то есть указателя, простой указатель внутри которого равен нулю. Расслабьтесь. Вы можете сделать все, что угодно. Результат разыменования нулевого указателя не определен, поэтому не существует неправильного поведения. Хотите сгенерировать исключение? Давайте, генерируйте. Хотите вызвать функцию abort (возможно, завершив неудачей вызов assert)? Прекрасно, вызывайте ее. Желаете обойти всю память, присвоив каждому байту дату вашего рождения по модулю 256? Это также допустимо. В данном слзае вы совершенно свободны от ограничений языка. Описание operator-> аналогично описанию operator*, но прежде чем рассмотреть operator->, вспомним о необычном значении вызова этой функции. Рассмотрим снова функцию editTuple, использующую интеллектуальный указатель на объект Tuple: void editTuple(DBPtr<Tuple>& pt) { LogEntry<Tuple> entry{*pt) ; do { pt->displayEditDialog{); } while (pt->isValid{) == false); Оператор pt->displaYEditDialog{); интерпретируется компиляторами как (pt.operator->())->displaYEditDialog{); Это означает, что ко всему возвращающему operator-> допускается применить оператор выбора элемента (->). Поэтому функция operator-> может возвращать только две вещи: обычный указатель на объект или другой объект интеллектуального указателя. В большинстве случаев вам будет нужно возвращать обычный указатель. При этом operator-> реализуется следующим образом: template<class Т> Т* SmartPtr<T>::operator->() const { выполнить обработку интеллектуального указателя; return pointee; Эта конструкция будет прекрасно работать. Поскольку данная функция возвращает указатель, вызовы виртуальных функций при помощи operator-> будут вести себя так, как и предполагается. Для большинства приложений это все, что вам нужно знать об интеллектуальных указателях. Например, код для подсчета ссылок из правила 29 использует не больше функций, чем было описано выше. Однако если вы хотите обогатить свои представления об интеллектуальных указателях, вы должны больше узнать о поведении обычных указателей и о том, когда интеллектуальные указатели могут и не могут эмулировать такое поведение. Если ваш девиз Большинство людей останавливаются, дойдя до буквы Z, но только не я! , то следующий раздел предназначен для вас. Проверка равенства интеллектуальных указателей нулю При помощи функций, которые уже были рассмотрены, вы можете создавать, уничтожать, копировать, присваивать и разыменовывать интеллектуальные указатели. Но не можете определить, равен ли интеллектуальный указатель нулю: SmartPtr<TreeNode> ptn; if (ptn == 0) . . . Ошибка! if (ptn) ... Ошибка! if {!ptn) ... Ошибка! Это серьезное ограничение.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |