Программирование >>  Синтаксис инициирования исключений 

1 ... 22 23 24 [ 25 ] 26 27 28 ... 82


сохранить доступ к ним из конструкторов указателя. Теперь клиент никак не сможет добраться до 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;



1 ... 22 23 24 [ 25 ] 26 27 28 ... 82

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика