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

Название: Объектно-Ориентированное программирование
Раздел: Рефераты по информатике, программированию
Тип: реферат

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<7){...}; // не выполнится

}

if(i<7){...} // выполнится

// i внутри блока и i вне блока – это разные переменные


3. Цикл
for(int i=0; i<100; i++){

...

if(...)

break; // прервать цикл

}


if(i<99){...} //error, i не определено


4. Условный оператор

bool fOk=true; // true/false

int a, b=7;

...

fOk=(a==b); // вычисляется логическое значение a==b и присваивается

...

if(fOk){

//... выполняется, когда логическое выражение true

}

else{

//... выполняется, когда логическое выражение false

}


5. Цикл с постусловием и выбор.


char c; // 1 byte

bool fOk;

do{ // Начало цикла с постусловием

fOk=true;

printf(”Вы любите программировать?\n”);

printf(”Ответ Д/Н/?:”);

c=getc();


switch(c){ //

case ‘Д’: printf(“Yes, sir”);

break;

case ‘Н’: printf(“No”);

break;

case ‘?’: printf(“Не знаю”);

break;

default:

printf(“*Ошибка, повторите ввод”);

fOk=false;

}

}

while(!fOk); // Конец цикла с постусловием


6. Структуры.
Структуры в Си служат для конструирования новых типов сложных данных
(Павловская с.67, Березин. с 124)
struct Student{ // описание нового типа данных Student

char Name[51];

int Age;

char Course;

char Group[4]; // ПЭ2х
float Ball; //Average; // Средняя оценка

float S; // Стипендия

}; //! Важно, в конце описания структуры ставится точка-с-запятой;


Student S1, S2; // Объявление двух переменных (выделение памяти)

// Занесение значений в элементы структуры:

S1.Age=21;

S1.Kurs='2';

S1.S=550.5;

strcpy(S1.Name,"Мистер Х."); // Копирование строки в строку структуры


Student PE21[50]; // Массив структур

strcpy(PE21[0].Name,"Lady Y");

PE21[0].Age=17;

...

for(int i=0; i<50; i++){ // Начисление стипендии

if(PE21[i].Ball>=4.0)

PE21[i].S=400.0;

else

PE21[i].S=0.0;

}

Недостатки массивов – жесткие рамки

Усложнения:
Повышенная стипендия при 4.5
Расчет среднего бала по группе

расчет суммы стипендии по группе


C/C++ Функция. Модуль. Проект. Заголовочный файл.


В Pascal есть понятия процедура и функция.
Функция – это составная часть выражения, при вызове ей передаются аргументы, а возвращается результат.

Процедура – выполняет часть кода, не обязательно возвращает результат.


В С/С++ нет процедур – только функции. Функции не могут быть вложенными.

Функция вС/С++ - это самостоятельный фрагмент кода, имеющий свое имя, список параметров и возвращаемое значение.


[<тип возвр.значения>] <имя функции>([список параметров]){

// тело функции (body)

return [<возврашаемое значение>];

}

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

Функция, которая не возвращает значения, должна быть описана с типом void.


// Пример функции вычисления среднего:

float Average(float a, float b){

return (a+b)/2;

}

void main(){

//…

float A=10.5, B=11.7, C=0.0;

C=Average(A, B); // Вызов функции

//…

}

Любая функция (кроме main) должна быть описана в тексте программы до момента ее вызова.

В качестве описания можно применить объявление прототипа (прототип/заголовок/интерфейс функции - синонимы), т.е. указать имя функции, список передаваемых параметров и тип возвращаемого значения, например:
float Average(float a, float b); // Прототип функции вычисления среднего


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

В С++ допустимо иметь несколько функций с одинаковым именем и различающимися списками параметров (это проявление полиморфизма), например:

int Summ(int A, int B); // вычисление суммы для int

float Summ(float A, float B); // вычисление суммы для float


В программе С/С++ должна быть функция main (главная) – именно она вызывается при запуске программы на выполнение:

int main(int argc, char* argv[]){ // ДЗ: разобраться с параметрами main

// int argc – число параметров в командной строке
//
char* argv[] – массив строк, где каждая строка – параметр из
// командной строки.
argv[0] – имя программы.
//...
Здесь реализация, и возврат значения, например так:

if(fOk)

return 0; //возвращение кода завершения

else

return -1; //возвращение кода завершения

}

Параметры функций.


Параметр, или аргумент – это значение, передаваемое в функцию.

