Объектно-Ориентированное программирование

34318
знаков
0
таблиц
0
изображений

I. Развитие языков программирования

Определения:

ANSI – American National Standards Institute - Национальный Институт Стандартизации США

ISO - International Organization for Standardization - Международная организация по стандартизации

Цель развития языков программирования - более рациональная разработка ПП.

Схема развития:

Коды процессора а assembler а языки высокого уровня (ЯВУ)
Сначала из истории:

При разработке процессоров(П)/микропроцессоров – для каждого П разрабатывается набор команд, полный набор насчитывает ~150 команд: арифметика, логика, работа с памятью, ввод и вывод.

Команда для процессора – это цифровой код команды и операнд (операнды):

ячейки памяти, регистры, порты ввода/вывода...

Коды процессора – набор в цифровом коде команд процессора и их параметров, например, команды: занесение значения на регистр, вывод с регистра по адресу памяти, сложение, чтение байта из порта ввода, запись байта в порт вывода …
Именно коды процессора содержит исполняемый файл программы файл (*.exe )

Разработка программ в кодах была характерна для самых первых ВМ – это очень неудобно для человека-программиста.

Assembler – низкоуровневый язык программирования, разработанный для конкретного процессора.

Assembler использует мнемоническое обозначение кодов команд процессора и переменных памяти, что облегчает процесс программирования по сравнению с кодированием:
JUMP - переход, ADD - сложение, IN - ввод, OUT – вывод, и т.д. для всех команд процессора.
Assembler позволяет использовать весь набор команд процессора и напрямую работать с регистрами.
Используется там, где необходима высокая эффективность: ядро ОС, драйверы, программы, работающие в реальном времени.

Недостатки – высокая трудоемкость разработки, привязка программы к конкретному типу процессора.

Языки высокого уровня – FORTRAN, ALGOL, COBOL, PL/I, ADA, Prolog, PASCAL, C, C++, Perl, JavaScript, ASP, PHP, Java, С#, SQL…

ЯВУ не зависят от архитектуры компьютера, ориентированы на эффективную разработку ПП, обеспечивают быструю разработку и надежность ПО.
ЯВУ выполняются на любом компьютере, для которого реализован компилятор данного языка программирования.

Среди ЯВУ есть специализация: научные расчеты (FORTRAN), для обучения (ранний Basic, Pascal), экономические рачеты (COBOL), работа с БД (dBase, FoxPRO, SQL) ,
целое семейство сравнительно молодых языков для Internet (JavaScript, ASP, PHP),
языки системного программирования (ранний С, assembler’ы).
Некоторые языки считаются универсальными (поздний Pascal (Delphi), C/C++)

II. Развитие технологий разработки программ
Схема:

Низкоуровневое программирование (коды, Assembler’s) а
аПроцедурное/Cтруктурное программирование (Algol, Pascal, C) а

