2.5. 共享指针
这是使用率最高的智能指针,但是 C++ 标准的第一版中缺少这种指针。 它已经作为技术报告1(TR 1)的一部分被添加到标准里了。 如果开发环境支持的话,可以使用 memory
中定义的 std::shared_ptr
。 在 Boost C++ 库里,这个智能指针命名为 boost::shared_ptr
,定义在 boost/shared_ptr.hpp
里。
智能指针 boost::shared_ptr
基本上类似于 boost::scoped_ptr
。 关键不同之处在于 boost::shared_ptr
不一定要独占一个对象。 它可以和其他 boost::shared_ptr
类型的智能指针共享所有权。 在这种情况下,当引用对象的最后一个智能指针销毁后,对象才会被释放。
因为所有权可以在 boost::shared_ptr
之间共享,任何一个共享指针都可以被复制,这跟 boost::scoped_ptr
是不同的。 这样就可以在标准容器里存储智能指针了——你不能在标准容器中存储 std::auto_ptr
,因为它们在拷贝的时候传递了所有权。
- #include <boost/shared_ptr.hpp>
- #include <vector>
- int main()
- {
- std::vector<boost::shared_ptr<int> > v;
- v.push_back(boost::shared_ptr<int>(new int(1)));
- v.push_back(boost::shared_ptr<int>(new int(2)));
- }
多亏了有 boost::shared_ptr
,我们才能像上例中展示的那样,在标准容器中安全的使用动态分配的对象。 因为 boost::shared_ptr
能够共享它所含对象的所有权,所以保存在容器中的拷贝(包括容器在需要时额外创建的拷贝)都是和原件相同的。如前所述,std::auto_ptr
做不到这一点,所以绝对不应该在容器中保存它们。
类似于 boost::scoped_ptr
, boost::shared_ptr
类重载了以下这些操作符:operator*()
,operator->()
和 operator bool()
。另外还有 get()
和 reset()
函数来获取和重新初始化所包含的对象的地址。
- #include <boost/shared_ptr.hpp>
- int main()
- {
- boost::shared_ptr<int> i1(new int(1));
- boost::shared_ptr<int> i2(i1);
- i1.reset(new int(2));
- }
本例中定义了2个共享指针 i1 和 i2,它们都引用到同一个 int
类型的对象。i1 通过 new
操作符返回的地址显示的初始化,i2 通过 i1 拷贝构造而来。 i1 接着调用 reset()
,它所包含的整数的地址被重新初始化。不过它之前所包含的对象并没有被释放,因为 i2 仍然引用着它。 智能指针 boost::shared_ptr
记录了有多少个共享指针在引用同一个对象,只有在最后一个共享指针销毁时才会释放这个对象。
默认情况下,boost::shared_ptr
使用 delete
操作符来销毁所含的对象。 然而,具体通过什么方法来销毁,是可以指定的,就像下面的例子里所展示的:
- #include <boost/shared_ptr.hpp>
- #include <windows.h>
- int main()
- {
- boost::shared_ptr<void> h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId()), CloseHandle);
- SetPriorityClass(h.get(), HIGH_PRIORITY_CLASS);
- }
boost::sharedptr
的构造函数的第二个参数是一个普通函数或者函数对象,该参数用来销毁所含的对象。 在本例中,这个参数是 Windows API 函数 CloseHandle()
。 当变量 _h 超出它的作用域时,调用的是这个函数而不是 delete
操作符来销毁所含的对象。 为了避免编译错误,该函数只能带一个 HANDLE
类型的参数, CloseHandle()
正好符合要求。
该例和本章稍早讲述 RAII 习语时所用的例子的运行是一样的。 然而,本例没有单独定义一个 windows_handle
类,而是利用了 boost::shared_ptr
的特性,给它的构造函数传递一个方法,这个方法会在共享指针超出它的作用域时自动调用。