При разработке функций используются имена параметров, которые являются формальными (условными).

В момент вызова функции ей передаются фактические (реальные) значения параметров, которые подставляются на место формальных.

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


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

Это неудобно в следующих случаях:

  • когда передается большой массив, структура (создание копии параметра занимает много времени и памяти).

  • когда нужно изменить значение параметра внутри функции и вернуть его вызывающей программе (например, при вводе структуры, при сортировке массива и т.д.).


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

Изменение значения параметра приводит к изменению исходной переменной.


Передача параметра по ссылке подобна передаче в качестве параметра указателя на переменную.


Примеры:


// 1.Функция сохраняет значения структуры в файле

bool SaveToFile(TStudent S){ // Это передача параметра по значению,

//... // создается локальная копия параметра

FILE *outS;

outS=fopen(PathOutFile, "at");

//...

fprintf(outS, "%s %s \n",S.Name, strAge );

//...

fclose(outS);

return true;

}


// 2.Функция читает значения из файла в структуру и возвращает ввод

bool InputFromFile(TStudent &S){ // Это передача параметра по ссылке,

//... // иначе ввод будет потерян

FILE* inS;

inS=fopen(PathInFile,"rt");

scanf(inS,"%s", &S.Name);

//...

return true;

}


ДЗ Самостоятельно: функции, параметры, возвращаемые значения; функция main;
в книга: Паловская, стр 73, Березин, стр. 108.


Модуль.
Модуль в С/С++
– это отдельно компилируемый файл, который может содержать: описание типов, объявлений переменных и констант, код функций.
Имя модуля есть имя файла c расширением *.cpp (*.c).

Оптимальный размер модуля, удобный для разработки и отладки – 200-400 строк текста.
Если текст программы велик, то его делят на несколько модулей и объединяют модули в проект.

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


Здесь схема модуля C++, сравнение со структурой программы на Pascal

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

В проекте исполняемой программы есть головной модуль, который содержит функцию main.

В проект могут подключаться модули в виде исходного текста С++ (файлы *.cpp), в виде объектного модуля (файлы *.obj), в виде библиотек функций (файлы *.lib), и др...


В IDE Borland C++ есть средства ведения проекта: окно проекта, пункт меню Project.

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

На практической работе предполагается создание рабочего проекта, состоящего из нескольких модулей.


Заголовочные файлы. Препроцессор С/С++.

Обычно для модуля C/C++ разрабатывается заголовочный файл, в котором описываются общие константы, типы данных, и объявляются прототипы функций.
Заголовочные файлы имеют расширение *.h или *.hpp (от Header - заголовок).

Имя заголовочного файла может совпадать или не совпадать с именем модуля.


Пример заголовочного файла (Student.h)

//-----------------------------------------------------------------

#define MIN_AGE 12 // Константы, часто используются такие описания

#define MAX_AGE 99


struct TStudent {

//... Описание нового типа данных TStudent

};


bool InputDataStudent(TStudent* S); // Прототипы функций

void OutputDataStudent(TStudent* S);

int AverageAge();

//-----------------------------------------------------------------


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


#include <имя заголовочного файла.h>


Препроцессор был разработан для языка C.

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

Например, на место #include <файл.h> подставляется содержимое этого файла,

по тексту программы производится замена макросов из #define MIN_AGE на 12, и т. д.


С системой программирования C/C++ поставляется множество библиотек, содержащих предопределенные константы, макросы, функции и классы.
Библиотеки поставляются вместе с заголовочными файлами. Заголовочные файлы многих библиотек находятся в директории ...\Include.
Текст заголовочных файлов является самой точной документацией по использованию функций, поставляемых в библиотеках. а Значит, необходимо уметь анализировать Header-файлы:


Приложение:

// Пример заголовочного файла (Student.h)

#ifndef STUDENT_H

#define STUDENT_H


#define MIN_AGE 12 // Константы, часто используются такие описания

#define MAX_AGE 99

typedef struct s{ // Описание новых типов данных

char Name[20];

int Age;

//...

} TStudent;

bool InputDataStudent(TStudent* S); // Прототипы функций

void OutputDataStudent(TStudent* S);

int AverageAge();

#endif

//-----------------------------------------------------------


#include

#define EOF (-1) /* End of file indicator */

#define MAX(a,b) ((a>b)? a : b) // Макрос


Макросы ненадежны, их контроль компилятором ограничен.

