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

скачать (156 kb.)

  1   2   3
ВВЕДЕНИЕ В ОБЪЕКТНОЕ ПРОГРАММИРОВАНИЕ
Лекция 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;

{

if (p->month ==2 && p->day==29 && p->year %4 ==0) return(1);

if (p->month ==0 || p->month >12 ||

p->day ==0 || p->day >mm[p->month])

return(0);

return(1);

}

//------ Следующая дата ----------------------------------------void NextData(p)

DAT *p;

{

p->day++;

if (p->day <= mm[p->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 <========= x <======= ¦ переменная

a = Inc(Inc(b)); L---- +---+ L---p = &Inc(b); x.day++

a = *p;

}
Пример 2. Параметры - явные ссылки

---------------------------------------------------------dat* Inc(dat* x) x->day++

{ x->day++

x->day++; ----¬ стек +---+

return(x); г===== b <--------- x ¦

} ¦ --> <----¬ +-¦-+

¦ ¦ L---- ¦ ¦return(x)

¦ ¦ г======+=====void main() a=*..¦ ¦ ¦ ¦ +---+ стек

{ ¦ ¦ --¦-¬ L---- x ¦

dat a,b,*p; ¦ ¦ ¦ =========> ¦

a = *Inc(Inc(&b)); ¦ ¦ L---- +-¦-+

p = Inc(&b); ¦ ¦ ----¬ ¦return(x)

a = *p; ¦ L-- <===========} ¦ L--- ¦ ----¬

L====> a ¦

L---

Пример 3. Параметры - неявные ссылки

---------------------------------------------------------dat& Inc(dat& x) x.day++ неявная ссылка dat* px

{ x.day++

x.day++; ----¬ стек +---+

return(x); г===== b <--------- px¦

} ¦ --> <----¬ +-¦-+

¦ ¦ L---- ¦ ¦return(px)

¦ ¦ г======+=====void main() a=*..¦ ¦ ¦ ¦ +---+ стек

{ ¦ ¦ --¦-¬ L---- px¦

dat a,b,*p; ¦ ¦ ¦ =========> ¦

a = Inc(Inc(b)); ¦ ¦ L---- +-¦-+

p = &Inc(b); ¦ ¦ ----¬ ¦return(px)

a = *p; ¦ L-- <===========} ¦ L--- ¦ ----¬

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()

{

if (month ==2 && day==29 && year %4 ==0) return(1);

if (month ==0 || month >12 || day ==0 || day >mm[month])

return(0);

return(1);

}

//----------- Следующая дата ----------------------------------void dat::NextData()

{

day++;

if (day <= mm[month]) return;

if (month ==2 && day==29 && year %4 ==0) return;

day=1;

month++;

if (month !=13) return;

month=1;

year++;

}

//--------- Основная программа --------------------------------void main()

{

dat a;

do

{

scanf("%d%d%d", &a.day, &a.month, &a.year);

}

while(a.TestData() ==0);

a.PlusData(17);

}

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

выступать функции. Такие элементы-функции имеют следующие особенности:

- тело функции может быть определено в самой структуре (функция PlusData). В этом случае функция имеет стандартный вид;

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

имеет вид
<имя структуры>::<имя функции>
- в теле фукции неявно определен один формальный параметр с

именем 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 определять количество байтов, необходимое для размещения переменной указанного типа. В Си++ введены два оператора, аналогичные функциям malloc и free new и delete. Они отличаются от соответствующих функций тем, что допускают использования в качестве аргументов непосредственно спецификацию типа создаваемой динамической переменной и ссылки на динамическую переменную:
Си++ "Классический" Си

------------------------- ---------------------------------char *s,x[80]; char *s,x[80];

dat *p,*q; struct dat *p,*q;

void main() void main()

{ {

p = new dat; p = malloc(sizeof (struct dat));

q = new dat[15]; q = malloc(15*sizeof (struct dat));
  1   2   3



Рефераты Практические задания Лекции
Учебный контент

© ref.rushkolnik.ru
При копировании укажите ссылку.
обратиться к администрации