C++ 用户在处理资源时知道所有权智能指针的重要性。Boost 提供了各种类型的指针:intrusive_ptr<>
, scoped_ptr<>
, shared_ptr<>
...
在构建复杂的共享内存/内存映射文件结构时,程序员也希望利用这些智能指针的优势。问题在于 Boost 和 C++ TR1 智能指针不适合用于共享内存。原因在于这些智能指针包含原始指针,并且使用虚函数,如果您想将数据放在共享内存中,这是不可能的。虚函数的限制甚至使得无法使用 Boost.Interprocess 智能指针达到与 Boost 和 TR1 相同的功能级别。
进程间所有权智能指针主要是“包含智能指针的智能指针”,因此我们可以指定它们包含的指针类型。
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<class T, class VoidPointer> class intrusive_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> using namespace boost::interprocess; namespace N { //A class that has an internal reference count class reference_counted_class { private: //Non-copyable reference_counted_class(const reference_counted_class &); //Non-assignable reference_counted_class & operator=(const reference_counted_class &); //A typedef to save typing typedef managed_shared_memory::segment_manager segment_manager; //This is the reference count unsigned int m_use_count; //The segment manager allows deletion from shared memory segment offset_ptr<segment_manager> mp_segment_manager; public: //Constructor reference_counted_class(segment_manager *s_mngr) : m_use_count(0), mp_segment_manager(s_mngr){} //Destructor ~reference_counted_class(){} public: //Returns the reference count unsigned int use_count() const { return m_use_count; } //Adds a reference inline friend void intrusive_ptr_add_ref(reference_counted_class * p) { ++p->m_use_count; } //Releases a reference inline friend void intrusive_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_class class intrusive_ptr_owner { typedef intrusive_ptr<N::reference_counted_class, offset_ptr<void> > intrusive_ptr_t; intrusive_ptr_t m_intrusive_ptr; public: //Takes a pointer to the reference counted class intrusive_ptr_owner(N::reference_counted_class *ptr) : m_intrusive_ptr(ptr){} }; int main() { //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; //Create shared memory managed_shared_memory shmem(create_only, "MyName", 10000); //Create the unique reference counted object in shared memory N::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 memory intrusive_ptr_owner *intrusive_owner_array = shmem.construct<intrusive_ptr_owner> (anonymous_instance)[10](ref_counted); //Now test that reference count is ten if(ref_counted->use_count() != 10) return 1; //Now destroy the array of intrusive pointer owners //This should destroy every intrusive_ptr and because of //that reference_counted_class will be destroyed shmem.destroy_ptr(intrusive_owner_array); //Now the reference counted object should have been destroyed if(shmem.find<intrusive_ptr_owner>("ref_counted").first) return 1; //Success! return 0; }
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<class T, class Deleter> class scoped_ptr;
scoped_ptr<>
对于实现带有异常的回滚非常方便:如果在 scoped_ptr<>
的作用域中抛出异常或我们调用 return
,则会自动调用删除器,以便可以将删除器视为回滚函数。如果一切顺利,我们调用 release()
成员函数以避免在 scoped_ptr
超出范围时回滚。
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/smart_ptr/scoped_ptr.hpp> using namespace boost::interprocess; class my_class {}; class my_exception {}; //A functor that destroys the shared memory object template<class T> class my_deleter { private: //A typedef to save typing typedef managed_shared_memory::segment_manager segment_manager; //This my_deleter is created in the stack, not in shared memory, //so we can use raw pointers segment_manager *mp_segment_manager; public: //This typedef will specify the pointer type that //scoped_ptr will store typedef T *pointer; //Constructor my_deleter(segment_manager *s_mngr) : mp_segment_manager(s_mngr){} void operator()(pointer object_to_delete) { mp_segment_manager->destroy_ptr(object_to_delete); } }; int main () { //Create shared memory //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; managed_shared_memory shmem(create_only, "MyName", 10000); //In the first try, there will be no exceptions //in the second try we will throw an exception for(int i = 0; i < 2; ++i){ //Create an object in shared memory my_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 automatically my_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 exception if(i == 1){ throw(my_exception()); } //If we have passed the dangerous zone //we can release the scoped pointer //to avoid destruction s_ptr.release(); } catch(const my_exception &){} //Here, scoped_ptr is destroyed //so it we haven't thrown an exception //the object should be there, otherwise, destroyed if(i == 0){ //Make sure the object is alive if(!shmem.find<my_class>("my_object").first){ return 1; } //Now we can use it and delete it manually shmem.destroy<my_class>("my_object"); } else{ //Make sure the object has been deleted if(shmem.find<my_class>("my_object").first){ return 1; } } } return 0; }
Boost.Interprocess 还提供了在托管共享内存或映射文件中创建非侵入式引用计数对象的功能。
与 boost::shared_ptr 不同,由于映射段的限制,boost::interprocess::shared_ptr
无法利用虚函数在提供用户定义的分配器和删除器的同时保持相同的共享指针类型。分配器和删除器是共享指针的模板参数。
由于 shared_ptr
所需的引用计数和其他辅助数据也必须在托管段中创建,并且删除器必须从段中删除对象,因此用户在构造 shared_ptr
的非空实例时,必须指定一个分配器对象和一个删除器对象,就像 Boost.Interprocess 容器需要在其构造函数中传递分配器一样。
这是 shared_ptr
的声明
template<class T, class VoidAllocator, class Deleter> class shared_ptr;
pointer
typedef 将确定 shared_ptr 在内部使用的指针类型,因此定义 pointer
作为 offset_ptr<void>
的分配器将使 shared_ptr
使用的所有内部指针也成为相对指针。有关可用的分配器,请参见 boost::interprocess::allocator
。VoidAllocator::pointer
定义的 void 指针相同。有关从托管段中删除对象的通用删除器,请参见 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> using namespace boost::interprocess; //This is type of the object we want to share class MyType {}; typedef managed_shared_memory::segment_manager segment_manager_type; typedef allocator<void, segment_manager_type> void_allocator_type; typedef deleter<MyType, segment_manager_type> deleter_type; typedef shared_ptr<MyType, void_allocator_type, deleter_type> my_shared_ptr; int main () { //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; managed_shared_memory segment(create_only, "MyName", 4096); //Create a shared pointer in shared memory //pointing to a newly created object in the segment my_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 destroyed segment.destroy_ptr(&shared_ptr_instance); return 0; }
boost::interprocess::shared_ptr
非常灵活且可配置(例如,我们可以指定分配器和删除器),但如所示,在托管段中创建共享指针需要太多的输入。
为了简化这种用法,boost::interprocess::shared_ptr
头文件提供了一个共享指针定义辅助类(managed_shared_ptr
)和一个函数(make_managed_shared_ptr
),以便轻松地从在托管段中分配的类型构造共享指针,该分配器也会在托管段中分配引用计数,而删除器会从段中删除该对象。
这些实用程序将使用 Boost.Interprocess 分配器(boost::interprocess::allocator
)和删除器(boost::interprocess::deleter
)来完成它们的工作。可以将前面的共享指针的定义简化为以下内容
typedef managed_shared_ptr<MyType, managed_shared_memory>::type my_shared_ptr;
共享指针的创建可以简化为以下内容
my_shared_ptr sh_ptr = make_managed_shared_ptr (segment.construct<MyType>("object to share")(), segment);
Boost.Interprocess 还提供了一个名为 weak_ptr
的弱指针(及其相应的 managed_weak_ptr
和 make_managed_weak_ptr
实用程序),以实现 shared_ptr
所拥有的对象的非所有者观察器。
现在让我们看一个使用 shared_ptr
和 weak_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> using namespace boost::interprocess; //This is type of the object we want to share struct type_to_share {}; //This is the type of a shared pointer to the previous type //that will be built in the mapped file typedef managed_shared_ptr<type_to_share, managed_mapped_file>::type shared_ptr_type; typedef managed_weak_ptr<type_to_share, managed_mapped_file>::type weak_ptr_type; //This is a type holding a shared pointer struct shared_ptr_owner { shared_ptr_owner(const shared_ptr_type &other_shared_ptr) : shared_ptr_(other_shared_ptr) {} shared_ptr_owner(const shared_ptr_owner &other_owner) : shared_ptr_(other_owner.shared_ptr_) {} shared_ptr_type shared_ptr_; //... }; int main () { //Define file names const char *MappedFile = "MyMappedFile"; //Destroy any previous file with the name to be used. struct file_remove { file_remove(const char *MappedFile) : MappedFile_(MappedFile) { file_mapping::remove(MappedFile_); } ~file_remove(){ file_mapping::remove(MappedFile_); } const char *MappedFile_; } remover(MappedFile); { managed_mapped_file file(create_only, MappedFile, 65536); //Construct the shared type in the file and //pass ownership to this local shared pointer shared_ptr_type local_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 ownership local_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 owners managed_mapped_file file(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 expected assert(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 pointer weak_ptr_type local_observer1(owner2->shared_ptr_); assert(local_observer1.use_count() == owner2->shared_ptr_.use_count()); { //Create a local shared pointer from the weak pointer shared_ptr_type local_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 destroyed file.destroy_ptr(owner2); assert(file.find<type_to_share>("object to share").first == 0); //Test observer assert(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. } return 0; }
通常,使用 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 容器中。Interprocess 曾经有自己的 unique_ptr
实现,但从 Boost 1.57 开始,Boost.Interprocess 使用了改进的通用 boost::unique_ptr
实现。以下是 unique_ptr 类的声明:
template <class T, class D> class unique_ptr;
unique_ptr
所指向的对象的类型。pointer
typedef,unique_ptr
将使用相同类型的内部指针。因此,如果 D::pointer
是 offset_ptr<T>
,则 unique_ptr 将存储一个相对指针而不是原始指针。这允许将 unique_ptr
放置在共享内存和内存映射文件中。
unique_ptr
可以释放存储指针的所有权,因此它也可以用作回滚函数。该类的一个主要特性是不可复制,而只能移动。当一个 unique_ptr 被移动到另一个 unique_ptr 时,指针的所有权会从源 unique_ptr 转移到目标 unique_ptr。如果目标 unique_ptr 拥有一个对象,则在获取新对象的所有权之前,会先删除该对象。
Boost.Interprocess 还提供了辅助类型,以便轻松定义和构造可以放置在托管段中的 unique_ptr,并且可以正确地从段中删除拥有的对象: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/container/vector.hpp> #include <boost/container/list.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <cassert> using namespace boost::interprocess; //This is type of the object we'll allocate dynamically struct MyType { MyType(int number = 0) : number_(number) {} int number_; }; //This is the type of a unique pointer to the previous type //that will be built in the mapped file typedef managed_unique_ptr<MyType, managed_mapped_file>::type unique_ptr_type; //Define containers of unique pointer. Unique pointer simplifies object management typedef boost::container::vector < unique_ptr_type , allocator<unique_ptr_type, managed_mapped_file::segment_manager> > unique_ptr_vector_t; typedef boost::container::list < unique_ptr_type , allocator<unique_ptr_type, managed_mapped_file::segment_manager> > unique_ptr_list_t; int main () { //Define file names const char *MappedFile = "MyMappedFile"; //Destroy any previous file with the name to be used. struct file_remove { file_remove(const char *MappedFile) : MappedFile_(MappedFile) { file_mapping::remove(MappedFile_); } ~file_remove(){ file_mapping::remove(MappedFile_); } const char *MappedFile_; } remover(MappedFile); { managed_mapped_file file(create_only, MappedFile, 65536); //Construct an object in the file and //pass ownership to this local unique pointer unique_ptr_type local_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 destroyed local_unique_ptr.reset(); assert(file.find<MyType>("unique object").first == 0); //Now create a vector of unique pointers unique_ptr_vector_t *unique_vector = file.construct<unique_ptr_vector_t>("unique vector")(file.get_segment_manager()); //Speed optimization unique_vector->reserve(100); //Now insert all values for(int i = 0; i < 100; ++i){ unique_ptr_type p(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 pointers unique_ptr_list_t *unique_list = file.construct<unique_ptr_list_t>("unique list")(file.get_segment_manager()); //Pass ownership of all values to the list for(int i = 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 value assert(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 list managed_mapped_file file(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_iterator list_it = unique_list->begin(); for(int i = 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); } return 0; }