87
сохранить доступ к ним из конструкторов указателя. Теперь клиент никак не сможет добраться до
Foo
,
кроме как через
MP
. Мы еще неоднократно вернемся к вопросу о том, как, когда и где создавать
указываемые объекты, а пока давайте немного отвлечемся.
Если конструкторы
Foo
вызываются с аргументами, существуют две альтернативы:
1. Вместо того чтобы пользоваться универсальным шаблоном ведущего указателя, создайте для
Foo
нестандартный класс ведущего указателя
MPFoo
. Для каждого конструктора
Foo
создайте
конструктор
MPFoo
с точно такой же сигнатурой и передайте его аргументы конструктору
Foo
.
2. Воспользуйтесь безаргументным конструктором для создания объекта и предоставьте
отдельную функцию инициализации, которая может вызываться клиентом после
конструирования.
Второй вариант выглядит так:
class Foo {
friend class MP;
protected:
Foo();
//
Единственный конструктор
public:
Initialized(int,
char*);
//
Оставшаяся часть интерфейса
};
MP mpf;
mpf->Initialize(17, “Hello”); // Завершить конструирование
Такое решение выглядит довольно неуклюже, но оно позволяет работать с универсальным шаблоном
ведущего указателя. Существуют и другие причины для использования инициализирующих функций, о
которых будет рассказано в следующих главах. Любой из этих вариантов вполне приемлем для
решения наших текущих задач.
Уничтожение
Нет ничего проще: в деструкторе ведущего указателя удаляется и указываемый объект.
template
class MP {
private:
Type*
t;
public:
~MP() { delete t; }
};
Копирование
Ой! Опять эти проклятые пользователи...
MP mpf1;
// Создает Foo, на который ссылается указатель
MP mpf2 = mpf1;
// Неудача!
Пусть знак равенства не вводит вас в заблуждение — здесь происходит конструирование, и эта строка
эквивалентна строке
MP mpf2(mpfl)
;. Если не перегрузить конструктор копий и разрешить
компилятору C++ внести свою лепту, мы получим два ведущих указателя, которые ссылаются на один
и тот же объект
Foo
. По умолчанию конструктор копий, генерируемый компилятором, радостно копи-
рует содержащийся в переменной адрес из старого указателя в новый. Проблема решается
относительно просто.
template
class MP {
88
private:
Type*
t;
public:
MP();
//
Нормальный
MP(const MP & mp) : t(*(mp.t)) {}
// Конструктор копий
};
Этот конструктор копий создает дубликат указываемого объекта, используя для этого конструктор
копий указываемого объекта. Получается не очень эффективно, но работает. В некоторых ситуациях, с
которыми мы столкнемся позже, лучше вообще запретить копирование. Проще всего для этого
объявить конструктор копий закрытым и не назначать ему никаких действий.
template
class MP {
private:
Type*
t;
MP(const MP&) : t(NULL) {}
// Никогда не будет вызываться
public:
MP();
};
Тем самым мы предотвращаем непреднамеренное копирование в ситуациях вроде следующей:
void f(MP);
MP mpf;
f(mpf);
//
Создается временная копия
Для предотвращения копирования также можно воспользоваться дескрипторами, о которых будет
рассказано ниже.
Присваивание
Ааааа! Эти зловредные пользователи когда-нибудь угомонятся?!
MP mpf1;
MP mpf2;
mpf2 = mpf1;
// Нет, только не это...
В приведенном фрагменте возникают сразу две проблемы. Во-первых, указываемый объект, созданный
конструктором
mpf2
, никогда не удаляется. Он превращается в Летучего Голландца, обреченного на
вечные скитания в океане памяти. Во-вторых, оператор
=
, используемый компилятором по умолчанию,
копирует адрес, находящийся в
t
, из одного указателя в другой, что приводит к появлению двух
ведущих указателей, ссылающихся на один объект. В исправленном варианте перегруженный оператор
=
удаляет объект, на который ссылается левосторонний указатель, и заменяет его копией объекта, на
который ссылается правосторонний указатель.
template
class MP {
private:
Type*
t;
public:
MP(); //
Нормальный конструктор
MP& operator=(const MP& mp)
{
if (&mp != this) {
delete
t;
t = new Type(*(mp.t));
Do'stlaringiz bilan baham: |