|
Программирование >> Поддержка объектно-ориентированного программирования
Если статического контроля типов нет, приведенная выше ошибка обнаружится только при выполнении программы: пример динамическое контроля типа вместо статического; это не С++ Stack s; стек может хранить указатели на объекты произвольного типа void f () s.push ( new Saab900 ); s.push ( new Saab3 7B ); s.pop () -> takeoff (); прекрасно: Saab 37B - самолет cs.pop () -> takeoff (); динамическая ошибка: машина не может взлететь Для способа определения, допустима ли операция над объектом, обычно требуется больше дополнительных расходов, чем для механизма вызова виртуальных функций в С++. Рассчитывая на статический контроль типов и вызов виртуальных функций, мы приходим к иному стилю программирования, чем надеясь только на динамический контроль типов. Класс в С++ задает строго определенный интерфейс для множества объектов этого и любого производного класса, тогда как в Smalltalk класс задает только минимально необходимое число операций, и пользователь вправе применять незаданные в классе операции. Иными словами, класс в С++ содержит точное описание операций, и пользователю гарантируется, что только эти операции транслятор сочтет допустимыми. 1.5.3 Множественное наследование Если класс A является базовым классом для B, то B наследует атрибуты A. т.е. B содержит A плюс еще что-то. С учетом этого становится очевидно, что хорошо, когда класс B может наследовать из двух базовых классов A1 и A2. Это называется множественным наследованием. Приведем некий типичный пример множественного наследования. Пусть есть два библиотечных класса displayed и task. Первый представляет задачи, информация о которых может выдаваться на экран с помощью некоторого монитора, а второй - задачи, выполняемые под управлением некоторого диспетчера. Программист может создавать собственные классы, например, такие: class my displayed task: public displayed, public task текст пользователя class my task: public task { эта задача не изображается на экране, т.к. не содержит класс displayed текст пользователя class my displayed: public displayed а это не задача т.к. не содержит класс task текст пользователя Если наследоваться может только один класс, то пользователю доступны только два из трех приведенных классов. В результате либо получается дублирование частей программы, либо теряется гибкость, а, как правило, происходит и то, и другое. Приведенный пример проходит в С++ безо всяких дополнительных расходов времени и памяти по сравнению с программами, в которых наследуется не более одного класса. Статический контроль типов от этого тоже не страдает. Все неоднозначности выявляются на стадии трансляции: class task public: void trace (); ... class displayed public: void trace (); ... class my displayed task:public displayed, public task в этом классе trace () не определяется void g ( my displayed task * p ) p - > trace (); ошибка: неоднозначность В этом примере видны отличия С++ от объектно-ориентированных диалектов языка Лисп, в которых есть множественное наследование. В этих диалектах неоднозначность разрешается так: или считается существенным порядок описания, или считаются идентичными объекты с одним и тем же именем в разных базовых классах, или используются комбинированные способы, когда совпадение объектов доля базовых классов сочетается с более сложным способом для производных классов. В С++ неоднозначность, как правило, разрешается введением еще одной функции: class my displayed task:public displayed, public task ... public: void trace () { текст пользователя displayed::trace (); вызов trace () из displayed task::trace (); вызов trace () из task ... void g ( my displayed task * p ) p - > trace (); теперь нормально 1 .5.4 Инкапсуляция Пусть члену класса (неважно функции-члену или члену, представляющему данные) требуется защита от несанкционированного доступа . Как разумно ограничить множество функций, которым такой член будет доступен? Очевидный ответ для языков, поддерживающих объектно-ориентированное программирование, таков: доступ имеют все операции, которые определены для этого объекта, иными словами, все функции-члены. Например: class window ... protected: Rectangle inside; ... class dumb terminal : public window public: void prompt (); Здесь в базовом классе window член inside типа Rectangle описывается как защищенный (protected), но функции-члены производных классов, например, dumb terminal::prompt(), могут обратиться к нему и выяснить, с какого вида окном они работают. Для всех других функций член window::inside недоступен. В таком подходе сочетается высокая степень защищенности (действительно, вряд ли вы случайно определите производный класс) с гибкостью, необходимой для программ, которые создают классы и используют их иерархию (действительно, для себя всегда можно в производных классах предусмотреть доступ к защищенным членам). Неочевидное следствие из этого: нельзя составить полный и окончательный список всех функций, которым будет доступен защищенный член, поскольку всегда можно добавить еще одну, определив ее как функцию-член в новом производном классе. Для метода абстракции данных такой подход часто бывает мало приемлемым. Если язык ориентируется на метод абстракции данных, то очевидное для него решение - это требование указывать в описании класса список всех функций, которым нужен доступ к члену. В С++ для этой цели используется описание частных (private) членов. Оно использовалось и в приводившихся описаниях классов complex и shape. Важность инкапсуляции, т.е. заключения членов в защитную оболочку, резко возрастает с ростом размеров программы и увеличивающимся разбросом областей приложения. В $$6.6 более подробно обсуждаются возможности языка по инкапсуляции. 1.6 Пределы совершенства Язык С++ проектировался как лучший С , поддерживающий абстракцию данных и объектно-ориентированное программирование. При этом он должен быть пригодным для большинства основных задач системного программирования. Основная трудность для языка, который создавался в расчете на методы упрятывания данных, абстракции данных и объектно-ориентированного программирования, в том, что для того, чтобы быть языком общего назначения, он должен: - идти на традиционных машинах; - сосуществовать с традиционными операционными системами и языками; - соперничать с традиционными языками программирования в эффективности выполнения программы; - быть пригодным во всех основных областях приложения. Это значит, что должны быть возможности для эффективных числовых операций (арифметика с плавающей точкой без особых накладных расходов, иначе пользователь предпочтет Фортран) и средства такого доступа к памяти, который позволит писать на этом языке драйверы устройств. Кроме того, надо уметь писать вызовы функций в достаточно непривычной записи, принятой для обращений в традиционных операционных системах. Наконец, должна быть возможность из языка, поддерживающего объектно-ориентированное программирование, вызывать функции, написанные на других языках, а из других языков вызывать функцию на этом языке, поддерживающем объектно-ориентированное программирование. Далее, нельзя рассчитывать на широкое использование искомого языка программирования как языка общего назначения, если реализация его целиком полагается на возможности, которые отсутствуют в машинах с традиционной архитектурой. Если не вводить в язык возможности низкого уровня, то придется для основных задач большинства областей приложения использовать некоторые языки низкого уровня, например С или ассемблер. Но С++ проектировался с расчетом, что в нем можно сделать все, что допустимо на С, причем без увеличения времени выполнения. Вообще, С++ проектировался, исходя из принципа, что не должно возникать никаких дополнительных затрат времени и памяти, если только этого явно не пожелает сам
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |