Главная » Просмотр файлов » Бьерн Страуструп

Бьерн Страуструп (947334), страница 31

Файл №947334 Бьерн Страуструп (Стpаустpуп - Книга о C++) 31 страницаБьерн Страуструп (947334) страница 312013-09-15СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 31)

Он не вызывается для t2, поскольку этот объект инициализируется

присваиванием. Тем не менее, деструктор для table вызывается три

раза: для t1, t2 и t3! Далее, стандартная интерпретация

присваивания - это копирование по членам, поэтому перед выходом

из h() t1, t2 и t3 будут содержать указатель на массив имен, память

для которого была выделена в свободной памяти при создании t1.

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

t3, будет потерян. Этих неприятностей можно избежать (см. $$1.4.2

и $$7.6).

5.5.2 Статическая память

Рассмотрим такой пример:

table tbl(100);

void f(int i)

{

static table tbl2(i);

}

int main()

{

f(200);

// ...

}

Здесь конструктор, определенный в $$5.3.1, будет вызываться дважды:

один раз для tbl и один раз для tbl2. Деструктор table::~table()

также будет вызван дважды: для уничтожения tbl и tbl2 по выходе

из main(). Конструкторы глобальных статических объектов в файле

вызываются в том же порядке, в каком встречаются в файле

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

порядке. Конструктор локального статического объекта вызывается,

когда при выполнении программы первый раз встречается определение

объекта.

Традиционно выполнение main() рассматривалось как выполнение

всей программы. На самом деле, это не так даже для С. Уже

размещение статического объекта класса с конструктором и (или)

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

будут выполняться до вызова main() и (или) по выходе из main().

Вызов конструкторов и деструкторов для статических объектов

играет в С++ чрезвычайно важную роль. С их помощью можно обеспечить

соответствующую инициализацию и удаление структур данных,

используемых в библиотеках. Рассмотрим <iostream.h>. Откуда

берутся cin, cout и cerr? Когда они инициализируются? Более

существенный вопрос: поскольку для выходных потоков используются

внутренние буфера символов, то происходит выталкивание этих

буферов, но когда? Есть простой и очевидный ответ: все действия

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

запуска main() и по выходе из нее (см. $$10.5.1). Существуют альтернативы

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

уничтожения библиотечных структур данных, но все они или очень

специализированы, или неуклюжи, или и то и другое вместе.

Если программа завершается обращение к функции exit(), то

вызываются деструкторы для всех построенных статических объектов.

Однако, если программа завершается обращением к abort(), этого

не происходит. Заметим, что exit() не завершает

программу немедленно. Вызов exit() в деструкторе может привести

к бесконечной рекурсии. Если нужна гарантия, что будут уничтожены

как статические, так и автоматические объекты, можно воспользоваться

особыми ситуациями ($$9).

Иногда при разработке библиотеки бывает необходимо или просто

удобно создать тип с конструктором и деструктором только для

одной цели: инициализации и уничтожения объектов. Такой тип

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

чтобы вызвать конструкторы и деструкторы.

5.5.3 Свободная память

Рассмотрим пример:

main()

{

table* p = new table(100);

table* q = new table(200);

delete p;

delete p; // вероятно, вызовет ошибку при выполнении

}

Конструктор table::table() будет вызываться дважды, как и деструктор

table::~table(). Но это ничего не значит, т.к. в С++ не

гарантируется, что деструктор будет вызываться только для объекта,

созданного операцией new. В этом примере q не уничтожается вообще,

зато p уничтожается дважды! В зависимости от типа p и q программист

может считать или не считать это ошибкой. То, что объект не

удаляется, обычно бывает не ошибкой, а просто потерей памяти. В то же

время повторное удаление p - серьезная ошибка. Повторное применение

delete к тому же самому указателю может привести к бесконечному

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

результат повторного удаления не определен, и он зависит от

реализации.

Пользователь может определить свою реализацию операций new и

delete (см. $$3.2.6 и $$6.7). Кроме того, можно установить

взаимодействие конструктора или деструктора с операциями new и

delete (см. $$5.5.6 и $$6.7.2). Размещение массивов в свободной

памяти обсуждается в $$5.5.5.

5.5.4 Объекты класса как члены

Рассмотрим пример:

class classdef {

table members;

int no_of_members;

// ...

classdef(int size);

~classdef();

};

