Чтение онлайн

на главную - закладки

Жанры

Шрифт:

Рассмотрим класс name, который использовался в примерах table. Его можно было бы определить так:

struct name (* char* string; name* next; double value;

name(char*, double, name*); ~name; *);

Программист может воспользоваться тем, что размещение и освобождение объектов заранее известного размера можно обрбатывать гораздо эффективнее (и по памяти, и по времени), чем с помощью общей реализации new и delete. Общая идея состоит в том, чтобы предварительно разместить «куски» из объектов name, а затем сцеплять их, чтобы свести выделение и освободение к простым операциям над связанным списком. Переменная nfree является вершиной списка неиспользованных name:

const NALL = 128; name* nfree;

Распределитель, используемый операцией new, хранит рамер объекта вместе с объектом, чтобы обеспечить правильную работу операции delete. С помощью распределителя, специализрованного для типа, можно избежать этих накладных расходов. Например, на моей машине следующий распределитель использует для хранения name 16 байт, тогда как для стандартного распрделителя свободной памяти нужно 20 байт. Вот как это можно сделать:

name::name(char* s, double v, name* n) (* register name* p = nfree; // сначала выделить

if (p) nfree = p-»next; else (* // выделить и сцепить name* q = (name*)new char[ NALL*sizeof(name) ]; for (p=nfree= amp;q[NALL-1]; q«p; p–) p-»next = p-1; (p+1)-»next = 0; *)

this = p; // затем инициализировать string = s; value = v; next = n; *)

Присвоение указателю this информирует компилятор о том, что программист взял себе управление, и что не надо использвать стандартный механизм распределения памяти. Конструктор name::name обрабатывает только тот случай, когда name рамещается посредством new, но для большей части типов это всегда так. В #5.5.8 объясняется, как написать конструктор для обработки как размещения в свободной памяти, так и других видов размещения.

Заметьте, что просто как

name* q = new name[NALL];

память выделять нельзя, поскольку это приведет к бескнечной рекурсии, когда new вызовет name::name.

Освобождение памяти обычно тривиально:

name::~name (* next = nfree; nfree = this; this = 0; *)

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

5.5.7 Предостережение

Когда в конструкторе производится указателю this, значние this до этого присваивания неопределено. Таким образом, ссылка на член до этого присваивания неопределена и скорее всего приведет к катастрофе. Имеющийся компилятор не пытается убедиться в том, что присваивание указателю this происходит на всех траекториях выполнения:

mytype::mytype(int i) (* if (i) this = mytype_alloc; // присваивание членам *);

откомпилируется, и при i==0 никакой объект размещен не будет.

Конструктор может определить, был ли он вызван операцией new, или нет. Если он вызван new, то указатель this на входе имеет нулевое значение, в противном случае this указывает на пространство, уже выделенное для объекта (например, на стек). Поэтому можно просто написать конструктор, который выделяет память, если (и только если) он был вызван через new. Напрмер:

mytype::mytype(int i) (* if (this == 0) this = mytype_alloc; // присваивание членам *);

Эквивалентного средства, которое позволяет деструктору решить вопрос, был ли его объект создан с помощью new, не имеется, как нет и средства, позволяющего ему узнать, вызвала ли его delete, или он вызван объектом, выходящим из области видимости. Если для пользователя это существенно, то он может сохранить где-то соответствующую информацию для деструктора. Другой способ – когда пользователь обеспечивает, что объекты этого класса размещаются только соответствующим образом. Если удается справиться с первой проблемой, то второй способ интреса не представляет.

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

5.5.8 Объекты переменного размера

Когда пользователь берет управление распределением и овобождением памяти, он может конструировать объекты размеры, которых во время компиляции недетерминирован. В предыдущих примерах вмещающие (или контейнерные – перев.) классы vector, stack, intset и table реализовывались как структуры доступа фиксированного размера, содержащие указатели на реальную пмять. Это подразумевает, что для создания таких объектов в свободной памяти необходимо две операции по выделению памяти, и что любое обращение к хранимой информации будет содержать дополнительную косвенную адресацию. Например:

class char_stack (* int size; char* top; char* s; public: char_stack(int sz) (* top=s=new char[size=sz]; *) ~char_stack (* delete s; *) // деструктор void push(char c) (* *top++ = c; *) char pop (* return *–top; *) *);

Если каждый объект класса размещается в свободной памти, это делать не нужно. Вот другой вариант:

class char_stack (* int size; char* top; char s[1]; public: char_stack(int sz); void push(char c) (* *top++ = c; *) char pop (* return *–top; *) *);

char_stack::char_stack(int sz) (* if (this) error(«стек не в свободной памяти»); if (sz « 1) error(„размер стека « 1“); this = (char_stack*) new char[sizeof(char_stack)+sz-1]; size = sz; top = s; *)

Заметьте, что деструктор больше не нужен, поскольку пмять, которую использует char_stack, может освободить delete без всякого содействия со стороны программиста.

5.6 Упражнения

1. (*1) Модифицируйте настольный калькулятор из Главы 3, чтобы использовать класс table.

2. (*1) Разработайте tnode (#с.8.5) как класс с контрукторами, деструкторами и т.п. Определите дерево из tnode'ов как класс с конструкторами, деструкторами и т.п.

3. (*1) Преобразуйте класс intset (#5.3.2) в множество строк.

4. (*1) Преобразуйте класс intset в множество узлов node, где node – определяемая вами структура.

5. (*3) Определите класс для анализа, хранения, вычислния и печати простых арифметических выражений, состоящих из целых констант и операций +, -, * и /. Открытый итерфейс должен выглядеть примерно так:

class expr (* // ... public: expr(char*); int eval; void print; *) Параметр строка конструктора expr::expr является выржением. Функция expr::eval возвращает значение выражния, а expr::print печатает представление выражения в cout. Программа может выглядеть, например, так:

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

Лекарь

Назимов Константин Геннадьевич
2. Травник
Фантастика:
фэнтези
5.25
рейтинг книги
Лекарь

An ordinary sex life

Астердис
Любовные романы:
современные любовные романы
love action
5.00
рейтинг книги
An ordinary sex life

Кодекс Охотника. Книга XXXV

Винокуров Юрий
35. Кодекс Охотника
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга XXXV

Вечный. Книга I

Рокотов Алексей
1. Вечный
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Вечный. Книга I

Курс 1. Сентябрь

Фокс Гарри
1. Маркатис
Фантастика:
аниме
фэнтези
сказочная фантастика
5.00
рейтинг книги
Курс 1. Сентябрь

Мальвиль

Мерль Робер
Фантастика:
социально-философская фантастика
научная фантастика
альтернативная история
8.29
рейтинг книги
Мальвиль

Охотник за головами

Вайс Александр
1. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
5.00
рейтинг книги
Охотник за головами

Я не князь. Книга XIII

Дрейк Сириус
13. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я не князь. Книга XIII

Убивать чтобы жить 2

Бор Жорж
2. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 2

Глэрд VIII: Базис 2

Владимиров Денис
8. Глэрд
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Глэрд VIII: Базис 2

Бояръ-Аниме. Газлайтер. Том 35

Володин Григорий Григорьевич
35. История Телепата
Фантастика:
аниме
боевая фантастика
фэнтези
5.00
рейтинг книги
Бояръ-Аниме. Газлайтер. Том 35

Гримуар темного лорда VI

Грехов Тимофей
6. Гримуар темного лорда
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Гримуар темного лорда VI

Наномашины, ученик! Том 6

Новиков Николай Васильевич
6. Первый среди карапузов
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Наномашины, ученик! Том 6

Второгодка. Книга 3. Ученье свет

Ромов Дмитрий
3. Второгодка
Фантастика:
городское фэнтези
сказочная фантастика
альтернативная история
5.00
рейтинг книги
Второгодка. Книга 3. Ученье свет