Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
267
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

I 850 1 Часть iV * Расширенное использование C-i-f

Чтобы глобальные переменные стали видимыми в других файлах, используйте внешнее объявление. Это популярный метод обмена данными между функциями, реализованными в разных файлах.

Класс статической памяти С+Ч- представляет собой компромисс при проекти­ ровании. Это зарезервированное слово, которое может использоваться в несколь­ ких контекстах.

Когда глобальный объект определяется как static, память для него выделяет­ ся до начала выполнения main и освобождается при завершении программы. Но в отличие от обычной глобальной переменной статическую глобальную перемен­ ную невозможно сделать внешней в других файлах. Она видима только в одном файле.

Когда локальный объект определяется как статический, память для него также выделяется до начала выполнения main и освобождается по завершению програм­ мы. Статическая локальная переменная является закрытой в области действия — другие функции не могут осуш^ествлять к ней доступ. Однако переменная и ее зна­ чение суш^ествуют даже вне области действия. Переменная становится доступной при последуюш^ем вызове функции.

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

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

Если память, выделенная программе, не возвраш^ается, происходит утечка па­ мяти. За счет этого уменьшается объем памяти для доступной программы, особен­ но когда она работает круглосуточно. Работа программы завершается аварийно или она выдает неверные результаты.

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

C++ как объектно-ориентированный язык

C + + более совершенный язык по сравнению с С. Качество программы улуч­ шается за счет использования таких возможностей, как комментарии в конце строки, гибкие определения переменных в середине области действия, символиче­ ские константы для переменных и указателей, оператор области действия, приве­ дения типа функции между типами, операторы new и delete, параметры по умолчанию, параметры, передаваемые по ссылке, перегрузка функций и операций, строковые функции, библиотека iost ream объектов и операций ввода/вывода и др.

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

Классы C+ +

Основная цель классов С+Н предоставить программисту инструмент для связывания зависимых данных и операций. Это исключает недостатки использо­ вания автономных глобальных функций для реализации операций в отношении значений типов данных, определенных программистом.

Глава 19« Подученные уроки

