
учебное пособие. Часть1. Информатика
.pdfl = k = left; delete q;
}
else
if ( k == rigth ) //Удаление самого правого
{
q = k;
right = k->llink; rigth->rlink = NULL; k = NULL;
delete q;
}
else // Удаление из середины списка
{
q =k;
l->rlink = k->rlink; k->rlink->llink = l; delete q;
k = l->rlink;
}
}
else //Если не надо удалять, то вперед
{
l = k;
k = k->rlink;
}
}
puts ( "Вывод после удаления нулей" ); puts ( "Слева напpаво " ); llist ( left ); puts ( "Спpава налево" ); rlist ( right ); return 0;
}
Следующая программа демонстрирует поиск в двусвязном списке. Постpоение двусвязного линейного списка из входной последовательности данных, читаемых из текстового файла. В каждой строке файла фамилия, имя, место рождения (название населенного пункта) и год рождения. Вывес- ти фамилии студентов, родившихся в 1985 году.
//Пример Pr59;
#include <string> #include <cstdio> using namespace std; struct node
291
{
char fam[10], im[10], mesto[10];
int godr;
node *rlink, *llink; };
int main()
{
node *k, *left, *rigth, *q; FILE *f1;
f1 = fopen ("rspisok.dat", "r" ); //Cоздание первого узла
k = new node;
fscanf ( f1, "%10s %10s %10s %d \n", k->fam, k->im, k->mesto, k->godr );
k->rlink = NULL;
k->llink = NULL; right = k;
q=k;
while ( !feof (f1) )
{
k = new node;
fscanf ( f1,"%10s %10s% 10s% d\n", k->fam, k->im, k->mesto, k->godr );
k->rlink = q; q->llink = k; q = k;
}
q->llink = NULL; left = q;
puts ( "Список всех студентов" ); k = left;
while ( k != NULL )
{
printf ( "%s %s\n", k->fam, k->mesto ); k = k->rlink;
}
puts ( "Студенты, родившиеся в 1985 году " ); k = rigth;
while ( k != NULL )
{
292
if ( k->godr == 1985 ) printf ( "%s", k->fam ); k = k->llink;
}
return 0;
}
Контрольные вопросы
1.Какая организация списков называется динамической?
2.Какая организация списка называется стеком?
3.Чем стек отличается от очереди?
4.Какие списки называются двусвязными?
5.Как организовать кольцевой список?
6.Как удалить узел из стека?
7.Как добавить узел в очередь?
8.Какие преимущества появляются у двусвязных списков в отличии от односвязных?
9.Как удалить узел из очереди?
10.Как добавить узел в стек?
293
12. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
Объектно-ориентированный язык программирования – это обычно язык программирования, который предоставляет удобные механизмы под- держки объектно-ориентированного стиля программирования [25].
Класс является ключевым механизмом, с введением которого язык С приобретает объектно-ориентированные черты и становится С++. Концепция классов предоставляет программисту средства создания но- вых типов, которые также удобны как встроенные типы. Класс – это производный тип, введенный программистом на основе уже сущест- вующих типов. Класс задает структурированную совокупность типизи- рованных данных и набор правил преобразования этих данных. Меха- низм классов позволяет создавать типы, с наибольшей полнотой ото- бражающие особенности решаемой задачи. Таким образом, тип, опреде- ляемый программистом, ведет себя так же как встроенный. Такой тип часто называют абстрактным типом данных. Для абстрактного типа пра- вильность использования данных гарантируется тем, что для данных разрешаются правила преобразования из заданного набора. В качестве правил преобразования выступают функции, конструкторы. Такой под- ход называется инкапсуляцией данных.
Определение класса напоминает определение структуры, за исключе- нием того, что [26]:
• наряду с ключевым словом struct могут применяться слова class
или union;
• обычно содержит одну или несколько спецификаций доступа, зада- ваемых с помощью ключевых слов public (общедоступный), protected (защищенный), private (собственный);
•обычно включает в себя наряду с данными правила их преобразова- ния (функции-члены);
•обычно в нем имеются некоторые специальные функции, такие как конструктор и деструктор.
Рассмотрим примеры описания классов.
Описание класса rect аналогично обычной структуре.
struct rect
{
int x1; int x2; int y1; int y2;
};
Класс point содержит как данные, так и функции-члены. struct point
294
{
private
int x; // Данные класса int y;
public:
int getx (); // Функции-члены int gety ();
void setx (int); void sety (int);
};
В следующем примере класс line описывается с помощью ключевого слова class:
class line
{
point pt1; // Данные класса point pt2;
int width; public:
line ( int _x, int _y); // Конструктор ~line(); //Деструктор
};
Для изменения области видимости назначаются спецификаторы досту-
па:
public – данные и правила их преобразования доступны для функ- ций-членов этого класса и других функций программы, в которых имеется представитель класса;
private -– данные и правила их преобразования доступны только для функций-членов этого класса;
protected -– данные и правила их преобразования доступны только для функций-членов этого класса и классов, производных от него.
Описание классов с помощью ключевых слов struct и class очень похоже внешне. Различаются они спецификатором доступа по умолчанию.
Если спецификатор доступа явно не указан, то он определяется по умолчанию. Как известно, в структуре и объединении данные доступны в пределах области действия структуры, т. е. по умолчанию, доступ определя-
ется как public. В то время как в классе, описанном с помощью слова class, доступ по умолчанию определяется как private. Спецификатор доступа применяется ко всем полям класса, следующим за ним, пока не встретится другой спецификатор или кончится описание класса.
Функции-члены являются функциями, описанными внутри определе- ния класса. Тело функции может определяться внутри определения класса. В
295
этом случае она будет встроенной inline функцией-членом. Когда тело функции определяется вне тела класса, перед именем функции ставится пре- фикс из имени класса и операции расширения области видимости (::). Можно объявить встроенную функцию-член вне класса, указав в заголовке определения ключевое слово inline. Каждый представитель класса назы- вается объектом. Следует подчеркнуть, что описание класса – это описание типа и при этом не создается объект данного класса. Объекты создаются только при описании переменных, например:
line obj1, obj2, objarray[10];
При работе с многомодульными проектами определение класса должно присутствовать во всех модулях, где используются объекты данного класса или определяются его функции-члены. Поэтому целесообразно размещать определение класса в заголовочном файле и подключать его с помощью ди- рективы include в те модули, в которых оно необходимо.
Рассмотрим пример вызова функций-членов:
//Пример pr59 #include <iostream.h>
#include<assert.h>//Заголовкифункцийдиагностикипрограммы const int MAX_X = 100;
const int MAX_Y = 200; class pointcoord
{
int x, y; public:
void setcoord ( int _x, int _y )
{
assert ( _x > 0 );/*Если условие ложно, то выводится сообщение о не- выполнении условия, а также имя файла и номер строки, где это произошло, работа программы экстренно прерывается */
assert ( _x <= MAX_X ); x = _x;
assert ( _y > 0 );
assert ( _y <= MAX_Y ); y = _y;
}
void getcoord ( int &_x, int &_y ); };
void pointcoord::getcoord(int &_x, int &_y)
{
_x = x; _y = y;
}
296
void main()
{
pointcoord obj;
pointcoord *ptr_obj = &obj; obj.setcoord ( 200, 30 );
int xpoint, ypoint; ptr_obj->getcoord ( xpoint, ypoint );
cout <<"\n xpoint=" <<xpoint <<"\n ypoint="<<ypoint;
}
Одно из важнейших свойств при построении классов – наследование. Наследование – это использование ранее определенного класса. Язык С++ позволяет классу наследовать данные и функции-члены одного или несколь- ких других классов. Другими словами, новый класс может получать атрибу- ты и поведение от уже существующего класса. Новый класс называют произ- водным классом. Класс, элементы которого наследуются производным клас- сом, называется базовым классом. В свою очередь, производный класс может быть базовым для других классов.
Наследование дает возможность заключить (абстрагировать) некоторое общее или схожее поведение различных объектов в одном базовом классе. Производный класс может переопределить некоторые функции-члены базо- вого класса, наследуя, тем не менее, основной объем свойств и атрибутов ба- зового класса.
Синтаксис наследования имеет следующий вид:
сlass base
{
…
};
class derived: <спецификатор доступа>base [, <спе-
цификатор доступа> base1, …]
{
…
};
Ключ доступа не является обязательным и может быть public, private, protected. Если ключ доступа не указан, доступ определяет- ся по умолчанию. Для классов, описанных с помощью слов struct и union, – это public, а для классов, описанных с помощью слова class,
– это private.
Следующее свойство объектно-ориентированного программирования – полиморфизм. Полиморфизм реализуется посредством виртуальных функ- ций. К механизму виртуальных функций обращаются в тех случаях, когда в базовый класс необходимо поместить функцию, которая должна работать по- разному в производных классах, т. е. в каждом классе есть свой вариант этой
297
функции. Чтобы функция была виртуальной необходимо ее объявить со спе- цификатором virtual. В этом случае обеспечивается позднее или динами- ческое связывание, когда по типу объекта определяется функция, для кото- рой осуществляется вызов. В производном классе спецификатор virtual использовать необязательно.
Теперь, когда познакомились с основными свойствами объектно- ориентированного программирования: инкапсуляция, наследование и поли- морфизм, осталось познакомиться со специальными функциями – конструк- тором и деструктором.
Конструктор вызывается при создании каждого объекта класса и вы- полняет все необходимые операции, как для выделения памяти, так и для ее инициализации. Имя этой функции должно совпадать с именем класса. Такая функция вызывается автоматически при определении или размещении в па- мяти с помощью операции new каждого объекта класса. В соответствии с синтаксисом конструктор не возвращает никакого значения.
Когда объект уничтожается при завершении программы или при выходе из области действия определения соответствующего класса, не- обходимы противоположные операции и самая важная из них: освобож- дение памяти. Поэтому в определении класса явно или по умолчанию включают специальную функцию – деструктор. Имя этой функции сов- падает с именем класса и имеет префикс ~. У деструктора не может быть параметров, и он не может возвращать какой-либо результат. В неслож- ных классах деструктор обычно определяется по умолчанию и компиля- тор может предполагать для приведенных выше примеров, что имеет вид:
~point(){};
Для примера возьмем программу, не несущую особый смысл, но де- монстрирующую все свойства объектно-ориентированного программирова- ния. Эта программа многомодульная, и состоит из двух файлов vpoint.cpp и vcircle.cpp. В заголовочном файле vpoint.h, содержатся описания двух классов: класс Location предназначен для определения координат аб-
страктного объекта и класс Point – для демонстрации и удаления точки. В файле vpoint.h описаны виртуальные функции, которые используются и
переопределяются в vcircle.cpp.
//Пример pr60
//Перечисляемыйтип,используетсякаклогический enum Boolean{false,true };
//Описаниекласса,определяющегоместоположениеабстрактногообъекта class Location
{
protected: int X;
298
int Y; public:
Location ( int InitX, int InitY ); int GetX ();
int GetY (); };
// Класс Point, производныйот Location class Point : public Location
{
protected: Boolean Visible; public:
Point ( int InitX, int InitY ); // Конструктор virtual void Show (); // Виртуальные функции virtual void Hide ();
Boolean IsVisible ();
void MoveTo ( int NewX, int NewY ); };
Файл Vpoint.cpp содержит описания функций, декларированных
вvpoint.h.
//Пример pr60a
Location::Location ( int InitX, int InitY )
{
X = InitX;
Y = InitY; };
int Location::GetX ( void )
{ return X; };
int Location::GetY ( void ) { return Y;
};
//Конструктор класса Point сначала вызывает
//конструктор класса Location
Point::Point (int InitX, int InitY ):Location (InitX, InitY)
{
Visible = false;// По умолчанию сделать точку невидимой
};
void Point::Show ( void )
{
299
Visible = true;
Putpixel(X,Y,getcolor());//Показатьточкутекущимцветом };
void Point::Hide ( void )
{
Visible = false;
putpixel (X, Y, getbkcolor()); // Показать точку
// цветом фона для стирания
};
Boolean Point::IsVisible ( void )
{ return Visible; };
void Point::MoveTo ( int NewX, int NewY )
{ |
|
Hide (); |
// Сделать точку невидимой |
X = NewX; |
// Изменить координаты Х и Y на новые |
Y = NewY; |
|
Show(); |
// Показать точку на новом месте |
}; |
|
Файл Vcirce.cpp содержит описание класса Circle, производного от класса Point, и главную функцию main, демонстрирующую использование объекта MyCircle класса Circle
// Пример pr60b
class Circle : public Point
{
int Radius; public:
Circle ( int InitX, int InitY, int InitRadius ); void Show ( void );
void Hide ( void );
void Expand (int ExpandBy);//Увеличить окружность void Contract(int ContractBy);//Уменьшить окружность
};
// Конструктор Circle вызывает конструктор базового
//класса Point
Circle::Circle (int InitX, int InitY, int InitRadius) : Point(InitX, InitY)
{
Radius = InitRadius; };
void Circle::Show ()
300