В С++ применение макросов не рекомендовано, в этом нет необходимости, т.к. в базовых конструкциях языка появились константы, шаблоны и inline функции.


Ввод/вывод C/С++.

Ввод/вывод (Input/Output, I/O) – это операции обмена данными между программой и внешними устройствами.

Операции ввода/вывода инициирует программа, поэтому:

  • ввод – перемещение данных с внешнего устройства в программу (в структуру данных в ОП)

  • вывод - перемещение данных из программы на внешнее устройство (в файл, на экран, на печать)

Операции Assembler:

IN: взять байт из порта и поместить в ОП

OUT: взять байт из ОП и поместить в порт

Потоки и файлы

Файл (file) – это способ хранения информации на физическом устройстве:
дисковый файл, МЛ, распечатка, вывод на дисплей – это все файлы.

Ввод/вывод в программе не должен зависеть от устройства,
Т.е. разработчик программы определяет только тип операций I/O (последовательный доступ, произвольный доступ), а устройство определяется при операции открытия файла.

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

function I/O в программе а

а stream (поток) а

а file (файл на устройстве)

Поток (stream) – это абстрактный уровень взаимодействия между программой и физическим устройством.
Информация выводится в поток (вводится из потока) как последовательность байтов – символ за символом.
В момент открытия потока он связывается с файлом на конкретном устройстве.

В Си (не в С++) для этого служит управляющая структура потока FILE.

(FILE, например, включает буфер для промежуточного вв/выв, указатель на текущую позицию в буфере) (FILE описана с , задание на практику – ознакомиться)

Потоки бывают буферизованные (блокированные) и небуферизованные (НЕблокированные).

Буферизованные потоки вывода накапливают информацию в буфере перед выводом на устройство.

Буферизованные потоки ввода производят опережающее чтение в буфер потока.

Буферизация служит для оптимизации вв/вывода (размер сектора на диске 512 байт, кластер. Для вв/вывода даже 1 байта необходимо записать/перезаписать сектор/кластер).

Небуферизованные потоки выводят информацию немедленно – например, срочные сообщения необходимо выводить в небуферизованный поток (стандартный поток stderr).

Для принудительного вывода при работе с буферизованными потоками есть специальная операция: fflush(...) – выравнивание, сброс на диск

Ввод/вывод в С

В описании языка Си нет операций ввода/вывода, они реализованы через функции.

Ключевые слова в названиях функций:

read/write – неформатированный вв/вывод, сплошной массив байт

get/put,

scan/print,

open/close

например:

printf, fprintf, sprintf - ключевые слова используются с различными префиксами – вывод в стандартный поток, вывод в файл, вывод в область памяти.


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

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

Что-то все-равно нужно знать аДокументация (общее представление) а Help а Заголовочный файл

Пример Си, вывод данных структуры в текстовый файл:


struct Student{

char* Name;

int Age;

};

Student AS[43];

//...


FILE *outS; // Объявление потока


outS=fopen("D:\\Data\\Person.dat", "wt"); // Открытие потока, связывание с файлом
//
режим write,text

for(int i=0; i<43; i++){

fprintf(outs,"%s, - ему %s лет\n", AS[i].Name, itoa(AS[i].Age) );

}


fclose (outS);

//--------------------------------


//--------------------------------

Student P;

FILE*InP=fopen("Person.dat", "rt"); // Открытие потока, связывание его с конкретным
//
файлом "Person.dat"

char* cAge[3]; //Временная переменая для ввода символьного числа, соответств. Age

fscanf(InP,"%s %s", &P.Name, cAge); // Ввод из файла через поток


//или

fgets(InP,P.Name);

fgetc(InP,cAge);


fclose(InP); // закрытие потока и файла.


Ввод/вывод в С++(начальные сведения)

Функции Си можно использовать в С++.

В С++ ввод/вывод определен через базовый набор классов, поставляемых с компилятором.

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

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

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


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

Подробнее о переопределении операций вв/выв поговорим позже, когда будем конструировать классы.А пока познакомимся со стандартными средствами:


Cтандартные потоки С++:

cin – стандартный поток ввода (консоль)

cout – стандартный поток вывода (консоль)

cerr – стандартный поток вывода ошибок небуферизированный

clog – стандартный поток вывода ошибок буферизированный

Стандартные потоки объявлять не надо.


Операция ввода из потока >> , операция вывода в поток <<
(это двойные угловые скобки)


