Объективное программирование
Объективное программирование
ВВЕДЕНИЕ В ОБЪЕКТНОЕ ПРОГРАММИРОВАНИЕ
Лекция 1. Объектное программирование как технология программирования
-------------------------------------------------------------
Традиционная технология программирования 70-х годов - структурное
программирование:
- модульное программирование;
- нисходящее программирование;
- структурное проектирование процедур и данных (программирование без
goto).
Язык Паскаль - соответствует указанным принципам и был разработан под
влиянием идей структурного программирования.
Альтернативный подход - восходящее программирование - предполагает в
простейшем случае создание слоя структур данных и процедур, обеспечивающих
полный набор действий над объектами, которые представлены в данной задаче.
Пример традиционного подхода библиотека стандартных функций.
Следующий шаг - введение в программу объектов. Под объектом
понимается структура данных, которая содержит полную информацию о состоянии
соответствующего физического объекта, который отображается программой. В
Си этому может соответствовать структура struct), в Паскале - запись
(record). Множество объектов одного типа составляют понятие класса.
Объектно-ориентированный подход к разработке программ предполагает, что в
программе устанавливается взаимно-однозначное соответствие между
физическими объектами,
отображаемыми программой, и програмнными объектами, являющимися, по
существу, структурированными переменными (в дальнейшем под термином
"объект" будем понимать программный объект).
Традиционный подход: ---------- переменная тип данных
Объектно-ориентиро- физический программный класс
ванный подход: объект объект объектов
При создании объектов программист определяет множество функций, при
помощи которых (а точнее, исключительно через которые)над объектом
выполняется некоторое допустимое множество операций.
Такие функции должны иметь обязательный параметр - ссылку на текущий
объект, для которого они вызываются. Сами функции являются неотъемлимой
частью понятия класса объектов, так как они определяют возможные
действия над объектами одного и того же типа (то есть класса).
Объектно-ориентированные программы можно разрабатывать и с помощью
традиционных языков программирования. Рассмотрим пример определения
объектов типа "дата" на классическом Си.
//------ структура dat - аналог класса объектов "дата" --------typedef
struct dat
{
unsigned day;
unsigned month;
unsigned year;
}
DAT;
//----- набор функций для класса объектов "дата" --------------static int
mm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
//----- Проверка на корректность -----------------------------int
TestData(p)
DAT *p;
//------ Следующая дата ----------------------------------------void
NextData(p)
DAT *p;
{
p->day++;
if (p->day month]) return;
if (p->month ==2 && p->day==29 && p->year %4 ==0) return;
p->day=1;
p->month++;
if (p->month !=13) return;
p->month=1;
p->year++;
}
//------- Следующая дата через n дней --------------------------void
PlusData(p,n)
DAT *p;
int n;
{
while (n-- !=0) NextData(p);
}
//------- Основная программа ---------------------------------
void main()
{
DAT a;
do
{
scanf("%d%d%d", &a.day, &a.month, &a.year);
}
while(TestData(&a) ==0);
PlusData(&a, 17);
}
//--------------------------------------------------------
Фактически определение класса объектов как типа данных и известного
набора функций для выполнения операций над переменными этого типа
эквивалентно понятию базового типа данных (БТД) языка программирования.
Единственное отличие класса от БТД заключается в том, что первый
определяется программистом, а второй встроен в определение языка
программирования.
Язык программирования Си++ представляет собой расширение языка Си
для программирования объектов и их классов. При этом использование
классов эквивалентно вплоть до синтаксиса использованию базовых типов
данных:
Понятия классического Си Понятия Си++
------------------------- ----------- БТД:
Класс:
элемент данных языка, для определяемая пользователем
которого известно множество структура, элементы которой
значений, форма представления, являются ранее определен набор операций.
ными типами данных и классами,
и множества функций,оперирующих с ним.
--------------------------------------------------------- Переменная:
Объект:
область памяти, содержащая переменная, содержащая
структуру данных определенного структуру данных, определенную
типа. как класс.
--------------------------------------------------------- Операция:
Переопределение операторов:
операция над переменной интер- функция, определенная для объек
претируется по отношению к тому тов указанного класса может быть
БТД, к которому относится пере- вызвана в виде одной из стандарт менная
(так операция '+' ных операций языка Си, которая
по-разному интерпретируется для переопределяется, если операндом
переменных типа int и double). ее является объект класса, а не
переменная БТД.
Лекция 2. Дополнительные возможности языка Си++
-----------------------------------------------
Ниже рассмотрим средства, расширяющие классический Си. Хотя
они и не относятся непосредственно к классам, с их помощью можно
реализовать рассмотренные выше принципы объектно-ориентированного
программирования.
2.1. Присваивание структур
------------------------- Операция присваивания может быть
применена к структурам одного типа. В этом случае предполагается их
побайтное копирование
одной в другую. Она (а не ссылка на нее) может быть также фактическим
параметром и результатом функции. Если имеется ссылка на
структуру с именем p, то результатом операции *p является структура в
целом. Таким образом, структура приближается к базовым типам данных в том
смысле, что над ней возможны вышеуказанные операции. Для обозначения
структуры можно также использовать имя
структуры без ключевого слова struct.
struct dat
{ int day,month,year; }
dat NextDat(dat x) // Формальный параметр - структура
{ ... return(x); } // Возвратить структуру как результат
dat Nextdat1(dat *p)
{ ... return(*p); } // Возврат структуры косвенно по ссылке
dat a,b,c,*q; // Ключевое слово struct не используется
void main()
{
q = &b;
a = b; // Прямое присваивание структур
a = *q; // Косвенное присваивание по ссылке
c = NextDat(b); // Присваивание структуры как результата
c = NextDat1(&b); // функции, фактический параметр в
} // NextDat - копия структуры
2.2. Обращения по адресу (неявная ссылка)
----------------------------------------
При работе со структурами большого размера - при передаче их
в качестве параметров и результатов функций - копирование их является
неэффективной операцией. Гораздо эффективнее передавать ссылку на эту
структуру. Для того, чтобы постоянно не указывать операции взятия адреса
и косвенного обращения по ссылке в Си++ введен тип - неявная ссылка:
при определении переменной неявно вводится ссылка, указывающая на эту
переменную. Использование этой переменной в большинстве операций
предполагает косвенное обращение по соответствующей ссылке. При
инициализации такой переменной значением другой переменной создается ссылка
на эту другую переменную. При использовании в любом выражении переменной -
неявной ссылки реально производится косвенное обращение по созданной
ссылке.
Си++ Эквивалент в "классическом" Си
------------------------ -----------------------------//-------------
-- Инициализация константой -----------------int &a = 5;
int a, *pa =a;
*pa = 5;
//--------------- Инициализация переменной -----------------int x;
int x,*pa;
int &a = x; pa = &x;
a = 5; *pa = 5;
//-------------- Неявная ссылка на структуру ----------------struct dat
struct dat
{ int day,month,year }; { int day,month, year };
dat x; dat x;
dat& b = x; dat* pb = &x;
dat& c = {12,12,1990}; dat cc = {12,12,1990};
dat *pc = &cc;
b.year = 1990; pb->year= 1990;
c.day=b.day+3; pc->day = pb->day+3;
c = b; // Копирование pc->day = pb->day;
// структуры pc->month = pb->month;
pc->year = pb->year;
Наиболее часто неявные ссылки используются при передаче параметров и
результатов функций. В этом случае транслятор сам выбирает, что необходимо
использовать в качестве фактического параметра - переменную или ссылку на
нее, и что используется в качестве результата - ссылка или переменная,
косвенно адресуемая по ссылке. Цель подобных ухищрений будет видна позднее
- при переопределении операторов, а пока можно заметить, что вызов функций,
с
параметрами - обычными значениями и неявными ссылками - синтаксически
идентичен. То же самое касается результатов.
В качестве иллюстрации рассмотрим три примера функций, имеющих в
качестве формального параметра и результата структуру, которая передается
соответственно:
- значением;
- явной ссылкой;
- неявной ссылкой.
Пример 1. Параметры - значения
---------------------------------------------------------dat Inc(dat x)
========> - копирование
{ --------> - ссылка
x.day++;
return(x); ----¬ стек +---+x.day++
} ¦ b =========> x =========¬
L---- +---+ ¦ return(x)
void main() ¦
{ ----¬ стек +---+ --¦-¬ временная
dat a,b,*p; ¦ a day++
{ x->day++
x->day++; ----¬ стек +---+
return(x); г===== b ¦
a = *Inc(Inc(&b)); ¦ ¦ L---- +-¦-+
p = Inc(&b); ¦ ¦ ----¬ ¦return(x)
a = *p; ¦ L-- a ¦
L---
Пример 3. Параметры - неявные ссылки
---------------------------------------------------------dat& Inc(dat&
x) x.day++ неявная ссылка dat* px
{ x.day++
x.day++; ----¬ стек +---+
return(x); г===== b ¦
a = Inc(Inc(b)); ¦ ¦ L---- +-¦-+
p = &Inc(b); ¦ ¦ ----¬ ¦return(px)
a = *p; ¦ L-- a ¦
L---
Сравнение этих примеров показывает следующее:
- при работе с формальным параметром - неявной ссылкой
используется имя формального параметра в качестве идентификатора
переменной, которая заменяется транслятором на косвенное обращение по
неявной ссылке;
- при возвращении результата используется имя переменной,которая
заменяется транслятором неявной ссылкой на нее;
- примеры 2 и 3 идентичны по реализации, но отличаются по
синтаксису вызова функции;
- примеры 1 и 3 отличаются по реализации, но идентичны по
синтаксису вызова функции;
- из предыдущего следует, что при вызове функции список фактический
параметров недостаточен для определения транслятором способа их передачи
(значением или ссылкой), поэтому в Си++ для каждой внешней функции
необходимо задать прототип.
Так как размер структуры, передаваемой в качестве результата функции,
может быть сколь угодно большим, то для его хранения необходимо создать
временную переменную. Транслятор "Borland C" в этом случае поступает
следующим образом:
- при входе в функцию в стеке предполагается существование неявного
параметра - "длинной" ссылки на структуру, в которой размещается
результат функции;
- при выполнении операции return(x), где x - локальная переменная или
формальный параметр, выполняется побайтовое копирование переменной x по
адресу, заданному неявным параметром;
- если результат функции непосредственно присваивается другой
переменной-структуре, то при вызове такой функции в стек помещается
неявный параметр - ссылка на переменную в левой части операции
присваивания;
- во всех остальных случаях в вызывающей функции создается по одной
неявной автоматической переменной на каждый вызов функции, возвращающей
структуру в качестве результата, а при вызове передается соответствующая
ссылка на эту переменную-структуру. Такие переменные имеют все свойства
автоматических, они существуют все время работы вызывающей функции,
возможно даже получить ссылку на такую переменную.
Программа на Си++ Реализация
----------------- ---------- -- неявный параметр
dat Inc(dat x) void Inc(dat *r,dat x)
{ {
x.day++; x.day++;
return(x); *r = x; // Копирование
} } // результата
void main() void main()
{ {
dat a,b*p; dat a,b,*p;
dat t,u; // Временнye переменнye
a = Inc(b); Inc(&a,b); // Ссылка на левую часть
p = &Inc(b); Inc(&t,b); // Присаивание временной
p = &t; // переменной и получение
a = *p; a = *p; // ссылки на нее
a = Inc(Inc(b)); Inc(&u,b); // Промежуточный результат
Inc(&a,u); // во временной переменной
} }
2.3. Функции - элементы структуры
-------------------------------- Повторим рассмотренный выше
пример в другой форме:
//------------ структура dat - аналог класса объектов "дата" --struct dat
{
unsigned day;
unsigned month;
unsigned year;
int TestData();
void NextData();
void PlusData(int n)
{
while(n-- !=0) dat::NextData(this);
}
};
//----------- набор функций для класса объектов "дата" --------static int
mm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
//----------- Проверка на корректность -----------------------int
dat::TestData()
//----------- Следующая дата ----------------------------------void
dat::NextData()
{
day++;
if (day ::
- в теле фукции неявно определен один формальный параметр с
именем this - ссылка на структуру, для которой вызывается функция
(В нашем примере это будет struct dat *this ). Поля этой структуры доступны
через явное использование этой ссылки
this->month = 5;
this->day++;
или неявно
month = 5;
day++;
- для переменной, имеющей тип некоторой структуры, вызов
функцииэлемента этой структуры имеет вид
. ( )
2.4. Переопределение функций
--------------------------- В Си++ возможно определение
нескольких функций с одинаковым
именем, но с разными типами формальных параметров. При этом компилятор
выбирает соответствующую функцию по типу фактических параметров.
Переопределяемую функцию необходимо объявить с ключевым
словом overload:
overload SetDat;
void SetDat(int dd,int mm,int yy,dat *p)
{ // Дата вводится в виде трех целых
p->day=dd;
p->month=mm;
p->year=yy;
}
void SetDat(char *s,dat *p) // Дата вводится в виде строки
{
sscanf(s,"%d%d%d", &p->day, &p->month, &p->year);
}
void main()
{
dat a,b;
SetDat(12, 12, 1990, &a); // Вызов первой функции
SetDat("12,12,1990", &b); // Вызов второй функции
}
Функции-элементы также могут быть переопределены, при этом явного
объявления не требуется.
struct dat
{
int day,month,year;
void SetDat(int,int,int);
void Setdat(char *);
}
void dat::SetDat(int dd,int mm,int yy)
{
day=dd; month=mm; year=yy;
}
void dat::SetDat(char *s)
{
sscanf(s,"%d%d%d",&day,&month,&year);
}
void main()
{
dat a,b;
a.SetDat(12,12,1990);
b.SetDat("12,12,1990");
}
2.5. Операторы управления динамической памятью
---------------------------------------------
В библиотеке Си имеются две функции управления динамической памятью -
malloc() и free(), которые выделяют и освобождают область памяти
заданного размера (в байтах). В этой области программа может разместить
переменную (или массив), которая называется динамической. При выделении
памяти под динамическую переменную необходимо при помощи операции sizeof
определять количество байтов, необходимое для размещения переменной
Страницы: 1, 2, 3, 4, 5
|