|
Программирование >> Синтаксис инициирования исключений
Ведущие указатели и дескрипторы После совсем непродолжительного знакомства с умными указателями мы успели наткнуться на целый ряд фундаментальных проблем. Многие из них связаны с тем фактом, что на один объект может ссылаться любое количество умных указателей. Как в этом случае узнать, когда можно удалить объект? Кто ведет статистику использования объекта и обращается к ней при необходимости (если вам вдруг понадобится такая возможность)? Кто создает объект? Что означает присваивание одного умного указателя другому? Заиграет ли наконец в этом сезоне наша любимая команда или она снова разобьет наши сердца? Ниже вы найдете ответы на эти и многие другие интригующие вопросы. Семантика ведущих указателей При работе с умными указателями имеется один важный частный случай - когда два умных указателя не должны одновременно ссылаться на один объект. Между указателем и объектом, на который он ссылается, существует однозначное соответствие (за исключением особого случая умных указателей, ссылающихся на NULL). Если в программном дизайне действует такое ограничение, говорят, что реализуется семантика ведущих указателей (master pointers). Конечно, можно просто объявить через местную газету, что указатели должны использоваться таким и только таким образом. А можно защитить ваших пользователей от самих себя и подкрепить семантические правила языковыми средствами C++. Если вы мудро выберете второй вариант, придется учесть следующее: 1 . Указываемые объекты должны создаваться указателями в конструкторах. 2. Деструктор указателя должен удалять указываемый объект. 3. Конструктор копий должен создавать точную копию указываемого объекта. 4. Оператор присваивания operator= должен удалять текущий указываемый объект, находящийся слева от него, и заменять его копией указываемого объекта справа. Кроме того, было бы разумно сделать еще две вещи: 5. Ограничить доступ к конструкторам класса указываемого объекта. 6. Создавать указатели с помощью производящих функций (factory functions). Обе рекомендации будут подробно рассмотрены в последующих разделах. Прототип ведущего указателя, который мы собираемся воплотить, выглядит так: template <c1ass Type> class MP { private: Type* t; public: MP(); Создает указываемый объект MP(const MP<Type>&); Копирует указываемый объект ~MP(); Удаляет указываемый объект MP<Type>& operator=(const MP<Type>&); Удаляет левосторонний объект, копирует правосторонний Type* operator->() const; Конструирование Предположим, вы разрешили пользователям создавать собственные указываемые объекты и передавать их ведущим указателям во время конструирования. template <c1ass Type> class MP { private: Type* t; public: MP() : t(NULL) {} MP(Type* pointer) : t(pointer) {} ~MP() { delete t; } И т.д. Foo* foo = new Foo; MP<Foo> mpl(foo); MP<Foo> mp2(foo); Облом! Насколько проще была бы жизнь без таких пользователей! Когда mpl удаляется, пропадает и foo. В итоге mp2 начинает указывать неизвестно куда. Зачем кому-нибудь потребуется вытворять такое? - спросите вы. Как говорилось в одном рекламном ролике: Зачем спрашивать Зачем ? Если вы оставите такую дыру, можете не сомневаться: кто-нибудь когда-нибудь изобретет дьявольский план, использует ее и обвинит во всех смертных грехах вашу программу. Пользователь прямо-таки кричит: Держите меня, пока я не натворил бед . Для этого существует надежный способ: отобрать у него ключи от конструкторов класса указываемого объекта. class Foo { friend class MP<Foo>; protected: Foo(); Теперь доступ к конструктору имеет только MP<Foo> public: Оставшаяся часть интерфейса template <c1ass Type> class MP { private: Type* t; public: MP() : t(new Type) {} И т.д. Ага, уже лучше. При создании указателя его конструктор также конструирует и указываемый объект. Объявляя указатель другом, мы можем сделать конструкторы Foo закрытыми или защищенными и
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |