五. 独立于映射地址的指针:offset_ptr
当创建共享内存和内存映射文件进行两个进程通信时,内存片段在各进程上能够被映射到不同的地址:
- #include<boost/interprocess/shared_memory_object.hpp>
- // ...
- using boost::interprocess;
- //Open a shared memory segment
- shared_memory_object shm_obj
- (open_only //open or create
- ,"shared_memory" //name
- ,read_only //read-only mode
- );
- //Map the whole shared memory
- mapped_region region
- ( shm //Memory-mappable object
- , read_write //Access mode
- );
- //This address can be different in each process
- void *addr = region.get_address();
这使得在映射区域上创建复杂对象很困难:一个位于某映射区域的C++类实例可能有一个指针指向位于同一映射区域的另一个对象。因为指针存储了一个绝对地址,因此除非所有进程在同样的地址上映射映射区域,放置该对象的进程的地址才有效。
为了在映射区域上模拟指针使用,使用者必须使用偏移量(两个对象间的距离)来代替绝对地址。两个对象在映射区域的偏移量对任何映射该映射区域的进程来说都是相同的,即使区域位于不同的基地址上。为方便使用偏移量,Boost.Interprocess提供了offset_ptr.
offset_ptr封装了所有必须的后台操作,进而提供了一个类似指针的接口。这个类的接口受到了Boost智能指针的启发,并且这种智能指针存储了两个指针地址间的偏移量(字节数距离)和它自己的指针。试想在一个通常的32位处理器下的结构:
- struct structure
- {
- int integer1; //The compiler places this at offset 0 in the structure
- offset_ptr<int> ptr; //The compiler places this at offset 4 in the structure
- int integer2; //The compiler places this at offset 8 in the structure
- };
- //...
- structure s;
- //Assign the address of "integer1" to "ptr".
- //"ptr" will store internally "-4":
- // (char*)&s.integer1 - (char*)&s.ptr;
- s.ptr = &s.integer1;
- //Assign the address of "integer2" to "ptr".
- //"ptr" will store internally "4":
- // (char*)&s.integer2 - (char*)&s.ptr;
- s.ptr = &s.integer2;
offset_ptr一个最大的问题是如何表示空指针。空指针不能使用偏移量来安全地表示,因为绝对地址0总是不在映射区域中。基于在各进程中内存片段能够被映射到不同基地址上的事实,地址0和offset_ptr在每个进程中的距离是不同的。
一些实现方案选择偏移量0(即:一个指向自己的offset_ptr)来代表空指针,但是这在许多情况下也是不行的,因为很多结构例如链表或STL容器中的节点都指向它们自己(例如,空容器中的尾节点),且需要0偏移值。一个替代方案是除了偏移量,还存储一个布尔值以指示是否指针为空。然而,这增大了这种指针的大小并且影响性能。
因此,offset_ptr定义偏移值1做为空指针,这意味着此类不能指向它自身this指针后的那个字节。
- using namespace boost::interprocess;
- offset_ptr<char> ptr;
- //Pointing to the next byte of it's own address
- //marks the smart pointer as null.
- ptr = (char*)&ptr + 1;
- //ptr is equal to null
- assert(!ptr);
- //This is the same as assigning the null value...
- ptr = 0;
- //ptr is also equal to null
- assert(!ptr);
在实际中,这种限制是不重要的,因为使用者几乎从不会去指向那个地址。
offset_ptr提供所有的类似于指针的操作,并且定义了random_access_iterator,因此它能被使用在需要随机访问迭代器的STL算法中,且能通过性状进行检测。更多关于此类成员变量和成员函数的详情,请参考offset_ptr reference
.