Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Реализация с помощью классов динамических структур данных




 

Пример: реализация класса «список». Он состоит из узлов, связанных между собой с помощью указателей. Каждый узел хранит целое число, являющееся ключом списка.

Опишем вспомогательный класс для представления одного узла списка:

class Node{

public:

int d; // Данные

Node *next; // Указатель на последующий узел

Node *prev; // Указатель на предыдущий узел

Node (int dat = 0){ // Конструктор

d = dat; next = 0; prev = 0;

}

};

Этот класс будет описан внутри класса, представляющего список (класс List), поэтому для простоты доступа из внешнего класса поля сделаны доступными (public). Это позволяет обойтись без функций доступа и изменения полей.

class List{

class Node{…};

Node *pbeg, *pend; // Указатели на начало и конец списка

public:

List(){pbeg = 0; pend =0;} // Конструктор

~List (); // Деструктор

void add(int d); // Добавление узла в конец списка

Node * find (int i); // Поиск узла по ключу

Node * insert (int key, int d); // Вставка узла d после узла с ключом key:

bool remove (int key); // Удаление узла

void print(); // Печать списка в прямом направлении

void print_back(); // Печать списка в обратном направлении

};

Метод add выделяет память под новый объект типа Node и присоединяет его к списку, обновляя указатели на его начало и конец:

void List::add(int d) {

Node *pv = new Node(d); // Выделение памяти под новый узел

if (pbeg == 0) pbeg = pend = pv; // Первый узел списка

else {

// Связывание нового узла с предыдущим:

pv->prev = pend;

pend->next = pv;

pend = pv; } // Обновление указателя на конец списка

}

Можно получить отсортированный список, заменив данный метод на метод, аналогичный функции формирования отсортированного списка add_sort (лекция «Линейные списки»).

Метод find выполняет поиск узла с заданным ключом и возвращает указатель на него в случае успешного поиска и 0 в случае отсутствия такого узла в списке:

Node * List::find(int d){

Node *pv = pbeg;

while (pv){

if (pv->d == d) break;

pv = pv->next;

}

return pv;

}

Метод insert вставляет в список узел после узла с ключом key и возвращает указатель на вставленный узел. Если такого узла в списке нет, вставка не выполняется и возвращается значение 0:

Node * List::insert(int key, int d){

if(Node *pkey = find(key)){ // Поиск узла с ключом key

// Выделение памяти под новый узел и его инициализация:

Node *pv = new Node(d);

// Установление связи нового узла с последующим:

pv->next = pkey->next;

// Установление связи нового узла с предыдущим:

pv->prev = рkеу;

// Установление связи предыдущего узла с новым:

pkey->next = pv;

// Установление связи последующего узла с новым:

if (рkеу!= pend) (pv->next)->prev = pv;

// Обновление указателя на конец списка.

// если узел вставляется в конец:

else pend = pv;

return pv;

}

return 0;

}

Метод remove удаляет узел с заданным ключом из списка и возвращает значение true в случае успешного удаления и false, если узел с таким ключом в списке не найден:

bool List::remove(int key){

if(Node *pkey = find(key)){

if (pkey == pbeg){ // Удаление из начала списка

pbeg = pbeg->next;

pbeg->prev = 0;}

else if (pkey == pend){ // Удаление из конца списка

pend = pend->prev;

pend->next - 0;}

else { // Удаление из середины списка

(pkey->prev)->next = pkey->next;

(pkey->next)->prev = pkey->prev;}

delete pkey;

return true;

}

return false;

}

Методы печати списка в прямом и обратном направлении поэлементно просматривают список, переходя по соответствующим ссылкам:

void List::print (){

Node *pv = pbeg;

cout << endl << "1ist: ";

while (pv){

cout << pv->d << ' ';

pv = pv->next;}

cout << endl;

}

 

void List::print_back(){

Node *pv = pend;

cout << endl << "List back: ";

while (pv){

cout << pv->d << ' ';

pv = pv->prev;}

cout << endl;

}

Деструктор списка освобождает память из-под всех его элементов:

List::~List () {

if (pbeg!= 0){

Node *pv = pbeg;

while (pv){

pv = pv->next;

delete pbeg;

pbeg = pv;}

}

}

В функции main, используется класс List: формируется список из 5 чисел, выводится на экран, добавляется число в список, удаляется число из списка и снова выводится список на экран:

int main(){

List L;

for (int i = 2; i<6; i++) L.add(i);

L.print();

L.print_back();

L.insert(2, 200);

if (!L.remove(5)) cout << "not found";

L.print();

L.print_back(); }

Тема 2.11

Шаблоны классов

 

Шаблоны классов позволяют создавать параметризованные классы. Параметризованный класс - семейство родственных классов, которые можно применять к любому типу данных, передаваемому в качестве параметра.

Наиболее широкое применение шаблоны находят при создании контейнерных классов. Такие классы предназначены для хранения каким-либо образом организованных данных и работы с ними. Стандартная библиотека C++ содержит множество контейнерных классов для организации структур данных различного вида.

Преимущество использования шаблонов состоит в том, что если определен алгоритм работы с данными, он может применяться к любым типам данных без переписывания кода.

Пример:

Определить в виде класса параметризованного класса двухсвязный список (список может потребоваться хранить данные различных типов).

Класс List предназначен для хранения целых чисел. Чтобы хранить в нем данные любого типа, требуется описать этот класс как шаблон и передать тип в качестве параметра.

Синтаксис описания шаблона:

template <описание_параметров_шаблона> определение_класса;