Пример:

cout<<"Введите имя:"; // Вывод строки на консоль (в поток cout)

cin>>P.Name; // Ввод значения с клавиатуры через поток cin
// в переменную структуры
P

cout<<"\n"<<"Введено имя: "<<P.Name<<"\n";

Для освоения темы ввода/вывода в С++ необходимо разобраться с переопределением операций ввода/вывода для классов (см. "Практическую работу №2")

Потоковые классы, стандартные потоки, файловые потоки С++: Павловская, стр 265.


Схемы иерархии модулей в типичном проекте программы обработки данных.

Проект-проектирование.

Структура проекта – состав модулей и их взаимодействие.


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

Здесь типичная схема программы обработки данных:

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

Ввод данныха Обработка а Диалог с оператором а Сохранение данных


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


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

Далее вызывается модуль обработки, ему передаются данные ввода,

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

Диалог с оператором ведется по мере необходимости.


Модуль на этапе проектирования - это функционально обособленый фрагмент проекта, например: модуль ввода, модуль обработки, головной модуль программы.

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


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


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


C/C++. Преобразование проекта из исходного текста в исполняемый модуль


Рассмотрим процесс разработки программы.

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


TextС/С++-> PreProc-> Compile-> ObjectModule(*.obj)-> Link-> EXEModule

Edit Library(*.lib)->

(*.c,*.cpp,*.h) ЯDebugЯ

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

TextEditor – создание текста исходных модулей проекта на языке программирования – "кодирование"( "кодировщики" - жаргон) Кодированию предшествует проектирование..

Compile (compilation, компилятор, составитель) – производит контроль текста программы, выдает сообщения об ошибках времени компиляции – это начальный период отладки.
В результате успешной компиляции из текста исходного модуля получается объектный модуль.

Object module(obj) – операторы исходного текста программы, преобразованные во внутреннее машинное представление – "код", инструкции процессора.
(формат кода отличается для каждой архитектуры).

Obj содержит откомпилированные операторы языка + вызовы функций.
Вызовы функций внутри obj называются “неразрешенные ссылки”:

например, это вызовы библиотечных функций printf, scanf, …, или вызовы разработанных ранее функций из другого модуля проекта.

Link – редактор связей, сборщик программы - производит сборку исполняемого модуля - execute module, exe - из одного или нескольких объектных модулей и библиотек.

При сборке в ехе -модуль из библиотек и других obj-модулей проекта заносится код функций, вызовы которых указаны в исходном тексте программы
– этот процесс называется “разрешение ссылок”.

Далее *.ехе запускается на выполнение, тестируется, отлаживается.


Замечания по стилю разработки и оформлению текстов программ

Комментирование

Первой строкй любого модуля д.б. комментарий с указанием

// Автор, ПЭ21, ДВГТУ, ООП, dd.mm.yy

// Проект, назначение модуля


Каждую функцию также следует предворять комментарием, если ее код не тривиален.

Тест для комментария – понятность для не посвященного в проблему специалиста.

Комментирование программ – профессиональный навык.
Хороший прием – сначала создавать скелетный код модуля из текстов комментариев и заголовков функций с пустым блоком реализации (функции заглушки).

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

Правила именования переменных

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

TCar, TAutobus

InputFromFileToPerson

Разумное комментирование и выбор имен придает текстам программы свойство самодокументированности.
Отступы как средство структурирования текстов:

Текст программы легче анализировать, когда он структурирован, разбит на блоки
Блок в программе – это фрагмент текста внутри фигурных скобок {…}.

Например: описание структуры, функция, тело цикла и др.
Любой вложенный в блок должен быть сдвинут вправо на табуляцию (обычно от 2 до 8 символов).

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

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

Комбинация клавиш < Ctrl+ {> и < Ctrl }> – позволяет контролировать блоки.

!Все без исключения практические работы необходимо оформлять по этим правилам
(см. также Павловская, стр 102-114)

C/C++. Интегрированная среда разработки фирмы Borland
(Используется в С/С++ Builder и в Delphi)

IDE - Integrated Development Environment

IDE для разных платформ разработки ПО:
Borland:
среда turbo-vision -> Turbo Pascal, Turbo C, СУБД Paradox
Delphi и Builder (Visual Component Library) СУБД Interbase, JBuilder -> Borland Studio

MS Visual Studio
IBM Visual Age

Linux (?) (Emacs)

