А. Александреску - Современное проектирование на C++ (1119444), страница 28
Текст из файла (страница 28)
Часть!. Методы тевр1ате <теар1ате <с1ааа т> с1ааа тпгеаббпдмоде1> с1ааа вва11оЬ)ест : рцЬ)сс тнгеабспдмобе1<заа11ОЬ)ест> с как и раньше Опрелеления операторов пеп и бе1ете также подвергаются пластической операции. теар1ате <теар1ате <с1азз т> с1ааа тйгеабзпдмобе1> чосб* вва11оЬ)ест<тнгеабз пдмобе1>".: орегатог пепСзтб:: аз хе т зС ве) ьоск 1оск; гесцгп мул11ос:цвпзтапсеО .411осатеСзхае); ) теар1ате <тевр1ате <с1азз т> с1азз тйгеаббпдмобе1> чосб вва11оЬ)ест<тйгеабзпдмобе1>::орегатог де1ете(чохб* р, зтб::эбхе т зсхе) ьоск 1оск; мул11ос::тпзтапсеО .оеа11осате(р, асье); ) Вот и все! Все нижележащие уровни нашей реализации изменять не нужно — их функциональные возможности уже зашишены блокировкой на верхнем уровне. Синглтоны и механизмы многопоточности, предусмотренные в библиотеке ЕоЫ, свидетельствуют о могуществе повторного использования кола.
Каждый из этих вопросов — время жизни глобатьной переменной и многопоточность — по-своему сложен. Преодолеть эти сложности на основе первого способа реализации класса вва1- 1оЬ)ест слишком трудно — хотя бы потому, что в классе гсхебд11осатог приходится выполнять кэширование при инициализации одного и того же объекта для каждого потока в отдельности. 4.9.
Применение В этом разделе показано, как использовать файл ваа11оЬ). Ь в приложениях. Для того чтобы применить класс вва110Ь), нужно задать соответствующие параметры конструктора класса зва11а11осатог: размер объекта класса сйцпк и максимальный размер объекта, который может еше считаться небольшим. Что значит "небольшой" объект? Какие размеры считаются "небольшими"? Чтобы ответить на эти вопросы, вернемся к предназначению механизма распределения памяти для небольших объектов.
С его помощью мы хотели повысить эффективность использования памяти и понизить затраты времени, преодолев ограничения, наложенные стандартным механизмом. Размеры дополнительной памяти, которая используется стандартным распределителем, сильно варьируются. Как-никак, стандартный механизм распределения памяти тоже может применять стратегии, изложенные в этой главе. В большинстве случае, однако, следует ожидать, что размер вспомогательной памяти на обычных машинах будет изменяться от 4 до 32 байт для каждого объекта.
Для распределителя памяти, который для каждого объекта резервирует 1б байт дополнительной памяти, непроизводительные затраты составят 25 %; таким образом, объект размером 64 байт можно считать "небольшим". С другой стороны, если объект класса Бва11411осатог размещает в памяти большие объекты, в конце концов выделенной памяти окажется намного больше, чем 115 Глава 4. Размещение в памяти небольших объектов Таблица 4.1. Символы прапроцоссора, использованные в Файла Впза11ОЬ).Ь Символ Прелназначенне Значение по умолчанию 0ЕЕАЦЬТ СНЦНК 512Е Размер участка памяти (а байтах), заданный по умолчанию Максимальное значение, обрабатываемое классом 5ма110ЬЗА11осатог Стандартная модель потоков, используемая в приложении.
В многопоточном приложении этот символ следует определять как с1аззьече1сос!саЬ)е 4096 МАХ 5МАЬЬ ОВЗЕСТ 512Е 64 0ЕГАЦЬТ ТНЯЕА01МЦ Наследуется от файла тпгеадз.Ь 4.10. Резюме В некоторых идиомах языка С++ очень широко применяются небольшие обьекты, расположенные в динамической памяти, Это происходит благодаря тому, что в языке С++ динамический полиморфизм (шибкие ро!утойзЬ)зт) тесно связан с распределением динамической памяти и семантикой указателей и ссылок, Однако станлартные Часть!. Методы нужно (не забывайте, что класс Р)хедА11осатог стремится оставить в памяти хотя бы один объект класса спип1с, лаже если все обьекты удалены). Библиотека 1л)ц' прелоставляет выбор. В Файле 5ма110Ь).Ь опрелелены три символа препроцессора, приведенные в табл.
4.1. Все исходные Файлы, входяшие в проект, слелует компилиронать, указав один и тот же символ препроцессора (или не задавая его вообше). Если этого не сделать, ничего смертельного не случится — просто будет созлано больше обьекгов Р)хедА11осагог, предназначенных лля разных размеров.
Параметры, предусмотренные по умолчанию, предназначены для машин с разум- ным объемом физической памяти. Если в директиве вдеН пе символы млх 5мльь овзест 522е или 0еелцьт сицнк 522е определить равными нулю, то в заголовочном файле 5аа110Ь).Ь булет применяться условная компиляция, которая просто использует обычные операторы::орегатог пем и::орегатог ое1ете, не прибегая к выделению вспомогательной памяти вообше. Интерфейс объектов остается прежним, но их Функции становятся подставляемыми заглушками (!п1|пе мцЬз), что приводит к стандартному механизму распределения динамической памяти. Обычно шаблонный класс 5аа110Ь)ест имеет только один парал1етр. Для под- держки разных размеров участков памяти и небольших обьектов этот класс получает еше два шаблонных параметра.
По умолчанию ими являются константы пе- РАцьт снцнк 522е и млх 5мдьь овзест 522е соответственно. теар1ате темр1ате <с1ааа т> с)азз тпгеао1пймобе1 = 0ЕРАЦЬТ ТННЕА01НЦ, зео::а1ге т сйцп!с51ге = пегдцьт снцнк 522е, зто::азге т аах5ва110Ь)ест51ге = нлх 5мдьь овзест 522е > с1ааа 5ма110Ь)осе; Если просто написать 5аа110ЬЗ<>, вы получите класс, котоРый может работать со станлартной моделью потоков.
механизмы распределения памяти (с помошью операторов::орегасог пел и ::орегасог де1есе) часто оптимизируются для работы с большими объектами, а не маленькими. Это делает стандартный механизм распределения памяти непригодным для работы с небольшими объектами, поскольку он медленно работает и неэффективно использует память. Для решения этой проблемы необходимо создать специальные механизмы распределения памяти для небольших объектов, настроенные на работу с маленькими блоками памяти (от лесятков до сотен байтов).
Эти механизмы объединяют элементарные блоки в более крупные участки (сйилкз), стремясь минимизировать накладные расходы памяти и времени. Система поддержки выполнения программ на языке С++ сама определяет размер освобожлаемого блока. Эту информацию можно перехватить, просто применяя малоизвестную форму перегрузки оператора ое1есе. Может ли механизм распределения памяти для небольших объектов, предусмотренный в библиотеке (.о)о, работать еше быстрее? Конечно, да. Этот механизм не выходит за пределы стандарта языка С++.
Как показано выше, ~акис проблемы, как выравнивание блоков памяти, решаются традиционным способом, что приволит к снижению эффективности его работы. И все же этот механизм работает достаточно быстро, просто и надежно, что гарантирует его ш~атформную независимость. 4.11. Краткое описание механизма распределения памяти для небольших обьектов ° Механизм, реализованный в библиотеке (.о)гй имеет четыре уровня. Первый уровень состоит из закрытого типа сйцпк, прелназначенного лля организации памяти в виде участков (сйцпхз), состояших из блоков одинакового размера.
На втором уровне архитектуры находится класс язхедл11осасог, в котором используется вектор переменной длины, состояший из участков памяти. Этот класс предназначен для выполнения запросов на выделение динамической памяти. На третьем уровне расположен класс 5па110Ь)411осасог, использующий несколько объектов класса яз хеол11осасог. Это позволяет выделять память лля объекта любого размера. Небольшие объекты размещаются в памяти с помощью объекта класса ясхедл11осасог, а запросы на выделение больших участков памяти переадресовываются станлартному оператору::орегасог пеи. Последний, четвертый, уровень содержит шаблонный класс 5иа110Ь1есс, реализующий интерфейс объектов класса 5иа110Ь)л11осасог, ° Краткое описание шаблонного класса 5па110Ь) есс выглядит так.
сеир1асе сеир1асе <с1ааа т> с1ааа тйгеасн пймоде1 = оеглоьт тняеаосна, асг)::ззхе с слцпк5зхе = оегдцьт снцнк 5сге, асд::ззхе с изх5иа110Ь)есс5зхе = мдх 5мльь Овзест 51ее с1ава 5па110Ь)есс ( рцЬ)з с: асзсзс чозд* орегасог пел(зсб::ззхе с авве); зсас(с чо(б орегасог ое1есе(чо(д' р, зсб::ззгве с зсге); ч)гсца1 -5па110Ь)ессО Глава 4. Размещение в памяти небольших объектов ° Функциональные возможности механизма распределения памяти лля небольших объектов можно унаследовать от конкретизации класса виа11оЬзест. Шаблонный класс виа110Ь1ест можно конкретизировать с параметрами, заданными по умолчанию (зва11оЬЗесс«>), настроить его модель потоков или задать параметры выделяемой памяти.
* Если в нескольких потоках объекты создаются с помошью оператора пен, следует использовать многопоточную модель, задав параметр тЬгеабтпомоде1. Информация об этой модели привелена в приложении. ° Значение константы пявлц~т снцнк Бтдд по умолчанию равно 4096. ° Значение константы мах вмльь овзвст $тдв по умолчанию равно б4.
° В директиве вбеЛпе можно определить константы мах вмл~ь овзвст втдв или оввлцьт снинк зтдв, или обе одновременно, заместив их значения, принятые по умолчанию. После расширения макрос распространяется на константы типа зтд:: аз хе т (нли совместимого типа). ° Если в директиве «беФ1пе константы мак вмл~ь овзвст втдв или оввдцьт снцнк втдд определить равными нулю, то в заголовочном файле зва110Ьт'.Ь будет применяться условная компиляция, которая просто использует стандартный механизм распределения памяти.
Интерфейс остается прежним. Это полезно, если нужно сравнить работу программы со специализированным механизмом распределения памяти и без него. 118 Часть Ь Мвтоды ОБОБЩЕННЫЕ ФУНКТОРЫ Эта глава посвящена обобщенным функторам (яепега!1хед бзпсгогз) — мощной абстракции, позволяющей осуществлять несвязное взаимодействие между объектами (десоцр!ед 1пгегоЬ!есг согпгпоп(сайоп).
Обобщенные функторы особенно полезны, котла в объектах необходимо хранить запросы. Шаблон проектирования, описывающий инкапсулированные запросы, называется соваапд (Оапнпа е! а1., !995). Кратко говоря, обобщенный функтор — это любой вызов процедуры, позволенный в языке Сч.+, инкапсулированный в обьекте первого класса, гарантирующем типовую безопасность (!уреза(е йгзг-с!аза оЬ)ес!). (К первому классу относятся типы, облалаюшие всеми свойствами встроенных типов.
Например, в языке Сч-ь типы !от и сйаг являются типами первого класса. Объекты первого класса обладают полной семантикой значений. Их можно объявлять в любом месте программы, присваивать любым переменным, копировать и передавать по ссылке или по значению, а также возвращать в качестве значения функции. — Прим. ред.) Более детальное определение приводится ниже. ° Обобшенный функтор инкапсулирует вызов любой процедуры, поскольку он получает указатели на простые функции, функции-члены, функторы н даже на другие обобщенные функторы, а также некоторые или все их аргументы. ° Обобщенный функтор гарантирует типовую безопасность, поскольку он никогда не передает аргументы неверного типа неправильным функциям.