Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 3883 / 149 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 17:

От Simula к Java и далее: основные ОО-языки и окружения

Расширения C

Трансформацию объектной технологии в 1980-х гг. от привлекательной идеи к промышленной практике во многом можно объяснить появлением и огромным коммерческим успехом языков, добавивших ОО-расширения к стабильному и широко распространенному языку С. Первой такой попыткой, привлекшей широкое внимание, был язык Objective-C, а самой известной - C++.

Эти расширения отражают два радикально различных подхода к проблеме проектирования "гибридных" языков, называемых так по той причине, что при расширении приходится сочетать ОО-механизмы с механизмами языка, основанного совсем на других принципах. (Примерами других гибридных языков являются Ada 95 и Borland Pascal.) Язык Objective-C при построении объектного расширения иллюстрирует ортогональный подход: добавляя ОО-слой к существующему языку, сохраняя при этом обе части как можно более независимыми. Язык C++ иллюстрирует подход слияния, сближая, насколько это возможно, концепции. Потенциальные преимущества каждого стиля ясны: ортогональный подход облегчает переход, избегая непредвиденных взаимных влияний, а подход слияния ведет к более согласованному языку.

Фундаментом успеха в обоих случаях был язык С, ставший к тому времени одним из доминирующих языков в промышленности. Призыв к менеджерам был понятен - превратить С-программистов в ОО-разработчиков без особого "культурного" шока. Моделью такого подхода, востребованной Бредом Коксом, была модель препроцессоров C и Fortran, например Ratfor, позволившая познакомить в 70-х гг. часть ПО сообщества с концепциями "структурного программирования", оставаясь в рамках привычного языка.

Objective-C

Созданный в корпорации Stepstone (первоначально Productivity Products International) Бредом Коксом (Brad Cox) язык Objective-C представлял ортогональное дополнение концепций Smalltalk к языку C. Это был базовый язык для рабочей станции и операционной системы NEXTSTEP. Хотя успех C++ отчасти затмил популярность этого языка, Objective-C все же сохранил активное сообщество пользователей.

Как и в Smalltalk, акцент делается на полиморфизм и динамическое связывание, но современные версии Objective-C предлагают статическую типизацию, а некоторых из них и статическое связывание. Вот пример синтаксис Objective-C:

= Proceedings: Publication {id date, place; id articles;}
    + new {return [[super new] initialize]}
    - initialize {articles = [OrderedCollection new]; return self;}
    - add: anArticle {return [contents add: anArticle];}
    - remove: anArticle {return [contents remove:anArticle];}
    - (int) size {return [contents size];}
=:

Класс Proceedings определяется как наследник Publication (Objective-C поддерживает только единичное наследование). Скобки вводят атрибуты ("переменные экземпляра"). Далее описываются подпрограммы; self, как и в Smalltalk, обозначает текущий экземпляр. Имя id обозначает для варианта без статической типизации общий тип всех не-С объектов. Подпрограммы, вводимые знаком +, являются "методами класса". Здесь таким методом является конструктор new. Другие подпрограммы, вводимые знаком -, являются нормальными "методами объектов", посылающими сообщения экземплярам класса.

Objective-C корпорации Stepstone оснащен библиотекой классов, первоначально построенных по образцу аналогов Smalltalk. Для NEXTSTEP также доступны многие другие классы.

C++

Язык C++ создан примерно в 1986 г. Бьерном Страуструпом в AT&T Bell Laboratories (организации, известной помимо других достижений разработкой Unix и C). Он быстро развивался и занял лидирующую позицию в промышленных разработках, стремившихся получить преимущества объектной технологии при сохранении совместимости с языком С. Язык остался почти полностью снизу вверх совместимым (корректная программа на С является в нормальных обстоятельствах корректной программой на C++).

Первые реализации C++ были простыми препроцессорами, преобразующими ОО-конструкции в обычный С, основываясь на технике, описанной в предыдущей лекции. Современные компиляторы, однако, являются "родными" реализациями C++. Теперь трудно найти компилятор С, становящийся одновременно компилятором C++ при включении специального параметра компиляции "C++ конструкции". Это один из показателей успеха. Компиляторы C++ доступны практически для большинства платформ.

Первоначально C++ представлял улучшенную версию C благодаря конструкции класса и строгой форме типизации. Вот пример класса:

class POINT {
    float xx, yy;
    public:
        void translate (float, float);
        void rotate (float);
        float x ();
        float y ();
        friend void p_translate (POINT*, float, float);
        friend void p_rotate (POINT*, float);
        friend float p_x (POINT*);
        friend float p_y (POINT*);
    };

Первые четыре подпрограммы задают привычный ОО-интерфейс класса. Как показывает этот пример, объявление класса содержит только заголовки подпрограмм, а не их реализации, определяемые отдельно. В связи с этим возникают вопросы области действия объявлений, важные и для компиляторов, и для читателей.

Другие четыре подпрограммы - это примеры "дружественных" подпрограмм. Это понятие характерно для C++ и дает возможность вызова подпрограмм C++ из нормального кода С. Дружественные подпрограммы нуждаются в дополнительном аргументе, задающем объект, к которому применяется операции. Здесь этот аргумент имеет тип POINT*, означающий указатель на POINT.