Цель этого определения, очевидно, в том, чтобы classdef содержал

член, являющийся таблицей размером size, но есть сложность: надо

обеспечить вызов конструктора table::table() с параметром size. Это

можно сделать, например, так:

classdef::classdef(int size)

:members(size)

{

no_of_members = size;

// ...

}

Параметр для конструктора члена (т.е. для table::table()) указывается

в определении (но не в описании) конструктора класса, содержащего

член (т.е. в определении classdef::classdef()). Конструктор для

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

задает для него список параметров.

Аналогично можно задать параметры для конструкторов других членов

(если есть еще другие члены):

class classdef {

table members;

table friends;

int no_of_members;

// ...

classdef(int size);

~classdef();

};

Списки параметров для членов отделяются друг от друга запятыми (а не

двоеточиями), а список инициализаторов для членов можно задавать в

произвольном порядке:

classdef::classdef(int size)

: friends(size), members(size), no_of_members(size)

{

// ...

}

Конструкторы вызываются в том порядке, в котором они заданы в

описании класса.

Подобные описания конструкторов существенны для типов,

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

словами, для объектов, являющихся членами класса с конструктором,

для постоянных членов или для членов типа ссылки. Однако, как

показывает член no_of_members из приведенного примера, такие

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

типа.

Если конструктору члена не требуется параметров, то и не нужно

задавать никаких списков параметров. Так, поскольку конструктор

table::table() был определен со стандартным значением параметра,

равным 15, достаточно такого определения:

classdef::classdef(int size)

: members(size), no_of_members(size)

{

// ...

}

Тогда размер таблицы friends будет равен 15.

Если уничтожается объект класса, который сам содержит объекты

класса (например, classdef), то вначале выполняется тело

деструктора объемлющего класса, а затем деструкторы членов в порядке,

обратном их описанию.

Рассмотрим вместо вхождения объектов класса в качестве членов

традиционное альтернативное ему решение: иметь в классе указатели

на члены и инициализировать члены в конструкторе:

class classdef {

table* members;

table* friends;

int no_of_members;

// ...

};

classdef::classdef(int size)

{

members = new table(size);

friends = new table; // используется стандартный

// размер table

no_of_members = size;

// ...

}

Поскольку таблицы создавались с помощью операции new, они должны

уничтожаться операцией delete:

classdef::~classdef()

{

// ...

delete members;

delete friends;

}

Такие отдельно создаваемые объекты могут оказаться полезными, но

учтите, что members и friends указывают на независимые от них

объекты, каждый из которых надо явно размещать и удалять. Кроме

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

больше места, чем объект-член.

5.5.5 Массивы объектов класса

Чтобы можно было описать массив объектов класса с конструктором,

этот класс должен иметь стандартный конструктор, т.е. конструктор,

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

table tbl[10];

будет создан массив из 10 таблиц, каждая из которых инициализируется

вызовом table::table(15), поскольку вызов table::table() будет

происходить с фактическим параметром 15.

В описании массива объектов не предусмотрено возможности указать

параметры для конструктора. Если члены массива обязательно надо

инициализировать разными значениями, то начинаются трюки с

глобальными или статическими членами.

Когда уничтожается массив, деструктор должен вызываться для

каждого элемента массива. Для массивов, которые размещаются не

с помощью new, это делается неявно. Однако для размещенных в свободной

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

не отличит указатель на отдельный объект массива от указателя на начало

массива, например:

void f()

{

table* t1 = new table;

table* t2 = new table[10];

delete t1; // удаляется одна таблица

delete t2; // неприятность:

// на самом деле удаляется 10 таблиц

}

В данном случае программист должен указать, что t2 - указатель

на массив:

void g(int sz)

{

table* t1 = new table;

table* t2 = new table[sz];

delete t1;

delete[] t2;

}

Функция размещения хранит число элементов для каждого размещаемого

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

delete[] освобождает функцию размещения от обязанности хранить счетчики

числа элементов для каждого массива. Исполнение такой обязанности в

реализациях С++ вызывало бы существенные потери времени и памяти

и нарушило совместимость с С.

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<p; p--) p->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().

Характеристики

Тип файла
Документ
Размер
4,26 Mb
Тип материала
Учебное заведение
Неизвестно

Список файлов книги

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6418
Авторов
на СтудИзбе
307
Средний доход
с одного платного файла
Обучение Подробнее