Если вы планируете вставить класс в назойливый контейнер, вы должны принять некоторые решения, влияющие на определение класса. Каждый класс, который будет использоваться в инвазивном контейнере, нуждается в некоторых соответствующих элементах данных, хранящих информацию, необходимую для контейнера. Мы возьмем простой назойливый контейнер, назойливый список<boost::intrusive::list>, для следующих примеров, но все.Контейнеры очень похожи. Чтобы составить пример с использованием<boost::intrusive::list>, просто включите:
#include<boost/intrusive/list.hpp>
Каждый класс, который должен быть вставлен в назойливый контейнер, должен содержать крючок, который будет предлагать необходимые данные и ресурсы для вставки в контейнер. СBoost.Вы просто выбираете крючок, чтобы быть общедоступным базовым классом или публичным членом класса, который будет вставлен.Навязчивыйтакже предлагает более гибкие крючки для продвинутых пользователей, как объясняется в главеИспользование функциональных крючков, но обычно базовые или членские крючки достаточно хороши для большинства пользователей.
Класс может принять несколько вариантов.Навязчивыеклассы получают аргументы в виде<option_name<option_value>>. Вы можете указать следующие варианты:
<tag<classTag>>: Этот аргумент служит меткой, поэтому вы можете вывести из более чем одного<list_base_hook>и, следовательно, поместить объект в несколько навязчивых списков одновременно. Неполный тип может служить тегом. Если вы указали два базовых крюка, выдолжныуказать разные метки для каждого из них. Пример:<list_base_hook<tag<tag1>>>. Если тег не указан по умолчанию, он будет использоваться (подробнее о тегах по умолчанию позже).
<link_mode<link_mode_typeLinkMode>>: Второй аргумент шаблона контролирует политику связывания.Boost.Intrusiveв настоящее время поддерживает 3 режима:<normal_link>,<safe_link>и<auto_unlink>. По умолчанию используется<safe_link>режим. Подробнее об этом в разделахБезопасные крючкииАвтоматические крючки. Пример:<list_base_hook<link_mode<auto_unlink>>>
<void_pointer<classVoidPointer>>: этот вариант - тип указателя, который используется внутри крючка. Значение по умолчанию<void*>, что означает, что в крючке будут использоваться необработанные указатели. Подробнее об этом в разделе под названиемИспользование умных указателей с помощью Boost. Навязчивые контейнеры. Пример:<list_base_hook<void_pointer<my_smart_ptr<void>>>
Для следующих примеров давайте забудем опции и используем значения по умолчанию:
#include<boost/intrusive/list.hpp>usingnamespaceboost::intrusive;classFoo//Base hook with default tag, raw pointers and safe_link mode:publiclist_base_hook<>{/**/};
После этого мы можем определить навязчивый список:
template<classT,class...Options>classlist;
<list>получает тип, который должен быть вставлен в контейнер<T>, в качестве первого параметра и необязательно пользователь может указать параметры. У нас есть 3 типа вариантов:
<base_hook<classHook>>/<member_hook<classT,classHook,HookT::*PtrToMember>><value_traits<classValueTraits>>: Все эти параметры определяют связь между типом<T>, который будет вставлен в список, и крюком (поскольку мы можем иметь несколько крючков в одном<T>типе).<member_hook>будет объяснено немного позже, и<value_traits>будет объяснено вконтейнерах с пользовательскими товарными знаками.Если опция не указана, контейнер будет сконфигурирован для использования базового крючка с тегом по умолчанию. Некоторые опции, настроенные для крючка (тип указателей, режим ссылки и т.д.), будут распространяться на контейнер.
<constant_time_size<boolEnabled>>: Указывается, требуется ли для контейнера функция постоянного времени<size()>. Это позволит инвазивному контейнеру хранить дополнительный элемент, чтобы отслеживать текущий размер контейнера. По умолчанию активируется постоянный размер времени.
<size_type<classSizeType>>: Указывает неподписанный тип, который может содержать размер контейнера. Этот тип будет типом, возвращенным<list.size()>, и типом, хранящимся в интрузивном контейнере, если<constant_time_size<true>>запрашивается. Обычно пользователю не нужно менять этот тип, но некоторые контейнеры могут иметь<size_type>, которые могут отличаться от<std::size_t>(например, контейнеры, подобные STL, используют<size_type>, определенный их распределителем).Boost.Intrusiveможет использоваться для реализации таких контейнеров, определяющих тип размера. По умолчанию это<std::size_t>.
Пример навязчивого списка постоянного размера, который будет хранить объекты Foo, используя базовый крюк с тегом по умолчанию:
typedeflist<Foo>FooList;
Пример навязчивого списка с непостоянным размером времени, который будет храниться Объекты Фу:
Помните, что пользователь должен указать базовый крючок в декларации контейнера, если базовый крючок не имеет метки по умолчанию, поскольку это обычно означает, что тип имеет более одного базового крючка, и контейнер должен знать, какой крючок будет использовать:
Иногда нежелательна связь «is-a» между крючками списка и типами значений списка. В этом случае использование членского крючка в качестве члена данных вместо «нарушения» иерархии может быть правильным способом: вы можете добавить общедоступный член данных<list_member_hook<...>>в свой класс. Этот класс может быть сконфигурирован с теми же опциями, что и<list_base_hook>, за исключением опции<tag>:
При использовании членских крючков опция<member_hook>используется для настройки списка:
//This option will configure "list" to use the member hooktypedefmember_hook<Foo,list_member_hook<>,&Foo::hook_>MemberHookOption;//This list will use the member hooktypedeflist<Foo,MemberHookOption>FooList;
Теперь можно использовать контейнер:
//An object to be inserted in the listFoofoo_object;FooListlist;list.push_back(object);assert(&list.front()==&foo_object);
Однако у членских крючков есть некоторые ограничения реализации: Если существует виртуальная наследственная связь между родителем и крюком-членом, то расстояние между родителем и крюком не является фиксированным значением времени компиляции, поэтому получение адреса родителя от крючка-члена невозможно без обратного инженерного компилятора, произведенного RTTI. Кроме того, нестандартный указатель на реализацию членов для классов со сложными наследственными отношениями в совместимых компиляторах MSVC ABI не поддерживается членскими крючками, поскольку он также зависит от информации RTTI, произведенной компилятором.
Вы можете вставить один и тот же объект в несколько интрузивных контейнеров одновременно, используя один крючок на контейнер. Это полный пример использования базовых и членских крючков:
#include<boost/intrusive/list.hpp>#include<vector>usingnamespaceboost::intrusive;classMyClass:publiclist_base_hook<>{intint_;public:list_member_hook<>member_hook_;MyClass(inti):int_(i){}};//Define a list that will store MyClass using the base hooktypedeflist<MyClass>BaseList;//Define a list that will store MyClass using the member hooktypedefmember_hook<MyClass,list_member_hook<>,&MyClass::member_hook_>MemberOption;typedeflist<MyClass,MemberOption>MemberList;intmain(){typedefstd::vector<MyClass>::iteratorVectIt;//Create several MyClass objects, each one with a different valuestd::vector<MyClass>values;for(inti=0;i<100;++i)values.push_back(MyClass(i));BaseListbaselist;MemberListmemberlist;//Now insert them in the reverse order in the base hook listfor(VectItit(values.begin()),itend(values.end());it!=itend;++it){baselist.push_front(*it);}//Now insert them in the same order as in vector in the member hook listfor(VectItit(values.begin()),itend(values.end());it!=itend;++it)memberlist.push_back(*it);//Now test lists{BaseList::reverse_iteratorrbit(baselist.rbegin());MemberList::iteratormit(memberlist.begin());VectItit(values.begin()),itend(values.end());//Test the objects inserted in the base hook listfor(;it!=itend;++it,++rbit)if(&*rbit!=&*it)return1;//Test the objects inserted in the member hook listfor(it=values.begin();it!=itend;++it,++mit)if(&*mit!=&*it)return1;}return0;}
Даже если интерфейс<list>похож на<std::list>, его использование немного отличается: Вы всегда должны иметь в виду, что вы непосредственно храните объекты в навязчивых контейнерах, а не в копиях. Срок службы хранимого объекта не связан и не управляется контейнером:
Когда контейнер разрушается до объекта, объект не разрушается, поэтому вы должны быть осторожны, чтобы избежать утечки ресурсов.
Когда объект разрушается перед контейнером, ваша программа, скорее всего, рухнет, потому что контейнер содержит указатель на несуществующий объект.
Статья How to use Boost.Intrusive раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 17. Boost.Intrusive может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.