- •Содержание
- •Благодарности
- •Как читать эту книгу
- •Несколько слов о стиле программирования
- •Переменные и константы
- •const
- •Стековые и динамические объекты
- •Области действия и функции
- •Области действия
- •Перегрузка
- •Видимость
- •Типы и операторы
- •Конструкторы
- •Деструкторы
- •Присваивание
- •Перегрузка операторов
- •Что такое шаблоны и зачем они нужны?
- •Проблемы
- •Обходные решения
- •Синтаксис шаблонов
- •Параметризованные типы
- •Параметризованные функции
- •Параметризованные функции классов
- •Передача параметра
- •Шаблоны с несколькими параметрами
- •Долой вложенные параметризованные типы!
- •Наследование
- •Комбинации простых и параметризованных типов
- •Небезопасные типы в открытых базовых классах
- •Небезопасные типы в закрытых базовых классах
- •Небезопасные типы в переменных класса
- •Глава 4. Исключения
- •Обработка исключений в стандарте ANSI
- •Синтаксис инициирования исключений
- •Синтаксис перехвата исключений
- •Конструкторы и деструкторы
- •Нестандартная обработка исключений
- •Условные обозначения
- •Глава 5. Умные указатели
- •Глупые указатели
- •Умные указатели как идиома
- •Оператор ->
- •Параметризованные умные указатели
- •Иерархия умных указателей
- •Арифметические операции с указателями
- •Во что обходится умный указатель?
- •Применения
- •Разыменование значения NULL
- •Отладка и трассировка
- •Кэширование
- •Семантика ведущих указателей
- •Конструирование
- •Уничтожение
- •Копирование
- •Присваивание
- •Прототип шаблона ведущего указателя
- •Дескрипторы в C++
- •Что же получается?
- •Подсчет объектов
- •Указатели только для чтения
- •Указатели для чтения/записи
- •Интерфейсные указатели
- •Дублирование интерфейса
- •Маскировка указываемого объекта
- •Изменение интерфейса
- •Грани
- •Преобразование указываемого объекта в грань
- •Кристаллы
- •Вариации на тему граней
- •Инкапсуляция указываемого объекта
- •Проверка граней
- •Обеспечение согласованности
- •Грани и ведущие указатели
- •Переходные типы
- •Полиморфные указываемые объекты
- •Выбор типа указываемого объекта во время конструирования
- •Изменение указываемого объекта во время выполнения программы
- •Посредники
- •Функторы
- •Массивы и оператор []
- •Проверка границ и присваивание
- •Оператор [] с нецелыми аргументами
- •Имитация многомерных массивов
- •Множественные перегрузки оператора []
- •Виртуальный оператор []
- •Курсоры
- •Простой класс разреженного массива
- •Курсоры и разреженные массивы
- •Операторы преобразования и оператор ->
- •Итераторы
- •Активные итераторы
- •Пассивные итераторы
- •Что лучше?
- •Убогие, но распространенные варианты
- •Лучшие варианты
- •Итератор абстрактного массива
- •Операторы коллекций
- •Мудрые курсоры и надежность итераторов
- •Частные копии коллекций
- •Внутренние и внешние итераторы
- •Временная пометка
- •Пример
- •Тернистые пути дизайна
- •Транзакции
- •Отмена
- •Хватит?
- •Образы и указатели
- •Простой указатель образов
- •Стеки образов
- •Образы автоматических объектов
- •Образы указателей
- •Комбинации и вариации
- •Транзакции и отмена
- •Транзакции и блокировки
- •Класс ConstPtr
- •Класс LockPtr
- •Создание и уничтожение объектов
- •Упрощенное создание объектов
- •Отмена
- •Варианты
- •Вложенные блокировки
- •Взаимные блокировки и очереди
- •Многоуровневая отмена
- •Оптимизация объема
- •Несколько прощальных слов
- •Часть 3. Снова о типах
- •Гомоморфные иерархии классов
- •Взаимозаменяемость производных классов
- •Нормальное наследование
- •Инкапсуляция производных классов
- •Множественная передача
- •Двойная передача
- •Гетероморфная двойная передача
- •Передача более высокого порядка
- •Группировка передач и преобразования
- •Производящие функции
- •make-функции
- •Символические классы и перегруженные make-функции
- •Оптимизация с применением производящих функций
- •Локализованное использование производящих функций
- •Уничтожающие функции
- •Снова о двойной передаче: промежуточные базовые классы
- •Объекты классов
- •Информация о классе
- •Еще несколько слов об уничтожающих функциях
- •Определение класса по объекту
- •Представители
- •Основные концепции
- •Инкапсуляция указателей и указываемых объектов
- •Производящие функции
- •Ссылки на указатели
- •Неведущие указатели
- •Ведущие указатели
- •Снова о двойной передаче
- •Удвоенная двойная передача
- •Самомодификация и переходимость
- •Множественная двойная передача
- •Применение невидимых указателей
- •Кэширование
- •Распределенные объекты и посредники
- •Нетривиальные распределенные архитектуры
- •Часть 4. Управление памятью
- •Перегрузка операторов new и delete
- •Простой список свободной памяти
- •Наследование операторов new и delete
- •Аргументы оператора new
- •Конструирование с разделением фаз
- •Уничтожение с разделением фаз
- •Кто управляет выделением памяти?
- •Глобальное управление
- •Выделение и освобождение памяти в классах
- •Объекты классов и производящие функции
- •Управление памятью под руководством клиента
- •Управление памятью с применением ведущих указателей
- •Перспективы
- •Строительные блоки
- •Поблочное освобождение памяти
- •Скрытая информация
- •Подсчет ссылок
- •Базовый класс с подсчетом ссылок
- •Ведущие указатели с подсчетом ссылок
- •Дескрипторы с подсчетом ссылок
- •Трудности подсчета ссылок
- •Подсчет ссылок и ведущие указатели
- •Деление по классам
- •Деление по размеру
- •Деление по средствам доступа
- •Пространства стека и кучи
- •Поиск указателей
- •Мама, откуда берутся указатели?
- •Поиск указателей
- •Дескрипторы, повсюду дескрипторы
- •Общее описание архитектуры
- •Ведущие указатели
- •Вариации
- •Оптимизация в особых ситуациях
- •Алгоритм Бейкера
- •Пространства объектов
- •Последовательное копирование
- •Внешние объекты
- •Алгоритм Бейкера: уход и кормление в C++
- •Уплотнение на месте
- •Базовый класс VoidPtr
- •Пул ведущих указателей
- •Итератор ведущих указателей
- •Алгоритм уплотнения
- •Оптимизация
- •Перспективы
- •Глава 16. Сборка мусора
- •Доступность
- •Периметр
- •Внутри периметра
- •Анализ экземпляров
- •Перебор графа объектов
- •Сборка мусора по алгоритму Бейкера
- •Шаблон слабого дескриптора
- •Шаблон сильного дескриптора
- •Итераторы ведущих указателей
- •Перебор указателей
- •Оптимизация
- •Внешние объекты
- •Множественные пространства
- •Сборка мусора и уплотнение на месте
- •Нужно ли вызывать деструкторы?
- •Только для профессиональных каскадеров
- •Организация памяти
- •Поиск периметра
- •Перебор внутри периметра
- •Сборка мусора
- •Последовательная сборка мусора
- •Итоговые перспективы
227
virtual VoidPtr* Next() = 0;
};
Сам итератор работает весьма прямолинейно. Он просто перебирает блоки в цикле и ищет указатели с ненулевым значением переменной size.
class VoidPtrPoolIterator : public VoidPtrIterator { private:
VoidPtrBlock* block;
int slot; // Номер позиции в текущем блоке
virtual void Advance() // Найти следующую используемую позицию
{
while (block != NULL)
{
if (slot >= BLOCKSIZE)
{
block = block->next; slot = 0;
}
else if (block->slots[slot].size != 0) break;
slot++;
}
}
public:
VoidPtrPoolIterator(VoidPtrBlock* vpb)
: block(vpb), slot(0), { Advance(); } virtual bool More() { return block != NULL; } virtual VoidPtr* Next()
{
VoidPtr* vp = &block->slots[slot]; Advance();
return vp;
}
};
Кроме того, мы добавим в VoidPtrPool следующую функцию:
VoidPtrIterator* iterator()
{ return new VoidPtrPoolIterator(this); }
Наконец, нам пришлось объявить VoidPtrPoolIterator другом VoidPtr, чтобы в программе можно было обратиться к его переменной size. Забегая вперед, скажу, что в главе 16 мы воспользуемся этим итератором для других целей; поэтому функция Advance() и объявлена виртуальной, чтобы производные классы могли добавить свою собственную фильтрацию. Если найденная позиция имеет нулевое значение size, мы пропускаем ее. Во всем остальном работа сводится к простому перебору в массивах, образующих блоки указателей.
Вариации
Перед тем как описывать сами алгоритмы уплотнения, давайте рассмотрим другие варианты исходной постановки задачи. Они не оказывают принципиального влияния на архитектуру или алгоритмы, а только на идиомы С++ в их реализации.
228
Невидимые ведущие указатели
Чтобы не использовать шаблоны дескрипторов и ведущих указателей, можно было организовать множественное наследование ведущих указателей от VoidPtr и гомоморфного базового класса. Иначе говоря, ведущие указатели становятся невидимыми, как объяснялось в предыдущих главах. В игру вступают все механизмы, сопутствующие идиоме невидимых указателей (например, производящие функции).
class Foo { |
|
public: |
|
static Foo* make(); |
// Возвращает пару указатель-указываемый объект |
virtual void Member1() = 0;
// И т.д. для открытого интерфейса
};
// В файле foo.cpp
class FooPtr : public Foo, public VoidPtr { public:
FooPtr(Foo* foo, size_t size) : VoidPtr(foo, size) {} virtual ~FooPtr() { delete (Foo*)address; }
virtual void Member1()
{((Foo*)address)->Member1(); }
//И т.д. для остальных открытых функций
};
class RealFoo : public Foo { ... }; Foo* Foo::make()
{
return new FooPtr(new RealFoo, sizeof(RealFoo));
}
// В клиентском коде class Bar {
private:
Foo* foo; // На самом деле невидимый указатель public:
Bar() : foo(Foo::make()) {}
};
Такое решение улучшает инкапсуляцию применения ведущих указателей. Кроме того, оно позволяет производящим функциям решить, какие экземпляры должны управляться подобным образом, а какие
— с помощью обычных невидимых указателей или даже вообще без указателей. Все прекрасно работает, пока вы соблюдаете осторожность и выделяете в VoidPtrPool достаточно места для FooPtr. Помните, что из-за множественного наследования по сравнению с VoidPtr размер увеличивается, по крайней мере, на v-таблицу.
Объекты классов
Возможен и другой вариант — потребовать, чтобы все объекты происходили от общего класса-предка, способного возвратить объект класса для данного объекта или, по крайней мере, размер объекта. В этом случае вам уже не придется хранить в указателе объект экземпляра, поскольку его можно будет получить у объекта класса. Если вы готовы смириться с некоторым насилием в отношении типов в дескрипторах, это также позволит вам избежать шаблонов второго уровня, используемых для главных указателей. Вместо void* в VoidPtr можно будет хранить CommonBase* (где CommonBase — общий базовый класс). Мы избавляемся от переменной size, от необходимости иметь шаблон, производный от VoidPtr, и от виртуального деструктора в VoidPtr, и как следствие — от 4-байтового адреса v- таблицы. С другой стороны, если управляемые объекты уже содержат v-таблицы и не принуждают вас к применению множественного наследования, дополнительных затрат не будет.
- #08.05.20136.97 Mб16W.H.Press, S.A.Teukolsky, W.T.Vetterling, B.P.Flannery - FORTRAN NUMERICAL RECIPES (Fortran 77) Vol.1.djvu
- #08.05.20133.43 Mб19W.H.Press, S.A.Teukolsky, W.T.Vetterling, B.P.Flannery - FORTRAN NUMERICAL RECIPES (Fortran 90) Vol.2.djvu
- #08.05.201310.54 Mб21W.H.Press, S.A.Teukolsky, W.T.Vetterling, B.P.Flannery - NUMERICAL RECIPES IN C.djvu
- #
- #
- #
- #
- #