В большинстве случаев конфигурация по умолчанию flyweight достаточно хороша, и пользователю не нужно беспокоиться о дальнейшей настройке ее инстанций flyweight, однако, когда необходимо больше контроля над Boost. Возникает лишний вес, предоставляются комплексные механизмы выбора, настройки и даже расширения следующих аспектов реализации:
Политика отслеживания, контролирующая, как обрабатывается стоимость, хранящаяся на заводе, когда все связанные с ней объекты с массой тела уничтожаются.
Шаблон класса flyweight имеет «умный» интерфейс спецификации, с помощью которого аспекты конфигурации могут быть предоставлены в качестве дополнительных аргументов шаблона в любом порядке, который нравится пользователю. Например, тег flyweightstd::strings с set-based factory и no tracking можно указать следующим образом:
В примере кода, показанном в вводном разделе , используется "boost/flyweight.hpp" удобный заголовок, который просто включает заголовки для шаблона класса flyweight и его компонентов конфигурации по умолчанию:
Хотя технически оба типа идентичны, это так в силу совпадения, поскольку нет разумной связи между именами и IP-адресами. Внутренне тот факт, что name_t и ip_address_t являются одним и тем же типом веса, приводит к тому, что значения обоих классов хранятся вместе на одной и той же фабрике, хотя их соответствующие диапазоны, как ожидается, не пересекаются. Тагирование может быть использовано, чтобы превратить их в действительно разные типы:
Теперь name_t и ip_address_t представляют собой разные классы веса, имеющие отдельные заводы. Теги являются чисто синтаксическим устройством: любой тип может использоваться для маркировки внутри конструкции tag, хотя хороший стиль рекомендует использовать классы тегов с описательными именами, которые являются локальными в контексте, где определяется тип веса.
flyweight использует тип внутреннего компонента factory Цель которых состоит в том, чтобы хранить и извлекать различные значения, на которые ссылаются объекты в данный момент времени. По умолчанию используется фабрика на основе хешированного контейнера, так что flyweight фактически эквивалентно
flyweight<T,hashed_factory<>>
где hashed_factory — так называемый factory specifier. Повышаю. Flyweight предоставляет несколько предопределенных заводских спецификаторов, которые не только позволяют пользователю выбирать конкретный тип используемой фабрики, но и принимают свои собственные шаблонные аргументы для настройки каждой фабрики.
Заданное значение flyweight имеет ассоциированные flyweight::key_type и flyweight::value_type типы (которые равны в случае обычных flyweights или различны, если используются key-value flyweights). Кроме того, существует внутренний тип Entry, который соответствует типу объектов, фактически хранящихся на заводе: Entry содержит общие value_type объекты flyweight, а также некоторую внутреннюю бухгалтерскую информацию; также, Entry неявно конвертируем в const key_type&, так что заводы могут полагаться на key_type, чтобы искать Entries. Поскольку Entry является внутренним для реализации flyweight, пользователь не может напрямую ссылаться на него в конфигурации заводов. Вместо этого можно использовать прокси placeholder type boost::mpl::_1.
Это спецификатор, который повышает. Flyweight принимает по умолчанию, контролирует использование фабрики на внутренней основе в хеш-контейнере. Значения определяются как эквивалентные с помощью Binary PredicatePred и индексируются в заводской контейнер с использованием Hash, которая, как предполагается, является функцией hash, т.е. Unary Function присваивая каждому значению хеш-идентификатор типа std::size_t. Параметр Allocator используется заводским контейнером для распределения памяти. Типы по умолчанию для этих параметров такие, что выражение
где key_type является ключевым типом мухоловки и boost::mpl:::1, как объяснено выше, обозначает внутренний Entry тип элементов, хранящихся на заводе. Предположим, что мы хотели бы настроить hashed_factory для std::string flyweight со специальным хеш-предикатом special_hash и пользовательским распределителем custom_allocator; это будет указано следующим образом:
set_factory прибегает к std::set-подобному упорядоченному контейнеру для реализации фабрики. Сравнение должно быть Строгая слабая упорядочивание по типу значения flyweight действует; как это принято в контейнерах, заказанных STL, два значения считаются эквивалентными, если ни одно не меньше другого согласно Pred. Allocator - тип распределителя, передаваемый заводскому внутреннему контейнеру для выполнения задач, связанных с памятью. Когда используются параметры по умолчанию, выражение
Обычные компромиссы, возникающие при сравнении упорядоченных и хешированных контейнеров, также применяются при выборе между set_factory и hashed_factory: так, поиск на основе набора и вставка значений, как правило, медленнее, чем те, которые основаны на хешировании, но на последнем могут влиять патологические наихудшие сценарии с очень плохой производительностью.
Этот спецификатор можно рассматривать как обобщение hashed_factory и set_factory, где пользователь поставляет точный тип контейнера, на котором основана фабрика. Способ указания контейнера может показаться сначала немного пугающим для тех, кто не знаком с библиотекой Boost MPL: ContainerSpecifier должен быть MPL Lambda Expression таким, что при вызове с типами Entry и key_type объясняется above, он производит тип контейнера элементов Entry, удовлетворяющий следующим требованиям:
Контейнера]стабильного, кабельного и терапического кабеля, дисплея и деформации. Сверх того, что мы знаем, мы знаем, что мы знаем, что мы знаем, что мы знаем, что мы знаем, что мы знаем, что мы знаем.
Тип контейнера должен быть модельюUnique
Associative Container, где эквивалентностьEntrys определяется значениямиkey_type, к которым могут быть конвертированы записи.
Контейнер должен бытьстабильным, то есть его итераторы должны оставаться действительными после операций вставки и стирания. Обратите внимание, что это условие не удовлетворяется многими существующими реализациями хешированных контейнеров, которые аннулируют итераторы при повторной операции.
[ORIG_END] -->
Давайте посмотрим, как выглядит спецификатор контейнера с примером. Предположим, что у нас есть собственный заказанный контейнер, такой как:
Как было объяснено, mpl::_1 представляет собой так называемый заполнитель MPL, стоящий в качестве «слота», который должен быть заменен Entry внутренним механизмом Boost. Весом. Обратите внимание, что мы не полагались на аргумент по умолчанию ultrafast_set для Compare и вместо этого мы предоставили фиксированную инстанциацию для string::string: это так, потому что требования утверждают, что тип, с которым ContainerSpecifier будет заполнен внутри, конвертируем в const key_type& (здесь const std::string&), и он основан на key_type, что поиск и эквивалентность записей должны быть определены. С другой стороны, аргумент по умолчанию для параметра Allocator работает просто отлично, что более очевидно, если мы запишем его явно:
Каждый тип мухоловки, то есть каждое отдельное воплощение шаблона класса мухоловка , связан именно с одним заводским объектом. В большинстве случаев то, как создается этот заводской объект, не имеет большого значения для пользователя Boost. Вес, но есть особые обстоятельства, когда контроль над этим аспектом необходим. Внутренний компонент, называемый holder, отвечает за инстанцирование фабричного класса и некоторой другой внутренней информации; этот компонент определяется с помощью holder specifier, static_holder по умолчанию.
Это спецификатор держателя по умолчанию Boost. Flyweight и производит держателей, где уникальный завод живет как локальная статическая переменная программы.
В большинстве сред C++ статические переменные плохо смешиваются с динамически загружаемыми модулями в том смысле, что экземпляры одной и той же статической переменной могут дублироваться в разных модулях, хотя по определению переменная должна быть уникальной. Во многих случаях это дублирование остается незамеченным, если модули не взаимодействуют друг с другом с использованием затронутых типов, но рассмотрим случай, когда такая связь действительно происходит:
// module 1typedefflyweight<std::string>flyweight_string;// produce_string is exported so that it can be dynamically
// linkedflyweight_stringproduce_string(){returnflyweight_string("boost");}
// main programtypedefflyweight<std::string>flyweight_string;intmain(){...// import module 1flyweight_stringstr1=produce_string();flyweight_stringstr2("boost");assert(str1==str2);}
Во многих средах эта программа приводит к отказу утверждения, потому что объект фабрики, используемый flyweight_string Как видно в модуле 1, это не тот же заводской объект, как видно в основной программе: следовательно, представления значений, на которые внутренне указывают str1 и str2, будут отличаться и будут ошибочно считаться не равными. Многие другие проблемы могут возникнуть из-за фабричного дублирования, включая неопределенное поведение.
intermodule_holder определяет заводского владельца, который способен избежать проблемы дублирования и гарантировать, что все модули программы используют один и тот же заводской экземпляр. Чтобы исправить приведенный выше пример, достаточно переопределить flyweight_string в обоих модулях как:
intermodule_holder является значительно более обременительным, чем static_holder, с точки зрения времени компиляции и вводит незначительные накладные расходы при запуске программы.
Внутренняя фабрика, связанная с каждым типом flyweight, является общим ресурсом, и поэтому доступ к нему должен быть правильно синхронизирован в многопоточной среде. Политика блокировки определяет механизмы синхронизации, которые будут использоваться для этой цели.
Это политика блокировки по умолчанию. Он определяет простейшие нативные примитивы синхронизации, предоставляемые операционной системой, когда это возможно.
Никакая синхронизация не обеспечивается для обеспечения неограниченного внутреннего доступа к общим ресурсам реализации. Выбор no_locking приводит к несколько более быстрому выполнению, чем по умолчанию simple_locking, но это делает тип потока небезопасным, что может иметь катастрофические последствия. Эта политика не должна использоваться, кроме как в однопоточной среде или когда есть абсолютная гарантия того, что конкретный вес Тип не будет использоваться в параллельном сценарии.
Политика отслеживания контролирует время жизни объектов flyweight и может действовать на основе этой информации. Например, подходящий механизм отслеживания может определить, когда данное значение, хранящееся на заводе, может быть безопасно удалено, потому что на него больше не ссылаются никакие flyweight; это именно то, что делает политика отслеживания по умолчанию refcounted.
Эта политика отслеживания определяет, что значения, хранящиеся на заводе, должны быть оснащены механизмами подсчета ссылок, чтобы запись на заводе была стерта, когда последний объект flyweight, связанный с ним, будет уничтожен.
Отслеживание веса не выполняется при выборе этой политики, что означает, что значения, хранящиеся на заводе, остаются в нем до окончания программы. По сравнению с refcounted, no_tracking представляет преимущества и недостатки. Преимущества:
Не отслеживаемые объекты в весе летают быстрее, чем пересчитанные.
Существует некоторое сокращение использования памяти из-за отсутствия счетчиков отсчета.
whereas potential drawbacks of using no_tracking include:
Количество неиспользуемых записей, хранящихся на заводе, может продолжать расти в течение срока службы программы, что может стать проблемой для определенных моделей создания веса, где набор активных значений «дрейфует» с течением времени.
Возможна задержка при прекращении программы, так как именно тогда все заводские записи уничтожаются сразу.
Статья Boost.Flyweight Documentation - Tutorial - Configuring Boost.Flyweight раздела Boost.Flyweight Documentation - Tutorial может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.