Основные компоненты IDE:

  • специализированный редактор текста программ (многодокументное окно )

  • средства ведения проекта, состоящего из нескольких модулей (окно проекта)

  • система запуска компилятора (пункт меню, комбинация клавиш)

  • окно просмотра сообщений компиляции.

  • Help-система (!)

  • встроенный отладчик (Debuger)

А также:

  • визуальный конструктор форм

  • инспектор объектов (ООП)

  • Class explorer

  • Клиент для работы с БД

  • Графический редактор

  • и много других вспомогательных функций


Цикл разработки ПО с точки зрения программиста:
(?Начальная идея)

->Подготовка текста программы в редакторе

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

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

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

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

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

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


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


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

Компилятор – отдельная программа для преобразования исходного текста в исполняемый код программы.
Компилятор запускается на одном из этапов разработки.
Он существует отдельно от 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)


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


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

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

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


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


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

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


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

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

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

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

  • И т.д.


Что делать?

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


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


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

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

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

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

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


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

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

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

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

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


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

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


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

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

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

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

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(){cout<<"This is Shape";};


// или

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

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

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


}


// Через наследование создаем новый класс:

class Circle : public Shape{ // окружность, через наследование

protected:

int Radius;

public:

...

void Draw(); // переопределение виртуального метода для Circle

}

Circle C1; // Объект типа Circle

C1.Draw(); // Вызов метода Draw для объекта Circle

C1.Shape::Draw(); // Вызов метода Draw для объекта Shape


Shape* pSh; // Указатель на базовый класс

pSh=&C1; // Указатель на базовый класс используется для

// объекта порожденного класса

pSh->Draw(); // Вызов метода Draw для объекта Shape

((Circle*)pSh)->Draw(); // Преобразование указателя на базовый класс

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

// метода 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<<"i="<<i<<";"; // несколько операторов вывода в одной конструкции

cout<<"Input int:";

cin>>i;


Перегрузка операторов ввода << и вывода >> для создаваемого класса может заменить методы ввода/вывода и упростить использование класса.


Форма записи:


ostream &operator<<(ostream &stream, имя_класса ob){

// реализация вывода;

}


istream &operator>>(istream &stream, имя_класса ob){

// реализация ввода;

}


Оператор ввода/вывода должен возвращать ссылку на поток для корректной работы такой конструкции:
cout<<ob1<<ob2<<ob3<<…<<obN;

выполняется вывод ob1- возврашается ссылка на поток,
затем выполняется вывод ob2- возврашается ссылка на поток, и т.д.
Слева от оператора ввода/вывода всегда должна стоять ссылка на поток.
Перегрузка операций ввода/вывода не может быть реализована как метод класса, т.к. метод вызывается объектом класса(Object.Method()), и методу при этом передается неявный указатель на объект this.
Форма записи оператора ввода/вывода предполагает слева имя потока, а не объекта класса:

поэтому, перегрузка операторов вв/выв. для класса реализуется как дружественные функции класса.

Пример:


class Coord{

friend istream &operator>>(istream &In, Coord &Ob2);

friend ostream& operator<<(ostream &Out, Coord Ob2);

private:
int x,y;

public:

// Constuctors:

Coord(){ x=0; y=0;};

//...

};

//-------------------------------------------------------------

// Переопределение оператора вывода, дружественная функция, реализация:

ostream &operator<<(ostream &Out, Coord Ob2){

Out<<"x="<

Out<<"y="<

return Out;

}


cout<


Итог:

Перегрузка операций – это проявление полиморфизма ООП.

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

Замечание: Невозможно изменить приоритет выполнения операций.


Проработать эту тему подробно по литературе (например, Березин. С и С++. стр 249)

Пример перегрузки операций сложения, присваивания, ввода/вывода

(файл Coord.cpp)

// A. Zhuravlev.

// OOP. Themes: OOP->Inheritance, OOP->Polymorphism

#include


// Base class Coord

class Coord{

// Redefine input/output

//friend istream &operator>>(istream &In, Coord &Ob2);

friend ostream& operator<<(ostream &Out, Coord Ob2);

privateЖ:

int x,y;

public:

// Constuctors:

Coord(){ x=0; y=0;}

Coord(int X, int Y){ x=X; y=Y;}

// Implementation:

void Set_Coord(int X, int Y){ x=X; y=Y;}

int GetX(){return x;}

int GetY(){return y;}

// Redefine operators:

Coord operator+(Coord Ob2);

Coord operator=(Coord Ob2);

};

