Идиомы и стили С++ [Albert Makhmutov] (fb2) читать постранично, страница - 3
[Настройки текста] [Cбросить фильтры]
- 1
- 2
- 3
- 4
- 5
- . . .
- последняя (22) »
4. Кэширование.
Здесь сложнее. Об этом мне самому нужно почитать и полапать руками. Идея, как можно догадаться, в том, что если при обращении к умному указателю объект отсутствует в памяти, он считывается с диска. Проблемы самые очевидные в том, когда его снова отгружать на диск, разрушать объект, и как гарантировать единичность копии объекта при наличии многих ссылок. Так. Пока тормозим. Интересно, о чем я напишу следующий шаг?Шаг 4 - О двойной диспетчеризации.
Предположим, у нас есть массив, в котором мы храним карту местности. Разумеется, что элементы массива разнообразные - дома, колодцы, казино… ничего общего. Кроме суперкласса - предка естественно. CBuilding ¦ ______¦_______ ¦ ¦ ¦ CHouse CWell CCasino А карту эту мы отражаем разными способами. И даже не то, что разными способами, а имеем для такой благой цели несколько видов карт. Ну я не знаю, не картограф. Черви и пики. Нет, ладно. Радиоактивность и карма. CMap | ____________ | | CRadioMap CCarmaMap И что получается? Кто будет себя отрисовывать? И кто кого? Для каждой комбинации наследников CBuilding и CMap свой уникальный алгоритм. Что делать то будем? Какие феерические решения приходят… нет… не вам! Вашему коллеге или начальнику или подчиненному в голову? Да они ни сном ни духом о двойной диспетчеризации! Они скорее всего предложат получить информацию о типе во время исполнения, и запузырить в Ваш прекрасный проект кривоногий switch (){}. Да еще и положить в каждый класс статический член с информацией о типе… Одно звучание предыдущей фразы наводит на подозрения. Но что делаем мы? вот что: class CBuilding: { public: virtual void doDraw(CMap* map)=0; }class CHouse: public CBuilding { public: virtual void doDraw (CMap* map) { // ВОТ ОНА САМАЯ КОРКА! map-›doDraw(*this); } };
// Эти такие же. class CWell: public CBuilding { public: virtual void doDraw (CMap* map) {map-›doDraw(*this);} };
class CCasino: public CBuilding { public: virtual void doDraw (CMap* map) {map-›doDraw(*this);} };
// Это абстрактный класс для карт. class CMap { public: virtual void doDraw (CHouse& cb)=0; virtual void doDraw (CWell& cb)=0; virtual void doDraw (CCasino& cb)=0; }; Это конечно не все. Теперь нужно наследовать CRadioMap и CcarmaMap от общего предка CMap и в каждом классе рисовать реализацию алгоритма. За отрисовку отвечает карта, но какая масть - решает виртуальная CBuilding::doDraw(), а какое строение - выбирается перегруженная CMap::doDraw(). Одинаковое имя для функций отрисовки в разных классах давать не обязательно, но это является хорошим тоном при двойной диспетчеризации, и плохим без нее. Круто? Это - подвиг неизвестного программиста. У Элджера был разобран пример со сложением чисел, очень красивый, но не сразу понятный. Там числа происходят от одного предка, что левый, что правый операнд оператора +, и по моему, обе диспетчеризации происходят по механизму виртуальных функций. Увы, мне лень набирать код. Код к данному шагу я не проверял, в отличие от предыдущих. К диспетчеризации мы еще вернемся. Или не вернемся. Но следующий шаг однозначно про указатели.
Шаг 5 - Ведущие указатели (Master Pointers). Важные конструкторы.
Если мы уж взялись заниматься умными указателями, то очень быстро придем к выводу, что неплохо ограничить их свободу так, чтобы два указателя не указывали на один объект. Далее я их называю ведущими указателями. Для этого нужно реализовать буквально три-четыре правила: 1. Порождение ведущего указателя порождает объект, уничтожение ведущего указателя уничтожает объект; 2. Копирование ведущего указателя создает точную копию объекта; 3. Присваивание ведущего указателя уничтожает предыдущий объект и ставит на его место копию нового объекта. Если же мы хотим получить однозначное соответствие объекта и его ведущего указателя, то нужно запретить создание объекта, кроме как при помощи ведущего указателя, и запретить создание ведущего указателя, кроме как специальной функцией. Последнее в общем не обязательно, а первое весьма важно. Такие простые, но замечательно полезные механизмы просто сами набираются на клавиатуре сначала в виде класса, а потом в виде шаблона класса (мы же не последний день на свете живем, пригодится еще). class CThat { private: int i; public: CThat (int _i=0):i(_i) {} CThat (const CThat& _that):i(_that.i) {} CThat& operator=(const CThat& _that) { if (this == &_that) return *this; i = _that.i; return *this; } };class MasterPointer { private: CThat* t; public: // MasterPointer():t(new CThat){} MasterPointer(CThat _that=0):t(new CThat(_that)) {} MasterPointer(const MasterPointer& mp): t(new CThat((*mp.t))) {} ~MasterPointer() { delete t; } MasterPointer& operator=(const MasterPointer& mp) { if (this != &mp) { delete t; t = new CThat(*(mp.t)); } return *this; } }; Напоминать не надо, что this - это указатель на самого себя? Кстати и оказалось, что для реализации ведущего указателя класс указываемого объекта должен и сам иметь: 1. Конструктор
- 1
- 2
- 3
- 4
- 5
- . . .
- последняя (22) »
Последние комментарии
6 часов 17 минут назад
8 часов 34 минут назад
23 часов 15 минут назад
23 часов 16 минут назад
1 день 4 часов назад
1 день 8 часов назад