Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 210
Текст из файла (страница 210)
Но сборщику мусора нет дела до замаскированных указателей; дойдя до точки №1, он преспокойно освободит память, вьшеленную ранее под целое значение. Фактически, подобные программы вряд ли будут корректно работать и без сборщика мусора, поскольку применение гегигегргег сазтдля преобразования целых в указатели в лучшем случае зависит от реализации. Объединение, которое содержит как указатели, так и не указатели, представляет особую проблему для сборщика мусора, поскольку нет способа узнать, содержит объединение в данный момент указатель или нет.
Например: ииюи «У [ шг* 8) ии г'; )' гоЫ1[1ти, б'и2, ГиЗ) [ и.р = иен )иг) и2.) = 999999; и.)=8; у... ) Здесь будет надежнее полагать, что любое значение в таком объединении представляет собой указатель. Но умный сборщик мусора может сделать кое-что и получше: он может заметить, что для данной реализации, например, целые не располагаются по нечетным адресам, или что никакой обьект не может иметь адрес 8 и т.д. Это позволит сборщику мусора воздержаться от предположения, что обьекты со значениями 999999 или 8 используются функцией 1[) . С.9.1.2. Операция де! е1е Если в конкретной реализации применяется автоматическая сборка мусора, то операции Ые1еге и 8е1еге [ ) больше не нужны, и программист может просто воздержаться от их применения. Однако помимо освобождения памяти эти операции вызывают деструкторы объектов.
Поэтому в случае присутствия сборщика мусора, Ие!еы р; вызывает деструктор для объекта, на который указывает р, а освобождение памяти откладывается до момента ее обработки сборщиком мусора. Одномоментная «переработка» сборщиком мусора множества объектов помогает избежать сильной дефрагментации памяти (5С.9.1.4). Кроме того, нивелируются в общем случае серьезные последствия ошибочного повторного удаления обьекта, деструктор которого сам освобождает памятв. Как всегда, доступ к объекту после его уничтожения не определен. 982 Приложение С.
Технические подробности С.9.1.3. Деструкторы В момент, когда сборщик мусора собирается обработать висящий объект, возникает альтернатива: 1. Вызвать для объекта деструктор (если он имеется). 2. Отнестись к объекту просто как к «сырой» памяти (то есть не вызывать деструктор). По умолчанию сборщик мусора должен выбрать второй вариант, ибо объекты, созданные операцией вел, для которых «(е1еге не вызывался, не должны уничтожаться. В то же время, можно так спроектировать сборщик мусора, чтобы он вызывал деструкторы для зарегистрированных у него обьектов.
Заметьте, что важно уничтожать объекты в правильном порядке, чтобы избегать ситуаций, когда деструктор одного объекта ссылается на обьект, который был уничтожен ранее. Сбор- шику мусора трудно реализовать правильный порядок уничтожения объектов без помощи со стороны программиста. С.9.1.4. Фрагментация памяти Когда память выделяется для множества разных объектов, а затем освобождается, то она с неизбежностью 4)рагменаируется (~арлелгз) — большая часть памяти разбивается на кусочки, слишком малые для эффективного использования.
Причина заключается в том, что универсальный распределитель памяти не всегда может найти кусок памяти нужного размера. А применить для объекта кусок памяти несколько большего размера, значит оставить часть памяти неиспользуемой. Поработает программа с таким неэффективным распределителем памяти чуть подольше, и уже добрая половина памяти использована под маленькие фрагменты, которые трудно снова пустить в дело. Существует несколько методик борьбы с фрагментацией памяти.
Простейшая методика заключается в запросе у распределителя (аллокатора, а1!оса!от) большого куска памяти, который затем нарезается на одинаковые доли под обьекты одинакового размера (915.3, 919.4.2). Поскольку большинство актов вьшеления/освобождения памяти относятся к маленьким объектам вроде узлов дерева, объектов связи (1!п)сз) и т.д., то такая методика оказывается очень эффективной.
Иногда аллокаторы могут автоматически применять похожие методики. В любом случае, фрагментация снижается еще сильнее, если все большие «куски» (спцп)гз) имеют одинаковый размер (скажем, размер страницы памяти), так что их самих можно динамически размещать в памяти без фрагментации последней. Существуют сборщики мусора двух стилей; 1. копирующий сборщик (сорутл со11есгог) для дефрагментации памяти перемешает в ней объекты. 2. Консервалшвный сборщик (солееггагве соПесюг) размещает объекты так, чтобы минимизировать фрагментацию. С точки зрения языка С++ предпочтительнее консервативный сборщик, ибо исключительно трудно (если вообще возможно) переместить объект в памяти и корректно модифицировать все указатели на него.
Кроме того, консервативный сборщик позволяет сосуществовать фрагментам программы на С++ с фрагментами на других языках, например, на С. Традиционно копирующие сборщики пользовались популярностью у программистов, имевших дело с языками типа Ь!зр и Бта1!!ай, в которых обьекты адресуются исключительно косвенно через уникальные указате- С.)0. Пространства имен 983 ли или ссылки. Для большинства больших программ, где важны объем копирования и степень взаимодействия аллокаторов со страничными подсистемами операционных систем, современные консервативные сборщики почти также эффективны, как и копирующие. Для программ поменьше часто достижим практически идеальный вариант, когда сборщик мусора вообще никогда не вызывается — особенно в С++, где многие объекты автоматические.
С.10. Пространства имен Этот раздел посвящен небольшим подробностям, относящимся к пространствам имен, которые выглядят как мелкие технические детали, но часто мелькают в публичных дискуссиях и реальном программном коде. С.10.1. Удобство против безопасности Объявление ив1ия вносит имя в локальную область видимости, а директива ив1ия — нет; она просто делает доступными все имена из области видимости, в которой они были обьявлены.
Например: иатеврасе Х ( (лс 1,1,)сс ) иоЫ)2 ( ) ( ияиеХ: с1; пяля Хс с)с ияли Хс: )с; /естог: с дважды определяется в)2() // скрывает глобальную (с с»-+; йь+с ) //Хг) // Х:)с Локально объявленное имя (обычным образом или ил1ия-объявлением) скрывает нелокаяьные обьявления того же имени, а все незаконные перегрузки имени выявляются в точке объявления. иоЫ)7 () ( сис с = 01 ияпа патеврасе Хс с'+»- с 1++ г Ь- »-; :: А'н- с Х: сйь»-с //делает доступными имена из Х //локальная 1 //Х д1 / еггогс Х:)с или глобальная )ср // глобальнал 1с //Х:(с Приложение С. Технические подробности 984 Обратите внимание на ошибку неоднозначности для а«+ в Г7О.
Глобальные имена не имеют приоритета перед именами из пространства имен, ставших доступными в глобальной области видимости. Это обеспечивает надежную защиту от случайных конфликтов имен и — что важно — нивелирует какие-либо преимущества «заселения» именами глобального пространства (с его неминуемым замусориванием). Когда библиотеки, объявляющие много имен, становятся доступными благодаря ивгая-директивам, очень важно то, что конфликты неиспользуемых имен не считаются ошибками.
Глобальная область видимости — это просто еще одно пространство имен. Оно отличается лишь тем, что его не нужно явно квалифицировать. То есть:: й означает, что й нужно искать в глобальном пространстве имен и в пространствах имен, упомянутых в из1пй-директивах, расположенных в глобальной области видимости, в то время как Х::й означает «к, обьявленное в пространстве имен Хили в пространствах имен, упомянутых в ивия-директивах в Х» (58.2.8). Я надеюсь, что в новых программах с применением пространств имен я увижу радикальное снижение использования глобального пространства имен по сравнению со старыми, традиционными программами на С и С++.
Правила работы с пространствами имен нарочито разрабатывались таким образом, чтобы у ленивого почитателя глобального пространства не было никаких преимушеств перед теми, кто не замусоривает глобальную область видимости. , С.10.2. Вложенные пространства имен Очевидное применение пространства имен заключается в том, что в него помещают весь набор объявлений и определений: аатеврасе Х У все мои объявления ) В общем случае, список объявлений может включать в себя пространства имен.
Так возникают вложенные пространства имен. Это допускается из практических соображений и согласуется с идеей, что конструкции могут быть вложенными всегда, если только нет серьезных возражений против этого. Вот пример; ыоЫ о () вате»расе Х ыоЫя (); Ю... аатеврасе У ( ыоЫ Г(); ыоЫЯ'( ); У... 985 С.11. Управление режимами доступа Применяются обычные правила видимости и квалификации: ЫХ:: У::бг() ) юЫХ::л() у'() ' у::.(() ' ) УУеггог нет (О в Х УУ о)г юЫЬ() ( ,(() ' у::(() Х::т(); Х:: У: 4'(); ) УУ еггог; нет глобальной 2() У еггог нет глобального )" УУ еггоьт нет ~() в Х УУ о/с С.10.3.
Пространства имен и классы С.11. Управление режимами доступа В настоящем разделе приведено некоторое количество технических примеров, иллюстрирующих управление режимами доступа в дополнение к примерам из 815.3. С.11.1. Доступ к членам класса Рассмотрим фрагмент кода: сйлл Х ( (перел) рготестео: (нт ргоо Пространство имен — это именованная область видимости. Класс — это тип с определенной областью видимости, которая описывает, как могут объекты класса создаваться и использоваться. Таким образом, пространство имен — это более простая концепция, нежели класс, и было бы идеально определить класс как пространство имен плюс несколько дополнительных средств. Это почти так. Пространство имен открыто (88.2.9.3), а класс закрыт.