//-------------------------------------------------------------

// Redefine operations:

Coord Coord::operator+(Coord Ob2){

Coord temp;

temp.x=x+Ob2.x;

temp.y=y+Ob2.y;

return temp;

}

Coord Coord::operator=(Coord Ob2){

x=Ob2.x;

y=Ob2.y;

return *this;

}

//-------------------------------------------------------------

// Redefine input/output

ostream &operator<<(ostream &Out, Coord Ob2){

Out<<"x="<

Out<<"y="<

return Out;

}

/*

istream &operator>>(istream &In, Coord Ob2){

//...

} */

//------------------------------------------------------------

// Testing:

void main(){

Coord C1(10,20);

Coord C2;

//Coord* C3=new Coord(-1,-1);

C2=C1;

C1.Set_Coord(9999,7777);

//C2=C1+C3; //???

cout<<"Coord C1:"<

cout<<"Coord C2:\n"<

//cout<<"(C1+C3)="<<(C1+ *C3);

//cout<

return;

}


ДВГТУ, ООП, Май 2004

Вопросы на экзамен по учебной дисциплине
"Объектно-ориентированное программирование"

I. Общие вопросы программирования

  1. Развитие языков программирования. Языки программирования C и C++.

  2. Процедурное программирование. Основные идеи структурного программирования.

  3. Базовые конструкции структурного программирования, их реализация в Си.

  4. Циклы. Оператор ветвления. Выбор.

  5. Функция, модуль, заголовочный файл, проект.

  6. Функция: передача параметров и возврат значений при вызове. Прототип функции.

  7. Передача параметров в функцию по значению и по ссылке.

  8. Массивы. Строки. Динамическое выделение памяти под массив.

  9. Конструирование сложных типов данных. Структуры в С.

  10. Понятие указателя. Работа с указателями.
    Управление памятью. Динамическое выделение памяти. Утечка памяти. "Сборка мусора".

  11. Преобразование программы на С/С++ из исходного текста в исполняемый модуль.
    Объектный модуль. Сборки загрузочного модуля.

  12. Заголовочные файлы. Препроцессор С/С++.

  13. Препроцессор С, директивы #include, #define, #ifdef, #endif

  14. Проектирование программы, схема иерархии модулей. Структура сложных программ.


II. Основы объектно-ориентированного программирования

  1. Основная идея ООП. Классы и объекты. Разделы класса. Интерфейс класса.

  2. Переход к ООП как этап развития технологии разработки ПП.

  3. Основные понятия ООП. Класс и объект.

  4. Принципы ООП. Инкапсуляция. Наследование. Полиморфизм.

  5. Конструирование классов. Контроль доступа к элементам класса, разделы класса private и public. Классы и функции – члены класса.

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

  7. Виды объектов в памяти и время их жизни, статические члены класса.

  8. Указатель this, модификатор inline .

  9. Наследование: конструирование нового класса на базе существующего класса; раздел класса protected; построение иерархии классов.

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

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

  12. Виртуальные методы.

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

  14. Переопределение операций для классов. Пример.

  15. "Друзья" класса (friend), переопределение операций ввода/вывода.


III. Практика программирования.

  1. Стиль программирования: правила составления имен, комментирование, отступы.

  2. Содержимое заголовочного файла в Вашем проекте.

  3. Анализ заголовочных файлов стандартных библиотек, например, .

  4. Прототипы функций, назначение, использование в проекте.

  5. Разбиение программы на модули, назначение заголовочных файлов.

  6. Конструирование классов на базе структур данных и функций обработки.

  7. Описание класса и объявление объекта, вызов методов объекта.

  8. Пример наследования классов. Пример множественного наследования.

  9. Управление проектом в интегрированной среде, разбиение проекта на модули и состав модулей; контроль размещения исходных файлов, назначение директорий.

  10. Типы файлов, создаваемые системой программирования в проекте.
    Обеспечение сохранности файлов проекта, резервное копирование.

  11. Использование отладчика для локализации ошибки в программе.


IV. Ввод/вывод в С/С++.

  1. Понятие потока и файла. Буферизованный и небуферизованный ввод/вывод.

  2. Имена стандартных потоков в С и в С++, их назначение. Объявление потоков, связывание их с файлами, закрытие потоков.

  3. Ввод/вывод в С++. Переопределение операций ввода/вывода.