十一.所有权的智能指针
浸入式指针(Intrusive pointer) 作用域指针(Scoped pointer) 共享指针和弱指针(Shared pointer and weak pointer) 唯一指针(Unique pointer) |
当处理资源时,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中的指针类型:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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 {
-
-
- class reference_counted_class
- {
- private:
-
- reference_counted_class(const reference_counted_class &);
-
- reference_counted_class & operator=(const reference_counted_class &);
-
- typedef managed_shared_memory::segment_manager segment_manager;
-
- unsigned int m_use_count;
-
- offset_ptr<segment_manager> mp_segment_manager;
-
- public:
-
- reference_counted_class(segment_manager *s_mngr)
- : m_use_count(0), mp_segment_manager(s_mngr){}
-
- ~reference_counted_class(){}
-
- public:
-
- unsigned int use_count() const
- { return m_use_count; }
-
-
- inline friend void intrusive_ptr_add_ref(reference_counted_class * p)
- { ++p->m_use_count; }
-
-
- inline friend void intrusive_ptr_release(reference_counted_class * p)
- { if(--p->m_use_count == 0) p->mp_segment_manager->destroy_ptr(p); }
- };
-
- }
-
-
- class intrusive_ptr_owner
- {
- typedef intrusive_ptr<N::reference_counted_class,
- offset_ptr<void> > intrusive_ptr_t;
- intrusive_ptr_t m_intrusive_ptr;
-
- public:
-
- intrusive_ptr_owner(N::reference_counted_class *ptr)
- : m_intrusive_ptr(ptr){}
- };
-
- int main()
- {
-
- struct shm_remove
- {
- shm_remove() { shared_memory_object::remove("MySharedMemory"); }
- ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
- } remover;
-
-
- managed_shared_memory shmem(create_only, "MySharedMemory", 10000);
-
-
- N::reference_counted_class *ref_counted =
- shmem.construct<N::reference_counted_class>
- ("ref_counted")(shmem.get_segment_manager());
-
-
- intrusive_ptr_owner *intrusive_owner_array =
- shmem.construct<intrusive_ptr_owner>
- (anonymous_instance)[10](ref_counted);
-
-
- if(ref_counted->use_count() != 10)
- return 1;
-
-
-
-
- shmem.destroy_ptr(intrusive_owner_array);
-
-
- if(shmem.find<intrusive_ptr_owner>("ref_counted").first)
- return 1;
-
- return 0;
- }
作用域指针
boost::interprocess::scoped_ptr<>是boost::scoped_ptr<>的大哥,它增加了一个定制的删除器用于指定如何销毁传入至scoped_ptr的指针。并且,删除器的指针类型定义也将指定被scoped_ptr存储的指针类型。
-
-
-
-
-
-
-
-
-
- template<class T, class Deleter>
- class scoped_ptr;
scoped_ptr<>在执行异常回滚时是得心应手的:如果抛出异常或我们在scoped_ptr<>作用域内调用了return,删除器将自动调用以便删除器能被认为是一个回滚函数。如果一切顺利,当scoped_ptr超出作用域时,我们调用release()成员函数来避免回滚。
- #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
- {};
-
-
- template<class T>
- class my_deleter
- {
- private:
-
- typedef managed_shared_memory::segment_manager segment_manager;
-
-
- segment_manager *mp_segment_manager;
-
- public:
-
-
- typedef T *pointer;
-
- 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 ()
- {
-
-
- struct shm_remove
- {
- shm_remove() { shared_memory_object::remove("MySharedMemory"); }
- ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
- } remover;
-
- managed_shared_memory shmem(create_only, "MySharedMemory", 10000);
-
-
-
- for(int i = 0; i < 2; ++i){
-
- 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);
-
-
-
-
- my_deleter<my_class> d(shmem.get_segment_manager());
-
- try{
- scoped_ptr<my_class, my_deleter<my_class> > s_ptr(my_object, d);
-
-
- if(i == 1){
- throw(my_exception());
- }
-
-
-
- s_ptr.release();
- }
- catch(const my_exception &){}
-
-
-
- if(i == 0){
-
- if(!shmem.find<my_class>("my_object").first){
- return 1;
- }
-
- shmem.destroy<my_class>("my_object");
- }
- else{
-
- if(shmem.find<my_class>("my_object").first){
- return 1;
- }
- }
- }
- return 0;
- }
共享指针和弱指针
Boost.Interprocess也提供了在托管共享内存或映射文件中创建非浸入式引用计数(non-intrusive reference-counted)对象的可能性。
与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;
- T是指向的类型的类型。
- VoidAllocator是用来分配辅助成员例如引用计数、删除器等的分配器。分配器的内部指针类型定义将决定shared_ptr内部使用的指针类型,因此分配器定义指针offset_ptr<void>将使得所有shared_ptr使用的内部指针也是相对指针。参考boost::interprocess::allocator以获取工作分配器的详细信息。
- Deleter是函数对象,当对对象的最后一个引用被销毁后,它被用来销毁指向的对象。删除器函数将使用一个与VoidAllocator::pointer定义的空指针一样的T指针。参考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;
-
-
- 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 ()
- {
-
- struct shm_remove
- {
- shm_remove() { shared_memory_object::remove("MySharedMemory"); }
- ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
- } remover;
-
- managed_shared_memory segment(create_only, "MySharedMemory", 4096);
-
-
-
- my_shared_ptr &shared_ptr_instance =
- *segment.construct<my_shared_ptr>("shared ptr")
-
- ( segment.construct<MyType>("object to share")()
- , void_allocator_type(segment.get_segment_manager())
- , deleter_type(segment.get_segment_manager())
- );
- assert(shared_ptr_instance.use_count() == 1);
-
-
- 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的详细例子:
一般来说,Boost.Interprocess的shared_ptr和weak_ptr使用和它们对应的boost::shared_ptr和boost::weak_ptr是非常类似的,但它们需要更多的模板参数和更多的运行时参数在其构造函数中。
就好像boost::shared_ptr能被存储在STL容器中一样,shared_ptr也能被存储在Boost.Interprocess容器中。
如果一个程序员仅使用shared_ptr来插入动态构建于托管内存片段上的对象至容器中,但不需要与其他对象共享此对象的所有权,则unique_ptr是一个更快速和易用的替代品。
唯一指针
唯一所有权智能指针是非常有用的,它将程序员从非共享对象手工资源中解放出来。Boost.Interprocess的unique_ptr与scoped_ptr很类似,但它是活动的并且能够很容易的插入至Boost.Interprocess容器。这是唯一指针类声明:
- template <class T, class D>
- class unique_ptr;
- T是unique_ptr指向的对象的类型。
- D是删除器,当唯一指针被销毁时(并且如果还拥有对象的所有权),它将删除unique_ptr指向的类型对象。如果删除器定义了一个内部指针类型,unique_ptr将使用一个相同类型的内部指针。因此,如果D::pointer是offset_ptr<T>,则唯一指针将存储一个相对指针而不是原始指针。这允许放置unique_ptr在共享内存和内存映射文件中。
unique_ptr能释放存储的指针的所有权,因此它也能被用做一个回滚函数。此类的一个主要特性是不可拷贝性,仅能移动。当一个唯一指针被移动至另一个,则指针的所有权从源唯一指针转移至目标唯一指针。如果目标唯一指针拥有一个对象,则在拥有新对象之前,原对象先被删除。
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/interprocess/containers/vector.hpp>
- #include <boost/interprocess/containers/list.hpp>
- #include <boost/interprocess/allocators/allocator.hpp>
- #include <cassert>
-
- using namespace boost::interprocess;
-
-
- struct MyType
- {
- MyType(int number = 0)
- : number_(number)
- {}
- int number_;
- };
-
-
-
- typedef managed_unique_ptr<MyType, managed_mapped_file>::type unique_ptr_type;
-
-
- typedef vector
- < unique_ptr_type
- , allocator<unique_ptr_type, managed_mapped_file::segment_manager>
- > unique_ptr_vector_t;
-
- typedef list
- < unique_ptr_type
- , allocator<unique_ptr_type, managed_mapped_file::segment_manager>
- > unique_ptr_list_t;
-
- int main ()
- {
-
- const char *MappedFile = "MyMappedFile";
-
-
- 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);
-
-
-
- unique_ptr_type local_unique_ptr (make_managed_unique_ptr
- (file.construct<MyType>("unique object")(), file));
- assert(local_unique_ptr.get() != 0);
-
-
- local_unique_ptr.reset();
- assert(file.find<MyType>("unique object").first == 0);
-
-
- unique_ptr_vector_t *unique_vector =
- file.construct<unique_ptr_vector_t>("unique vector")(file.get_segment_manager());
-
-
- unique_vector->reserve(100);
-
-
- 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);
- }
-
-
- unique_ptr_list_t *unique_list =
- file.construct<unique_ptr_list_t>("unique list")(file.get_segment_manager());
-
-
- for(int i = 99; !unique_vector->empty(); --i){
- unique_list->push_front(boost::move(unique_vector->back()));
-
- assert(unique_vector->back() == 0);
- unique_vector->pop_back();
-
- assert(unique_list->front() != 0);
- assert(unique_list->front()->number_ == i);
- }
- assert(unique_list->size() == 100);
-
-
- file.destroy_ptr(unique_vector);
-
- }
- {
-
- 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);
- }
-
-
- file.destroy_ptr(unique_list);
- }
- return 0;
- }