Программирование >>  Решение нетривиальных задач 

1 ... 43 44 45 [ 46 ] 47 48 49 ... 77


6 Эта цитата является отрывком из статьи, размещенной Страуструпом в телеконференции BIX в декабре 1992 г. Полностью статья опубликована в книге Мартина Хеллера Advanced Win32 Programming (New York: Wiley,1993), pp.72-78.

Си++ посредством оператора new, который в действительности является функцией глобального уровня. Фактически вы можете смотреть на глобальный уровень Си++, как на функциональный эквивалент object в Smalltalk. Хорошая иерархия классов Си++ представляет собой обычно коллекцию иерархий меньшего размера. Процитируем такого авторитета, как самого Бьярна Страуструпа - создателя Си++ - по этому поводу6 :

Смысл здесь заключается в том, что те стили, которые подходят и хорошо поддерживаются в Smalltalk, не обязательно подходят для Си++. В частности, рабское следование стилю Smalltalk в Си++ ведет к неэффективным, уродливым и с трудом сопровождаемым программам на Си++. Причина в том, что хороший Си++ требует проекта, который извлекает преимущества из системы статических типов Си++, а не борется с ней. Smalltalk поддерживает систему динамических типов (и только) и эта точка зрения, переведенная на Си++, ведет к чрезвычайно ненадежному и уродливому приведению типов.

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

Одной из больших проблем плохо организованных иерархий является превышение багажной нормы. Базовые классы должны иметь поля для поддержки возможностей, реализуемых различными обработчиками. Если производный класс не использует такую возможность, тогда его объект распространяет всюду связанные с ним накладные расходы, не давая выгод. Это одна из проблем иерархии в стиле Smalltalk, имеющей одну вершину в виде общего объекта. Все поля, помещенные в него вами (и все ячейки в таблице виртуальных функций), будут переняты каждым объектом в системе, независимо от того, использует объект эти поля, или нет.

Лучшим способом избежать этой проблемы является использование множественного наследования для реализации классов-смешений. Вот как



смешение работает. Возвратившись к нашему примеру с employee, вы могли бы реализовать его в виде системы классов следующим образом:

class employee

содержит всю информацию, общую для всех служащих: фамилия, адрес и т.д.

class manager : public employee

добавляет информацию, специфичную для управляющего,

такую, как список подчиненных служащих. Управляющий тоже

является служащим, поэтому применимо наследование

database list of managed emploees;

class peon : public employee

добавляет информацию, специфичную для поденщика manager *this boss;

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

class storable; сохраняем1й

class employee : public storable { /* ... */ } class manager : public employee { /* ... */ } class peon : public employee { /* ... */ }

Например, метод add() класса database мог бы получать указатель на объект storable в качестве своего аргумента. Таким способом любой объект storable (или объект, производный от storable) может быть добавлен в database без необходимости модифицировать что-либо в программе, в состав которой входит класс database.

Все кажется правильным до тех пор, пока мы реально не взглянем на то, как используются классы. Давайте скажем, что это средняя фирма, где число управляющих относится к числу поденщиков как 100 к 1. Однако списка управляющих нет, есть лишь список поденщиков. Тем не менее, каждый manager обладает излишней сохраняемостью, которая никогда не используется. Решим эту проблему при помощи множественного



7 Не путайте этот процесс с объединением. У mother нет поля parent, скорее та часть mother, которая определена на уровне базового класса, изображается как компонент parent .

наследования.

class storable;

class employee { /* ... */ }

class manager : public employee { /* ... */ }

class peon : public employee, public storable { /* ... */ }

Проблема здесь в том, что эта сохраняемость является атрибутом объекта. Это не является базовым классом в стандартном смысле типа круг является фигурой , а скорее - поденщик является сохраняемым . Здесь важна замена существительного на прилагательное. Базовый класс, который реализует свойство типа сохраняемости, называется классом-смешением, потому что вы можете примешивать это свойство к тем классам, которым оно нужно, и только к этим классам. Хороший метод распознавания этих двух употреблений наследования состоит в том, что имя класса-смешения обычно выражено прилагательным (сохраняемый, сортируемый, устойчивый, динамический и т.д.). Именем настоящего базового класса обычно является существительное.

Вследствие природы Си++ во всех учебниках рассматривается несколько проблем с множественным наследованием, большинство из которых вызывается ромбовидной иерархией классов:

class parent {}; родитель

class mother : public parent {}; мать

class father : public parent {}; отец

class child : public mother, public father {} потомок

Здесь имеется две трудности. Если у parent есть метод для укладывания спать с названием go to sleep(), то вы получите ошибку, попытавшись послать такое сообщение:

child philip; Филипп - потомок

philip.go to sleep(); Филипп, иди спать!

Проблема состоит в том, что в объекте child на самом деле два объекта parent. Запомните, что наследование просто добавляет поля (данные-члены) и обработчики сообщений (функции-члены). Объект mother имеет компонент parent: он содержит дополнительно к своим собственным все поля parent.7 То же самое относится и к father.



1 ... 43 44 45 [ 46 ] 47 48 49 ... 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика