|
Программирование >> Синтаксис инициирования исключений
сохранить доступ к ним из конструкторов указателя. Теперь клиент никак не сможет добраться до Foo, кроме как через MP<Foo>. Мы еще неоднократно вернемся к вопросу о том, как, когда и где создавать указываемые объекты, а пока давайте немного отвлечемся. Если конструкторы Foo вызываются с аргументами, существуют две альтернативы: 1 . Вместо того чтобы пользоваться универсальным шаблоном ведущего указателя, создайте для Foo нестандартный класс ведущего указателя MPFoo. Для каждого конструктора Foo создайте конструктор MPFoo с точно такой же сигнатурой и передайте его аргументы конструктору Foo. 2. Воспользуйтесь безаргументным конструктором для создания объекта и предоставьте отдельную функцию инициализации, которая может вызываться клиентом после конструирования. Второй вариант выглядит так: class Foo { friend class MP<Foo>; protected: Foo(); Единственный конструктор public: Initia1ized(int, char*); Оставшаяся часть интерфейса MP<Foo> mpf; mpf->Initia1ize(17, Hello ); Завершить конструирование Такое решение выглядит довольно неуклюже, но оно позволяет работать с универсальным шаблоном ведущего указателя. Существуют и другие причины для использования инициализирующих функций, о которых будет рассказано в следующих главах. Любой из этих вариантов вполне приемлем для решения наших текущих задач. Уничтожение Нет ничего проще: в деструкторе ведущего указателя удаляется и указываемый объект. template <c1ass Type> class MP { private: Type* t; public: ~MP() { delete t; } Копирование Ой! Опять эти проклятые пользователи... MP<Foo> mpfl; Создает Foo, на который ссылается указатель MP<Foo> mpf2 = mpfl; Неудача! Пусть знак равенства не вводит вас в заблуждение - здесь происходит конструирование, и эта строка эквивалентна строке MP<Foo> mpf2(mpf1);. Если не перегрузить конструктор копий и разрешить компилятору C++ внести свою лепту, мы получим два ведущих указателя, которые ссылаются на один и тот же объект Foo. По умолчанию конструктор копий, генерируемый компилятором, радостно копирует содержащийся в переменной адрес из старого указателя в новый. Проблема решается относительно просто. template <c1ass Type> class MP { 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;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |