Идиомы и стили С++ - [2]

Шрифт
Интервал

) - определение функций в субклассах с тем же именем. Правила переопределения функций достаточно сложны, и я не хотел бы грузить вас ими. Скажу только, что в Object Pascal они не в пример яснее, и компилятор укажет вам, что вы тормоз, если тормознете. А в Плюсах компилятор просто скроет функции, которые вы переопределите неправильно. Ну мы ему отомстим.

В C++ мы можем перегрузить почти все операторы, за исключением нескольких. Во всяком случае, оператор -› перегружается, и это имеет значение крайне важное. Кстати, он называется селектором (member selector). Итак, попробуем:

>#include ‹mem.h›

>class Cthat {

>public:

> void doIt(void){return;};

>};


>class CPthat {

>private:

> Cthat* aThat;

>public:

> CPthat(Cthat* _that=NULL):aThat(_that){}

> ~CPthat() { if (aThat) delete aThat; }

> operator Cthat* () { return aThat;} // Оператор преобразования типа

> CThat* operator-›() { return aThat; }; // Оператор селектора -›

> CPthat operator+(ptrdiff_t _offset) { return CPthat(aThat+_offset); }

>// ^^^^^^^^^

>};


>int main () {

> Cthat* aThat = new Cthat;

> aThat-›doSomething();

> CPthat pthat(new Cthat);

> pthat-›doIt(); // Вариант обращения через -›

> ((Cthat*)pthat)-›doIt (); //Вариант обращения через Cthat*

> delete aThat;

> return 0;

>}

Что получилось: Имеем класс Cthat, который может иметь экземпляры, хотя и не имеет наполнения, и может исполнить пустую функцию. (Обратите внимание. Пустой объект имеет размер 1, и если добавить переменную char, то размер будет тот же. Экземпляры пустых объектов существуют, и они различаются.) Имеем класс объекта-указателя CPthat, в котором храним обычный указатель, но доступ к нему ограничиваем, и перегружаем для него операторы:

1. приведения типа Cthat

2. member selector -›.

3. Операторы арифметики указателей. Я указал только один, сложение.

Идея ясная. Нужно переопределить все восемь, или не переопределять их вовсе. Вопрос в том, направлен ли Ваш указатель на массив, или нет. Во всяком случае, не спешите с этим. Да, и в Ваших плюсах скорее всего тип ptrdiff_t надо заменить на ptr_diff. Я просто дома на BC3.1 все проверяю.

Что здесь хорошего? Мы получили класс объектов-указателей, которые можно смело применять вместо настоящих. Деструктор ~CPthat() уничтожает указуемый объект, поскольку сам по себе последний не имеет имени, и без своего указателя утрачивает идентичность. Проще говоря, останется в нашей памяти навечно, как герой. Ну можно конечно вызывать деструктор и явно, а что? Вот так:

>pthat-›~Cthat();

Тогда удаление уберите из деструктора указателя.

Напоследок сделаем очевидный шаг - сделаем умный указатель параметризированным классом.

>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; }

>};

Для интереса посмотрите, как сделан auto_ptr в STL.

Передохнем. Кофе. Джоггинг. Пиво. Сигарета. Нужное подчеркнуть, выпить, покурить.

Шаг 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. Кэширование.

Здесь сложнее. Об этом мне самому нужно почитать и полапать руками. Идея, как можно догадаться, в том, что если при обращении к умному указателю объект отсутствует в памяти, он считывается с диска. Проблемы самые очевидные в том, когда его снова отгружать на диск, разрушать объект, и как гарантировать единичность копии объекта при наличии многих ссылок.

Так. Пока тормозим. Интересно, о чем я напишу следующий шаг?


Еще от автора Альберт Махмутов
Человек! Чего же ты хочешь, скажи!

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


И всё-таки люди созданы для счастья

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.