аООП (C++,Object PASCAL, Java, C#…) а

а…(что дальше?)

Вспоминаем, как обстояло дело с разработкой программ в 60-е – 70-е годы,
технологическая ступенька называлась:

Процедурное программирование

Основная идея – выделение части кода в отдельную процедуру (подпрограмму, функцию) (SUBROUTINE в FORTRAN, PROCEDURE и FUNCTION в PASCAL).

Будем считать для нашего курса понятия процедура, подпрограмма и функция синонимами – так в Си есть только функция.
Функция обозначается именем, при вызове ей передается список параметров (возможно, пустой), после выполнения она возвращает управление в точку вызова и, возможно, возвращает результаты работы (вычисленные значения, код завершения).

Пример:
real sin(real x){

// здесь реализация

}

В процедурных языках поддерживается вызов процедур/подпрограмм/функций, который обычно включает такие механизмы:

передача параметров в процедуру и возврат значений,

рекурсивность, т.е. возможность процедуры вызывать саму себя (известная задача о Ханойской башне),

хранение подпрограмм в отдельных библиотеках.


Далее, технологическая ступенька начала 70-х годов:
Структурное программирование – основные положения:
любую программу можно написать, пользуясь ограниченным набором базовых конструкций (здесь схемы основныхконструкций структурного программирования, Павловская, стр 39):

Последовательность операторов/блоков,

Ветвление или выбор if, if..else, swith-case

Циклы: с постусловием (do while), цикл с предусловием (while)


Каждая из базовых конструкций имеет один вход и один выход.

“Правильная” структура программы (блока программы, подпрограммы), имеет один вход и один выход.

Причем, любую программу можно и нужно писать без использования оператора GoTo, который очень запутывает структуру программы (аналог – команда перехода JMP в assembler).
Для исключения оператора GoTo достаточно применять базовые конструкции “цикл” - не всегда это удобно, но почти всегда оправдано.

Одновременно с определением набора базовых конструкций была осознана необходимость предварительного анализа данных и конструирования структур данных на начальной стадии разработки программы.

Следование этим правилам при разработке программ и означало применение структурного подхода к программированию. Эти правила ограничивали “свободу” программистов (по сравнению с assembler и Fortran), но позволяли писать более понятные, простые, надежные программы. Улучшился контроль над кодом, стала возможна реализация более крупных проектов.

На базе принципов структурного программирования (СП) был создан новый, элегантный язык PASCAL (примерно 1968 г, Никлаус Вирт)
Проанализируем программирование на PASCAL с позиций СП.
Базовые конструкции СП внесены в язык: IF..[ELSE], CASE, WHILE, REPEAT..UNTIL.
Для описания сложных типов данных в языке есть массивы, записи (RECORD), множества.

!Важно:

Структура программы на Pascal состоит из разделов:

Uses - включение модулей

Const – раздел констант

Type – описание новых типов данных (конструирование)

Var – объявление (выделение памяти под реальные переменные)

Процедуры – реализуют автономные фрагменты вычислений

Главная программа – Реализует полный алгоритм решения задачи.

PASCAL жестко навязывал программистам использование стиля СП – это был переход на новый технологический уровень в разработке программ общего назначения (обработка данных, научные расчеты) – это был технологический прорыв.
Пожалуй, все последующие реализации ЯВУ, включали конструкции СП.
Разработка в стиле СП имеет накладные расходы (по сравнению с assembler), код программы получается больше за счет избыточных проверок в циклах и отсутсвия GoTo – это был основной аргумент противников СП (в основном, противниками были программисты на assemblere).


Cтруктурное программирование на С/С++

На прошлом занятии говорили о СП, акцентировали основные положения:
процедурное П, П с исп. базовых конструкций СП, структура программы на PASCAL.

Сложнее всего следовать стилю СП было на assembler’s.

Язык С(Си) создан в конце 60-х - начале 70-х для разработки системного ПО в рамках проекта ОС Unix (Деннис Ричи – один из авторов).

С включает конструкции СП, в нем реализован механизм построения сложных структур данных.

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

С дает большую свободу действий, но требует от программиста большей самодисциплины.
Многие программисты на assembler в 80-е годы перешли на C.

В настоящее время С – универсальный язык программирования для разработки системного ПО (Unix и Windows), графических интерфейсов, сложного прикладного ПО (например, СУБД) - задач, где необходима эффективность выполнения программ.

С – распространен, компиляторы реализованы в большинстве ОС.

В каждой реализации UNIX есть компилятор С/С++ как важная часть ОС.
C++ является объектно-ориентированным расширением языка C.

Первая широко известная реализация принципов ООП – это описание языка С++, начало 80-х, автором является Бьярне Страуструп.

Кроме компиляторов в каждой Unix-подобной системе распространены компиляторы
Borland C++ и Microsoft C++ для платформы Windows,

В 1997 г был принят международный стандарт ANSI C/C++ - итог 20-тилетнего развития

Существующий стандарт ANSI C++ - это классическое описание ООП.

Сейчас язык С++ является языком публикаций по вопросам ООП.

Практикум на С/С++:
Фактически С++ содержит 2 языка:
Полностью включает низкоуровневый Си, поддерживающий конструкции СП, и, собственно, С++ (Си с классами) – язык объектно-ориентированного программирования (ООП).

Мы находимся сейчас на технологической ступени структурного программирования, поэтому начинаем с Си:

Знакомство с С, некоторые конструкции СП:


0.Программа выводит на экран строку "Hello? World".

#include // подключение заголовочного файла, в котором описаны функции i/o printf и getc

void main(){ // в программе всегда д.б. функция main, с нее начинается выполнение

printf("Hello, World!");

getc();

}

1.// Комментарий до конца строки

/* Комментарий много-

строчный. В С/С++ отличаются прописные и строчные буквы.

*/


2.Операторные скобки {} задают программный блок.


int i=5; // выделение памяти и присваивание значения
{

int i=7;

..

if(iПодготовка текста программы в редакторе

-> Запуск компилятора

->Возврат:Анализ и исправление ошибок

->Получение загрузочного модуля

-> Выполнение загрузочного модуля

-> Возврат:Анализ и исправление ошибок

->… и так далее


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


Не путать интегрированную среду и компилятор языка программирования!

Компилятор – отдельная программа для преобразования исходного текста в исполняемый код программы.
Компилятор запускается на одном из этапов разработки.
Он существует отдельно от IDE, и может запускаться из командной строки (Unix).
Интегрированная среда не является обязательной – возможна разработка ПО отдельными инструментами: редактор текста, компилятор, редактор связей, отладчик.


Современные IDE предлагают также визуальное конструирование для оконного интерфейса и создания скелетного кода программы. Пример: обработка события нажатия кнопки.


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

Освоение современной среды программирования сравнимо по сложности с освоением нового языка программирования.

Borland IDE – это современная профессиональная среда разработки, весьма сложная в освоении.


Начало работы в IDE:

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


Первоначально будем создавать программы, работающие в стиле DOS (WIN32) – текстовый режим консоли.Их называют также console applications.

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

Создание программ с графическим интерфейсом Windows – в перспективе, на следующей дсциплине.


Последовательность действий в IDE по созданию приложеня Console application:
Меню File->New-> Console Wizard - создание проекта (создается скелетная схема проекта, заготовка функции main), далее
->Собственно разработка текста программы

->Запуск на компиляцию – выявление ошибок – сначала потребует сохранить файлы
-> если компиляция проходит, то –> создание загрузочного модуля (ЗМ)

–> выполнение ЗМ с возможностью отладки.

Типы файлов (расширения), создаваемые системой IDE в проекте:


*.cpp, *.h - исходные тексты программы, исходные модули; этим файлам необходимо обеспечить максимальную защиту.

*.bpr, *.bpg, *.bpf – файлы проекта и группы проектов; (желательно сохранять, но проект легко можно собрать и заново из исходных модулей)

*.~cpp, *.~h *.~bpr - backup files (со знаком "тильда") – содержат копию исходного текста/проекта, предшествующую последней корректировке.

*.obj – объектные модули, промежуточный формат, создаются каждый раз при компиляции исходных модулей (опция настройки Intermediate output)

*.tds – временный файл отладчика, обычно это самый большой файл в проекте.

*.exe – загрузочный модуль, исполняемый модуль (опция настройки Final output)


Рекомендации по управлению файлами проекта:


Необходимо обеспечить сохранность исходных файлов проекта *.cpp, *.h, *.bpr, *.bpg, *.bpf.
Желательно располагать файлы каждого проекта в отдельной директории.
Необходимо хранить одну или более предыдущих версий работы (для этого создается специальная директории архива, традиционное название - BACUP).

Желательно перенаправить промежуточный вывод *.obj, и результирующий вывод *.tds, *.exe во вспомогательные директории на локальном диске, т.е. не смешивать с исходными текстами:
Project->Options->Directories->Intermediate output/Final output
Эти файлы желательно удалять по завершении сеанса работы.

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


Отладка в интегрированной среде С++ Builder.


Цель отладки – локализация и устранение ошибок, приводящих к неверному функционированию программ.

Т. е. программа уже откомпилирована, запускается, но работает неверно, либо выдает системное сообщение и завершается с ошибкой.


Примеры ошибок:

Не выполняется условие перехода на ветвь алгоритма

Системное сообщение "Access denieded" при выполнении функции ввода.

Открытие несуществующего файла на чтение.

И т.д.


Что делать?

Ошибки в программе при разработке – это вполне нормально, и отладка - естественный этап разработки.


В основе отладки лежит возможность просмотра значения переменных в процессе выполнения программы.


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

Затем установить контрольную точку (break point) до этого места.

Запустить программу, она начнет выполняться и остановиться на строке break point.

В этот момент можно просмотреть значения переменных, критичных для выполнения последующих строк.

Далее возможно пошаговое выполнение программы с наблюдением значений переменных до возникновения ошибки.


Основные приемы отладки:

Установка контрольных точек и просмотр значений переменных

Пошаговое выполнение программы и просмотр значений переменных

Вставка отладочного кода в программу – ловушка

Контрольная печать, трассировка – давнее средство отладки.


Современные системы разработки имеют хорошие средства отладки.

В IDE C++Builer есть хороший встроенный отладчик.


Некоторые ключевые слова и клавиши управления

Inspect – Alt+F5 – наблюдение за объектом (подробно)

Watсh - – наблюдение за несколькими объектами

Trace into – F7 – пошаговое выполнение с переходом внутрь функций

Step ower - F8 – пошаговое выполнение, включая вызов функций


…Развитие С а С++ – переводит разработку ПП на современную технологию ООП.


С++. Основы Объектно-ориентированного программирования (ООП).
Введение в объектно-ориентированное программирование.


C++ является объектно-ориентированным расширением языка C.

Первая широко известная реализация принципов ООП – это описание языка С++, Бьярне Страуструп, начало 80-х.

В каждой реализации UNIX есть компилятор С/С++ как составная часть ОС.
Распространены компиляторы Borland C++ и Microsoft C++ для платформы Windows,

В 1997 г был принят международный стандарт ANSI C/C++ - итог 20-тилетнего развития

Существующий стандарт ANSI C++ - это классическое описание ООП.

Сейчас язык С++ является языком публикаций по вопросам ООП.

_______

Итак, С++, ООП:


Мы уже хорошо знаем работу со структурами данных в Си, это наша отправная точка..

А сейчас познакомимся с важными понятиями ООП в языке С++: объект и класс.


! Основная идея ООП – размещение внутри одного объекта структуры данных и функций обработки этих данных:


Объект = структура данных + функций обработки этих данных;


Объект – определение Ивари Якобсона:
"Объект – сущность, способная сохранять свое состояние и обеспечивающая набор операций для проверки и изменения состояния".


В С++:

Новое понятие class – служит для определения новых типов объектов.

(подобно структуре struct TStudent, которая служит для определения нового сложного типа данных.


Объектом в С++ называют конкретный экземпляр реализации класса.

(Как переменная типа TStruct является экземпляром структуры, который наполняется реальными данными)


class включает поля и функции / свойства и методы / атрибуты и операции – эти пары терминов, по терминологии процедурного программирования, относятся к данным и функциям обработки этих данных.

class имеет разделы:

private и protected – защищенные, недоступные снаружи, и

public – доступный, реализующий интерфейс класса, т.е. способы работы с ним.


Принципы, составляющие суть ООП.

Рассмотрим три:

Инкапсуляция (Encapsulation – сокрытие, герметизация внутри) – объединение данных и кода + защита от внешнего вмешательства и неверного использования.
Реализация класса может быть скрыта в защищенной области (private, protected), доступ к которой осуществляется через интерфейсные поля/функции публичной (public) области.

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

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


Д.З. Павловская. Часть II. ООП (стр. 173). Основные идеи, принципы ООП.

Глава 4. Классы (179). Глава 5. Наследование (201).

Березин, стр.166.


Описание класса и объявление объекта в языке С++:

Пример конструирования класса, подобного структуре TStudent


class TStudent{ // Описание класса

private:

char Name[20]; //... описание внутренних, недоступных снаружи,

int Age; // методов и свойств

public:

//... описание интерфейса, т.е. доступных методов и свойств класса

TStudent(...){...}; // Конструктор, об этом позднее

void SetAge(int a){Age=a;};

void SetName(char* n){strcpy(Name,n);};

char* GetName(){ return &Name[0];);

int GetAge(){ return Age;);

void InputFromConsole();

void OutToConsole();

bool SaveToFile(char* PathFile); // только прототипы методов,

bool GetFromFile(FILE* inStud); // реализация где-то позднее

};


TStudent S1; // Объявление объекта (конкретного экземпляра класса)

// Работа с объектом через вызов методов

S1.SetName("Илья Муромец");

S1.SetAge(33);

S1.SaveToFile("C:\\Temp\\Student.dat");


TStudent AS[43]; // Объявление массива объектов

AS[0].InputFromConsole();

AS[0].SaveToFile();


Классы позволяют осуществлять строгий контроль доступа к объекту.

Можно работать c объектом только через функции и переменные из раздела public, т.е. через интерфейс.

S1.Age=18; // это попытка прямого обращения к полю из private раздела,
// не пройдет, компилятор выдаст ошибку.

Такой контроль обеспечивает первый принцип ООП – инкапсуляцию – сокрытие и защиту механизма реализации класса.


-------------------

Далее важные вопросы ООП: классы и функции-члены класса, конструкторы и деструкторы.


Классы и функции - члены класса.


Класс – это определяемый разработчиком новый тип.

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


Функции, описанные внутри разделов класса (как полные функции, так и прототипы), называются members functions – функции-члены класса/методы класса.

Они могут вызываться только через обращение к объекту этого класса (аналогия с элементами структуры):

SaveToFile();// Неверно, не указан объект,

// для которого вызывается метод

S1.SaveToFile(); // Верно


Если в описании класса указан только прототип функции, а реализация находится за пределами класса (описание класса в заголовочном файле, а реализация функций-членов класса в файле *.cpp), то необходимо указывать имя класса и знак "::" :


void OutToConsole(); // так был описан прототип в классе TStudent


void TStudent::OutToConsole(){ // А это реализация в функции

printf("Name=%s\n", Name);

printf("Age=%d\n",Age);

}

-------------------------------------------------

Конструкторы и деструктор


Конструкторы

Есть необходимость инициализировать объекты класса в момент создания (т.е. задавать начальное состояние).

Для этого служат специальные функции-члены класса, которые называют конструкторами.

Имя функции-конструктора совпадает с именем класса.
Конструкторы не возвращают значений.

Конструкторов в классе может быть несколько, они отличаются списками параметров.

Возможность создать несколько одноименных функций класса (методов) - это проявление полиморфизма ООП


Примеры конструкторов:


class TDate{ // Класс для работы с датой

...

public:

// Конструкторы, только прототипы, реализации в другом месте

TDate(int dd, int mm, int yy); //

TDate(int); // порядковый номер дня от Р.Х.

TDate(char*); // строка "dd.mm.yy"

TDate(); // текущая дата по компьютерным часам

...

};

...

// Создание и инициализация объектов через вызов конструкторов

TDate today(22, 11, 2002);

TDate April1("01.04.2001");

TDate now();


В конструкторах, как и в обычных функциях, возможно использование значений по умолчанию:


class TDate{

...

public:

TDate(int d=0,int m=0,int y=0); // по умолчанию все значения 0

...

}

...

TDate::TDate(int d,int m,int y){

...

day=d?d:today.day; // если d==0, то d=today.day


};


//?TDate d=today; // возможна инициализация объекта посредством присваивания


Деструктор
Специализированная функция – деструктор – вызывается по окончании работы объекта.
Обычно выполняет завершающие действия: закрытие файла, восстановление состояния программы (экрана), освобождение памяти.

Имя функции-деструктора совпадает с именем класса с добавлением знака ~ "тильда".
Деструкторы не возвращают значений.


Деструктор вызывается автоматически при выходе объекта из зоны видимости.

Возможен явный вызов деструктора для динамически создаваемых объектов через операцию delete.


Пример деструктора:

class X{

char* S;

public:

X(int);

~X();

};


X::X(int N){ // Конструктор

S=new char[N]; // Динамическое выделение памяти

}

X::~X(){ // Деструктор

delete S; // Освобождение динамически выделенной памяти
}


X Obj1(256);

X* pObj2=new X(125);// динамическое выделение памяти под объект типа X


delete pObj2; // явный вызов деструктора


Итоговые замечания по конструкторам и деструкторам:

Если у класса есть конструктор, он вызывается каждый раз при создании объекта.

Если у класса есть деструктор, он вызывается, когда объект уничтожается.

Конструкторов может быть несколько, деструктор всегда один.

Прежде, чем идти дальше, необходимо поговорить про механизм распределения памяти в программах на С/С++.


С/С++: Виды объектов в памяти и время их жизни :


Автоматический объект: создается каждый раз, когда его описание встречается при выполнении программы, и уничтожается при выходе из области видимости (из блока).

Статический объект: создается один раз, при запуске программы, и уничтожается один раз, при завершении.

Динамический объект: (объект в свободной памяти) создается явно (программистом) с помощью операции new и удаляется явно с помощью delete, либо автоматически при завершении программы.


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


Примеры:

// Статическая переменная:

static char* sPathData="C:\\Temp\\Test.dat";


// Автоматическая переменная, "живет" только внутри блока:

{

char aS[80];

//...

}


// Динамически создаваемая переменная, явное выделение памяти

// и явное уничтожение:

char* pStr=new char[80];

//...

delete pStr;
-------------------------------------------------


С++. Основы ООП. Наследование классов.

Важнейшим свойством ООП является наследование.

Например, есть класс с определенными свойствами. В целом он нас устраивает, но необходимо добавить некоторую функциональность.

аМожно создать новый класс на основе существующего через наследование.

Исходный класс называют предок/родитель/базовый класс/parent, а новый класс - наследник/потомок/производный класс/derived/child.

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

Так строится иерархия классов – важное понятие ООП.


При необходимости в производном классе можно перекрыть (переопределить) свойства и методы базового класса.


Примером многоуровневой иерархии классов является VCL от фирмы Borland.

Форма записи заголовка производного класса:


class : {}


class B : public A // класс B наследует классу A, класс B выведен из A

{

// реализация B, расширяющая возможности А

}


class ColorPoint : public Coord{

//...

}


Режимы доступа: внешнее, защищенное и внутреннее наследование:

Вспомним описание разделов класса

сlass имеет разделы с различным режимом доступа:

public – доступный, открытый раздел, реализующий интерфейс класса, т.е. способы работы с ним.

private – внутренний, закрытый раздел класса, недоступный снаружи (недоступен и в порожденном классе тоже).

protected – защищеный раздел класса, недоступный снаружи, доступен только в порожденном классе при наследованиии.

Важно:

protected используется при проектировании базовых классов в иерархии. Т.е. классы разрабатываются в предположении возможного наследования.


Эти же режимы доступа - public, private и protected - используются при описании заголовка наследуемого класса перед именем базового класса:

class : {public | private | protected} {

// задается один из режимов

};


Режим доступа public – внешнее наследование - интерфейс базового класса (раздел public) становится внешним интерфейсом производного класса (применяется чаще всего).

Режим доступа protected – защищенное наследование - внешний и защищенный разделы базового класса становится защищеными разделами производного класса, т.е. доступны только при следующем наследовании.

Режим доступа private – внутреннее наследование - внешний и защищенный разделы базового класса становится внутренними разделами производного класса, недоступны снаружи.

Пример:

class Coord{ // базовый класс, описание двумерных координат

protected:

int x,y;

public:

Coord();

SetCoord(int x,int y);

void Draw();

}


// Через наследование создадим класс для описания трехмерных координат
class Coord3D : public Coord{ // внешнее наследование

protected:

int z; // Добавим новое свойство – еще одну координату

public:

Coord3D();

SetCoord(int x,int y, int z); // перекрытие метода, полиморфизм

void Draw();

}


void Coord3D::SetCoord(int X, int Y, int Z){ // реализация метода

z=Z;

// Задать координаты x и y можно через метод базового класса:

Coord::SetCoord(X,Y); // уточнение имени

// ... или прямым присваиванием:

x=X;

y=Y;

}


// Еще один порожденный класс:

class ColorPoint3D : protected Coord3D{ // защищенное наследование

private:

int Color;

int Radius;

public:

Draw();

};


Множественное наследование

В С++ при наследовании есть возможность задать несколько классов в качестве базовых:


class Brush{ // кисть для рисования, "пятно"

protected:

int Color;

int Radius;

};


// Новый класс является наследником сразу двух базовых классов:

class ColorPoint : protected Coord, private Brush{ //

public:

Draw();// метод Draw использует свойства и методы двух классов

};


class ColorPoint наследует двум классам: Coord и Brush – это и есть множественное наследование .


Множественное наследование – мощное средство языка С++, но порождает ряд проблем.
Не все системы ООП поддерживают множественное наследование (например, Java, С# - не поддерживают).


---------------------------------------------------

Наследование классов (продолжение)

Помним, что наследование применяется для построения иерархии классов.
Иерархия классов может быть основой системы (так Visual Component Library (VCL) является основой систем разработки программ Delphi и Builder)

При необходимости в производном классе можно перекрыть (переопределить) свойства и методы базового класса.

Виртуальные методы (виртуальные функции)

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

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

Реализации виртуальных методов в базовом классе может быть переопределена в производных классах.

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

Пример:

Иерархия классов для графического редактора:

Базовый класс Shape (форма, шаблон), порожденные Circle, Ellipse, Square, Triangle

У всех порожденных классов будет метод Draw.

Класс Shape объявляет метод Draw как virtual.


class Shape : public Coord{ // форма, шаблон, фигура

public:


// виртуальный метод, реализация фиктивна

virtual void Draw(){coutDraw(); // Преобразование указателя на базовый класс

// в указатель на производный и вызов

// метода Draw для объекта Circle

// подробнее см. в литературе


Чистые виртуальные методы и абстрактные классы


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


virtual void Draw()=0; // чистый виртуальный метод (pure virtual),

// реализация отсутствует, требуется

// переопределение в производном классе.


Если класс имеет чисто виртуальные методы, то такой класс называется абстрактным.

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

Абстрактные классы используются как базовые при создании иерархии классов.


Виртуальные методы - это одно из проявлений полиморфизма ООП.


С++. Основы ООП.


Спецификатор inline


class date{

int month, day, year;

public:

// inline members function

void date(int d,int m,int y){ day=d; month=m; year=y }

void set_date(char*);

}

...

// inline members function

inline void date::set_date(char* StrDate){

// здесь реализация

}


При разработке классов используется большое число маленьких функций.

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

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

Inline считаются все функции, определенные внутри описания класса.

Функции можно описать как inline и вне описания класса.


Статические члены класса


Класс - это тип, по которому строятся объекты.

Каждый объект имеет свое содержание, свою копию данных.

Иногда есть необходимость в единых данных для всех объектов одного класса.

Такие члены класса называют статическими, они создаются с помощью описателя static.

Статический элемент класса - только один для всех объектов этого класса.


class AirMove{

static float g=9.8; // статическая переменная,

... // единая для всех объектов типа AirMove

}


Указатель this


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


class TStudent{

char* Name;

int Age;

public:

SetData(char* Name, int Age);

//...

};


void TStudent::SetData(char* Name, int Age){

strcpy(this->Name,Name);

this->Age=Age;

}


Спецификатор доступа friend – используется для объявления функций, дружественных классу.

Дружественные функции не являются членами класса, но имеют доступ в закрытый и защищенный разделы класса (private и protected).
Дружественные функции самостоятельны, не вызываются через объекты класса.
Функции объявляются дружественными внутри класса, т.е. класс сам определяет, с кем он "дружит".

Почему бы не использовать наследование?

Главная цель создания "друзей" – это перегрузка бинарных операторов (=, +,-…) и операций ввода > - об этом позже.

Дружественными могут быть не только функции, но и классы.

(см. также Павловская, стр. 187)

--------------------------------------------


С++. Основы ООП.


Переопределение операторов (перегрузка операций)


В С++ понятие оператора (операции) трактуется очень широко:

операторы арифметические, логические, ввода/вывода, присваивания, индексирования [],
() – скобки – тоже вид оператора, new, delete…


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

Например, присваивание или сравнение для объекта типа TStudent хотелось бы записать так:


TStudent P1, P2; // Объявление двух переменных

P2=P1; // Присваивание

//...

if(P2==P1){ // Сравнение

// здесь действие ...;

}


В С++ есть механизм переопределения операций.

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

Для перегрузки операции задается функция операции:


::operator#()
{ // где # - знак операции

// здесь реализация операции

}


Приведем пример переопределения операций = и + для класс Сoord


Class Coord{ // Координаты на плоскости

private:

int x,y;

public:

//...

Coord operator= (Coord ob2); // Переопредение operator=, прототип

Coord operator+ (Coord ob2); // Переопредение operator+, прототип


}


Переопределим operator= для Coord как член класса, вот реализация:


Coord Coord::operator=(Coord ob2){

x=ob2.x;

y=ob2.y;

return this; // возвращение объекта, которому присвоено значение

}


Coord P1, P2, P3;

// Следующие записи эквивалентны:
P1.operator=(P2); // Это вызов operator=() как метод класса

P1=P2; // Это вызов operator=() через знак переопределенной операции


Переопределим operator+ для Coord как член класса, вот реализация:


Coord Coord:: operator+ (Coord ob2){

Coord temp;

temp.x=x+ob2.x;

temp.y=y+ob2.y;

return temp; //

}


// Следующие записи эквивалентны:
P3= P1+P2; // Это вызов operator+() через знак переопределенной операции

P3= P1.operator+(P2); // Это вызов operator+() как метод класса


Переопределим operator+ для Coord как дружественную функцию класса


Class Coord{ // Координаты на плоскости

// Объявление прототипа дружественной функции в описании класса:

friend Coord operator+ (Coord ob1, Coord ob2);

private:

//...

}


// Реализация дружественной функции

Coord operator+ (Coord ob1, Coord ob2){

Coord temp;

temp.x=ob1.x+ob2.x;

temp.y=ob1.y+ob2.y;

return temp; //

}


Переопределение операторов ввода/вывода


Терминология:
Вывод/вставка в поток/inserting

Ввод/извлечение из потока/extracting


Следующие классы реализуют потоки, объявлены в заголовочном файле :
ostream – поток для вывода

istream – поток для ввода

iostream - поток для ввода/вывода


Вспоминаем:

cin, cout – имена стандартных потоков

int i=10;

cout


Информация о работе «Объектно-Ориентированное программирование»
Раздел: Информатика, программирование
Количество знаков с пробелами: 34318
Количество таблиц: 0
Количество изображений: 0

Похожие работы

Скачать
29390
0
1

... используется вниз и вверх по иерархии объектов, причем каждый объект иерархии выполняет это действие способом, именно ему подходящим.     2.  Объект - как базовое понятие в объектно-ориентированном программировании Понятию “объект” сопоставляют ряд дополняющих друг друга определений. Ниже приведены некоторые из них. Объект - это осязаемая реальность, характеризующаяся четко определяемым ...

Скачать
15943
1
3

... поставленной задачи. 1. Постановка задачи Для формирования четкого представления о предложенном методе необходимо подробно рассмотреть предметную область, а именно, параметрический анализ структуры Тьюринга [2]. В общем случае под термином структура Тьюринга понимают систему дифференциальных уравнений определенного вида. Для реакции двух веществ с одномерной диффузией система уравнений будет ...

Скачать
71340
1
0

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

Скачать
16307
0
0

... . Поэтому естественным обобщением традиционного подхода к программированию является объединение данных и подпрограмм (процедур и функций), предназначенных для их обработки.   3. Объекты   Базовым в объектно-ориентированном программировании является понятие объекта. Объект имеет определённые свойства. Состояние объекта задаётся значениями его признаков. Объект «знает», как решать ...

0 комментариев


Наверх