- Любовные романы
- Фантастика и фэнтези
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
C++ - Страустрап Бьярн
Шрифт:
Интервал:
Закладка:
5.5.5 Вектора объектов класса
Чтобы описать вектор объектов класса, имеющего конструтор, этот класс должен иметь конструктор, который может вызваться без списка параметров. Нельзя использовать даже парметры по умолчанию. Например:
table tblvec[10];
будет ошибкой, так как для table::table() требуется целый параметр. Нет способа задать параметры конструктора в описании вектора. Чтобы можно было описывать вектор таблиц table, можно модифицировать описание table (#5.3.1), напрмер, так:
class table (* // ... void init(int sz); // как старый конструктор public: table(int sz) // как раньше, но без по умолчанию (* init(sz); *) table() // по умолчанию (* init(15); *) *)
Когда вектор уничтожается, деструктор должен вызываться для каждого элемента этого вектора. Для векторов, которые не были размещены с помощью new, это делается неявно. Однако для векторов в свободной памяти это не может быть сделано неявно, поскольку компилятор не может отличить указатель на один обект от указателя на первый элемент вектора объектов. Например:
void f() (* table* t1 = new table; table* t2 = new table[10]; delete t1; // одна таблица delete t2; // неприятность: 10 таблиц *)
В этом случае длину вектора должен задавать программист:
void g(int sz) (* table* t1 = new table; table* t2 = new table[sz]; delete t1; delete[] t2; *)
Но почему же компилятор не может найти число элементов вектора из объема выделенной памяти? Потому, что распределтель свободной памяти не является частью языка и может быть задан программистом.
5.5.6 Небольшие объекты
Когда вы используете много небольших объектов, размещамых в свободной памяти, то вы можете обнаружить, что ваша
программа тратит много времени выделяя и освобождая память под эти объекты. Первое решение – это обеспечить более хорший распределитель памяти общего назначения, второе для раработчика классов состоит в том, чтобы взять под контроль уравление свободной памятью для объектов некоторого класса с помощью подходящих конструкторов и деструкторов.
Рассмотрим класс 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.