
- •Полустатические структуры данных
- •4.1. Характерные особенности полустатических структур
- •4.2. Строки
- •4.2.1. Логическая структура строки
- •4.2.2. Операции над строками
- •4.2.3. Представление строк в памяти
- •Динамические структуры данных. Связные списки
- •5.1. Связное представление данных в памяти
- •5.2. Стеки
- •5.2.1. Логическая структура стека
- •5.2.2. Машинное представление стека и реализация операций
- •5.2.3. Стеки в вычислительных системах
- •5.3. Очереди fifo
- •5.3.1. Логическая структура очереди
- •5.3.2. Машинное представление очереди fifo и реализация операций
- •5.3.3. Очереди с приоритетами
- •5.3.4. Очереди в вычислительных системах
- •5.4. Деки
- •5.4.1. Логическая структура дека
- •5.4.2. Деки в вычислительных системах
- •5.5. Связные линейные списки
- •5.5.1. Машинное представление связных линейных списков
- •5.5.2. Реализация операций над связными линейными списками
- •5.5.3. Применение линейных списков
- •5.6 Мультисписки
- •5.7. Нелинейные разветвленные списки
- •5.7.1. Основные понятия
- •5.7.2. Представление списковых структур в памяти
- •5.7.3. Операции обработки списков
- •5.8. Управление динамически выделяемой памятью
- •6. Деревья
- •6.1. Бинарные деревья
- •6.2. “Прошитые” деревья
- •6.3. Графы
- •6.4. Алгоритмы поиска путей в графе
- •6.4.1. Путь с минимальным количеством промежуточных вершин (волновой алгоритм)
- •6.4.2. Путь минимальной суммарной длины во взвешенном графе с неотрицательными весами (алгоритм Дейкстры)
- •6.4.3. Путь минимальной суммарной длины во взвешенном графе с произвольными весами для всех пар вершин (алгоритм Флойда)
- •6.4.4. НахождениеKпутей минимальной суммарной длины во взвешенном графе с неотрицательными весами (алгоритм Йена)
- •7. Классы и объекты
- •8. Рекурсия
- •8.1. Некоторые задачи, где можно применить рекурсию
- •8.2. Использование рекурсии в графике
- •8.2.1. Кривые Гильберта
- •8.2.2. Кривые Серпинского
- •9. Алгоритмы Сжатия информации
- •9.1. Что такое архивирование и зачем оно нужно
- •9.2. Терминология
- •9.3. Методы кодирования
- •9.4. Модели входного потока
- •9.5. Моделирование и энтропия
- •9.6. Адаптированные и неадаптированные модели
- •9.7. Алгоритмы архивации данных
- •9.8. Сжатие способом кодирования серий (rle)
- •9.9. Алгоритм Хаффмана
- •9.10. Арифметическое кодирование
- •9.11. Алгоритм Лемпеля-Зива-Велча (Lempel-Ziv-Welch - lzw)
- •9.11.1. Двухступенчатое кодирование. Алгоритм Лемпеля-Зива
- •Библиографический Список
- •Оглавление
7. Классы и объекты
По мере прогресса в области вычислительной математики акцент в программировании смещается в какую-либо сторону. Первой была идея процедурного структурирования программ, затем – организация данных. В настоящее время акцент делается на объектно-ориентированное программирование(ООП).
Его руководящая идея заключается в стремлении связать данные с обрабатывающими эти данные функциями в единое целое – объект.
Класс (class) и объект (object) – два общепринятых термина. Однако, поскольку имеется несколько различных трактовок этих понятий, давайте сразу договоримся о том, как их будем понимать мы. Классом называется определенный пользователем тип данных, который включает в себя состояние (представление класса) и некие операции (поведение класса). Класс имеет некоторое количество внутренних данных и несколько методов, существующих в форме функций, и обычно описывает общее поведение и характеристики набора аналогичных друг другу объектов.
Объект есть экземпляр класса или, другими словами, переменная, тип которой является классом. Объекты в отличие от классов реальны в том смысле, что во время выполнения программы они хранятся в памяти. Соотношения между объектом и классом аналогичны соотношениям между переменной и типом.
К сожалению, в некоторых языках и средах эта разница не столь ясна. Дополнительную сложность создает также то обстоятельство, что ранние версии компилятора Borland Pascal использовали ключевое слово object для определения классов. По этой причине программисты на Pascal со стажем предпочитают использовать для обозначения типа термин “объект” вместо термина “класс”, а для обозначения реальных объектов - термин “экземпляр объекта” (object instance).
Чтобы определить в С++ новый класс данных, имеющий свои поля данных и методы, используется следующий синтаксис:
class СDate
{
int Month, Day, Year;
public:
void SetValue( int m, int d, int y );
bool LeapYear();
};
Методы класса должны быть реализованы в коде: чтобы подчеркнуть, что они являются частью класса Tdate.
В языке С++ принимается соглашение об использование трефикса С перед именем каждого определяемого класса. Это лишь соглашение, поскольку компилятор воспринимает символ С точно так же, как и все остальные буквы, но оно настолько распространено, что следование ему сделает ваш код значительно понятнее. Другое соглашение заключается в том, чтобы использовать двух- или трехбуквенный префикс для указания разработчика класса ( фирмы или конкретного человека). Это соглашение является общепринятым для компонентов. ( Например: cl_Date )
Для илюстрации использования объектов в программах, приведен класс простейшего связного списка. Этот список является двусвязным, и содержит объекты типа Node. Обратите внимание, что список имеет один фиктивный элемент. Первый элемент списка не содержит информации и является вспомагательным.
Класс имеет два конструктора, для создания пустого списка или клона к уже имеющемуся и заполненному списку, а так же свой оператор присваивания.
typedef class Node* link;
class Node{ public: // класс элемента списка
char* str;
link next;
link prev;
Node(){ str = NULL; next = NULL; prev = NULL; }
~Node(){ if( str ) delete str; str = NULL;
if( next ) delete next; next = NULL; prev = NULL; }
};
class List { // класс списка
public:
link head; // указатель на начало списка
link last; // указатель на конец списка
public:
List(){
head = NULL; last = NULL;
CreatList();
}
List( List& lt ) {
if( lt.head )
{
head = new Node;
CopyList( head, lt.head );
}
}
~List()
{
if( head ) DeleteList( head ); last = head;
}
void Set() // функция очистит список
{
if( head ) DeleteList( head ); last = head;
}
void Set( const List& lt )
{
if( head ) DeleteList( head );
if( lt.head )
{
head = new Node;
CopyList( head, lt.head );
}
}
const List& operator = ( const List& lt )
{
if( head ) DeleteList( head );
if( lt.head!=NULL )
{
head = new Node;
CopyList( head, lt.head );
}
return *this;
}
private: // служебные функции класса
// функция копирования списка
void CopyList( link dest, link source )
{
if ( source )
{
if( source->str )
{
dest->str = new char [ strlen(source->str)+1 ];
strcpy( dest->str, source->str );
}
if( source->next )
{
dest->next = new Node;
dest->next->prev = dest;
CopyList( dest->next, source->next);
}
else
{
dest->next = NULL;
last = dest;
}
}
}
// функция удаления списка
void DeleteList( link node )
{
if( node )
{
delete node;
}
node=NULL;
}
// функция создания нового списка
void CreatList()
{
if ( head == NULL )
{
head = new Node;
head->next = NULL;
head->prev = NULL;
last = head;
}
}
public:
// функция добавления элемента в конец
void AddNodeToEnd( const Node& nd )
{
link curr = new Node;
curr->str = new char [ strlen(nd.str)+1 ];
strcpy( curr->str, nd.str );
curr->next = NULL;
curr->prev = last;
last->next = curr;
last = curr;
}
// функция добавления элемента в начало
void AddNodeToBegin( const Node& nd )
{
link curr = new Node;
curr->str = new char [ strlen(nd.str)+1 ];
strcpy( curr->str, nd.str );
curr->next = head->next;
curr->prev = head;
head->next = curr;
}
// функция печати списка по порядку
void PrintListForward()
{
if ( head != NULL && head->next != NULL )
{
link curr = head->next;
int k = 1;
printf( "\n\nText:\n" );
while( curr )
{
printf( "Line %2d -> %s\n", k++, curr->str );
curr = curr->next;
}
}
}
// функция печати списка в обратном порядке
void PrintListBack()
{
if ( head != NULL && last != NULL )
{
link curr = last;
int k = 1;
printf( "\n\nText:\n" );
while( curr->prev )
{
printf( "Line %2d -> %s\n", k++, curr->str );
curr = curr->prev;
}
}
}
};
// демонстрация использования класса
int main()
{
Node a; // создание элемента
a.str = new char[100];
strcpy( a.str, "String 1" );
List list1; // создание списка
List1.AddNodeToEnd( a ); // добавление в конец
strcpy( a.str, "String 2" );
list1.AddNodeToEnd( a );
strcpy( a.str, "String 3" );
list1.AddNodeToEnd( a );
strcpy( a.str, "String 4" );
list1.AddNodeToBegin( a ); // добавление в начало
list1.PrintListForward();
List list2( list1 ); // создание другого списка копированием
list2.PrintListBack();
printf("\n");
return 0;
};
Обратите внимание, что при копировании списка происходит выделение памяти и копирование непосредственно каждого элемента. Ни в коем случае нельзя просто присвоить указателю на начало списка значение указателя на начало другого списка, т.к. при изменение данных в одном списке, произойдет изменение данных другого списка, а при удалении списка, элементы будут удаляться дважды, что приведет к ошибкам.