• 2.5. 共享指针

    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,因为它们在拷贝的时候传递了所有权。

    1. #include <boost/shared_ptr.hpp>
    2. #include <vector>
    3.  
    4. int main()
    5. {
    6. std::vector<boost::shared_ptr<int> > v;
    7. v.push_back(boost::shared_ptr<int>(new int(1)));
    8. v.push_back(boost::shared_ptr<int>(new int(2)));
    9. }
    • 下载源代码

    多亏了有 boost::shared_ptr,我们才能像上例中展示的那样,在标准容器中安全的使用动态分配的对象。 因为 boost::shared_ptr 能够共享它所含对象的所有权,所以保存在容器中的拷贝(包括容器在需要时额外创建的拷贝)都是和原件相同的。如前所述,std::auto_ptr做不到这一点,所以绝对不应该在容器中保存它们。

    类似于 boost::scoped_ptrboost::shared_ptr 类重载了以下这些操作符:operator*()operator->()operator bool()。另外还有 get()reset() 函数来获取和重新初始化所包含的对象的地址。

    1. #include <boost/shared_ptr.hpp>
    2.  
    3. int main()
    4. {
    5. boost::shared_ptr<int> i1(new int(1));
    6. boost::shared_ptr<int> i2(i1);
    7. i1.reset(new int(2));
    8. }
    • 下载源代码

    本例中定义了2个共享指针 i1i2,它们都引用到同一个 int 类型的对象。i1 通过 new 操作符返回的地址显示的初始化,i2 通过 i1 拷贝构造而来。 i1 接着调用 reset(),它所包含的整数的地址被重新初始化。不过它之前所包含的对象并没有被释放,因为 i2 仍然引用着它。 智能指针 boost::shared_ptr 记录了有多少个共享指针在引用同一个对象,只有在最后一个共享指针销毁时才会释放这个对象。

    默认情况下,boost::shared_ptr 使用 delete 操作符来销毁所含的对象。 然而,具体通过什么方法来销毁,是可以指定的,就像下面的例子里所展示的:

    1. #include <boost/shared_ptr.hpp>
    2. #include <windows.h>
    3.  
    4. int main()
    5. {
    6. boost::shared_ptr<void> h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId()), CloseHandle);
    7. SetPriorityClass(h.get(), HIGH_PRIORITY_CLASS);
    8. }
    • 下载源代码

    boost::sharedptr 的构造函数的第二个参数是一个普通函数或者函数对象,该参数用来销毁所含的对象。 在本例中,这个参数是 Windows API 函数 CloseHandle()。 当变量 _h 超出它的作用域时,调用的是这个函数而不是 delete 操作符来销毁所含的对象。 为了避免编译错误,该函数只能带一个 HANDLE 类型的参数, CloseHandle() 正好符合要求。

    该例和本章稍早讲述 RAII 习语时所用的例子的运行是一样的。 然而,本例没有单独定义一个 windows_handle 类,而是利用了 boost::shared_ptr 的特性,给它的构造函数传递一个方法,这个方法会在共享指针超出它的作用域时自动调用。