Пользователи C++ знают важность умных указателей собственности при работе с ресурсами. Широкий ассортимент таких указателей:<intrusive_ptr<>>,<scoped_ptr<>>,<shared_ptr<>>...
При создании сложных структур файлов с общей памятью / памятью программисты хотели бы использовать преимущества этих интеллектуальных указателей. Проблема в том, что умные указатели Boost и C++ TR1 не готовы к использованию для общей памяти. Причина в том, что эти умные указатели содержат необработанные указатели и используют виртуальные функции, что невозможно, если вы хотите разместить свои данные в общей памяти. Ограничение виртуальной функции делает даже невозможным достижение того же уровня функциональности Boost и TR1 сBoost.Interprocess.Умные указатели.
Интеллектуальные указатели собственности на межпроцессное взаимодействие в основном представляют собой «умные указатели, содержащие умные указатели», поэтому мы можем указать тип указателя, который они содержат.
<boost::interprocess::intrusive_ptr>является обобщением<boost::intrusive_ptr<>>, чтобы позволить несырьевые указатели в качестве навязчивых указателей. Как известно<boost::intrusive_ptr>, мы должны указать тип указателя, но мы также должны указать тип указателя, который будет храниться в intrusive_ptr:
//!The intrusive_ptr class template stores a pointer to an object//!with an embedded reference count. intrusive_ptr is parameterized on//!T (the type of the object pointed to) and VoidPointer(a void pointer type//!that defines the type of pointer that intrusive_ptr will store).//!intrusive_ptr<T, void *> defines a class with a T* member whereas//!intrusive_ptr<T, offset_ptr<void> > defines a class with a offset_ptr<T> member.//!Relies on unqualified calls to://!//!void intrusive_ptr_add_ref(T * p);//!void intrusive_ptr_release(T * p);//!//!with (p != 0)//!//!The object is responsible for destroying itself.template<classT,classVoidPointer>classintrusive_ptr;
<boost::interprocess::intrusive_ptr<MyClass,void*>>равнозначно<boost::intrusive_ptr<MyClass>>. Но если мы хотим поместить intrusive_ptr в общую память, мы должны указать относительный тип указателя, как<boost::interprocess::intrusive_ptr<MyClass,boost::interprocess::offset_ptr<void>>>.
#include<boost/interprocess/managed_shared_memory.hpp>#include<boost/interprocess/smart_ptr/intrusive_ptr.hpp>usingnamespaceboost::interprocess;namespaceN{//A class that has an internal reference countclassreference_counted_class{private://Non-copyablereference_counted_class(constreference_counted_class&);//Non-assignablereference_counted_class&operator=(constreference_counted_class&);//A typedef to save typingtypedefmanaged_shared_memory::segment_managersegment_manager;//This is the reference countunsignedintm_use_count;//The segment manager allows deletion from shared memory segmentoffset_ptr<segment_manager>mp_segment_manager;public://Constructorreference_counted_class(segment_manager*s_mngr):m_use_count(0),mp_segment_manager(s_mngr){}//Destructor~reference_counted_class(){}public://Returns the reference countunsignedintuse_count()const{returnm_use_count;}//Adds a referenceinlinefriendvoidintrusive_ptr_add_ref(reference_counted_class*p){++p->m_use_count;}//Releases a referenceinlinefriendvoidintrusive_ptr_release(reference_counted_class*p){if(--p->m_use_count==0)p->mp_segment_manager->destroy_ptr(p);}};}//namespace N {//A class that has an intrusive pointer to reference_counted_classclassintrusive_ptr_owner{typedefintrusive_ptr<N::reference_counted_class,offset_ptr<void>>intrusive_ptr_t;intrusive_ptr_tm_intrusive_ptr;public://Takes a pointer to the reference counted classintrusive_ptr_owner(N::reference_counted_class*ptr):m_intrusive_ptr(ptr){}};intmain(){//Remove shared memory on construction and destructionstructshm_remove{shm_remove(){shared_memory_object::remove("MySharedMemory");}~shm_remove(){shared_memory_object::remove("MySharedMemory");}}remover;//Create shared memorymanaged_shared_memoryshmem(create_only,"MySharedMemory",10000);//Create the unique reference counted object in shared memoryN::reference_counted_class*ref_counted=shmem.construct<N::reference_counted_class>("ref_counted")(shmem.get_segment_manager());//Create an array of ten intrusive pointer owners in shared memoryintrusive_ptr_owner*intrusive_owner_array=shmem.construct<intrusive_ptr_owner>(anonymous_instance)[10](ref_counted);//Now test that reference count is tenif(ref_counted->use_count()!=10)return1;//Now destroy the array of intrusive pointer owners//This should destroy every intrusive_ptr and because of//that reference_counted_class will be destroyedshmem.destroy_ptr(intrusive_owner_array);//Now the reference counted object should have been destroyedif(shmem.find<intrusive_ptr_owner>("ref_counted").first)return1;//Success!return0;}
<boost::interprocess::scoped_ptr<>>является старшим братом<boost::scoped_ptr<>>, который добавляет пользовательский удалитель, чтобы указать, как указатель, переданный в scoped_ptr, должен быть уничтожен. Кроме того,<pointer>typedef удалителя будет указывать тип указателя, сохраненный посредством scoped_ptr.
//!scoped_ptr stores a pointer to a dynamically allocated object.//!The object pointed to is guaranteed to be deleted, either on destruction//!of the scoped_ptr, or via an explicit reset. The user can avoid this//!deletion using release().//!scoped_ptr is parameterized on T (the type of the object pointed to) and//!Deleter (the functor to be executed to delete the internal pointer).//!The internal pointer will be of the same pointer type as typename//!Deleter::pointer type (that is, if typename Deleter::pointer is//!offset_ptr<void>, the internal pointer will be offset_ptr<T>).template<classT,classDeleter>classscoped_ptr;
<scoped_ptr<>>пригодится для реализацииоткатовс исключениями: если отбрасывается исключение или мы называем<return>в рамках<scoped_ptr<>>, то автоматически вызывается удаляющий, так чтоудаляющий может рассматриваться как функция отката. Если все пойдет хорошо, мы вызовем<release()>функцию члена, чтобы избежать отката, когда<scoped_ptr>выходит из области применения.
#include<boost/interprocess/managed_shared_memory.hpp>#include<boost/interprocess/smart_ptr/scoped_ptr.hpp>usingnamespaceboost::interprocess;classmy_class{};classmy_exception{};//A functor that destroys the shared memory objecttemplate<classT>classmy_deleter{private://A typedef to save typingtypedefmanaged_shared_memory::segment_managersegment_manager;//This my_deleter is created in the stack, not in shared memory,//so we can use raw pointerssegment_manager*mp_segment_manager;public://This typedef will specify the pointer type that//scoped_ptr will storetypedefT*pointer;//Constructormy_deleter(segment_manager*s_mngr):mp_segment_manager(s_mngr){}voidoperator()(pointerobject_to_delete){mp_segment_manager->destroy_ptr(object_to_delete);}};intmain(){//Create shared memory//Remove shared memory on construction and destructionstructshm_remove{shm_remove(){shared_memory_object::remove("MySharedMemory");}~shm_remove(){shared_memory_object::remove("MySharedMemory");}}remover;managed_shared_memoryshmem(create_only,"MySharedMemory",10000);//In the first try, there will be no exceptions//in the second try we will throw an exceptionfor(inti=0;i<2;++i){//Create an object in shared memorymy_class*my_object=shmem.construct<my_class>("my_object")();my_class*my_object2=shmem.construct<my_class>(anonymous_instance)();shmem.destroy_ptr(my_object2);//Since the next shared memory allocation can throw//assign it to a scoped_ptr so that if an exception occurs//we destroy the object automaticallymy_deleter<my_class>d(shmem.get_segment_manager());try{scoped_ptr<my_class,my_deleter<my_class>>s_ptr(my_object,d);//Let's emulate a exception capable operation//In the second try, throw an exceptionif(i==1){throw(my_exception());}//If we have passed the dangerous zone//we can release the scoped pointer//to avoid destructions_ptr.release();}catch(constmy_exception&){}//Here, scoped_ptr is destroyed//so it we haven't thrown an exception//the object should be there, otherwise, destroyedif(i==0){//Make sure the object is aliveif(!shmem.find<my_class>("my_object").first){return1;}//Now we can use it and delete it manuallyshmem.destroy<my_class>("my_object");}else{//Make sure the object has been deletedif(shmem.find<my_class>("my_object").first){return1;}}}return0;}
Boost.Interprocessтакже предлагает возможность создания неинтрузивных объектов с учётом ссылок в управляемой совместно используемой памяти или отображенных файлах.
В отличие отboost::shared_ptr, из-за ограничений отображенных сегментов<boost::interprocess::shared_ptr>не может воспользоваться виртуальными функциями для поддержания одного и того же типа общих указателей, обеспечивая при этом определяемые пользователем распределители и удаляющие устройства. Аллокатор и удалятель являются шаблонными параметрами общего указателя.
Поскольку количество ссылок и другие вспомогательные данные, необходимые<shared_ptr>, должны быть созданы также в управляемом сегменте, и удаляющий должен удалить объект из сегмента, пользователь должен указать объект распределителя и объект удаляющего при построении непустого экземпляра<shared_ptr>, как.Интерпроцессконтейнеры должны проходить распределители в своих конструкторах.
VoidAllocator - это распределитель, который используется для выделения вспомогательных элементов, таких как количество ссылок, удаляющий элемент. Внутренний<pointer>типдеф распределителя будет определять тип указателя, который будет использоваться внутри Shared_ptr, поэтому распределители, определяющие<pointer>как<offset_ptr<void>>, сделают все внутренние указатели, используемые<shared_ptr>, также относительными указателями. См.<boost::interprocess::allocator>для рабочего распределителя.
Удаление - это функциональный объект, который будет использоваться для уничтожения заостренного объекта при уничтожении последней ссылки на объект. Удаляющий функтор принимает указатель на T той же категории, что и указатель пустоты, определенный<VoidAllocator::pointer>. См.<boost::interprocess::deleter>для общего удаляющего устройства, которое стирает объект из управляемого сегмента.
При правильно заданных параметрахBoost.Interprocessпользователи могут создавать объекты в общей памяти, которые содержат общие указатели, указывающие на другие объекты также в общей памяти, получая преимущества подсчета ссылок. Давайте посмотрим, как создать общий указатель в управляемой общей памяти:
#include<boost/interprocess/managed_shared_memory.hpp>#include<boost/interprocess/smart_ptr/shared_ptr.hpp>#include<boost/interprocess/allocators/allocator.hpp>#include<boost/interprocess/smart_ptr/deleter.hpp>#include<cassert>usingnamespaceboost::interprocess;//This is type of the object we want to shareclassMyType{};typedefmanaged_shared_memory::segment_managersegment_manager_type;typedefallocator<void,segment_manager_type>void_allocator_type;typedefdeleter<MyType,segment_manager_type>deleter_type;typedefshared_ptr<MyType,void_allocator_type,deleter_type>my_shared_ptr;intmain(){//Remove shared memory on construction and destructionstructshm_remove{shm_remove(){shared_memory_object::remove("MySharedMemory");}~shm_remove(){shared_memory_object::remove("MySharedMemory");}}remover;managed_shared_memorysegment(create_only,"MySharedMemory",4096);//Create a shared pointer in shared memory//pointing to a newly created object in the segmentmy_shared_ptr&shared_ptr_instance=*segment.construct<my_shared_ptr>("shared ptr")//Arguments to construct the shared pointer(segment.construct<MyType>("object to share")()//object to own,void_allocator_type(segment.get_segment_manager())//allocator,deleter_type(segment.get_segment_manager())//deleter);assert(shared_ptr_instance.use_count()==1);//Destroy "shared ptr". "object to share" will be automatically destroyedsegment.destroy_ptr(&shared_ptr_instance);return0;}
<boost::interprocess::shared_ptr>очень гибкий и настраиваемый (мы можем указать, например, распределитель и удалитель), но, как показано, создание общего указателя в управляемых сегментах требует слишком большого набора текста.
Для упрощения этого использования заголовок<boost::interprocess::shared_ptr>предлагает общий класс помощников определения указателя (<managed_shared_ptr>) и функцию (<make_managed_shared_ptr>), чтобы легко построить общий указатель из типа, выделенного в управляемом сегменте, с распределителем, который будет распределять количество ссылок также в управляемом сегменте, и удаляющим устройством, которое будет стирать объект из сегмента.
#include<boost/interprocess/managed_mapped_file.hpp>#include<boost/interprocess/smart_ptr/shared_ptr.hpp>#include<boost/interprocess/smart_ptr/weak_ptr.hpp>#include<cassert>usingnamespaceboost::interprocess;//This is type of the object we want to sharestructtype_to_share{};//This is the type of a shared pointer to the previous type//that will be built in the mapped filetypedefmanaged_shared_ptr<type_to_share,managed_mapped_file>::typeshared_ptr_type;typedefmanaged_weak_ptr<type_to_share,managed_mapped_file>::typeweak_ptr_type;//This is a type holding a shared pointerstructshared_ptr_owner{shared_ptr_owner(constshared_ptr_type&other_shared_ptr):shared_ptr_(other_shared_ptr){}shared_ptr_owner(constshared_ptr_owner&other_owner):shared_ptr_(other_owner.shared_ptr_){}shared_ptr_typeshared_ptr_;//...};intmain(){//Define file namesconstchar*MappedFile="MyMappedFile";//Destroy any previous file with the name to be used.structfile_remove{file_remove(constchar*MappedFile):MappedFile_(MappedFile){file_mapping::remove(MappedFile_);}~file_remove(){file_mapping::remove(MappedFile_);}constchar*MappedFile_;}remover(MappedFile);{managed_mapped_filefile(create_only,MappedFile,65536);//Construct the shared type in the file and//pass ownership to this local shared pointershared_ptr_typelocal_shared_ptr=make_managed_shared_ptr(file.construct<type_to_share>("object to share")(),file);assert(local_shared_ptr.use_count()==1);//Share ownership of the object between local_shared_ptr and a new "owner1"shared_ptr_owner*owner1=file.construct<shared_ptr_owner>("owner1")(local_shared_ptr);assert(local_shared_ptr.use_count()==2);//local_shared_ptr releases object ownershiplocal_shared_ptr.reset();assert(local_shared_ptr.use_count()==0);assert(owner1->shared_ptr_.use_count()==1);//Share ownership of the object between "owner1" and a new "owner2"shared_ptr_owner*owner2=file.construct<shared_ptr_owner>("owner2")(*owner1);assert(owner1->shared_ptr_.use_count()==2);assert(owner2->shared_ptr_.use_count()==2);assert(owner1->shared_ptr_.get()==owner2->shared_ptr_.get());//The mapped file is unmapped here. Objects have been flushed to disk}{//Reopen the mapped file and find again all ownersmanaged_mapped_filefile(open_only,MappedFile);shared_ptr_owner*owner1=file.find<shared_ptr_owner>("owner1").first;shared_ptr_owner*owner2=file.find<shared_ptr_owner>("owner2").first;assert(owner1&&owner2);//Check everything is as expectedassert(file.find<type_to_share>("object to share").first!=0);assert(owner1->shared_ptr_.use_count()==2);assert(owner2->shared_ptr_.use_count()==2);assert(owner1->shared_ptr_.get()==owner2->shared_ptr_.get());//Now destroy one of the owners, the reference count drops.file.destroy_ptr(owner1);assert(owner2->shared_ptr_.use_count()==1);//Create a weak pointerweak_ptr_typelocal_observer1(owner2->shared_ptr_);assert(local_observer1.use_count()==owner2->shared_ptr_.use_count());{//Create a local shared pointer from the weak pointershared_ptr_typelocal_shared_ptr=local_observer1.lock();assert(local_observer1.use_count()==owner2->shared_ptr_.use_count());assert(local_observer1.use_count()==2);}//Now destroy the remaining owner. "object to share" will be destroyedfile.destroy_ptr(owner2);assert(file.find<type_to_share>("object to share").first==0);//Test observerassert(local_observer1.expired());assert(local_observer1.use_count()==0);//The reference count will be deallocated when all weak pointers//disappear. After that, the file is unmapped.}return0;}
В целом, использованиеBoost.Interprocess<shared_ptr>и<weak_ptr>очень похоже на их аналогиboost::shared_ptrиboost::weak_ptr, но им нужно больше параметров шаблона и больше параметров времени выполнения в своих конструкторах.
Так же, какboost::shared_ptrможет храниться в STL-контейнере,<shared_ptr>также может храниться вBoost.Interprocessконтейнерах.
Если программист просто использует<shared_ptr>, чтобы иметь возможность вставлять динамически сконструированные объекты в контейнер, сконструированный в управляемом сегменте, но ему не нужно делить право собственности на этот объект с другими объектами<managed_unique_ptr>, это гораздо быстрее и проще использовать альтернативу.
Уникальные умные указатели собственности действительно полезны для свободных программистов от ручного освобождения ресурсов необщих объектов.Boost.Interprocess'<unique_ptr>очень похож на<scoped_ptr>, но онподвижныйи может быть легко вставлен вBoost.Interprocessконтейнеры. Интерпроцесс имел свою собственную реализацию<unique_ptr>, но от Boost 1.57Boost.Interprocessиспользует улучшенную и общую реализацию<boost::unique_ptr>. Вот декларация уникального класса указателей:
template<classT,classD>classunique_ptr;
Т — тип объекта, на который указывает<unique_ptr>.
<unique_ptr>может освободить право собственности на сохраненный указатель, поэтому его полезно также использовать в качестве функции отката. Одним из основных свойств класса является то, чтоне копируемый, а только подвижный. Когда уникальный указатель перемещается в другой, право собственности на указатель передается от уникального указателя источника к уникальному указателю цели. Если целевому уникальному указателю принадлежал объект, этот объект сначала удаляется, прежде чем он становится владельцем нового объекта.
Boost.Interprocessтакже предлагает вспомогательные типы для легкого определения и построения уникальных указателей, которые могут быть размещены в управляемых сегментах и будут правильно удалять принадлежащий объект из сегмента:<managed_unique_ptr>и<make_managed_unique_ptr>утилиты.
Здесь мы видим пример использования<unique_ptr>в том числе создания контейнеров таких объектов:
#include<boost/interprocess/managed_mapped_file.hpp>#include<boost/interprocess/smart_ptr/unique_ptr.hpp>#include<boost/interprocess/containers/vector.hpp>#include<boost/interprocess/containers/list.hpp>#include<boost/interprocess/allocators/allocator.hpp>#include<cassert>usingnamespaceboost::interprocess;//This is type of the object we'll allocate dynamicallystructMyType{MyType(intnumber=0):number_(number){}intnumber_;};//This is the type of a unique pointer to the previous type//that will be built in the mapped filetypedefmanaged_unique_ptr<MyType,managed_mapped_file>::typeunique_ptr_type;//Define containers of unique pointer. Unique pointer simplifies object managementtypedefvector<unique_ptr_type,allocator<unique_ptr_type,managed_mapped_file::segment_manager>>unique_ptr_vector_t;typedeflist<unique_ptr_type,allocator<unique_ptr_type,managed_mapped_file::segment_manager>>unique_ptr_list_t;intmain(){//Define file namesconstchar*MappedFile="MyMappedFile";//Destroy any previous file with the name to be used.structfile_remove{file_remove(constchar*MappedFile):MappedFile_(MappedFile){file_mapping::remove(MappedFile_);}~file_remove(){file_mapping::remove(MappedFile_);}constchar*MappedFile_;}remover(MappedFile);{managed_mapped_filefile(create_only,MappedFile,65536);//Construct an object in the file and//pass ownership to this local unique pointerunique_ptr_typelocal_unique_ptr(make_managed_unique_ptr(file.construct<MyType>("unique object")(),file));assert(local_unique_ptr.get()!=0);//Reset the unique pointer. The object is automatically destroyedlocal_unique_ptr.reset();assert(file.find<MyType>("unique object").first==0);//Now create a vector of unique pointersunique_ptr_vector_t*unique_vector=file.construct<unique_ptr_vector_t>("unique vector")(file.get_segment_manager());//Speed optimizationunique_vector->reserve(100);//Now insert all valuesfor(inti=0;i<100;++i){unique_ptr_typep(make_managed_unique_ptr(file.construct<MyType>(anonymous_instance)(i),file));unique_vector->push_back(boost::move(p));assert(unique_vector->back()->number_==i);}//Now create a list of unique pointersunique_ptr_list_t*unique_list=file.construct<unique_ptr_list_t>("unique list")(file.get_segment_manager());//Pass ownership of all values to the listfor(inti=99;!unique_vector->empty();--i){unique_list->push_front(boost::move(unique_vector->back()));//The unique ptr of the vector is now empty...assert(unique_vector->back()==0);unique_vector->pop_back();//...and the list has taken ownership of the valueassert(unique_list->front()!=0);assert(unique_list->front()->number_==i);}assert(unique_list->size()==100);//Now destroy the empty vector.file.destroy_ptr(unique_vector);//The mapped file is unmapped here. Objects have been flushed to disk}{//Reopen the mapped file and find again the listmanaged_mapped_filefile(open_only,MappedFile);unique_ptr_list_t*unique_list=file.find<unique_ptr_list_t>("unique list").first;assert(unique_list);assert(unique_list->size()==100);unique_ptr_list_t::const_iteratorlist_it=unique_list->begin();for(inti=0;i<100;++i,++list_it){assert((*list_it)->number_==i);}//Now destroy the list. All elements will be automatically deallocated.file.destroy_ptr(unique_list);}return0;}
Статья Ownership smart pointers раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.