Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 210
Текст из файла (страница 210)
Простейшая стратегия — для управления соответствующими объектами в свободной памяти пользоваться автоматическими объектами. Поэтому многие контейнеры реализуются как обработчики элементов, хранящихся в свободной памяти 8 25.7). Например, автоматическая строка $(ппд Я 11.12) управляет последовательностью символов в свободной памяти и автоматически освобождает эту память, когда выходит из области видимости. Все стандартные контейнеры Я 1Г>.3, главы 17 и 20, б 22А) удобно реализовать таким образом. 921 9.9.
Управление памятью В.9.1. Автоматическая сборка мусора Когда регулярного подхода недостаточно, программист может воспользоваться диспетчером памяти. Этот диспетчер находит объекты, к которым программа болыпе не может обратиться, и забирает их память обратно, чтобы можно было хранить в ней другие объекты. Обычно ато называют автоматической сборкой лсусора или просто сборкой мусора.
Естественно, такой диспетчер памяти называют сбор!цинам мусора. Основная идея сборки мусора заключается в том, что объект, на который больше нет ссылок в программе, больше не доступен, и занимаемую им память можно безопасно использовать под какие-то другие объекты, Папример: ооЫЯ ( иМ' р = посв !пг, р=о, сйаг* д = пвш сарае Здесь присваивание р=О оставляет целую переменную без ссылки, так что ее память можно использовать под какой-нибудь другой объект. Таким образом, следующую переменную айаг мол«но расположить в той же памяти, так что а будет иметь то же значение, что первоначально имело р.
Стандарт не требует, чтобы реализация обеспечивала сборщик мусора, но сборщики мусора все больше используются в С.н- в областях, где они выгоднее ручного управления свободной памятью. При сравнении затрат учитывайте время выполнения, занимаемую память, належностгп переносимость, денежную стоимость программирования, денежную стоимость сборщика мусора и предсказуемость производительности. В.9.1.1.
Замаскированные указатели На объект нет ссылок — что зто должно означать? Рассмотрим пример: оои! Я ( !п1* р = пвш !п1; 1опд !1 = гет!егргв! сав!<1опу> фйдхРРРРОООО; 1опу !2 = гегп!вгрге! сав!«!опи> фйдхООООРГгГ, р=о, )/ точка и1: указателя на целую пвремвнную здесь нвт р = гв! и!егргв! сав!<т!'> !!1~с2? 11 теперь снова появляется ссылка на целую переменную Указатели, хранящиеся в программе как не указатели, часто называют «замаскированными указателямию В частности, указатель, первоначально находящийся в р, замаскирован в целых с1 и !2. Однако замаскированные указатели не должны беспокоить сборщик мусора.
Если сборщик мусора дойде~ до точки !11, память, хранящую целую переменную, можно взять обратно. Фактически, такие программы не могут гарантированно работать даже без использования сборщика мусора, поскольку применение гвсп!вгрге! сав!для преобразования целых в указатели в лучшем случае определяется в реализации. 922 Приложение В.
Технические подробности Объединение, которое может содержать н указатели, и не указатели, представляет особую проблему для сборшика мусора. Вообще говоря, невозможно знать, содержит такое объединение указа~ель или нет. Рассмотрим: 11 объединение о членами-указателялил и не указателями алкал СУ( ю'лг"р; (л1 й оои1У((з и, 13и2,1(иЗ) ( и р = леы Глб и2л'= 999999; ил=8; Безопасное предположение состоит в том, что любое значение, которое появляется в таком объединении, — это значение указателя. Умный сборщик мусора может сделать кое-что получше. Например, он может заметить, что (в данной реализации) целые персменныс не располагаются по нечетным адресам, и что никакие объекты не располагаются по таким маленьким адресам, как 8.
Заметив это, сборщик мусора не предположит, что об ьекты, содержащие адреса 999999 и 8, используются функцией„Я. В.9.1.2. с(е(е1е Если реализация предоставляет автоматическую уборку мусора, операторы аИе1е и г1е1е1е[) больше не нужны, чтобы освобождать память для потенциально~о повторного использования. Таким образом, пользователь, опирающийся на сборку мусора, может просто отказаться от использования этих операторов.
Однако кроме высвобождения памяти Не1е1е и г1е1е1е[] вызывают деструкторы. При наличии сборщика мусора Йе!его р; вьшывает деструктор для обьекта, па который указывает р (еслн он есть). Однако повторное использование памяти может быть отложено до тех пор, пока она не будет собрана. Возвращение памяти из под многих объектов сразу помогает ограничить фрагментацию (С.9.1.4). Также в важных случаях, когда деструктор просто уничтожает выделенную память, сборщик мусора делает безвредной ошибку по удалению одного объекта дважды, которая в противном случае является довольно серьезной.
Как всегда, доступ к объекту после его уничтожения не определен. В.9.1.3. Деструкторы Когда сборщик мусора собирается вернуть память, занимаемую объектом, есть две альтернативы: [1[ Вызвать для объекта деструктор (еслн таковой есть). [21 Отнестись к объекту, как к «сырой» памяти (не вызывать его деструктор). По умолчанию сборщик мусора должен выбрать вариант 2, так как объекты, созданные при помощи лев и не удаленные при помощи г1е1е1е, никогда не уничтожаются. 923 В.9.
Управление памятью Таким образом, сборщик мусора можно рассматривать как механизм имитации бесконечной памяти. Можно спроектировать сборщик мусора так, чтобы он вызывал деструктор для обьектов, которые специально «зарсгистрированыь сборщиком. Однако нет стандартного способа «регистрации» объектов. Отме~им, что всегда важно уничтожать объекты в таком порядке, чтобы гарантировать, что деструктор для одного об ьекта не ссылается на об ьект, который уже был удален.
Этого нелегко добиться от сборщика мусора без помощи со стороны программиста. В.9.1.4. Фрагментация памяти Когда памя~ь выделяется и освобождается для множества объектов разного размера, она фрагжеяглируется. То есть большая часть памяти разбивается на кусочки, которые слишком малы, чтобы их эффективно использовать. Причина в том, что универсальный распределитель памяти не всегда может найти для объекта область памяти в точности нужного размера. Использовать область чуть побольше — значит оставить кусочек памяти неиспользованным. т1ерез некоторое время работы программы с простым распределителем памяти не так уж редко оказывается, что половина доступной памяти занята фрагментами, слишком маленькими для повторного использования. Для борьбы с фрагментацией существует несколько приемов. Простейший из них— запрашивать у распределителя только большие куски памяти и использовать каждый кусок для объектов одного размера (9 15.3, 9 19А.2).
Поскольку большинстворазмещений н переразмещений производится для маленьких обьектов таких типов, как узлы деревьев, связи и т. п., этот прием может оказаться очень эффективным. Иногда распределитель памяти может применять схожие приемы автоматически. В обоих случаях фрагментация еще более снижается, если все относительно большие «кускиь имеют один размер (скажем, в одну страницу), так что их самих можно размещать и переразмещать без фрагментации. Существует два стиля сборщиков мусора: [Ц Копирующий сборп1ик для сжатия фрагментированной области перемещает объекты в памяти. [2 [ Консервативный сборщик размещает объекты так, чтобы минимизировать фрагментацию. С точки зрения С++ предпочтительнес консервативные сборщики, так как очень трудно (вероятно, в реальных программах и невозможно) переместить объект и правильно модифицировать вес указатели на него.
Кроме того консервативный сборщик позволяет фрагментам программы на С++ сосуществовать с программами, написанными на таких языках, как С. Традиционно копирующие сборщики пользовались популярностью среди тех, кто программировал на языках, имеюьпих дело с объектами не непосредственно, а через уникальные указатели и ссылки (такими языками являются, например, 1.1зр и Бша11га1[г).
Однако для относительно больших программ, гле объем копирования и взаимодействие между распределителем памяти и страничной системой приобретают болыпую важность, современные консервативные сборщики, похоже, не менее эффективны, чем копирующие. Для программ поменьше часто достижим просто идеальный вариант, когда сборщик мусора никогда пе вызывается, — особенно на С++, где многие объекты естественным образом являются автоматическими. Приложение В. Технические подробности 924 В.10.
Пространства имен Этот раздел знакомит вас с подробностями, относящимися к пространствам имен, которые выглядят техническими мелочами, однако в дискуссиях и реальных про- граммах часто всплывают на поверхность. В.10.1. Удобство против безопасности изей-обьявяениедобавляет имя к локальной области видимости. изглд-директива нв добавляет имени, она просто делает доступными имена в той области видимости, в которой она указана.
Например; аатезрасеХ( тгб/й; ) (пг(г; ооЫГг )) (пгг = 0; из(па патезрасех, ) нь; йн-, ;:й++; Хг(г.~-; //делает ииена изхдоступныяи //локальная ( //Х: У //ошибка:Х: (г или глобальная нр // глобальная(г //лизх ооЫ/2 )) ( !лг!=0; из(пдХл'; изгоях.:у; изгпиХ;:(г; // ошибка; ( объявлено в/2 () движды //скрывает глобальнуш)г г'++; /++ ' й-н-; //х:.у //Хлл Локально объявленное имя (объявленное или обычным объявлением илн изгиб-объявлением) скрывает нелокальные объявления с тем же именем, и в точке объявления выявляются все незаконные перегрузки этого имени. Обратите внимание на ошибку неоднозначности для ль+ в /7 )). Глобальные имена не имеют приоритета перед именами из пространств имен, ставшими доступными в глобальной области видимости.
Этим обеспечивается значительная зашишенность от случайных конфликтов имен и — что важно — гарантируется отсутствие каких- либо выгод от замусоривания глобального пространства имен. Когда библиотека, объявляюшая много имен, делается доступной посредством из(лд-директив, очень важным достоинством является то, что конфликты неиспользуемых имен не рассматриваются как ошибки. 925 В.10, Пространства имен Глобальная область видимости — зто просто еще одно пространство имен. Оно отличается лишь тем, что вам нет необходимости упоминать его имя явно. То есть эа означает «искать /е в глобальном пространстве имен н в пространствах имен, упомянутых в ив(ля-директивах в глобальном пространстве имен», в то время как Х::/е означает «й, объявленное в пространстве имен Х или пространствах имен, упомянутых в ив(лд-директивах в Х» (9 5.2.8).
Надеюсь, что в новых программах с использованием пространств имен я увижу радикальное снижение использования глобальных имен по сравнению с традиционными программами ца С и С++. Правила работы с пространствами имен разрабатывались специально для того, чтобы «ленивые» любители глобальных имен не получали никаких преимуществ перед теми, кто старается не замусоривать глобальную область видимости. В.10.2. Вложение пространств имен Одним из очевидных примеров использования пространств имен является помеще- ние полного набора объявлений и определений в отдельное пространство имен: патеярасеХ ( // все»ии объявления Список объявлений в общем случае будет содержать пространства имен. Таким образом возникают вложенные пространства имен. Это допускается из практических соображений, а также из той простой идеи, что конструкциям следует' быть вложенными, если только нет сильных аргументов против этого.