Анимация
JavaScript
|
Главная Библионтека private: Type* t; public: MP(); Нормальный MP(const MP<Type>& mp) : t(*(mp.t)) {} Конструктор копий Этот конструктор копий создает дубликат указываемого объекта, используя для этого конструктор копий указываемого объекта. Получается не очень эффективно, но работает. В некоторых ситуациях, с которыми мы столкнемся позже, лучше вообще запретить копирование. Проще всего для этого объявить конструктор копий закрытым и не назначать ему никаких действий. template <c1ass Type> class MP { private: Type* t; MP(const MP<Type>&) : t(NULL) {} Никогда не будет вызываться public: MP(); Тем самым мы предотвращаем непреднамеренное копирование в ситуациях вроде следующей: void f(MP<Foo>); MP<Foo> mpf; f(mpf); Создается временная копия Для предотвращения копирования также можно воспользоваться дескрипторами, о которых будет рассказано ниже. Присваивание Ааааа! Эти зловредные пользователи когда-нибудь угомонятся?! MP<Foo> mpfl; MP<Foo> mpf2; mpf2 = mpfl; Нет, только не это... В приведенном фрагменте возникают сразу две проблемы. Во-первых, указываемый объект, созданный конструктором mpf2, никогда не удаляется. Он превращается в Летучего Голландца, обреченного на вечные скитания в океане памяти. Во-вторых, оператор =, используемый компилятором по умолчанию, копирует адрес, находящийся в t, из одного указателя в другой, что приводит к появлению двух ведущих указателей, ссылающихся на один объект. В исправленном варианте перегруженный оператор = удаляет объект, на который ссылается левосторонний указатель, и заменяет его копией объекта, на который ссылается правосторонний указатель. template <c1ass Type> class MP { private: Type* t; public: MP(); Нормальный конструктор MP<Type>& operator=(const MP<Type>& mp) if (&mp != this) { delete t; t = new Type(*(mp.t)); return *this; Разумеется, если вы вообще не хотите поддерживать присваивание, достаточно объявить оператор = закрытым. Прототип шаблона ведущего указателя Ниже приведен конечный результат наших усилий. Подставляемые функции переместились на привычное место после объявления класса. Параметр, класс указываемого объекта, должен удовлетворять следующим требованиям: 1 . Он должен иметь безаргументный конструктор. 2. Он должен либо перегружать конструктор копий, либо предоставленный компилятором конструктор копий по умолчанию должен подходить для использования в шаблоне ведущего указателя. Если хотя бы одно из этих требований не выполняется, придется внести изменения в класс указываемого объекта или ведущего указателя, или в оба класса сразу. Помните: в реализации конструктора копий и оператора присваивания ведущего указателя будет использоваться конструктор копий указываемого объекта (то есть параметра шаблона). 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> inline MP<Type>::MP() : t(new Type) template <c1ass Type> inline MP<Type>::MP(const MP<Type>& mp) : t(new Type(*(mp.t))) template <c1ass Type> inline MP<Type>::~MP() delete t; template <c1ass Type> inline MP<Type>& MP<Type>::operator=(const MP<Type>& mp) if (this != &mp) { delete t; t = new Type(*(mp.t)); return *this; template <c1ass Type> inline Type* MP<Type>::operator->() const return t; Дескрипторы в C++ Итак, после небольшого подогрева умные указатели превратились в ведущие. Теперь в нашем вареве появляется еще один ингредиент - дескрипторы (handles) C++. Не путайте этот термин с дескрипторами, используемыми в операционных системах Macintosh и Windows. Некоторое сходство существует, но идиома дескрипторов C++ имеет собственную уникальную семантику и набор правил. Основная идея заключается в том, чтобы использовать умные указатели для ссылок на ведущие указатели. Эти дополнительные указатели и называются дескрипторами. Основа, на которой мы будем строить класс дескрипторов, в первом приближении выглядит так: template <c1ass Type> class H { private: MP<Type>& ptr; Ссылка на ведущий указатель public: H() : ptr(*(new MP<Type>)) {} См. Ниже H(MP<Type>& mp) : ptr(mp) {} MP<Type>& operator->() const { return ptr; } Безаргументный конструктор Н создает новый ведущий указатель. Этот ведущий указатель, в свою очередь, создает указываемый объект. Существует второй конструктор, который получает ведущий указатель и инициализирует им переменную ptr. Конструктор копий и оператор = по умолчанию годятся, поскольку любому ведущему указателю может соответствовать несколько дескрипторов. Работа оператора -> основана на рекурсивном алгоритме, используемом компилятором: оператор -> дескриптора возвращает ведущий указатель; затем оператор -> ведущего указателя возвращает указатель Type* который является одним из базовых типов компилятора. Приведенное решение не назовешь изящным - вложенные шаблоны порождают путаницу, к тому же совершенно неясно, когда и как удалять ведущие указатели. Кроме того, следует ли разрешить пользователю напрямую создавать и уничтожать ведущие указатели или же заключить их внутри дескрипторов так, как мы заключили указываемые объекты внутри ведущих указателей? Неужели мы трудились над решением этих проблем для указываемых объектов лишь затем, чтобы столкнуться с ними снова для ведущих указателей? Терпение - в свое время мы найдем ответ на эти и многие другие вопросы. Что же получается? Мы начнем с простого примера ведущих указателей и усовершенствуем его до уровня, который удовлетворил бы и более требовательную аудиторию. На этой стадии еще трудно понять всю пользу дескрипторов, но в следующих главах они будут играть очень важную роль. Подсчет объектов Допустим, вы хотите следить за количеством созданных или находящихся в памяти объектов некоторого класса. Одно из возможных решений - хранить эти сведения в статических переменных самого класса. class CountedStuff { private: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |