89
}
return
*this;
}
};
Разумеется, если вы вообще не хотите поддерживать присваивание, достаточно объявить оператор
=
закрытым.
Прототип шаблона ведущего указателя
Ниже приведен конечный результат наших усилий. Подставляемые функции переместились на
привычное место после объявления класса. Параметр, класс указываемого объекта, должен
удовлетворять следующим требованиям:
1. Он должен иметь безаргументный конструктор.
2. Он должен либо перегружать конструктор копий, либо предоставленный компилятором
конструктор копий по умолчанию должен подходить для использования в шаблоне ведущего
указателя.
Если хотя бы одно из этих требований не выполняется, придется внести изменения в класс
указываемого объекта или ведущего указателя, или в оба класса сразу. Помните: в реализации
конструктора копий и оператора присваивания ведущего указателя будет использоваться конструктор
копий указываемого объекта (то есть параметра шаблона).
template
class MP {
private:
Type*
t;
public:
MP(); //
Создает указываемый объект
MP(const
MP&);
//
Копирует указываемый объект
~MP();
//
Удаляет указываемый объект
MP&
operator=(const
MP&);
Type*
operator->()
const;
};
template
inline MP::MP() : t(new Type)
{}
template
inline MP::MP(const MP& mp) : t(new Type(*(mp.t)))
{}
template
inline MP::~MP()
{
delete
t;
}
template
inline MP& MP::operator=(const MP& mp)
{
if (this != &mp) {
delete
t;
t = new Type(*(mp.t));
}
return
*this;
90
}
template
inline Type* MP::operator->() const
{
return
t;
}
Дескрипторы в C++
Итак, после небольшого подогрева умные указатели превратились в ведущие. Теперь в нашем вареве
появляется еще один ингредиент — дескрипторы (handles) C++. Не путайте этот термин с
дескрипторами, используемыми в операционных системах Macintosh и Windows. Некоторое сходство
существует, но идиома дескрипторов C++ имеет собственную уникальную семантику и набор правил.
Основная идея заключается в том, чтобы использовать умные указатели для ссылок на ведущие
указатели. Эти дополнительные указатели и называются дескрипторами. Основа, на которой мы будем
строить класс дескрипторов, в первом приближении выглядит так:
template
class H {
private:
MP&
ptr;
//
Ссылка на ведущий указатель
public:
H() : ptr(*(new MP)) {}
// См. Ниже
H(MP& mp) : ptr(mp) {}
MP& operator->() const { return ptr; }
};
Безаргументный конструктор
Н
создает новый ведущий указатель. Этот ведущий указатель, в свою
очередь, создает указываемый объект. Существует второй конструктор, который получает ведущий
указатель и инициализирует им переменную
ptr
. Конструктор копий и оператор
=
по умолчанию
годятся, поскольку любому ведущему указателю может соответствовать несколько дескрипторов.
Работа оператора
->
основана на рекурсивном алгоритме, используемом компилятором: оператор
->
дескриптора возвращает ведущий указатель; затем оператор
->
ведущего указателя возвращает
указатель
Type*
который является одним из базовых типов компилятора.
Приведенное решение не назовешь изящным — вложенные шаблоны порождают путаницу, к тому же
совершенно неясно, когда и как удалять ведущие указатели. Кроме того, следует ли разрешить
пользователю напрямую создавать и уничтожать ведущие указатели или же заключить их внутри
дескрипторов так, как мы заключили указываемые объекты внутри ведущих указателей? Неужели мы
трудились над решением этих проблем для указываемых объектов лишь затем, чтобы столкнуться с
ними снова для ведущих указателей? Терпение — в свое время мы найдем ответ на эти и многие
другие вопросы.
Что же получается?
Мы начнем с простого примера ведущих указателей и усовершенствуем его до уровня, который
удовлетворил бы и более требовательную аудиторию. На этой стадии еще трудно понять всю пользу
дескрипторов, но в следующих главах они будут играть очень важную роль.
Подсчет объектов
Допустим, вы хотите следить за количеством созданных или находящихся в памяти объектов
некоторого класса. Одно из возможных решений — хранить эти сведения в статических переменных
самого класса.
class CountedStuff {
private:
Do'stlaringiz bilan baham: |