Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

/* my_windows_header.h: */

#ifndef MY_WINDOWS_HEADER

#define MY_WINDOWS_HEADER

/* информация о заголовочном файле */

#endif

Директива

#ifndef
проверяет, не было ли нечто определено раньше; например,
#ifndef
противоположна директиве
#ifdef
. С логической точки зрения эти макросы, использующиеся для контроля исходного файла, сильно отличаются от макросов, использованных для модификации исходного кода. Просто они используют одинаковый базовый механизм для выполнения своих функций.

27.9. Пример: интрузивные контейнеры

Контейнеры из стандартной библиотеки языка С++, такие как

vector
и
map
, являются неинтрузивными; иначе говоря, они не требуют информации о типах данных, использованных как их элементы. Это позволяет обобщить их для практически всех типов (как встроенных, так и пользовательских), поскольку эти типы допускают операцию копирования. Существует и другая разновидность контейнеров — интрузивные контейнеры (intrusive container), популярные в языках C и C++. Для того чтобы проиллюстрировать использование структур, указателей и свободной памяти, будем использовать неинтрузивный список.

Определим двухсвязный список с девятью операциями.

void init(struct List* lst); /* инициализирует lst пустым */

struct List* create; /* создает новый пустой список

в свободной памяти */

void clear(struct List* lst); /* удаляет все элементы списка lst */

void destroy(struct List* lst); /* удаляет все элементы списка lst,

а затем удаляет сам lst */

void push_back(struct List* lst, struct Link* p); /* добавляет

элемент p в конец списка lst */

void push_front(struct List*, struct Link* p); /* добавляет элемент p

в начало списка lst */

/* вставляет элемент q перед элементом p in lst: */

void insert(struct List* lst, struct Link* p, struct Link* q);

struct Link* erase(struct List* lst, struct Link* p); /* удаляет

элемент p из списка lst */

/* возвращает элемент, находящийся за n до или через n узлов

после узла p:*/

struct Link* advance(struct Link* p, int n);

Мы хотим определить эти операции так, чтобы их пользователям было достаточно использовать только указатели

List*
и
Link*
. Это значит, что реализации этих функций можно кардинально изменять, не влияя на работу их пользователей. Очевидно, что выбор имен был сделан под влиянием библиотеки STL. Структуры
List
и
Link
можно определить очевидным и тривиальным образом.

struct List {

struct Link* first;

struct Link* last;

};

struct Link { /* узел двухсвязного списка */

struct Link* pre;

struct Link* suc;

};

Приведем графическое представление контейнера

List
:

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

Link
и
List
. Данные для хранения в узлах будут предоставлены позднее. Указатели
Link*
и
List*
иногда называют непрозрачными типами (opaque types); иначе говоря, передавая указатели
Link*
и
List*
своим функциям, мы получаем возможность манипулировать элементами контейнера
List
, ничего не зная о внутреннем устройстве структур
Link
и
List
.

Для реализации функций структуры

List
сначала включаем некоторые стандартные библиотечные заголовки.

#include<stdio.h>

#include<stdlib.h>

#include<assert.h>

В языке C нет пространств имен, поэтому можно не беспокоиться о декларациях или директивах

using
. С другой стороны, мы должны были бы побеспокоиться о слишком коротких и слишком популярных именах (
Link
,
insert
,
init
и т.д.), поэтому такой набор функций нельзя использовать в реальных программах.

Инициализация тривиальна, но обратите внимание на использование функции

assert
.

void init(struct List* lst) /* инициализируем *lst

пустым списком */

{

assert(lst);

lst–>first = lst–>last = 0;

}

Мы решили не связываться с обработкой ошибок, связанных с некорректными указателями на списки, во время выполнения программы. Используя макрос

assert
, мы просто получим сообщение о системной ошибке (во время выполнения программы), если указатель на список окажется нулевым. Эта системная ошибка просто выдаст нам имя файла и номер строки, если будет нарушено условие, указанное как аргумент макроса
assert
;
assert
— это макрос, определенный в заголовочном файле
<assert.h>
, а проверка доступна только в режиме отладки. В отсутствие исключений нелегко понять, что делать с некорректными указателями.

  • Читать дальше
  • 1
  • ...
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: