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)
А
также:
Цикл
разработки
ПО с точки зрения
программиста: (?Начальная
идея)
->Подготовка
текста программы
в редакторе
-> Запуск
компилятора
->Возврат:Анализ
и исправление
ошибок
->Получение
загрузочного
модуля
->
Выполнение
загрузочного
модуля
->
Возврат:Анализ
и исправление
ошибок
->…
и так далее
Интегрированная
среда разработки
ПО объединяет
все этапы в
единую технологическую
цепочку, помогает
программисту
на каждом этапе
цикла разработки
ПО.
Не
путать интегрированную
среду и компилятор
языка программирования!
Компилятор
– отдельная
программа для
преобразования
исходного
текста в исполняемый
код программы.
Компилятор
запускается
на одном из
этапов разработки.
Он существует
отдельно от
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(){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.
Общие вопросы
программирования
Развитие
языков программирования.
Языки программирования
C
и C++.
Процедурное
программирование.
Основные идеи
структурного
программирования.
Базовые
конструкции
структурного
программирования,
их реализация
в Си.
Циклы.
Оператор ветвления.
Выбор.
Функция,
модуль, заголовочный
файл, проект.
Функция:
передача параметров
и возврат значений
при вызове.
Прототип функции.
Передача
параметров
в функцию по
значению и по
ссылке.
Массивы.
Строки. Динамическое
выделение
памяти под
массив.
Конструирование
сложных типов
данных. Структуры
в С.
Понятие
указателя.
Работа с указателями.
Управление
памятью. Динамическое
выделение
памяти. Утечка
памяти. "Сборка
мусора".
Преобразование
программы на
С/С++ из исходного
текста в исполняемый
модуль. Объектный
модуль. Сборки
загрузочного
модуля.
Заголовочные
файлы. Препроцессор
С/С++.
Препроцессор
С, директивы
#include,
#define,
#ifdef,
#endif
Проектирование
программы,
схема иерархии
модулей. Структура
сложных программ.
II.
Основы объектно-ориентированного
программирования
Основная
идея ООП. Классы
и объекты. Разделы
класса. Интерфейс
класса.
Переход
к ООП как этап
развития технологии
разработки
ПП.
Основные
понятия ООП.
Класс и объект.
Принципы
ООП. Инкапсуляция.
Наследование.
Полиморфизм.
Конструирование
классов. Контроль
доступа к элементам
класса, разделы
класса private
и public.
Классы и функции
– члены класса.
Конструкторы
и деструкторы.
Виды
объектов в
памяти и время
их жизни, статические
члены класса.
Указатель
this,
модификатор
inline
.
Наследование:
конструирование
нового класса
на базе существующего
класса; раздел
класса protected;
построение
иерархии классов.
Режимы
доступа: внешнее,
защищенное
и внутреннее
наследование.
Множественное
наследование.
Виртуальные
методы.
Чистые
виртуальные
методы и абстрактные
классы.
Переопределение
операций для
классов. Пример.
"Друзья"
класса (friend),
переопределение
операций
ввода/вывода.
III.
Практика
программирования.
Стиль
программирования:
правила составления
имен, комментирование,
отступы.
Содержимое
заголовочного
файла в Вашем
проекте.
Анализ
заголовочных
файлов стандартных
библиотек,
например, .
Прототипы
функций, назначение,
использование
в проекте.
Разбиение
программы на
модули, назначение
заголовочных
файлов.
Конструирование
классов на
базе структур
данных и функций
обработки.
Описание
класса и объявление
объекта, вызов
методов объекта.
Пример
наследования
классов. Пример
множественного
наследования.
Управление
проектом в
интегрированной
среде, разбиение
проекта на
модули и состав
модулей; контроль
размещения
исходных файлов,
назначение
директорий.
Типы
файлов, создаваемые
системой
программирования
в проекте. Обеспечение
сохранности
файлов проекта,
резервное
копирование.
Использование
отладчика для
локализации
ошибки в программе.
IV.
Ввод/вывод в
С/С++.
Понятие
потока и файла.
Буферизованный
и небуферизованный
ввод/вывод.
Имена
стандартных
потоков в С и
в С++, их назначение.
Объявление
потоков, связывание
их с файлами,
закрытие потоков.
Ввод/вывод
в С++. Переопределение
операций
ввода/вывода.
|