183
2. Реализуется схема сборки мусора, которая позволяет найти все указатели и указываемые
объекты. Мы помечаем все указываемые объекты, на которые ссылается хотя бы один
указатель, а затем удаляем все непомеченные указываемые объекты.
Подсчет ссылок используется не так часто, но наша задача прямо-таки создана для него. Сборка мусора
— тема, над которой нам предстоит основательно поразмыслить в главе 16.
Ведущие указатели
Невидимые указаьели чаще всего являются ведущими (то есть между указателем и указываемым
объектом существует однозначное соответствие). С момента рассмотрения ведущих указателей прошло
немало времени, поэтому я кратко напомню, что нам предстоит сделать для поддержания семантики
ведущих указателей в нашей ситуации:
1. Когда указатель уничтожается, должен уничтожаться и указываемый объект.
2. Когда указатель копируется, должен копироваться и указываемый объект.
3. Когда для указателя выполняется присваивание, он изменяется так, чтобы ссылаться на копию
указываемого объекта из правой части выражения.
Недостаточно присвоить указываемый объект из правой части указываемому объекту из левой части,
поскольку они могут принадлежать к разным классам.
Уничтожение
Деструктор класса-предка должен быть виртуальным, а деструктор указателя должен уничтожать
указываемый объект.
class Foo {
public:
virtual ~Foo() {}
};
// В файле foo.cpp
class PFoo : public Foo {
private:
Foo*
foo;
public:
virtual ~PFoo() { delete foo; }
};
Копирование
Копирование невидимых ведущих указателей продолжается с того, на чем мы остановились при
копировании неведущих указателей. Каждый производный класс должен переопределить функцию
makeClone()
.
class Foo {
protected:
Foo(const
Foo&)
{}
public:
virtual Foo* makeClone() = 0;
// Для копирования
};
// В файле foo.cpp
class PFoo : public Foo {
private:
Foo*
foo;
public:
PFoo(Foo* f) : foo(f) {}
184
virtual Foo* makeClone() { return new PFoo(foo->makeClone()); }
};
class Bar : public Foo {
protected:
Bar(Bar&); //
Конструктор копий
public:
virtual Foo* makeClone();
};
Foo* Bar::makeClone()
{
return
new
Bar(*this);
}
Наконец мы добрались и до применения настоящего конструктора копий. Указатель создает копию —
не только свою, но и указываемого объекта. В свою очередь, указываемый объект перекладывает всю
тяжелую работу на свой собственный конструктор копий. Обратите внимание: чтобы это стало
возможным, мы сделали конструктор копий
Foo
защищенным.
Присваивание
Присваивание для невидимых ведущих указателей похоже на присваивание для любых других типов
ведущих указателей. И снова мы продолжаем с того места, на котором остановились при присваивании
неведущих указателей.
class Foo {
public:
virtual Foo& operator=(const Foo&);
};
// В файле foo.cpp
class PFoo : public Foo {
private:
Foo*
foo;
public:
virtual Foo& operator=(const Foo& f)
{
if (this == &f) return *this;
delete
foo;
foo
=
f.foo->makeClone();
return
*this;
}
};
Foo& Foo::operator=(const Foo&)
{
return
*this;
}
Снова о двойной передаче
Невидимые указатели позволяют элегантно решить многие проблемы. Одна из таких проблем —
улучшенная инкапсуляция двойной передачи, и в том числе решение неприятной проблемы, связанной
с оператором
+=
. Мы объединим двойную передачу с концепцией переходных типов, о которых
говорилось давным-давно, в главе 7.
Do'stlaringiz bilan baham: |