Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 18
Текст из файла (страница 18)
У нас есть возможность добавить новую фигуру к системе, только если мы имеем исходные тексты каждой функции. Так как добавление новой фигуры связано с внесением изменений в код каждой важной операции над фигурами, оно требует большого мастерства и потенциально влечет появление ошибок в коде, управляющем другими (старыми) фигурами. Кроме того, выбор представления той или иной фигуры может быть сильна ограничен, так как требуется, чтобы (по крайней мере некоторые) представления вписывались в фиксированные границы, заданные определением общего типа 5йаре. 2.6.2. Иерархия классов Проблема в том, что не видно разницы между общими свойствами всех фигур (все они имеют цвет, их можно нарисовать н т, д.) и особыми свойствами фигур определенного типа (у окружности есть радиус, она вычерчивается специфическим способом и т.
д.). Выражение этол разницы и использование вытекающих отсюда преимуществ составляет основу объектно-ориентированного программирования. Языки, располагающие конструкциями, которые позволяют выразить и использовать указанную разницу, поддерживают объектно-ориентированное программирование. Другие языки — нет.
Механизм наследования (позаимствованный в С++ из Уйпш)а) предоставляет следующее решение. Сначала мы описываем класс, который определяет общие свойства всех фигур: с1азз Буаре ( Ро1пГ сел!ел Со1ог со1; 0- риЫ1с: Ро!пгтипеге )) ( ге!игл сеп!ег; ) по!а гпопе )Ро!и! 1о) ( сеп!ег = го; 1* ... *1 Йгаге )), ) эггГиа1 ио10 Й агп )) =0; э!гГиа1 по!г(го!а!е )!п! апп!е) =0; Как и в абстрактном типе Яасй из 4 2.5А, функции, интерфейс вызова которых может быть определен, а реализация — нет, объявлены виртуальными. В частности, реализации функций Й аж )) и го1а1е )) могут быть определены только для конкретных фигур, поэтому эти функции объявлены виртуальными.
При наличии такого определения мы можем написать общие функции для работы с векторами указателей на фигуры: ио!г!гп1а!е а!1 (иег1ог<$!гаре">й и, гпгапа!е) !/ поворачивает апеменпгы и О на апу!е эрпдугов эог )!и! ! = 0;! < в.к!хе )); -ы-1) и[1)->гогаге )апа!е); Для определения конкретной фигуры мы должны сказать, что нечто является фигу- рой, и указать особые свойства (в том числе определить впртуальныс функции): Глава 2. Обзор С++ е!аее С!гс!е. риЫ!с 8)ларе ( !а! гад!ие, риЫ!а оои1дгаю))(/'- *!) иоЫ го!а!е )!лй () //да функл!ия ничего не делает В терминах С++ мы скажем, что класс С1гс1е является производным от Ядаре, а класс Заире является базовым для класса С)гс1е.
Альтернативная терминология называет С!гс1е и Кларе подклассом и суперклассом (или надклассом) соответственно. Говорят, что производный класс наследует членгл от базового класса, поэтому применение н базового и производного класса в совокупности обычно называют наследооанием. Парадигма программирования теперь звучит так; В тех случаях, когда такой общности нет, достаточно абстракции данных.
Степень общности между типами, которую можно выделить, использовав наследование и виртуальные функции, является лакмусовой бумажкой для определения, применим ли объектно-ориентированный подход к данной задаче. В некоторых областях, таких как интерактивная графика, очевидно, что объектно-ориентированное программирование весьма полезно.
В других задачах, таких как классические арифметические типы и вычисления, основанные на них, похоже, трудно найти применение чему-то большему, чем абстракция данных, а средства, необходимые для поддержки объектноориентированного программирования, выглядят бесполезными. Выявление общности между типами в системе является нетривиальным процессом.
Степень общности, которою можно выделить, зависит от способа организации системы. Когда система проектируется, и даже когда только пишутся технические требования, нужно активно искать общность, Классы можно спроектировать так, чтобы использовать их как строительные блоки для других типов. Следует также проверять, не проявляют ли существующие классы сходства, которые можно выделить в базовый класс.
Объяснение того, что такое объектно-ориентированное программирование, без подробного рассмотрения конкретных конструкций языков программирования, см, в работах [Кегг, 19871 и 1ВоосЬ, 19941 1подробные ссылки приведены в б 23.6). Иерархии классов и абстрактные классы Я 2.5 4) дополняют, а не исключают друг друга Я 12.5). Вообще, парадигмы, приведенные здесь, имеют тенденцию взаимно дополнять и поддерживать друг друга. Например, и классы, и модули содержат функции, тогда как модули содержат и классы, и функции. Опытный разработчик применяет различные парадигмы по мере необходимости. 2.7. Обобщенное программирование Тому, кто захочет испольаовать стек, вряд ли всегда будет нужен именно стек символов.
Стек — более обгнее понятие, которое не зависит от определения символа. Следовательно, он должен быть представлен независимо. 2.7. Обобщенное программирование Вообще, если алгоритм можно выразить независимо от деталей представления и если это можно сделать приемлемым!с точки зрения накладных расходов) способом и без логических искажений, то так и нужно поступить. Парадигма программирования для этого стиля звучит так: 2.?.1.
Контейнеры Мы можем обобщить стек символов до стека элементов любого типа, объявив его как шаблон (те|ар!ате) и заменив конкретный тип сйаг на параметр шаблона. Например: !етр!а!в<с(а ив Т > с1авв $!асй ( Т*в; го! тах Мхв; !п11вр; риЬ!1с: с)авв ТУпдвтЯовв(); с1авв ОввгЯвт ( ); $1асИ(1п!в); //конструктор -$!асй (), // деструктор во!д ривй (л); Трор(); Префикс 1етр!а!е<с1авв Т> делает Т параметром объявления, которому этот префикс предшествует. Функции-члены можно определить аналогичным образом: !етр)а1е<с1авв Т> во)д $1ас!ч<Т>зривЬ(Т й ( (Т (!ор == тах виве) тяготи Оовг/)в!в (); о[!ор) - с; !вр = !вр в 1; !етр1аге<с!авв Т> Т$!ас)«Т>срор () ( (1 (1вр == О) Пивов опдвтЯотв (); !ор = !вр — 1; гвсигп в[!ор); ) При наличии этих определений, стек можно использовать следующим образом: $!асй<с)чаг> вс (200); // стек для 200 силвол,ов $!ас)«сотр1ех>вср!х(80); //стек для 30 комплексных чисел $!ас5<!!в!<т!» вй(45); //стек для 45 списков иелыхчисвл Глава 2.
Обзор С«» 78 ио1с~Я ( зараза ('с'); (Г (зсрар ()!= 'с') 1дгаг»Ва«1 рар (); зср(х.риза (сот р1ех (1, 2)); (Г (вар(хрор () ~= сотр1ех (1, 2)) 1агат Ваг( рар (); Точно также в качестве шаблонов можно определить списки. вектора, ассоциативные массивы и т. д, Класс, содержащий набор элементов некоторого типа, обычно называют классам-кантейпером или просто контейнером.
Шаблоны являются механизмом времени компиляции, поэтому их использование не влечет дополнительных накладных расходов во время исполнения по сравнению с «программированием вручную». 2.?.2. Обобщенные алгоритмы Стандартная библиотека С++ предоставляет множество контейнеров, кроме того пользователи могут написать свои собственные (главы 3, 17 и 18). Оказывается, что мы можем применить парадигму обобщенного программирования еще и для параметризации алгоритмов контейнерами. Например, мы хотим сортировать, копировать, осуществлять поиск в векторах, списках и массивах и при этом не желаем писать функции зог1 (), сору () и зеагса () отдельно для каждого контейнера, Мы также не хотим заниматься преобразованием к какой-нибудь одной конкретной структуре данных, допустимой для единственной функции сортировки.
Поэтому мы должны найти обобщенный способ определения контейнеров, который позволит нам обрабатывать контейнер, не зная точно, что это за контейнер. Один из подходов, а именно подход, принятый для контейнеров и нечисловых алгоритмов в стандартной библиотеке С++ (2 3.8, глава 18), состоит во введении понятия последовательности и манипулировании последовательностями посредством итератаров.
Вот графическое представление понятия последовательности: начало конец элементы: Д вЂ” [ ~— Последовательность имеет начало и конец. Итератор ссылается на элемент и предоставляет операцию, заставляющую его ссылаться на следующий элемент последовательности.
Концом последовательности является итератор, который ссылается на элемент, следующий за последним, Физическим представлением конца может служить «элемент-страж», но это не обязательно. В действительности, такое определение последовательности охватывает множество различных представлений, включая списки и массивы. Нам требуются стандартные обозначения таких операций, как «получить доступ к элементу через итератор» и «заставить итератор ссылаться на следующий элемент».