[ 851 |

Данные определяются как элементы данных класса. Операции обозначаются как функции-члены класса. Синтаксис описания класса определяет элементы данных и функции-члены в области видимости класса, которая ограничивается открывающей и закрывающей фигурной скобками.

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

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

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

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

Конечно, выгоды объектно-ориентированного программирования не обеспе­ чиваются только благодаря использованию классов С+4-. Если функции-члены просто сохраняют значения элементов данных класса и извлекают их для исполь­ зования в клиентской программе, то использование этих функций в клиентской программе не облегчает ни чтение, ни изменение программы. Проектировщик класса должен передать обязательства в серверный класс вместо извлечения клиентской программы. Для этого к классу, реализующему операции, существен­ ные для достижения целей клиентской программы, добавляется функция-член.

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

Конструкторы, деструкторы и перегруженные операции

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

Конструктор автоматически вызывается сразу же после создания нового объекта класса, в стеке или в динамически распределяемой области памяти. В классе может быть несколько конструкторов. Все они имеют одно имя класса, но у них — разные списки параметров. От количества и типа параметров, которые определены клиентской программой при создании объекта, зависит, какой конст­ руктор вызывается.

щ 852 I Часть IV # Расширенное использование С+^

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

К важнейшим относятся конструкторы по умолчанию, конструктор копирова­ ния и конструкторы преобразования. Конструктор по умолчанию вызывается при создании объекта без передачи ему каких-либо параметров. Конструктор преобра­ зования задается, когда указывается только один параметр (обычно с типом одного из элементов данных класса).

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

Когда объекты класса динамически выделяют память в динамически распре­ деляемой области, передача этих объектов по значению создает проблемы цело­ стности данных. Программа завершается аварийно или ведет себя нестабильно (или выдает правильные результаты до тех пор, пока компьютер ведет себя ста­ бильно при использовании памяти. Чтобы предотвратить возможные проблемы с целостностью, конструктор копирования используется для реализации семан­ тики значений. Однако это замедляет работу программы. Лучше всего передать объекты класса по ссылке, а не по значению. Чтобы сделать передачу объектов класса по значению невозможной, конструктор копирования объявляется закры­ тым или защищенным.

Деструктор вызывается автоматически, перед тем как объект уничтожается. В деструкторе проектировщик класса определяет, каким образом ресурсы, выде­ ленные объекту за время его существования, возвращаются системе.

Деструкторы не содержит возвращаемые типы, они не возвращают значения и у них не может быть параметров. Следовательно, перегрузка деструкторов не­ возможна.

Для использования конструкторов и деструкторов надо знать способ выполне­ ния программы. В C + + (в отличие от других языков) отсутствует создание или уничтожение объекта с типом, определенным программистом. После создания объекта всегда вызывается конструктор, а перед уничтожением объекта вызыва­ ется деструктор.

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

Язык C + + одинаково интерпретирует объекты типов, определенных програм­ мистом, и переменные встроенных типов. В дополнение к перегрузке функций C + + поддерживает перегрузку операций. За счет этого клиентская программа может применять операции C + + к объектам класса.

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

Глава 19 • Полученные уроки

853

Композиция классов и наследование

Использование классов является наиболее эффективным методом. C + + под­ держивает реализацию взаимосвязей среди объектов, используя указатели и ссыл­ ки. Элементы данных одного класса указывают на объекты другого типа.

C + + также поддерживает композицию классов, в которой объекты одного класса используются как элементы данных другого класса. Другой важной связью между классами, которую можно реализовать в C+ + , является наследование класса. При наследовании один класс используется как базовый класс, а второй класс применяется как класс, порожденный из базового класса.

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

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

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

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

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

Синтаксис наследования повторно использует зарезервированные слова public, protected, private (и оператор двоеточия), но с другим значением. В открытом режиме наследования общедоступные, защищенные и закрытые элементы данных и функции-члены, определенные в базовом классе, остаются общедоступными, защищенными и закрытыми в объекте производного класса. В защищенном наследовании общедоступные базовые компоненты становятся защищенными в объекте производного класса и недоступны клиентской программе. В закрытом наследовании общедоступные и защищенные базовые компоненты являются за­ крытыми в объекте производного класса и недоступны для последующего порож­ дения.

I 854 I Часть IV # Расширенное ИСПОЛУ '. -:- ::ie C++

Это сложная система изменения прав доступа. Рекомендуем вам использовать открытое наследование, которое поддерживает естественные связи между класса­ ми. Значит, объект производного класса содержит все элементы данных и функ­ ции-члены производного класса.

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

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

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

Поскольку объект открыто порожденного класса имеет все свойства объекта базового класса, производный объект может использоваться в любом месте, где ожидается объект базового класса,— в присваивании, передаче параметра и манипуляции указателями. Преобразование из объектов производного класса в объекты базового класса является безопасным. Его можно выполнять всегда. При этом не требуется явное приведение типов.

Преобразование объекта базового класса в объект производного класса не является безопасным. Оно выполняется в случае, если имеется оператор явного присваивания или конструктор преобразования.

Преобразование указателя (ссылки) базового класса в указатель (ссылку) производного класса не является безопасным. Его нельзя выполнять неявно. Попытка сделать это помечается как синтаксическая ошибка. Если базовый указатель ссылается на объект производного класса, то преобразование в ука­ затель производного класса имеет смысл. Чтобы убедить компилятор принять такое решение, следует воспользоваться явным приведением типов. Если про­ граммист совершает ошибку (базовый указатель не ссылается на объект произ­ водного класса), то возникает ошибка в период выполнения.

Виртуальные функции и абстрактные классы

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

Каждый класс определяет функцию (используется то же самое имя, например update, и одинаковая сигнатура), которая выполняет сходную операцию (update) над объектами этих классов, но делает это по-разному для объектов разных классов (например, SavingsAccount и CheckingAccount).

Глава 19 * Подученные уроки

Ш1Ш

855

Цель проектирования заключается в достижении полиморфного эффекта. То есть вы должны отправить сообщение updateO каждому объекту в списке и вы­ звать либо сообщение updateO класса SavingsAccount, либо сообщение updateO класса CheckingAccount. При этом схожие классы порождаются из одного класса (например. Account).

Необходимо принять во внимание несколько ограничений. Порождение долж­ но быть открытым. Базовый класс должен иметь метод с тем же самым именем и той же сигнатурой (например, update), что и производные классы. Этот метод должен быть определен как виртуальный. Неоднородный набор объектов требу­ ется реализовать как список указателей базового класса, которые ссылаются на динамически распределенные объекты производных классов.

При соблюдении ограничений сообщение, отправленное базовому указателю, не вызывает метод базового класса, а задает метод производного класса, к кото­ рому принадлежит объект. Например, если базовый указатель ссылается на объект SavingsAccount, он вызовет метод updateO из класса SavingsAccount. Если он указывает на объект CheckingAccount, он вызовет метод updateO из класса CheckingAccount.

Многие программисты на C + + используют эту технологию. Такой метод кра­ сив и элегантен. Однако он не очень важен, поскольку обработка неоднородных списков не относится к наиболее распространенным задачам программирования.

Часто у базовых объектов такого семейства классов отсутствует задание в при­ ложении (например, приложение обрабатывает сберегательные счета и счета до востребования, но не занимается неопознанными счетами). ПоследуюЩ^ее расши­ рение данного метода преобразует базовый класс в абстрактный класс.

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

Шаблоны

Шаблоны представляют собой еще одно средство для повторного использова­ ния ПО. Если приложению требуется контейнерный класс, который содержит компоненты разных типов, советуем вам не повторять процесс проектирования для каждого составляющего типа — эти классы почти идентичны.

Шаблоны C + + позволяют программисту создавать класс, в котором тип ком­ понентов определяется как параметр класса. Когда задается экземпляр объекта этого типа, клиентская программа подставляет имя фактического типа, а компиля­ тор генерирует код объекта класса, в котором каждый экземпляр параметра клас­ са заменяется именем фактического типа, указанного клиентской программой.

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

Не совсем понятно, до какой степени отдельные приложения могут выиграть от использования шаблонов. Это относительно новая возможность, которая под­ держивается еще не всеми компиляторами, и опыт ее использования ограничен. Однако стандартная библиотека шаблонов C + + (STL) использует шаблоны для реализации таких структур данных, как списки, очереди, векторы, хеш-таблицы и т. д. Данные классы хорошо спроектированы и оптимизированы. Несомненно, использовать их в индивидуальных приложениях полезно. Именно поэтому важно понимать синтаксис создания экземпляра и использования объектов-шаблонов.

856 Часть IV » Расширенное исподьзо!

Исключительные ситуации

Исключительные ситуации также представляют собой новую возможность C++, созданную для поддержки раздельной обработки основного алгоритма и исключительных ситуаций. Такое распутывание запутанной программы умень­ шает сложность всей программы в целом.

Когда генерируется исключительная ситуация, создается объект встроенного или определенного программистом типа. Он отправляется от места возникновения исключительного состояния к месту, где исключительная ситуация должна будет обрабатываться. Это значение (или объект) передает информацию, которая по­ лезна для обработки исключительной ситуации (например, строку с сообидением об ошибке).

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

При обозначении исключительных ситуаций программист использует заре­ зервированное слово throw между заголовком функции и телом функции. За ним следует список типов, которые эта функция может сгенерировать (в круглых скобках, разделенные запятыми). Если определяется прототип функции, то заре­ зервированное слово throw и список вставляются между закрываюш.ей скобкой списка параметров и последней точкой с запятой.

При генерации исключительной ситуации программист использует зарезер­ вированное слово throw с одним параметром (значение встроенного типа или объект типа, определенного пользователем). Данный оператор применяется внут­ ри условного оператора. Проверяется некоторое условие, и если его результат будет true, то генерируется исключительная ситуация для уведомления об этом программы обработки исключительных ситуаций.

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

Отслеживая исключительные ситуации, следует использовать сложные син­ таксические конструкции — блок try с оператором, который сможет сгенериро­ вать исключительную ситуацию, и ряд блоков-"ловушек", которые следуют после блока try. Каждый блок-"ловушка" проектируется для обработки исключительной ситуации только одного типа.

Блок try является безымянным блоком, которому предшествует зарезервиро­ ванное слово try. Блок-"ловушка" — блок, перед которым располагается заре­ зервированное слово catch и список параметров только с одним параметром. Тип параметра catch является таким типом (встроенным или определенным про­ граммистом), для обработки которого спроектирован этот блок-"ловушка".

Если операторы в блоке try не генерируют исключительную ситуацию, выпол­ нение блока try завершается. Если один из операторов в блоке try генерирует исключительную ситуацию, часть программы от этого места до конца блока try пропускается. Она не будет выполняться, даже если исключительная ситуация отслежена и обработана.

Затем блоки-"ловушки" последовательно проверяются на совпадение типа в списке параметров и типа значения, сгенерированного исключительной ситуа­ цией. Если значение не совпадает, проверяется следуюш^ий блок-"ловушка". Если значение совпадает с типом параметра, блок-"ловушка" выполняется и делает то, что положено для обработки этой исключительной ситуации. Затем остальная часть блока-"ловушки" пропускается. Выполняется оператор, распдложенный после последнего блока-"ловушки".

Глава 19 • Полученные уроки

857

Если совпадение не найдено, то операторы, расположенные после последова­ тельности try-catch, пропускаются, а выполнение функции завершается. Перед завершением она генерирует исключительную ситуацию, которая не была отсле­ жена, и поиск продолжается в программе, вызвавшей эту функцию. Если блок- "ловушка", который обрабатывает исключительную ситуацию, обнаруживается, выполнение программы продолжается. Если нет, то поиск распространяется на mainO, а выполнение программы завершается.

В результате метод приводит к сложным образцам кодирования, потому что программист может поместить последовательность try-catch в любую функцию и в любой комбинации. Часто CJЮжнo понять, в каком месте обрабатывается иск­ лючительная ситуация и как программа продолжает выполнение. Действительно ли использование исключительных ситуаций упрош,ает обработку основной линии алгоритма и облегчает понимание обработки исключительных ситуаций?

Преимущество этого метода состоит в том, что место обнаружения ошибки может быть отделено от места исправления ошибки. Всю необходимую информа­ цию вы можете отправить от места обнаружения к месту исправления внутри объекта исключительной ситуации, сгенерированной оператором throw. Другое преимуш^ество заключается в том, что деструкторы вызываются для всех объек­ тов, удаленных из стека в процессе завершения функций, не содержаш,их соот­ ветствующий блок-"ловушку". Следовательно, если программа исправляет ошибку и продолжает выполнение, то она не пострадает от утечки памяти.

C++ И его конкуренты

C + + конкурирует со всеми языками программирования, от FORTRAN и COBOL до PL/I и Ada.

C++ и старшие языки

Конечно, даже у старших языков есть свои сильные стороны. К примеру, FORTRAN лучше всех подходит для реализации научных алгоритмов. Его библио­ теки серьезно облегчают работу программистов, решающих научные задачи.

COBOL и P L / I превосходят C+ + , когда требуется гибкое форматирование вывода. Программистам, использующим C+ + , придется изрядно потрудиться, чтобы достичь подобного результата, особенно при использовании библиотеки iostream. Если применяются старые стандартные библиотеки, часть программы, выполняющая форматирование вывода, может быть короче. Все же это довольно сложно и порождает ошибки.

Ada включает такие возможности, как параллельное программирование и па­ кеты для реализации простых объектов. В этом случае программист может реали­ зовать основные объектно-ориентрованные структуры.

Однако ни один из этих языков не поддерживает композицию, наследование и виртуальные функции. Объектно-ориентированные возможности дают C+ + превосходство над этими языками при написании крупных приложений

C++ и Visual Basic

Одним из интересных конкурентов C+ + , который становится все более попу­ лярным, является Visual Basic. За последние годы Visual Basic стал мощным и гибким средством программирования.

Возможности форматирования вывода, которые поддерживает Visual Basic и которые позволяют ему конкурировать с COBOL и PL/l, намного лучше воз­ можностей, поддерживаемых C++ . Visual Basic предоставляет программистам быстрый и легкий доступ к построению интерактивного ввода и вывода с исполь­ зованием графических интерфейсов пользователя (Graphical User Interfaces — GUIs). Чтобы достичь подобных эффектов, программистам на C + + потребуется

-.a^si^j

858 Часть IV • Расширенное а^спользование €«••+

изучить библиотеку для создания оконного интерфейса, а все библиотеки, имею­ щиеся сегодня на рынке, сложны для понимания и использования.

Кроме того. Visual Basic поддерживает некоторые объектно-ориентированные возможности, хотя его объектно-ориентрованные возможности не позволяют ему конкурировать с возможностями С+4-. Обучение на Visual Basic легче, чем на C+ + . Изучая Visual Basic, можно намного быстрее создавать осмысленные при­ ложения, чем на C+ + .

Однако объектно-ориентированные возможности C + + интегрированы в язык намного лучше, чем в Visual Basic, где они являются красивым, но чужеродным дополнением.

С-Н-Ь и С

Еще один язык, с которым C + + должен конкурировать сегодня, это его собст­ венный предшественник — язык программирования С.

Языку С отдают предпочтение программисты, разрабатывающие системы реального времени и встроенные системы. Программисты опасаются таких возможностей C+ + , как виртуальные функции, шаблоны, библиотека lost ream и исключительные ситуации. Они убеждены, что все это влияет на производитель­ ность и увеличивает размер выполняемой программы. Для систем реального вре­ мени и встроенных систем как производительность, так и размер исполняемых файлов имеют первостепенное значение. Именно поэтому C + + для такого вида приложений используется очень осторожно.

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

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

Нельзя быть полностью уверенным в том, что все описываемое является уни­ версальным. Однако несколько компаний, которые сопротивлялись переходу с С на C+ + , сталкивались с подобной ситуацией.

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

Полиморфные алгоритмы можно реализовать на любом языке. При их реали­ зации, например, на языке С, выделяется дополнительная память для сохранения информации о происхождении объекта, обрабатываемого программой. Программа тратит некоторое время на выяснение того, как интерпретировать этот объект. Использование C + + инкапсулирует данные сложности для программиста и не расходует напрасно дополнительные ресурсы.

К сожалению, многие компиляторы C + + выдают раздутый объектный код не­ зависимо от того, используют ли программисты шаблоны, библиотеки lost ream.

Глава 19 « Подученные уроки

859

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

C++ и Java

Сегодня язык Java наиболее опасный конкурент C + + . Java является дальним родственником C+ + . Подобно C+ + , он был создан как расширенное множест­ во С. Большая часть объектно-ориентированного синтаксиса Java позаимствована из C + + (с некоторыми изменениями).

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

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

Вы можете услышать о том, что в Java отсутствуют указатели, следовательно, исходная программа на Java намного проще программы на C++ . Программисты, которые отмечают это, просто не знают, о чем они ведут речь. В Java есть указате­ ли. Те, кто не верит, могут выполнить простую программу и увидеть сообщение "Исключительная ситуация — нулевой указатель" ("Null pointer exception"), ко­ торое появляется на экране перед аварийным прекращением программы.

Java содержит явную операцию new, подобную C + + . Однако в Java отсутствует явная операция delete. Вместо этого Java использует "сборку мусора". Это глав­ ное отличие от идеологии C/C+ + . Чем меньше времени тратится на отладку управления памятью, тем больше его остается для других алгоритмов.

Использование "сборщика мусора" вместе с интерпретацией при выполнении приводит к тому, что программы на Java работают намного медленнее, чем на C+ + . Несколько лет назад никто не стал бы использовать подобный язык. Но сегодня производительность уже не является самым важным критерием при оцен­ ке языков программирования. Переносимость, надежность и простота намного важнее (при условии, что язык поддерживает современный объектно-ориентиро­ ванный подход, основанный на программной инженерии).

Приложения Java являются переносимыми. Все типы данных Java имеют стан­ дартные размеры на всех машинах. Целое всегда имеет длину в 4 байта. Это не обязательно самый быстрый тип на рассматриваемой платформе, но программа выполняется одинаковым способом на всех машинах. Нельзя выбирать между типами данных со знаками и без знака. У числовых типов всегда есть знаки, а у логических и символьных типов их нет. Идентификаторы Java могут быть любой длины.

Java — "правильный" язык. Не разрешаются неявные приведения числовых типов. Явные приведения типов между числовыми типами допускаются, но не между числовыми типами и булевыми типами. В отличие от C/C++ реляционные и логические операции возвращают булевы значения true и false, а не 1 или 0. Неверное использование операции сравнения == как оператора присваивания = помечается как синтаксическая ошибка.

Соседние файлы в предмете Программирование на C++