Глава 13
Джесс Либерти "С++ за 21 день"
Вверх
Связанный список состоит из блоков. Класс самого блока пока остается абстрактным.
Посмотрите программу которая подробнейшим образом демонстрирует работу
#include <iostream>
enum
{
//Класс Data для размещения в связанном списке
class
Data
int Compare(const Data &);
private:
int myValue;
-/-/-/-2.
//Функция Compare() используется , чтобы определить
int Data::Compare(const Data & theOtherData)
// forward declarations объявления:
-/-/-/-3.
// ADT -представление объекта блока в списке
class
Node
//базовый класс
-/-/-/-4.
//Этот блок на действительно будет содержать объект
class
InternalNode:
public Node
private:
-/-/-/-5.
// конструктор лишь инициализирует значения защищенных переменных
InternalNode::InternalNode(Data * theData, Node * next):
-/-/-/-6.
//Суть списка:
Node * InternalNode::Insert(Data * theData)
// is the new guy bigger or smaller than me?
switch(result)
//он больше меня, поэтому пошел он к следующему
case kIsSmaller:
-/-/-/-7.
//хвостовой блок списка-не больше чем сторож
class
TailNode
: public Node
private:
-/-/-/-8.
//Если данные дошли до меня, то их следует
Node * TailNode::Insert(Data * theData)
-/-/-/-9.
//Головной блок не содержит ни каких данных
class
HeadNode
: public Node
-/-/-/-10.
//Хвост списка создается точно так же
163 HeadNode::HeadNode()
-/-/-/-11.
//Ничто не может находиться перед головным блоком
170:Node * HeadNode::Insert(Data * theData)
-/-/-/-12.
//Я только раздаю данные
class
LinkedList
-/-/-/-13.
//При рождении я создаю головной блок списка
LinkedList::LinkedList()
-/-/-/-14.
// Передать, передать, передать
void LinkedList::Insert(Data * pData)
* * * * * * * * * * *
// test driver program
int main()
// ask the user to produce some values
// now walk the list and show the data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
What value? (0 to stop): 9
Анализ:
-.-.-.- 1.
В первую очередь следует обратить внимание на перечисление enum,
Каждый сохраняемый в списке объект должен иметь метод Compare() ,
-.-.-.- 2.
Исключительно для иллюстрации работы со списком в строках 30-39
int Compare(const Data &);
private:
int myValue;
метод Compare() которого реализован в строках 41-51.
//Метод Compare() используется , чтобы определить относительное
Объект Data содержит значение int myValue , которое можно сравнить с
Объект Data так же содержит функцию Show(), которая отображает значение
Объект Data инициализируется при помощи конструктора
-.-.-.-3.
Кроме этого в программе есть следующие классы:
class Node;
Класс Node является базовым для остальных трех классов
// Каждый производный класс должен переопределить
Как видим функция Insert()
возвращает указатель на тип Node и в качестве аргумента ей тоже
-.-.-.-4.
Кроме того в программе создан класс LinkedList(связный список) строка 177
//Я только раздаю данные
Как видим здесь есть закрытый член -указатель на тип HeadNode (головной блок списка)
private:
-.-.-.-.- 4.
Простейший способ понять работу связного списка-использовать его на практике.
Посмотрим главную программу main() :
в строке 206 объявлен указатель на объект класса Data.
Далее В строке 208 -создан объект класса LinkedList
При создании объекта связанного списка, вызывается его конструктор.
Единственной задачей конструктора является создание объекта HeadNode
private:
Итак конструктор для LinkedList ll:
//При рождении я создаю блок заголовка списка
192 LinkedList::LinkedList() //конструктор создает голову списка
Получается что конструктор привязывает ранее объявленный указатель
Итак головной блок -объект HeadNode класса HeadNode создан в динамической памяти
-.-.-.- 5.
Итак при создании объекта класса LinkedList вызывается его
Итак в программе как мы знаем объявлен класс HeadNode:
//Головной блок не содержит ни каких данных
Как видим в классе HeadNode объявлен указатель на тип Node
Создание объекта HeadNode класса HeadNode вызывает одноименный
Итак конструктор для объекта HeadNode:
//Хвост списка создается точно так же
Получается что конструктор HeadNode создает хвостовой блок (TailNode) и
Итак хвостовой блок-объект TailNode класса TailNode создан!
-.-.-.-.-6.
Создание хвостового блока-объекта TailNode класса TailNode вызывает одноименный
Вот так легко и просто создается связанный список.
Сначала в стеке создается сам список,
Внутри этого списка объявлен указатель на тип HeadNode (головной блок)
Как мы видим создан список который указывает на созданный головной блок,
* * * * * * * * * * * * * *
В строке 212 начинается бесконечный цикл.
Пользователю предлагается ввести значение, которое будет добавлено
С каждой итерацией в динамической памяти будет создан новый объект
Итак если введенное значение отличается от 0, то в строке 218
а в строке 219 он вводится в связанный список
* * * * * * * * * * * * * *
Предположим пользователь ввел число 15,
она определена в строке 198
198: void LinkedList::Insert(Data * pData)
В качестве аргумента в этой функции используется указатель на только
связанный список немедленно передает ответственность за вставку
//Ничто не может находиться перед головным блоком
Эта функция-член класса HeadNode - Insert() как видим
Эта функция определена в строке 142
142: Node * TailNode::Insert(Data * theData)
Эта вызванная функция знает что переданный ей объект следует поставить
И мы видим как функция в строке 144 создает новый объект класса
Это приведет к вызову конструктора объекта InternalNode,
90: InternalNode::InternalNode(Data * theData, Node * next):
Как видим конструктор InternalNode просто инициализирует свой
Теперь когда новый внутренний блок InternalNode создан, и его адрес
сам этот адрес возвращается при помощи функции TailNode::Insert() стр 142,
return dataNode;
--/-/-/-/-/-/-/-/-/-/-
Вернемся к функции HeadNode::Insert(), когда адрес InternalNode
//Ничто не может находиться перед головным блоком
Теперь, поскольку указателю myNext в головном блоке присвоен
-/-/-/-/-/-/-/-/-/-/-
В заключение адрес HeadNode возвращается связанному списку
Теперь по прежнему указатель myHead объекта ll класса LinkedList
-/-/-/-/-/-/-/-
Зачем же возвращать адрес, если он не используется?
-/-/-/-/-/-/-/-/-
Итак что же произошло? Данные были вставлены в список.
* * * * * * * * * * * * * * *
После вставки первого блока. управление программой возвращается
Предположим что введено значение 3. Это приведет к созданию,
И вновь в стр 200 список передает данные своему головному блоку
Метод HeadNode::Insert() в свою очередь передает новое
Это приводит к вызову метода InternalNode::Insert() в стр 99.
99 Node * InternalNode::Insert(Data * theData)
// новичок больше или меньше меня?
switch(result)
// по соглашению если он такой же как я то идет первым
В стр 103 InternalNode использует свой указатель myData
Сравниваются два значения, и поскольку myValue равно 15, а
Для нового объекта Data создается новый блок InternalNode.
Головной -> Новый -> Текущий -> Хвостовой
-/-/-/-/-/-/-/-/-/-/-/-/-/-/-
В третий раз пользователь добавляет значение 8. Оно больше 3, но
И вновь выполняется сравнение, в результате которого создается
Главным результатом всех этих действий является то, что новый
Можно код этой прогграммы запустить в отладчике и с его помощью
Вверх
Процедурное программирование, которое было широко распространено
Вверх
Использование собственного класса массива вместо стандартного встроенного
-коллекция(Collection) -элементы упорядочены и отсортированы в определенном
-набор(set)-ни один из элементов не повторяется.
-словарь(dictionary)-набор соответствующих друг-другу пар элементов,
-разреженный массив(sparse array) -разрешены индексы любой величины,
-мешок(bag) -неупорядоченный набор элементов, добавлять и обращаться
Перегрузкой оператора индексирования [] , можно предбразовать связный
Вверх
Массив это набор строго фиксированных однотипных элементов.
Имя массива представляет собой постоянный указатель на первый его
Назад |
Начало урока |
Вверх |
Вперед
Компоненты связанных списков
сейчас рассмотрим лишь функциональное назначение блоков в списке.
Список будет иметь три компонента:головной блок, хвостовой блок, и любое количество
внутренних блоков. Фактическим хранилищем данных в списке будут внутренние блоки.
Обратите внимание, данные и список-совершенно разные вещи. Теоретически в списке
можно хранить любой тип данных. Но не из них состоит список, а из блоков,
которые могут быть связаны вместе и содержать любой тип данных.
Основной программе ничего не известно о блоках, она работает со списком
в целом. Список тоже делает не слишком много, он просто предоставляет блоки.
со связанным списком.
// ***********************************************
// FILE: Listing 13.13
//
// PURPOSE: Demonstrate ilinked list
// NOTES:
//
// COPYRIGHT: Copyright (C) 1998 Liberty Associates, Inc.
// All Rights Reserved
//
// Demonstrates an object-oriented approach to
// linked lists. The list delegates to the node.
// The node is an abstract data type. Three types of
// nodes are used, head nodes, tail nodes and internal
// nodes. Only the internal nodes hold data.
//
// The Data class is created to serve as an object to
// hold in the linked list.
//
// ***********************************************
using namespace std;
//Любой класс в связанном списке должен обладать
//двумя функциями:
//Show() -отображает значение
//Compare()-возвращает относительную позицию
//элемента в списке
-/-/-/-1.
{
Data(int val):myValue(val)
{}
~Data()
{}
void Show()
{
//относительное положение объекта в списке
{
return kIsSmaller;
if (myValue > theOtherData.myValue)
return kIsLarger;
else
return kIsSame;
class Node;
class HeadNode;
class TailNode;
class InternalNode;
// Каждый производный класс должен переопределить
// функции Insert() и Show()
{
Node()
{}
virtual ~Node()
{}
virtual Node * Insert(Data * theData)=0;
virtual void Show() = 0;
private:
//в данном случае объект имеет тип Data
//более общий случай будет продемонстрирован
//при рассмотрении шаблонов
{
InternalNode(Data * theData, Node * next);
~InternalNode()
{
delete myData;
virtual Node * Insert(Data * theData);
// delegate!
virtual void Show()
{
myNext->Show();
Data * myData; // the data itself
Node * myNext; // points to next node in the linked list
myData(theData), myNext(next)
{
//при поступлении в список нового объекта
//он передается в блок, который
//и будет добавлен в список
{
int result = myData->Compare(*theData);
{
case kIsSame: //придется пропустить новые данные
case kIsLarger: // впереди себя
{
return dataNode;
//блоку и пусть ОН его обработает
return this;
return this; // успокоить MSC
{
131 TailNode()
{}
~TailNode()
{}
virtual Node * Insert(Data * theData);
virtual void Show()
{
//поставить впереди меня, поскольку я ХВОСТ
//списка и ничего не должно быть вставлено
//после меня
{
return dataNode;//возвращен адрес вновь созданного блока InternalNode
//он только указывает на начало списка
{
HeadNode();
~HeadNode()
{
virtual Node * Insert(Data * theData);
virtual void Show()
{
private:
Node * myNext;
//как и голова
{
//поэтому данные передаются следующему блоку
{
return this;
//но сам не делаю ничего
{
LinkedList();
~LinkedList()
{
void Insert(Data * theData);
void ShowAll()
{
private:
HeadNode * myHead;
//Он создает хвост списка
//Так что пустой список указывает на голову,
//которая указывает на хвост,
//и между ними ничего нет
{
{
* * * * * * * * * * *
{
int val;
LinkedList ll;
// put them in the list
212: for (;;)
{
cin >> val;
if (!val)
break;
pData = new Data(val);
ll.Insert(pData);
ll.ShowAll();
return 0; // ll falls out of scope and is destroyed!
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Результат:
What value? (0 to stop): 3
What value? (0 to stop): 5
What value? (0 to stop): 4
What value? (0 to stop): 7
What value? (0 to stop): 13
What value? (0 to stop): 0
3
4
5
7
9
13
которое содержит три постоянных значения.
enum
{
возвращающий значение именно этого типа.
был создан класс Data,
class Data
{
Data(int val):myValue(val)
{}
~Data()
{}
void Show()
{
//положение объекта в списке
int Data::Compare(const Data & theOtherData)
{
return kIsSmaller;
if (myValue > theOtherData.myValue)
return kIsLarger;
else
return kIsSame;
аналогичным значением другого объекта Data при помощи
функции-члена Compare().
объекта на экране.
void Show()
{
Data(int val):myValue(val)
{}
// forward declarations объявления:
class HeadNode;
class TailNode;
class InternalNode;
// функции Insert() и Show()
class Node //базовый класс
{
Node()
{}
virtual ~Node()
{}
virtual Node * Insert(Data * theData)=0;
virtual void Show() = 0;
private:
virtual Node * Insert(Data * theData)=0;
передается указатель на тип Data.(То есть другими словами ей
передается в качестве аргумента адрес на объект класса Data,
а возвращает она адрес на объект класса Node.)
//но сам не делаю ничего
class LinkedList
{
LinkedList();
~LinkedList()
{
void Insert(Data * theData);
void ShowAll()
{
private:
185 HeadNode * myHead;
185 HeadNode * myHead;//указатель на тип HeadNode.
//объект HeadNode здесь еще не создан.
206: Data * pData;
локальный связанный список ll(LinkedList-связанный список).
208: LinkedList ll;//создан объект ll класса LinkedList-
//- локальный связанный список
(реализован в строке 192)
(головной блок) и присвоение адреса этого объекта указателю на головной блок.
Этот указатель был объявлен в классе LinkedList (строка 185)
а следовательно это будет одновременно и указатель на связанный список,
class LinkedList
{
185 HeadNode * myHead; //объявлен указатель на тип HeadNode (головной блок)
//Он создает блок хвоста списка
//Так что пустой список указывает на голову,
//которая указывает на хвост,
//и между ними ничего нет
{
к головному блоку, то есть теперь указатель указывает на головной блок,
который создан в динамической памяти последовательностью
таких инструкций:
private:
185 HeadNode * myHead; //указатель на тип это в объявлении класса LinkedList
myHead = new HeadNode; //переопределение указателя на головной блок,
// созданный в динамической памяти .
// эта инструкция в конструкторе
и указатель myHead указывает на него!
конструктор, который создает объект HeadNode, при создании этого
объекта (HeadNode) вызывается его собственный конструктор
который создает объект TailNode -хвостовой блок
//он только указывает на начало списка
class HeadNode : public Node
{
HeadNode();
~HeadNode()
{
virtual Node * Insert(Data * theData);
virtual void Show()
{
private:
Node * myNext;
private:
Node * myNext;//указатель на тип Node
конструктор HeadNode(), показанный в строках 163-166,
который в свою очередь создает объект TailNode(хвостовой блок)
класса NailNode и присваивает его адрес указателю MyNext(мой следующий)
Этот указатель был объявлен в классе HeadNode .
//как и голова
163 HeadNode::HeadNode() //конструктор создает хвост списка
{
помещает его в динамическую память
последовательностью таких инструкций:
private:
Node * myNext; //указатель на тип Node это в объявлении класса HeadNode
myNext = new TailNode;// переопределение указателя на объект
// TailNode (хвостовой блок) созданный в динамической
// памяти. Это в конструкторе HeadNode
конструктор TailNode(), показанный в строке 131.
Он является стандартным и ничего не делает.
class TailNode : public Node
{
131 TailNode()
{
}//конструктор который ничего не делает!
. . .
208: LinkedList ll;//создан объект ll класса LinkedList-
//- локальный связанный список
Затем когда автоматически вызывается конструктор этого объекта,
этот конструктор создает головной блок и переопределяет на него
этот указатель.Таким образом указатель теперь указывает и на
созданный головной блок и следовательно одновременно на сам
связанный список.
Внутри головного блока объявлен указатель на тип TailNode(хвостовой блок).
При создании головного блока автоматически вызывается его конструктор
который создает хвостовой блок и переопределяет этот указатель
на вновь созданный хвостовой блок.
который указывает на созданный хвостовой блок, который никуда не указывает.
Его конструктор ничего не делает -он пустой, а указателя на что-либо
хвостовой блок не имеет .
Головной и хвостовой блоки созданы в динамической памяти.
Все это делается автоматически при объявлении нами объекта класса LinkedList.
* * * * * * * * * * * * * *
212: for (;;)
{
cin >> val;
if (!val)
break;
218: pData = new Data(val);//создан объект класса Data
ll.Insert(pData);//функция Insert() вставляет объект Data в
в связанный список. Пользователь может ввести очень много значений,
пока не будет введено число 0.
Опять мы видим создание объектов в динамической памяти:
Data * pData; //объявлен указатель на тип Data (в начале главной програмы)
pData = new Data(val);//создан объкт класса Data в динамич.памяти
Data на который указывает указатель pData.
создается новый объект класса Data
218: pData = new Data(val);
при помощи вызова функции-члена класса LinkedList - Insert()
219: ll.Insert(pData);//вызов функции Insert() объектом ll
Это число будет присвоено переменной val и будет вставлено в качестве аргумента
для инициализации вновь созданного объекта типа Data в динамической памяти,
на этот объект будет указывать указатель pData, далее объект ll класса LinkedList
вызовет функцию-член класса LinkedList- Insert()
219: ll.Insert(pData);//вызов функции Insert() объектом ll
// Передать, передать, передать
{
что созданный объект класса Data.
Как видим Функция Insert(), которая является функцией-членом класса
LinkedList, вызывает посредством указателя myHead другую функцию Insert()
которая является функцией-членом класса HeadNode.
Напомним что указатель myHead указывает на объект HeadNode. Мы видим что
и в этой функции в качесве аргумента использован указатель на вновь
созданный объект класса Data.
объекта своему головному блоку, который вызывает
свою функцию -член Insert() она определена в строке 170:
//поэтому данные передаются следующему блоку
Node * HeadNode::Insert(Data * theData)
{
return this;
при помощи указателя myNext, а указатель myNext в настоящий момент
указывает на объект TailNode (хвостовой блок),
следовательно будет вызвана функция-член именно объекта этого класса.
Итак вызывается функция Insert(), которая является функцией_членом
класса TailNode .
И в этой третьей вызванной функции Insert() аргументом является
указатель на вновь созданный объект класса Data.
//Если данные дошли до меня, то их следует
//поставить впереди меня, поскольку я ХВОСТ
//списка и ничего не должно быть вставлено
//после меня
{
return dataNode;
непосредственно перед объектом TailNode (хвостовым блоком).
(Как она знает это?)
Она знает это потому что, внутри функции TailNode::Insert() создается новый
блок класса InternalNode и следовательно будет вызван конструктор этого
класса. И именно конструктор инициализирует вновь созданный объект так,
что поместит введенное пользователем значение внутрь в переменную и указатель,
который также присутствует в объектах этого класса, переадресует
на хвостовой блок списка, то есть фактически постави вновь созданный
объект как раз впереди хвостового блока. Что и должна была сделать
эта функция.
InternalNode (внутренний блок) , передает ему данные и указатель на себя.
(адрес объекта TailNode)
этот конструктор определен в строке 90
// конструктор лишь инициализирует значения myData и myNext
myData(theData), myNext(next)
{
указатель класса Data адресом объекта Data, переданного ему,
а свой указатель myNext - адресом того блока, который передал
ему этот объект. В данном случае это будет хвостовой объект списка
(помните, именно хвостовой блок списка передал его в указателе
this).
Таким образом если теперь указатель myNext этого вновь созданного
блока указывает на хвостовой блок, то тем самым значит что вновь
созданный блок стоит перед хвостовым блоком связного списка.
присвоен указателю dataNode в строке 144,
142: Node * TailNode::Insert(Data * theData)
{
return dataNode;//возвращен адрес вновь созданного блока InternalNode
и инструкции
присваивается указателю myNext объекта HeadNode. (в строке 172)
//поэтому данные передаются следующему блоку
Node * HeadNode::Insert(Data * theData)
{
return this;
адрес вновь созданного блока, то головной блок будет указывать уже не
на хвост списка, а на вновь созданный блок InternalNode.
в строке 200, дальнейшая передача прекращается , поскольку
связанный список уже "знает" адрес головного блока.
// Передать, передать, передать
198: void LinkedList::Insert(Data * pData)
{
указывает на головной блок. Поскольку функция HeadNode::Insert() вернула
адрес головного блока.
Функция Insert() объявлена в базовом классе Node ,
и возвращение его значения (адреса) необходимо
в других производных классах. Поэтому если не принять значение
возвращаемое функцией HeadNode::Insert() , то произойдет ошибка
компиляции. Во избежании этого необходимо позволить связанному
списку принять значение, возвращаемое объектом HeadNode,
и просто не использовать его.
Список передал их головному блоку. Головной блок вслепую передал
их по адресу в своем указателе, а в первом случае он указывал
на хвост списка. Хвост списка немедленно создал новый внутренний
блок, инициализировав его так, чтобы он указывал на хвост списка,
тем самым поставив новый блок перед собой.
Затем хвостовой блок списка возвратил адрес нового блока голове,
тем самым указатель myNext головного блока стал указывать
на новый блок.
Все! Конец! Данные находятся в списке в нужном месте!
* * * * * * * * * * * * * * *
к стр 214. И пользователя снова запрашивают о новом значении.
212: for (;;)
{
cin >> val;
if (!val)
break;
218: pData = new Data(val);
219 ll.Insert(pData);
и включению в список нового объекта класса Data (стр 218, 219).
HeadNode.
// Передать, передать, передать
void LinkedList::Insert(Data * pData)
{
значение по адресу в указателе myNext,
//Ничто не может находиться перед головным блоком
//поэтому данные передаются следующему блоку
Node * HeadNode::Insert(Data * theData)
{
return this;
который указывает теперь на блок, содержащий объект Data,
значением которого является 15.
//Суть списка:
//при поступлении в список нового объекта
//он передается в блок, который
//и будет добавлен в список
{
103 int result = myData->Compare(*theData);
{
case kIsSame: //придется пропустить новые данные
case kIsLarger: // впереди себя
{
return dataNode;
//он больше меня, поэтому пошел он к следующему
//блоку и пусть ОН его обработает
case kIsSmaller:
myNext = myNext->Insert(theData);
return this;
return this; // успокоить MSC
для обращения к методу Compare() своего объекта класса Data
(значением которого является 15). Это позволяет сравнить его
с вновь переданным объектом класса Data (значением которого
является 3). Это приводит к вызову метода Compare(),
представленному в стр 43.
//Функция Compare() используется , чтобы определить
//относительное положение объекта в списке
int Data::Compare(const Data & theOtherData)
{
return kIsSmaller;
if (myValue > theOtherData.myValue)
return kIsLarger;
else
return kIsSame;
theotherData.myValue -3, возвращается значение kIsLarger (если
больше). Это приведет к переходу процесса выполнения программы
к стр 112.
case kIsLarger: // впереди себя
{
return dataNode;
Новый блок указывает на текущий блок InternalNode, а новый
адрес InternalNode , возвращенный методом InternalNode::Insert(),
передается HeadNode. Теперь головной блок указывает на только что
вновь образованный блок.
Таким образом новый блок, значение объекта которого меньше значения
объекта текущего блока, вставляется в список перед текущим блоком
и список теперь выглядит так.(страница 411).
блок блок блок блок
меньше 15, поэтому должно быть вставлено между двумя существующими
блоками. Процесс протекает аналогично предыдущему, за исключением
того, что при сравнении с объектом, содержащим значение 3, вместо
kIsLarger будет возвращено kIsSmaller (означающее, что объект
содержащий значение 3, меньше чем новый объект содержащий значение
8).
Это приведет к вызову метода InternalNode::Insert в стр119.
Вместо создания и вставки нового блока, InternalNode лишь передаст
новые данные в метод Insert объекта, адрес которого содержит
указатель myNext . В данном случае будет вызван метод InsertNode
того блока InternalNode, значение объекта Data которого равно 15.
новый блок InternalNode. Указатель нового InternalNode содержит
адрес того блока InternalNode, значением объекта Data которого
является 15 и адрес которого будет передан обратно тому блоку
InternalNode, значение объекта Data которого является 3 (стр 119).
блок вставлен в список в соответствующем месте.
понаблюдать как все эти методы вызывают друг друга.
Чему вы научились?
до изобретения классов, отошло далеко на второй план. В процедурном
программировании основным методом было бы -исследование данных и вызов
соответствующих функций.
ООП подразумевает что каждый объект имеет четкие и узко-специализированные
обязанности.
Связанный список осуществляет поддержку головного блока. Головной блок
передает новые данные по адресу, содержащемуся в его указателе, не придавая
значения тому, куда именно он указывает.
Хвостовой блок создает и добавляет новый внутренний блок всякий раз, когда
получает данные. Он умеет делать только одно-если пришли данные , то их
следует поставить в список перед собой.
Внутренние блоки немного сложней:они требуют, чтобы их объект сравнил себя
с новым объектом. В зависимости от результата, новый объкт будет либо добавлен
перед существующим, либо передан следующему блоку.
Обратите вниманеи, что внутренние блоки в сравнении не участвуют, оно
выполняется самим объектом. Все, что должен уметь внутренний блок-потребовать
у объектов сравнения и ожидать один из трех возможных результатов.
Получив положительный ответ он вставляет блок в список перед собой,
в противном случае посылает его по адресу.
Итак кто же несет ответственность? В хорошо разработанной ООП программе-
никто! Каждый объект хорошо делает свое маленькое дело, а в результате
получается хорошая программа.
Классы массивов
массива дает множество преимуществ.
Во первых можно предотвратить возможность записи данных за пределы массива.
Во вторых можно создать такой класс массива, размер которого изменяется
динамически:при создании он будет размером в один элемент, а по мере
добавления новых, приобретает необходимый размер.
Элементы массива можно автоматически сортировать или выстраивать в необходимом
порядке. Кроме того такой подход позволяет создать целый ряд специальных
типов массивов. Наиболее популярными из них являются:
порядке.
в котором значение одного из них можно получить по значению другого.
но память занимают только те значения, содержащиеся в массиве,
к которым можно в произвольном порядке.
список в коллкцию. Исключив дублирование элементов, можно преобразовать
связный список в набор. Если каждый объект списка имеет пару значений,
то его можно использовать при создании словаря или разреженного массива.
Резюме.
Массивы не проверяют свой размер, поэтому вполне возможна ситуация,
когда элемент записывается за пределы области памяти, выделенной для
массива. Обычно это приводит к катастрофе. Массивы начинаются с 0.
Обычной ошибкой является попытка записи элемента номер n в массив
из n элементов.
Массивы могут быть одно или много мерными. В любом случае элементы
массива могут быть инициализированы при создании, не смотря на то,
содержит ли он встроенные типы(такие как int) или объекты класса,
располагающего стандартным конструктором.
Массивы и их содержание могут быть размещены как в динамической памяти,
так и в стеке. При удалении массивов, размещенных в динамической памяти,
не забывайте использовать квадратные скобки в операторе delete[].
элемент. Указатели и массивы используют арифметические операции над
указателями для поиска необходимого элемента массива.
Для тех наборов , размер который изначально не известен, можно создать
связанные списки. Из связанных списков можно создать множество
более сложных структур данных.
Строки-это разновидность массива символов. Язык С++ обладает специальными
средствами для работы с символьными масивами, включая возможность
инициализировать их с помощью строки заключенной в парные кавычки.
Содержание