Читаем Фундаментальные алгоритмы и структуры данных в Delphi полностью

Перед написанием класса связного списка нужно рассмотреть еще один вопрос. Мы начали с того, что объявили тип узла как запись (тип TSimpleNode), в которой хранятся (1) данные и (2) указатель на следующий узел списка. Второе поле записи удалить нельзя - оно требуется для организации связного списка. Но первое зависит от приложения или конкретного использования списка в данный момент. Фактически поле данных может представлять собой не одно, а несколько полей в отдельной записи или даже объект. Очень сложно написать общий класс связного списка, если неизвестен тип данных, который будет в нем содержаться.

Однако решение этой задачи существует, даже целых два. Первое - объявить класс родительского узла, который бы содержал только указатель Next, а пользователь сам в дочернем классе объявит элементы, содержащие данные узла. В таком случае реализация базового класса будет распределять и освобождать память для узла, поскольку все, что нужно для организации связного списка - это выделенные узлы, указателями Next которых можно манипулировать. Такое решение одновременно является как элегантным, так и неэлегантным. Оно соответствует парадигме объектно-ориентированного программирования, но для хранения данных приходится объявлять дочерний класс (а что если элементы, которые необходимо поместить в список, являются экземплярами класса, над которым вы не имеете контроля?).

Второе решение, которое можно считать более удачным, - представить данные в форме нетипизированного указателя. (Для этого имеются все предпосылки - в Delphi подобным образом работает стандартный класс TList.) При добавлении элемента в связный список классу списка передается только значение указателя (скажем, указателя на данные или объект в куче), а все остальное делает сам список: распределяет память под узел, устанавливает значения данных и манипулирует ссылками. Это решение обходит проблему незнания типа данных, поскольку пользователь класса может не беспокоиться об указателях Next, не должен распределять для них память, создавать специальные дочерние классы и т.д.

Но это второе решение имеет свой недостаток. Размер используемых классом узлов всегда будет равен 8 байтам - по 4 байта для указателя и данных.

-------

Обратите внимание, что в приведенных выше рассуждениях считается, что размер указателей составляет 4 байта. Можно предположить, что последующие версии Delphi будут написаны для 64-разрядных операционных систем. В таком случае длина указателей будет составлять 8 байт. Поэтому не основывайтесь на предположении, что длина указателя всегда 4 байта, пользуйтесь функцией sizeof(pointer). В будущем это существенно упростит перенос кода в новые версии Delphi. Тем не менее, в настоящей главе считается, что длина указателя составляет 4 байта, даже несмотря на то, что в коде используется sizeof(pointer). Такое допущение позволяет упростить выкладки, поскольку можно просто сказать "8 байт", а не "удвоенное значение sizeof(pointer)".

-------

Что дает нам постоянный размер узла? При необходимости записи данных в связный список нужно предварительно распределить память под узел. Для этого пришлось бы использовать очень сложный диспетчер кучи Delphi, всего лишь, чтобы распределить 8 байт памяти. Диспетчер кучи содержит код, который может манипулировать кусками памяти, а также распределять и освобождать память любого размера. Все операции выполняются быстро и эффективно. Но мы знаем, что будут использоваться только 8-байтные блоки, и нам нужны только такие блоки. Можно ли, учитывая постоянство размеров блоков, увеличить скорость распределения памяти для новых узлов и освобождения памяти, занимаемой удаляемыми узлами? Конечно же, ответ "да". Мы с помощью диспетчера кучи одновременно распределяем память для нескольких узлов, а затем используем ее по мере необходимости. Таким образом, память распределяется не для одного узла, а, скажем, для 100 узлов. Если потребуется большее количество, будет выделен еще один блок из 100 узлов.

А вот теперь начинается самое интересное. Массивы узлов хранятся во внутреннем связном списке, а узлы, которые берутся из этих массивов, попадают в свободный список, который также представляет собой связный список. Таким образом, для увеличения эффективности работы класс связного списка будет использовать массивы и собственно связные списки.

Перейти на страницу:

Похожие книги

C++
C++

С++ – это универсальный язык программирования, задуманный так, чтобы сделать программирование более приятным для серьезного программиста. За исключением второстепенных деталей С++ является надмножеством языка программирования C. Помимо возможностей, которые дает C, С++ предоставляет гибкие и эффективные средства определения новых типов. Используя определения новых типов, точно отвечающих концепциям приложения, программист может разделять разрабатываемую программу на легко поддающиеся контролю части. Такой метод построения программ часто называют абстракцией данных. Информация о типах содержится в некоторых объектах типов, определенных пользователем. Такие объекты просты и надежны в использовании в тех ситуациях, когда их тип нельзя установить на стадии компиляции. Программирование с применением таких объектов часто называют объектно-ориентированным. При правильном использовании этот метод дает более короткие, проще понимаемые и легче контролируемые программы. Ключевым понятием С++ является класс. Класс – это тип, определяемый пользователем. Классы обеспечивают сокрытие данных, гарантированную инициализацию данных, неявное преобразование типов для типов, определенных пользователем, динамическое задание типа, контролируемое пользователем управление памятью и механизмы перегрузки операций. С++ предоставляет гораздо лучшие, чем в C, средства выражения модульности программы и проверки типов. В языке есть также усовершенствования, не связанные непосредственно с классами, включающие в себя символические константы, inline-подстановку функций, параметры функции по умолчанию, перегруженные имена функций, операции управления свободной памятью и ссылочный тип. В С++ сохранены возможности языка C по работе с основными объектами аппаратного обеспечения (биты, байты, слова, адреса и т.п.). Это позволяет весьма эффективно реализовывать типы, определяемые пользователем. С++ и его стандартные библиотеки спроектированы так, чтобы обеспечивать переносимость. Имеющаяся на текущий момент реализация языка будет идти в большинстве систем, поддерживающих C. Из С++ программ можно использовать C библиотеки, и с С++ можно использовать большую часть инструментальных средств, поддерживающих программирование на C. Эта книга предназначена главным образом для того, чтобы помочь серьезным программистам изучить язык и применять его в нетривиальных проектах. В ней дано полное описание С++, много примеров и еще больше фрагментов программ.

Мюррей Хилл , Бьёрн Страуструп , Бьярн Страустрап

Программирование, программы, базы данных / Программирование / Книги по IT