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

17

ООП на C++

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

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

Требования к языку ООП

Характеристики языка ООП

• Инкапсуляция с сокрытием данных: возможность отделять внутреннее со­стояние и поведение объекта от его внешнего состояния и поведения.

• Расширяемость типов: возможность добавлять определяемые пользовате­лем типы для расширения набора собственных типов.

• Наследование: возможность создавать новые типы, импортируя или ис­пользуя повторно описания существующих типов.

• Полиморфизм и динамическое связывание: способность объектов отвечать за интерпретацию вызовов функций.

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

Типичные процедурные языки программирования, такие как Pascal и С, обладают ограниченными формами расширяемости типов и инкапсуляции. Оба языка имеют типы указателей и записей, предоставляющие данные свойства. Дополнительно в С имеется специальная ориентированная на файлы схема "закрытости" (privacy) на ос­нове объявлений static в области видимости файла. Такие языки, как Modula-2 и Ada, имеют более законченные формы инкапсуляции, а именно, модули и пакеты соот­ветственно. Эти языки позволяют пользователям легко строить АТД и предоставляют значительную библиотечную поддержку для множества прикладных областей. Такой язык, как чистый LISP, поддерживает динамическое связывание. Элементы ООП были доступны в разных языках на протяжении по крайней мере двадцати пяти лет.

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

Существенной была также легкость перехода от С к C++. В отличие от PL/1, кото­рый служил истоком для FORTRAN и COBOL, и Ada, из которого вышел Pascal, для C++ язык С является почти собственным подмножеством. А раз так, от основного уста­новленного кода на С не надо отказываться. Для остальных языков необходим нетриви­альный процесс преобразования для изменения существующего кода на языке-предке.

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

АТД в не-ООП языках

Существующие языки и методики поддерживают большую часть методологии ООП с помощью комбинирования свойств языка и дисциплины программирования. Дис­циплина программирования и общепринятые в сообществе разработчиков соглаше­ния действительно работают. В не-ООП языке возможно создание и использование АТД. Тремя примерами на С служат псевдотипы: строковый, булевский и файловый. Они являются псевдотипами (а не типами) в том смысле, что не обладают теми же привилегиями, что и собственные типы. Глядя на эти примеры можно лучше понять ограничения расширяемости типов в не-ООП контексте.

Булевский тип в С представлен неявно. А именно, логические выражения принима­ют нулевые значения за false, а ненулевые — за true. Поскольку ноль является универ­сальным значением, которое доступно для всех типов, ноль по соглашению использует­ся в качестве «охранного» (sentinel) значения. Ноль, используемый для представления конца списка, является идиомой при обработке данных, основанной на указателях.

while (р) { //р == 0 нулевой указатель (NULL)

• • • • • //обработка списка

р = р -> next; //переход

}

Часто для обеспечения лучшей документированности явно используются перечис­лимые типы:

enum boolean { FALSE, TRUE };

boolean search (int table[], int x, int & where)

{

where = -1;

for (int i = 0; i < N; ++i)

if (x = = table[i]) {

where = i ;

break;

}

return boolean (where != -1);

}

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

Файловый тип основан на использовании stdio.h. Тип структуры (зависящий от системы) определен с именем FILE. В stdio.h представлены такие функции, как от­крытие, закрытие и поиск файла. Эти процедуры ожидают в качестве параметров указатели на файл. Конкретные члены структуры не подвергаются непосредствен­ным манипуляциям, если программист придерживается данных соглашений. Опять-таки, С весьма успешно применялся для написания операционных систем и кода, манипулирующего файлами.

Подобные удачи не утверждают статус-кво. Напротив, они «выступают» за встра­ивание ООП в язык таким образом, чтобы гарантировалось, что библиотечные согла­шения не нарушатся.

Заметьте, что в C++ имеется булевский тип, а именно bool; стандартный строко­вый тип string, определенный в файле стандартной библиотеки string, и более удачная обработка файлов, определенная с помощью fstream.

Клиенты и производители

Чтобы полностью понять парадигму ООП, мы должны рассмотреть весь процесс про­граммирования (кодирования) как занятие, ответственность за которое разделяется и распределяется. Мы используем термин клиент (client) для обозначения пользователя класса и термин производитель (manufacturer) — для поставщика класса.

Клиент класса предполагает некое приближенное соответствие какой-то

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

Черный ящик в понимании клиента

• простой в использовании, легко понимаемый и привычный

• дешевый, эффективный и мощный

• может быть составной частью системы (может восприниматься системой как компонент)

Черный ящик в понимании производителя

• легко повторно использовать и изменять, но трудно использовать непра­вильно и сложно воспроизвести

• дешевый, эффективный и мощный

• выгодный для производства с большой базой клиентов