C++ предлагает широкий набор мощных механизмов:

  • Скрытие информации, включая способность скрывать компоненты от собственных наследников.
  • Поддержка наследования. Первоначальные версии поддерживали только единичное наследование, но теперь язык включает множественное наследование. Дублируемое наследование не обладает покомпонентной гибкостью. (В лекции, посвященной множественному наследованию, отмечалась важность этого свойства.) Вместо этого, разделяется или дублируется весь набор методов дублируемых предков.
  • По умолчанию предлагается статическое связывание, для динамического связывания функция должна быть определена как виртуальная. Подход C++ к этому вопросу подробно обсуждался.
  • Понятие "чистой виртуальной функции" напоминает отложенные методы.
  • Введена более строгая типизация, чем в языке С, но все же разрешающая преобразования типа (кастинг).
  • Сборка мусора обычно отсутствует (из-за приведений типа и использования указателей для массивов и подобных структур), хотя доступны некоторые инструменты для надлежаще ограниченных программ.
  • Из-за отсутствия автоматического управления памятью введено понятие деструктора для явного удаления объектов (понятие, дополняющее понятие конструктора класса - процедуры создания).
  • Обработка исключений не входила в первоначальное определение, но теперь поддерживается большинством компиляторов.
  • Введена некоторая форма попытки присваивания - downcasting.
  • Введена универсальность - "шаблоны". У них два ограничения: отсутствует ограниченная универсальность, и при конкретизации шаблона велика нагрузка на работу во время компиляции (известная в С++ как проблема).
  • Разрешена перегрузка операторов (знаков операций).
  • Введена инструкция assert для отладки, но отсутствуют утверждения для поддержки Проектирования по Контракту (предусловия, постусловия, инварианты классов), соединенные с ОО-конструкциями.
  • Библиотеки доступны от различных поставщиков, например библиотека MFC (Microsoft Foundation Classes).

Сложность

Размер C++ значительно вырос в сравнении с первой версией языка, и многие жалуются на его сложность. Для этого есть все основания. Для примера можно привести маленький отрывок из статьи учебного характера признанного авторитета в C и C++, председателя комитета по стандартам С Американского Института Национальных Стандартов (ANSI), автора словаря (Dictionary of Standard C) и нескольких уважаемых книг по C++. (Я надеялся научиться у него разнице между ссылкой и указателем в С++):

Хотя ссылка похожа на указатель, но указатель - это объект, занимающий память и имеющий адрес. Не константные указатели могут также быть применены для указания на различные объекты во время выполнения. С другой стороны, ссылка - это еще одно имя (псевдоним) объекта и сама не занимает памяти. Ее адрес и значение - это адрес и значение объекта, именуемого ею. И хотя вы можете иметь ссылку на указатель, но не можете иметь указатель на ссылку или массив ссылок, или объект некоторого ссылочного типа. Ссылки на тип void также запрещены.

Ссылки и указатель не взаимозаменяемы. Ссылка на int не может, например, быть присвоена указателю на int, и наоборот. Однако ссылка на указатель на int может быть присвоена указателю на int.

Клянусь, что я пытался понять. Я был почти уверен, что уловил суть, хотя, возможно, еще не готов к семестровому экзамену. (Приведите убедительные примеры случаев, когда уместно использовать: (1) только указатель, (2) только ссылку, (3) или то, или другое, (4) ни то, ни другое.) Потом я заметил, что пропустил начало следующего абзаца:

Из всего этого следует, что неясно, почему ссылки на самом деле существуют.

Защитники C++ несомненно заявят, что большинство пользователей могут игнорировать такие тонкости. Сторонники другой школы считают, что язык программирования, главный инструмент разработчиков ПО, должен основываться на разумном количестве надежных, мощных, полностью понятных концепций. Другими словами, каждый серьезный пользователь должен знать все о языке, и доверять всему. Но может быть невозможно примирить этот взгляд с самим понятием гибридного языка - понятием, в случае С++ непреодолимо напоминающем транскрипцию Листа восхитительной шубертовской "Фантазии странника", - трудно добавить целый симфонический оркестр и сохранить звучание фортепиано.

C++: оценка

Язык C++ мало кого оставляет безразличным. Известный автор Гради Буч называет его в интервью "Geek Chic", "языком моего предпочтения". Зато, по словам Дональда Кнута, Эдсгера Дейкстру " сама мысль о программировании на С++ сделала бы больным ".

В данном случае для C++ подходит ответ Юнии, данный Нерону в "Британике" Ж. Расина:

За что такая честь?
					За что такой позор?
					(пер. Э. .Л. .Линецкой)

Разочарование C++ является следствием преувеличенных надежд. Предыдущие обсуждения в данной книге тщательно анализировали некоторые наиболее противоречивые концепции языка - особенно в области типизации, управления памятью, соглашений по наследованию и динамического связывания - и показали возможность лучших решений. Но никто не может критиковать C++, полагая, что это первый и единственный ОО-язык. Язык C++ появился в нужное время и в нужном месте, ему удалось вне всяких сомнений поймать тот особенный момент в истории ПО, когда многие профессионалы и менеджеры были готовы использовать объектную технологию, но не были готовы отбросить существующую практику. C++ был почти магическим ответом: языка С достаточно, чтобы еще не напугать менеджеров, ОО уже достаточно, чтобы привлечь передовых специалистов. Уловив это обстоятельство, С++ просто следовал примеру С, который пятнадцать лет назад тоже был продуктом совпадающих возможностей - необходимости переносимого машинно-ориентированного языка, разра ботки Unix, появления персональных компьютеров, и наличия нескольких списанных машин в лаборатории Bell. Заслуга С++ в том, что он способствовал историческому подъему в развитии объектной технологии, представив ее всему сообществу, возможно не принявшему бы эти идеи в менее общепринятом облачении.

Тот факт, что C++ не является идеальным ОО-языком, о чем часто говорят авторы и лекторы и что ясно всем, изучавшим эти концепции, не должен умалять его заслугу. Не следует смотреть на C++, как будто бы ему суждено остаться основным инструментом программистского сообщества и в 21 веке, поскольку тогда он переживет себя. Тем временем С++ восхитительно играет свою роль - роль переходной технологии.

Михаил Широков
Михаил Широков
Россия, Москва, МГУПИ