>>>这是使用率最高的智能指针,但是 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/version.hpp"#include "boost/config.hpp"#include "boost/scoped_ptr.hpp" // 作用域指针#include "boost/scoped_array.hpp"#include "boost/shared_ptr.hpp"#include <vector>#include <boost/date_time/gregorian/gregorian.hpp>#include <iostream>void task() { std::cout << "do" << std::endl;}using namespace std;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))); // 遍历并打印元素 for (const auto& ptr : v) { std::cout << *ptr << std::endl; // 解引用打印内容 } return 0;}// 输出04:42:29: Starting /home/whois/MyQProject/boost/build/Desktop_Qt_6_5_3_GCC_64bit-Debug/boost...12
多亏了有 boost::shared_ptr,我们才能像上例中展示的那样,在标准容器中安全的使用动态分配的对象。因为 boost::shared_ptr 能够共享它所含对象的所有权,所以保存在容器中的拷贝(包括容器在需要时额外创建的拷贝)都是和原件相同的。如前所述,std::auto_ptr做不到这一点,所以绝对不应该在容器中保存它们。类似于 boost::scoped_ptr, boost::shared_ptr 类重载了以下这些操作符:operator*(),operator->() 和 operator bool()。另外还有 get() 和 reset() 函数来获取和重新初始化所包含的对象的地址。 |
---|
#include "boost/version.hpp"#include "boost/config.hpp"#include "boost/scoped_ptr.hpp" // 作用域指针#include "boost/scoped_array.hpp"#include "boost/shared_ptr.hpp"#include <vector>#include <boost/date_time/gregorian/gregorian.hpp>#include <iostream>void task() { std::cout << "do" << std::endl;}using namespace std;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))); // 遍历并打印元素 for (const auto& ptr : v) { std::cout << *ptr << std::endl; // 解引用打印内容 } boost::shared_ptr<int>i1(new int(1)); boost::shared_ptr<int>i2 = i1; std::cout << "i1 = " << *i1 << std::endl; std::cout << "i2 = " << *i2 << std::endl; i1.reset(new int(2)); std::cout << "i1 = " << *i1 << std::endl; std::cout << "i2 = " << *i2 << std::endl; return 0;}// 输出04:45:43: Starting /home/whois/MyQProject/boost/build/Desktop_Qt_6_5_3_GCC_64bit-Debug/boost...12i1 = 1i2 = 1i1 = 2i2 = 1
本例中定义了2个共享指针 i1 和 i2,它们都引用到同一个 int 类型的对象。i1 通过 new 操作符返回的地址显示的初始化,i2 通过 i1 拷贝构造而来。 i1 接着调用 reset(),它所包含的整数的地址被重新初始化。不过它之前所包含的对象并没有被释放,因为 i2 仍然引用着它。智能指针 boost::shared_ptr 记录了有多少个共享指针在引用同一个对象,只有在最后一个共享指针销毁时才会释放这个对象。 |
---|
>>>共享数组的行为类似于共享指针。关键不同在于共享数组在析构时,默认使用
delete[]
操作符来释放所含的对象。因为这个操作符只能用于数组对象,共享数组必须通过动态分配的数组的地址来初始化。 共享数组对应的类型是boost::shared_array
,它的定义在boost/shared_array.hpp
里。
#include "boost/version.hpp"#include "boost/config.hpp"#include "boost/scoped_ptr.hpp" // 作用域指针#include "boost/scoped_array.hpp"#include "boost/shared_ptr.hpp"#include "boost/shared_array.hpp"#include <vector>#include <boost/date_time/gregorian/gregorian.hpp>#include <iostream>void task() { std::cout << "do" << std::endl;}using namespace std;int main(){ boost::shared_array<int>i1(new int[2]); boost::shared_array<int>i2(i1); i1[0] = 2; std::cout << "i1[0] = " << i1[0] << std::endl; std::cout << "i2[0] = " << i2[0] << std::endl; i1[1] = 4; std::cout << "i1[1] = " << i1[1] << std::endl; std::cout << "i2[1] = " << i2[1] << std::endl; return 0;}// 输出04:56:43: Starting /home/whois/MyQProject/boost/build/Desktop_Qt_6_5_3_GCC_64bit-Debug/boost...i1[0] = 2i2[0] = 2i1[1] = 4i2[1] = 4
就像共享指针那样,所含对象的所有权可以跟其他共享数组来共享。这个例子中定义了2个变量 i1 和 i2,它们引用到同一个动态分配的数组。i1 通过 operator[]() 操作符保存了一个整数1——这个整数可以被 i2 引用,比如打印到标准输出。和本章中所有的智能指针一样,boost::shared_array 也同样提供了 get() 和 reset() 方法。另外还重载了 operator bool()。 |
---|
>>>到目前为止介绍的各种智能指针都能在不同的场合下独立使用。相反,弱指针只有在配合共享指针一起使用时才有意义。弱指针
boost::weak_ptr
的定义在boost/weak_ptr.hpp
里。
#include <windows.h> #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> #include <iostream> DWORD WINAPI reset(LPVOID p) { boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p); sh->reset(); return 0; } DWORD WINAPI print(LPVOID p) { boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p); boost::shared_ptr<int> sh = w->lock(); if (sh) std::cout << *sh << std::endl; return 0; } int main() { boost::shared_ptr<int> sh(new int(99)); boost::weak_ptr<int> w(sh); HANDLE threads[2]; threads[0] = CreateThread(0, 0, reset, &sh, 0, 0); threads[1] = CreateThread(0, 0, print, &w, 0, 0); WaitForMultipleObjects(2, threads, TRUE, INFINITE); }
boost::weak_ptr 必定总是通过 boost::shared_ptr 来初始化的。一旦初始化之后,它基本上只提供一个有用的方法: lock()。此方法返回的boost::shared_ptr 与用来初始化弱指针的共享指针共享所有权。如果这个共享指针不含有任何对象,返回的共享指针也将是空的。当函数需要一个由共享指针所管理的对象,而这个对象的生存期又不依赖于这个函数时,就可以使用弱指针。只要程序中还有一个共享指针掌管着这个对象,函数就可以使用该对象。如果共享指针复位了,就算函数里能得到一个共享指针,对象也不存在了。上例的 main() 函数中,通过 Windows API 创建了2个线程。于是乎,该例只能在 Windows 平台上编译运行。第一个线程函数 reset() 的参数是一个共享指针的地址。第二个线程函数 print() 的参数是一个弱指针的地址。这个弱指针是之前通过共享指针初始化的。一旦程序启动之后,reset() 和 print() 就都开始执行了。不过执行顺序是不确定的。这就导致了一个潜在的问题:reset() 线程在销毁对象的时候print() 线程可能正在访问它。通过调用弱指针的 lock() 函数可以解决这个问题:如果对象存在,那么 lock() 函数返回的共享指针指向这个合法的对象。否则,返回的共享指针被设置为0,这等价于标准的null指针。弱指针本身对于对象的生存期没有任何影响。 lock() 返回一个共享指针,print() 函数就可以安全的访问对象了。这就保证了——即使另一个线程要释放对象——由于我们有返回的共享指针,对象依然存在。 |
---|
>>>大体上,介入式指针的工作方式和共享指针完全一样。
boost::shared_ptr
在内部记录着引用到某个对象的共享指针的数量,可是对介入式指针来说,程序员就得自己来做记录。对于框架对象来说这就特别有用,因为它们记录着自身被引用的次数。 介入式指针boost::intrusive_ptr
定义在boost/intrusive_ptr.hpp
里。
>>>在你见过 Boost C++ 库的各种智能指针之后,应该能够编写安全的代码,来使用动态分配的对象和数组。多数时候,这些对象要存储在容器里——如上所述——使用
boost::shared_ptr
和boost::shared_array
这就相当简单了。
#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 拷进,拷出,或者在容器内部做拷贝,需要频繁的增加或者减少内部引用计数,这肯定效率不高。由于这些原因,Boost C++ 库提供了 指针容器 专门用来管理动态分配的对象。 |
---|
#include <boost/shared_ptr.hpp>#include <boost/ptr_container/ptr_vector.hpp>int main(){ boost::ptr_vector<int>v; v.push_back(new int(1)); v.push_back(new int(2)); return 0;}
boost::ptr_vector 类的定义在 boost/ptr_container/ptr_vector.hpp 里,它跟前一个例子中用 boost::shared_ptr 模板参数来初始化的容器具有相同的工作方式。 boost::ptr_vector 专门用于动态分配的对象,它使用起来更容易也更高效。 boost::ptr_vector 独占它所包含的对象,因而容器之外的共享指针不能共享所有权,这跟 std::vector<boost::shared_ptr<int> > 相反。除了 boost::ptr_vector 之外,专门用于管理动态分配对象的容器还包括:boost::ptr_deque, boost::ptr_list, boost::ptr_set, boost::ptr_map, boost::ptr_unordered_set 和 boost::ptr_unordered_map。这些容器等价于C++标准里提供的那些。最后两个容器对应于std::unordered_set 和 std::unordered_map,它们作为技术报告1的一部分加入 C++ 标准。如果所使用的 C++ 标准实现不支持技术报告1的话,还可以使用 Boost C++ 库里实现的 boost::unordered_set 和 boost::unordered_map。 |
---|
>>>知识回顾 开发环境:ubuntu20.04+Qt6.5.3+boost库
Qt | ubuntu20.04+boost_1_86_0搭建和编译(使用Qt6.5.3中运行测试程序) |
---|
Boost C++ 库 | 是什么? |
Boost C++ 库 | 智能指针(RAII、作用域指针、作用域数组) |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。