Идиомы и стили С++ - [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 от общего предка 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. Конструктор без аргументов или с аргуметами, имеющими значения по умолчанию (default constructor). Компилятор вам нарисует такой один, если вы не определите конструкторов вообще никаких. На вид это будет просто ежик чернобыльский. Попытка создать внутри функции (я имею в виду в стеке) массив таких объектов наплодит вам массив дегенератов (это вне семантики ведущих указателей, мы же договариваемся, что без ведущих указателей такие объекты вообще не существуют). Так что не рискуйте.
2. Конструктор копии. Если вы не определите его, то компилятор нарисует свой. ТАКОЕ компилятору можно позволять только в крайнем случае, или перед пенсией, ибо по сравнению с этим чудовищем упомянутый ранее ежик просто Киану Ривз.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В учебно-методическом пособии рассматриваются основы языка программирования PL/SQL, реализованного в системе управления базами данных Oracle Database Server. Приводятся сведения о поддерживаемых типах данных, структуре программ PL/SQL и выполнении SQL-предложений в них. Отдельно рассмотрено создание хранимых в базах данных Oracle программ PL/SQL – процедур, функций, пакетов и триггеров.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассматриваются базисные теоретические основы, необходимые для построения компиляторов, основные технологические приемы и методы их реализации. В ней приведены различные варианты заданий для выполнения лабораторного практикума по курсу «Системное программное обеспечение», а также примеры выполнения этих заданий. В каждом примере подробно рассматриваются все особенности его выполнения, как на этапе подготовки необходимой математической базы, так и на этапе программной реализации. В лабораторных работах автор обращает внимание на основные сложности, связанные с ее выполнением, а также на возможные типичные ошибки и недочеты, дает рекомендации по возможностям программной реализации, отличным от кода, приводимого в примерах.Книга ориентирована на студентов, обучающихся в технических вузах по специальностям, связанным с вычислительной техникой.
Книга известного специалиста по программированию (Югославия), содержащая основы языка Пролог и его приложения для решения задач искусственного интеллекта. Изложение отличается методическими достоинствами — книга написана в хорошем стиле, живым языком. Книга дополняет имеющуюся на русском языке литературу по языку Пролог.Для программистов разной квалификации, специалистов по искусственному интеллекту, для всех изучающих программирование.
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.
В этой книге содержится описание базовых принципов функционирования платформы .NET, системы типов .NET и различных инструментальных средств разработки, используемых при создании приложений .NET. Представлены базовые возможности языка программирования C# 2005, включая новые синтаксические конструкции, появившиеся с выходом .NET 2.0, а также синтаксис и семантика языка CIL. В книге рассматривается формат сборок .NET, библиотеки базовых классов .NET. файловый ввод-вывод, возможности удаленного доступа, конструкция приложений Windows Forms, доступ к базам данных с помощью ADO.NET, создание Web-приложений ASP.NET и Web-служб XML.