Производитель сражается за клиента, реализуя АТД-продукт, имеющий приемле­мую цену и эффективность. Производитель заинтересован в сокрытии деталей раз­работки. Такое сокрытие значительно упрощает все то, что производитель должен «объяснять»» клиенту. Это дает ему свободу для дальнейших внутренних усовершен­ствований продукта, которые не влияют на особенности использования продукта клиентом. Оно защищает клиента от опасного, пусть даже непреднамеренного иска­жения продукта.1

Структуры и обычные функции в С позволяют строить полезные АТД, но они не поддерживают разграничения между клиентом и производителем. Клиент имеет до­ступ к внутренним деталям и может модифицировать их нежелательным образом. Рассмотрим стек, представленный в качестве массива с целой переменной top. В С клиент такого стека может извлечь внутренний член массива, используемого для представления стека. Это нарушает абстракцию LIFO (Last-In-First-Out, первым во­шел — последним вышел), которую реализует стек.

Инкапсуляция объектов предотвращает подобные нарушения. Схема сокрытия данных, которая ограничивает доступ к деталям реализации для всех, кроме произ­водителя, гарантирует клиенту соответствие абстракции АТД. Закрытые члены скрыты от клиентской программы, а открытые доступны ей. Можно изменить пред­ставление скрытых данных, но не доступ к открытым членам и их функциональ­ность. Если сокрытие данных произведено правильно, клиентская программа не нуждается в изменениях, когда модифицируется представление скрытых данных.

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

Повторное использование кода и наследование

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

Наследование влияет на разработку программного обеспечения в целом. Оно пре­доставляет каркас, в котором фиксируются концептуальные элементы, ставшие предметом пристального внимания припостроении и использовании систем. Напри­мер, InterView представляет собой библиотеку C++, которая поддерживает построе­ние графического интерфейса пользователя. Главные категории объектов включают интерактивные объекты, текстовые объекты и графические объекты. Эти категории легко комбинируются, позволяя создавать различные приложения, например, систе­мы автоматизированного проектирования — САПР (computer-aided design — CAD), броузеры или редакторы WYSIWYG1.

Методология объектно-ориентированного проектирования

1. Выбери надлежащую совокупность АТД.

2. Спроектируй взаимосвязи между АТД, применяя наследование для исполь­зования общего кода и интерфейса.

3. Применяй виртуальные функции для обработки родственных объектов дина­мически.

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

Полиморфизм

Полиморфизм — это джин ООП, получающий от клиента приказы и верно интерпре­тирующий его желания. Полиморфная функция имеет множество форм. По Карделли и Вегнеру (Cardelli and Wegner, 1985) мы различаем:

Типы полиморфизма

1. Принудительное приведение (coercion) (ad hoc полиморфизм): функция или оператор работает с несколькими различными типами, преобразуя их значения к требуемому типу. Примером в ANSI С служит преобразование при присваи­вании арифметических типов во время вызова функции.

а / b //тип частного обуславливается

//собственными (встроенными в ядро) //принудительными приведениями

2. Перегрузка (ad hoc полиморфизм): функция вызывается на основе ее сигнату­ры, то есть списка типов аргументов. В С оператор целого деления и оператор деления с плавающей точкой различаются. Выбор варианта оператора деления основан на типах параметров.

cout << а //перегрузка функции

3. Включение (inclusion) (чистый полиморфизм): тип является подтипом другого типа. Функция, пригодная для базового типа, будет работать и с подтипом. Та­кая функция может иметь различные реализации, которые вызываются с уче­том выяснения подтипа на этапе выполнения. .

р -> draw ( ) //вызов виртуальной функции

4. Параметрический полиморфизм (чистый полиморфизм): тип остается неопре­деленным, а позже инстанцируется. В C++ это обеспечивается обобщенными указателями и шаблонами.

stack <window*> win[40]

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

Полиморфизм непосредственно содействует принципу черного

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

Сложность языка

За все свои достоинства C++ платит существенную цену: сложность языка весьма ощутима. Это приводит к дополнительным затратам на обучение и едва уловимым ошибкам. Стремительное развитие C++ при столь широком его распространении практически беспрецедентно. Язык С — небольшой и элегантный. Синтаксис C++ похож на С, но семантика первого сложнее. Чтобы оценить эти трудности, в следую­щей таблице приведены для сравнения некоторые характеристики языков Pascal, Modula-2, Modula-3, C++ и Ada.

Сложность языка

Язык Ключевые слова Инструкции Операторы Страницы

Pascal 35 9 16 28

Modula-2 40 10 19 25

Modula-3 53 22 25 50

C 29 13 44 40

C++v1.0 42 14 47 66

C++v3.0 1990 48 14 52 155

Ada 1980 63 17 21 241

C++ ANSI 1995 62 15 54 650

Соседние файлы в папке Тельминов (мб)