Параметры шаблона перечисляются через запятую. В качестве параметров могут использоваться типы, шаблоны и переменные. Типы могут быть стандартными или определенными пользователем. Для их описания используется ключевое слово class. Внутри шаблона параметр типа может применяться в любом месте, где допустимо использовать спецификацию типа:

template <class Data> class List{

class Node{

public:

Data d;

Node *next;

Node *prev;

Node (Data dat = 0){d = dat; next = 0; prev = 0;}

};

}

Класс Data является формальным параметром. На его место при компиляции будет подставлен конкретный тип данных.

Для любых параметров шаблона могут задаваться значения по умолчанию:

template <class Т> class myarray {/*..*/};

template <class K, class V, template <class T> class С = myarray>

class Map{

C<K> key;

C<V> value;

};

Область действия параметра шаблона – от точки описания до конца шаблона, поэтому параметр можно использовать при описании следующих за ним:

template <class Т, Т* p, class U = Т> class X { /*... */ };

Методы шаблона класса автоматически становятся шаблонами функций. Если метод описывается вне шаблона, его заголовок должен иметь следующие элементы:

template <описание_параметров_шаблона>

возвр_тип имя_класса <параметры_шаблона>::

имя_функции (список_параметров функции)

Описание параметров шаблона в заголовке функции должно соответствовать шаблону класса. При этом имена параметров могут не совпадать.

template <class Data> void List<Data>::print()

{ / * тело функции */ }

<class Data> - описание параметра шаблона, void – тип возвращаемого функцией значения, List – имя класса, <Data> - параметр шаблона, print — имя функции без параметров.

В случае нескольких параметров порядок их следования в списках <описание_параметров_шаблона> и <параметры_шаблона> должен совпадать:

template<class T1, class T2> struct A{

void f1 ();

};

template <class T2, class T1> void A<T2, T1>::f1() {... }

Правила описания шаблонов:

· Локальные классы не могут содержать шаблоны в качестве элементов,

· Шаблоны методов не могут быть виртуальными,

· Шаблоны классов могут содержать статические элементы, дружественные функции и классы,

· Шаблоны могут быть производными от шаблонов и от обычных классов, а также являться базовыми для шаблонов и обычных классов,

· Внутри шаблона нельзя определять friend-шаблоны.

Пример: полное описание параметризованного класса двусвязного списка List.

template <class Data> class List{

class Node{

public:

Data d;

Node *next, *prev;

Node (Data dat = 0){d = dat; next = 0; prev = 0;}

};

Node *pbeg, *pend;

public:

List(){pbeg = 0; pend = 0;}

~List ();

void add(Data d);

Node * find(Data i);

Node * insert(Data key, Data d);

bool remove(Data key);

void print();

void print_back();

};

//--------------------------

template <class Data>

List <Data>::~List(){

if (pbeg!=0){

Node *pv = pbeg;

while (pv){

pv = pv->next;

delete pbeg;

pbeg = pv;

}

}

}

//-------------------------

template <class Data>

void List <Data>::print(){

Node *pv = pbeg;

cout << endl << "list: ";

while (pv){

cout << pv->d << ' ';

pv = pv->next;

}

cout << endl;

}

//---------------------------------

template <class Data>

void List <Data>::print_back(){

Node *pv = pend;

cout << endl << " list back: ";

while (pv){

cout << pv->d << ' ';

pv = pv->prev;

}

cout << endl;

}

//--------------------------

template <class Data>

void List <Data>::add(Data d){

Node *pv = new Node(d);

if (pbeg == 0) pbeg = pend = pv;

else{

pv->prev = pend;

pend->next = pv;

pend = pv;

}

}

//------------------------------

template <class Data>

Node * List <Data>::find(Data d){

Node *pv = pbeg;

while (pv){

if(pv->d == d) break;

pv = pv->next;

}

return pv;

}

//-----------------------------------

template <class Data>

Node * List <Data>::insert(Data key, Data d){

if(Node *pkey = find(key)){

Node *pv = new Node(d);

pv->next = pkey->next;

pv->prev = рkеу;

pkey->next = pv;

if (рkеу!= pend) (pv->next)->prev = pv;

else pend = pv;

return pv;

}

return 0;

}

// ---------------------------------

template <class Data>

bool List <Data>::remove(Data key){

if(Node *pkey = find(key)){

if (pkey == pbeg){

pbeg = pbeg->next;

pbeg->prev = 0;

}

else if (pkey == pend){

pend = pend->prev;

pend->next = 0;

}

else {

(pkey->prev)->next = pkey->next;

(pkey->next)->prev = pkey->prev;

}

delete pkey;

return true;

}

return false;

}

Если требуется использовать шаблон List для хранения данных пользовательского типа, в описание этого типа необходимо добавить перегрузку операции вывода в поток и сравнения на равенство, а если для его полей используется динамическое выделение памяти, то и операцию присваивания.

Переменные, передаваемые в шаблон, могут быть целого или перечисляемого типа, а также указателями или ссылками на объект или функцию. В теле шаблона они могут применяться в любом месте, где допустимо использовать константное выражение.

Пример: шаблон класса, содержащего блок памяти определенной длины и типа:

template <class Type, int kol>

class Block{

public:

Block(){p = new Type [kol];}

~Block(){delete [] p;}

operator Type *();

protected:

Type * p;

}:

template <class Type, int kol>

Block <Type, kol>:: operator Type *(){

return p;

}

После создания и отладки шаблоны классов удобно помещать в заголовочные файлы.





Поделиться с друзьями:


Дата добавления: 2016-11-12; Мы поможем в написании ваших работ!; просмотров: 968 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Победа - это еще не все, все - это постоянное желание побеждать. © Винс Ломбарди
==> читать все изречения...

2268 - | 2092 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.012 с.