Бьерн Страуструп. Язык программирования С++



бет46/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   42   43   44   45   46   47   48   49   ...   124

5.5.6 Небольшие объекты


Если в вашей программе много небольших объектов, размещаемых в

свободной памяти, то может оказаться, что много времени тратится

на размещение и удаление таких объектов. Для выхода из этой

ситуации можно определить более оптимальный распределитель памяти

общего назначения, а можно передать обязанность распределения

свободной памяти создателю класса, который должен будет

определить соответствующие функции размещения и удаления.

Вернемся к классу name, который использовался в примерах с

table. Он мог бы определяться так:


struct name {

char* string;

name* next;

double value;


name(char*, double, name*);

~name();
void* operator new(size_t);

void operator delete(void*, size_t);

private:


enum { NALL = 128 };

static name* nfree;

};
Функции name::operator new() и name::operator delete() будут

использоваться (неявно) вместо глобальных функций operator new()

и operator delete(). Программист может для конкретного типа написать

более эффективные по времени и памяти функции размещения и

удаления, чем универсальные функции operator new() и

operator delete(). Можно, например, разместить заранее "куски"

памяти, достаточной для объектов типа name, и связать их в список;

тогда операции размещения и удаления сводятся к простым операциям

со списком. Переменная nfree используется как начало списка

неиспользованных кусков памяти:


void* name::operator new(size_t)

{

register name* p = nfree; // сначала выделить


if (p)

nfree = p->next;

else { // выделить и связать в список

name* q = (name*) new char[NALL*sizeof(name) ];

for (p=nfree=&q[NALL-1]; q
next = p-1;

(p+1)->next = 0;

}
return p;

}
Распределитель памяти, вызываемый new, хранит вместе с объектом его

размер, чтобы операция delete выполнялась правильно. Этого

дополнительного расхода памяти можно легко избежать, если

использовать распределитель, рассчитанный на конкретный тип. Так,

на машине автора функция name::operator new() для хранения объекта

name использует 16 байтов, тогда как стандартная глобальная

функция operator new() использует 20 байтов.

Отметим, что в самой функции name::operator new() память нельзя

выделять таким простым способом:


name* q= new name[NALL];
Это вызовет бесконечную рекурсию, т.к. new будет вызывать

name::name().

Освобождение памяти обычно тривиально:
void name::operator delete(void* p, size_t)

{

((name*)p)->next = nfree;



nfree = (name*) p;

}
Приведение параметра типа void* к типу name* необходимо, поскольку

функция освобождения вызывается после уничтожения объекта, так что

больше нет реального объекта типа name, а есть только кусок

памяти размером sizeof(name). Параметры типа size_t в приведенных

функциях name::operator new() и name::operator delete() не

использовались. Как можно их использовать, будет показано в $$6.7.

Отметим, что наши функции размещения и удаления используются

только для объектов типа name, но не для массивов names.

5.6 Упражнения


1. (*1) Измените программу калькулятора из главы 3 так, чтобы

можно было воспользоваться классом table.

2. (*1) Определите tnode ($$R.9) как класс с конструкторами и

деструкторами и т.п., определите дерево из объектов типа

tnode как класс с конструкторами и деструкторами и т.п.

3. (*1) Определите класс intset ($$5.3.2) как множество строк.

4. (*1) Определите класс intset как множество узлов типа tnode.

Структуру tnode придумайте сами.

5. (*3) Определите класс для разбора, хранения, вычисления и печати

простых арифметических выражений, состоящих из целых констант и

операций +, -, * и /. Общий интерфейс класса должен выглядеть

примерно так:
class expr {

// ...


public:

expr(char*);

int eval();

void print();

};
Конструктор expr::expr() имеет параметр-строку, задающую выражение.

Функция expr::eval() возвращает значение выражения, а expr::print()

выдает представление выражения в cout. Использовать эти функции

можно так:


expr("123/4+123*4-3");

cout << "x = " << x.eval() << "\n";

x.print();
Дайте два определения класса expr: пусть в первом для представления

используется связанный список узлов, а во втором - строка

символов. Поэкспериментируйте с разными форматами печати

выражения, а именно: с полностью расставленными скобками,

в постфиксной записи, в ассемблерном коде и т.д.

6. (*1) Определите класс char_queue (очередь символов) так, чтобы

его общий интерфейс не зависел от представления. Реализуйте

класс как: (1) связанный список и (2) вектор. О параллельности

не думайте.

7. (*2) Определите класс histogram (гистограмма), в котором ведется

подсчет чисел в определенных интервалах, задаваемых в виде

параметров конструктору этого класса. Определите функцию

выдачи гистограммы. Сделайте обработку значений, выходящих за

интервал. Подсказка: обратитесь к .

8. (*2) Определите несколько классов, порождающих случайные числа

с определенными распределениями. Каждый класс должен иметь

конструктор, задающий параметры распределения и функцию draw,

возвращающую "следующее" значение. Подсказка: обратитесь к



и классу intset.

9. (*2) Перепишите примеры date ($$5.2.2 и $$5.2.4), char_stack

($$5.2.5) и intset ($$5.3.2), не используя никаких функций-членов

(даже конструкторов и деструкторов). Используйте только class

и friend. Проверьте каждую из новых версий и сравните их

с версиями, в которых используются функции-члены.

10.(*3) Для некоторого языка составьте определения класса для таблицы

имен и класса, представляющего запись в этой таблице. Исследуйте

транслятор для этого языка, чтобы узнать, какой должна быть настоящая

таблица имен.

11.(*2) Измените класс expr из упражнения 5 так, чтобы в выражении

можно было использовать переменные и операцию присваивания =.

Используйте класс для таблицы имен из упражнения 10.

12.(*1) Пусть есть программа:


#include
main()

{

cout << "Всем привет\n";



}
Измените ее так, чтобы она выдавала:

Инициализация

Всем привет

Удаление


Саму функцию main() менять нельзя.



Достарыңызбен бөлісу:
1   ...   42   43   44   45   46   47   48   49   ...   124




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет