|
Программирование >> Поддержка объектно-ориентированного программирования
Большинство модулей (хотя и не все) лучше определять как пользовательские типы. 1.2.4 Пределы абстракции данных Абстрактный тип данных определяется как некий черный ящик . После своего определения он по сути никак не взаимодействует с программой. Его никак нельзя приспособить для новых целей, не меняя определения. В этом смысле это негибкое решение. Пусть, например, нужно определить для графической системы тип shape (фигура). Пока считаем, что в системе могут быть такие фигуры: окружность (circle), треугольник (triangle) и квадрат (square). Пусть уже есть определения точки и цвета: class point { /* ... */ }; class color { /* ... */ }; Тип shape можно определить следующим образом: enum kind { circle, triangle, square }; class shape point center; color col; kind k; представление фигуры public: point where () { return center; } void move ( point to ) { center = to; draw (); } void draw (); void rotate ( int ); еще некоторые операции Поле типа k необходимо для того, чтобы такие операции, как draw () и rotate (), могли определять, с какой фигурой они имеют дело (в языках вроде Паскаля можно использовать для этого запись с вариантами, в которой k является полем-дескриминантом). Функцию draw () можно определить так: void shape :: draw () switch ( k ) case circle: рисование окружности break; case triangle: рисование треугольника break; case square: рисование квадрата break; Это не функция, а кошмар. В ней нужно учесть все возможные фигуры, какие только есть. Поэтому она дополняется новыми операторами, как только в системе появляется новая фигура. Плохо то, что после определения новой фигуры нужно проверить и, возможно, изменить все старые операции класса. Поэтому, если вам недоступен исходный текст каждой операции класса, ввести новую фигуру в систему просто невозможно. Появление любой новой фигуры приводит к манипуляциям с текстом каждой существенной операции класса. Требуется достаточно высокая квалификация, чтобы справиться с этой задачей, но все равно могут появиться ошибки в уже отлаженных частях программы, работающих со старыми фигурами. Возможность выбора представления для конкретной фигуры сильно сужается, если требовать, чтобы все ее представления укладывались в уже заданный формат, специфицированный общим определением фигуры (т.е. определением типа shape). int i = 0; while ( i<size ) v [ i ] . rotate ( angle ); i = i + 1; Для определения конкретной фигуры следует указать, прежде всего, что это - именно фигура и задать ее особые свойства (включая и виртуальные функции): class circle : public shape int radius; public: void draw () { /* ... */ }; void rotate ( int ) {} да, пока пустая функция В языке С++ класс circle называется производным по отношению к классу shape, а класс shape называется базовым для класса circle. Возможна другая терминология, использующая названия подкласс и суперкласс для классов circle и shape соответственно. Теперь парадигма программирования формулируется так: Определите, какой класс вам необходим; предоставьте полный набор операций для каждого класса; общность классов выразите явно с помощью наследования. Если общность между классами отсутствует, вполне достаточно абстракции данных. Насколько применимо объектно-ориентированное программирование для данной области приложения 1.2.5 Объектно-ориентированное программирование Проблема состоит в том, что мы не различаем общие свойства фигур (например, фигура имеет цвет, ее можно нарисовать и т.д.) и свойства конкретной фигуры (например, окружность - это такая фигура, которая имеет радиус, она изображается с помощью функции, рисующей дуги и т.д.). Суть объектно-ориентированного программирования в том, что оно позволяет выражать эти различия и использует их. Язык, который имеет конструкции для выражения и использования подобных различий, поддерживает объектно-ориентированное программирование. Все другие языки не поддерживают его. Здесь основную роль играет механизм наследования, заимствованный из языка Симула. Вначале определим класс, задающий общие свойства всех фигур: class shape point center; color col; ... public: point where () { return center; } void move ( point to ) { center = to; draw(); } virtual void draw (); virtual void rotate ( int ); ... Те функции, для которых можно определить заявленный интерфейс, но реализация которых (т.е. тело с операторной частью) возможна только для конкретных фигур, отмечены служебным словом virtual (виртуальные). В Симуле и С++ виртуальность функции означает: функция может быть определена позднее в классе, производном от данного . С учетом такого определения класса можно написать общие функции, работающие с фигурами: void rotate all ( shape v [], int size, int angle ) повернуть все элементы массива v размера size на угол равный angle определяется степенью общности между разными типами, которая позволяет использовать наследование и виртуальные функции. В некоторых областях, таких, например, как интерактивная графика, есть широкий простор для объектно-ориентированного программирования. В других областях, в которых используются традиционные арифметические типы и вычисления над ними, трудно найти применение для более развитых стилей программирования, чем абстракция данных. Здесь средства, поддерживающие объектно-ориентированное программирование, очевидно, избыточны. Нахождение общности среди отдельных типов системы представляет собой нетривиальный процесс. Степень такой общности зависит от способа проектирования системы. В процессе проектирования выявление общности классов должно быть постоянной целью. Она достигается двумя способами: либо проектированием специальных классов, используемых как кирпичи при построении других, либо поиском похожих классов для выделения их общей части в один базовый класс. С попытками объяснить, что такое объектно-ориентированное программирование, не используя конкретных конструкций языков программирования, можно познакомиться в работах [2] и [6], приведенных в списке литературы в главе 11 . Итак, мы указали, какую минимальную поддержку должен обеспечивать язык программирования для процедурного программирования, для упрятывания данных, абстракции данных и объектно-ориентированного программирования. Теперь несколько подробнее опишем средства языка, хотя и не самые существенные, но позволяющие более эффективно реализовать абстракцию данных и объектно-ориентированное программирование. 1.3 Улучшенный С Минимальная поддержка процедурного программирования включает функции, арифметические операции, выбирающие операторы и циклы. Помимо этого должны быть предоставлены операции ввода- вывода. Базовые языковые средства С++ унаследовал от С (включая указатели), а операции ввода-вывода предоставляются библиотекой. Самая зачаточная концепция модульности реализуется с помощью механизма раздельной трансляции. 1.3.1 Программа и стандартный вывод Самая маленькая программа на С++ выглядит так: main () { } В этой программе определяется функция, называемая main, которая не имеет параметров и ничего не делает. Фигурные скобки { и } используются в С++ для группирования операторов. В данном случае они обозначают начало и конец тела (пустого) функции main. В каждой программе на С++ должна быть своя функция main(), и программа начинается с выполнения этой функции. Обычно программа выдает какие-то результаты. Вот программа, которая выдает приветствие Hello, World! (Всем привет!): #include <iostream.h> int main () cout << Hello, World!\n ; Строка #include <iostream.h> сообщает транслятору, что надо включить в программу описания, необходимые для работы стандартных потоков ввода-вывода, которые находятся в iostream.h. Без этих описаний выражение cout << Hello, World!\n не имело бы смысла. Операция << ( выдать ) записывает свой второй параметр в первый параметр. В данном случае строка Hello, World!\n записывается в стандартный выходной поток cout. Строка - это последовательность символов, заключенная в двойные кавычки. Два символа: обратной дробной черты \ и непосредственно следующий за ним - обозначают некоторый специальный символ. В данном случае
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |