Шаг 3 - Как это применять.
Берем код параметризированного класса.
>template ‹class T›
>class SmartPointer {
>private:
> T* tObj;
>public:
> SmartPointer(T* _t=NULL): tObj(_t);
> ~SmartPointer() {if (tObj) delete tObj;}
> operator T*(){return tObj;}
> T* operator-›(){return tObj;}
>};
1. Обработка обращения к NULL.
Заменяем реализацию оператора -› на:
>T* operator-›() {
> if (!tObj) {
> cerr ‹‹ "NULL";
> tObj = new T;
> }
> return tObj;
>}
или
>T* operator-›() {
> if (!tObj) throw CError;
> return tObj;
>};
Здесь CError класс исключения. Или втыкаем статический экземпляр-шпион.
>private:
> T* tObj; // Это было;
> static T* spy; // Это добавлено
Ну и сам перегруженный оператор.
>T* operator-›()
>{
> if (!tObj) return spy;
> return tObj;
>};
Здесь нужно пояснить: spy совсем не обязательно класса T. Можно воткнуть производный, и переопределить его функции. Тогда он будет Вам докладывать о попытках обращения к NULL. Не забудьте его создать, инициализировать, и прицепить к указателю. А то вся идея на помойку. Вы пытаетесь отловить обращение к NULL, а там… NULL!!! "Матрицу" видели?
2. Отладка и трассировка.
Ну это совсем банально. Выносим определение операторов за определение класса и ставим там точку останова. Чтобы не тормозило в релиз версии, окружаем слово inline ифдефами.
>template ‹class T›
>#ifndef DEBUG
>inline
>#endif
>SmartPointer‹T›::operator T*()
>{
> return tObj;
>}
>template ‹class T›
>#ifndef DEBUG
>inline
>#endif
>T* SmartPointer‹T›::operator T-›()
>{
> return tObj;
>}
3. Статистика классов и объектов.
Ну все, здесь уже совсем все просто. Ничего писать не буду, кроме напоминания о том, что всенепременнейше нужно определять статистические переменные класса, в том числе и для параметризированного (то бишь для шаблона), и ровно один раз.
Здесь сложнее. Об этом мне самому нужно почитать и полапать руками. Идея, как можно догадаться, в том, что если при обращении к умному указателю объект отсутствует в памяти, он считывается с диска. Проблемы самые очевидные в том, когда его снова отгружать на диск, разрушать объект, и как гарантировать единичность копии объекта при наличии многих ссылок.
Так. Пока тормозим. Интересно, о чем я напишу следующий шаг?
Шаг 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