Анализ экземпляров
Алгоритмы сборки мусора обычно начинают свою работу с периметра. Для каждого объекта периметра составляется список объектов, которые он содержит и на которые ссылается. Затем для каждого такого объекта составляется новый список и т.д. Рекурсивный перебор продолжается до тех пор, пока удается находить новые объекты. Для этого нам понадобятся некоторые средства, которые позволяют для данного объекта найти все его внедренные объекты и указатели/ссылки.
В SmallTalk и других динамических языках описание структуры экземпляра является задачей объекта класса. В С++ существует несколько вариантов. Первые два решения (см. ниже) вполне практичны, а третье - отчаянная мера, которая подходит только для профессиональных каскадеров на закрытых треках.
Виртуальные функции
Если все объекты происходят от одного общего базового класса, в этом базовом классе можно объявить виртуальную функцию для перечисления указателей и внедренных объектов. Эта функция переопределяется в каждом классе, который добавляет новые переменные или объединяет базовые классы посредством множественного наследования.
Объекты классов
Вы также можете создать свои собственные объекты классов, как было показано в предыдущих главах, и научить их перечислять внедренные объекты и указатели в экземплярах.
Осведомленные указатели
В крайнем случае можно воспользоваться умными указателями и обращаться к ним с просьбой описать объект.
class Foo {
private:
Bar* bar;
};
class PFoo { // Умный указатель на Foo
private:
Foo* foo;
public:
FunctionThatEnumeratesPointersInFoo();
};
Почему я называю этот случай крайним? Вы рискуете тем, что указатель неверно определит тип объекта, на который он ссылается. Конечно, если PFoo - ведущий указатель, мы знаем, что foo действительно является Foo*, но что делать с bar? Как узнать, что это действительно Bar, а не что-то производное от него? Если Bar не имеет только что упоминавшейся самоописывающей виртуальной функции и не возвращает объект класса, остается одно - повсюду раскидать умные указатели и надеяться на лучшее.
class Bar {
};
class Pbar { // Умный указатель на Bar
};
class Foo {
private:
Pbar bar;
};
class PFoo { // Умный указатель на Foo
private:
Foo* foo;
public:
FunctionThatEnumeratesPointersInFoo();
};
Теперь мы начинаем с одного умного указателя, PFoo, и рекурсивно находим другой, PBar. Каждый из этих умных указателей разбирается в особенностях строения объекта, на который он ссылается. В этом они превзошли умные указатели, поэтому я называю их осведомленными (ingenious), хотя циник, вероятно, назвал бы их нерассуждающими.