#Boost# Boost 智能指针
志扬工作室共
8345字,需浏览
17分钟
·
2022-11-17 16:47
“ 文章所涉及内容更多来自网络,在此声明,并感谢知识的贡献者!”
http://zh.highscore.de/cpp/boost/introduction.htmlBoost.Any 提供了一个名为boost::any 的数据类型,可以存放任意的类型。 例如,一个类型为 boost::any 的变量可以先存放一个 int 类型的值,然后替换为一个 std::string 类型的字符串。Boost.Array 可以把 C++数组视同 C++ 标准的容器。Boost.Asio 可用于开发异步处理数据的应用,如网络应用。Boost.Bimap 提供了一个名为boost::bimap 的类,它类似于 std::map. 主要的差别在于 boost::bimap 可以同时从键和值进行搜索。Boost.Bind 是一种适配器,可以将函数作为模板参数,即使该函数的签名与模板参数不兼容。Boost.Conversion 提供了三个转型操作符,分别执行向下转型、交叉转型,以及不同数字类型间的值转换。Boost.DateTime 可用于以灵活的格式处理、读入和写出日期及时间值。Boost.Exception 可以在抛出的异常中加入额外的数据,以便在 catch 处理中提供更多的信息。 这有助于更容易地调试,以及对异常情况更好地作出反应。Boost.Filesystem 提供了一个类来处理路径信息,还包含了几个访问文件和目录的函数。Boost.Format 以一个类型安全且可扩展的 boost::format 类替代了 std::printf() 函数。Boost.Function 简化了函数指针的定义。Boost.Interprocess 允许多个应用通过共享内存以快速、高效的方式进行通信。Boost.Lambda 可以定义匿名的函数。 代码被内联地声明和执行,避免了单独的函数调用。Boost.Multiindex 定义了一些新的容器,它们可以同时支持多个接口,如 std::vector 和 std::map 的接口。Boost.NumericConversion提供了一个转型操作符,可以安全地在不同的数字类型间进行值转换,不会生成上溢出或下溢出的条件。Boost.PointerContainer 提供了专门为动态分配对象进行优化的容器。Boost.Ref 的适配器可以将不可复制对象的引用传给需要复制的函数。Boost.Regex 提供了通过正则表达式进行文本搜索的函数。通过 Boost.Serialization,对象可以被序列化,如保存在文件中,并在以后重新导入。Boost.Signal 是一个事件处理的框架,基于所谓的 signal/slot 概念。 函数与信号相关联并在信号被触发时自动被调用。Boost.SmartPoiners 提供了多个智能指针,简化了动态分配对象的管理。Boost.Spirit 可以用类似于EBNF (扩展巴科斯范式)的语法生成词法分析器。Boost.StringAlgorithms 提供了多个独立的函数,以方便处理字符串。Boost.System 提供了一个处理系统相关或应用相关错误代码的框架。Boost.Tokenizer 可以对一个字符串的各个组件进行迭代。Boost.Tuple 提供了泛化版的std::pair,可以将任意数量的数据组在一起。Boost.Unordered 扩展了C++ 标准的容器,增加了boost::unordered_set 和 boost::unordered_map.Boost.Variant 可以定义多个数据类型,类似于 union, 将多个数据类型组在一起。 Boost.Variant 比 union 优胜的地方在于它可以使用类。C++标准只提供了一种智能指针: std::auto_ptr。 它基本上就像是个普通的指针: 通过地址来访问一个动态分配的对象。 std::auto_ptr之所以被看作是智能指针,是因为它会在析构的时候调用 delete 操作符来自动释放所包含的对象。
当然这要求在初始化的时候,传给它一个由 new 操作符返回的对象的地址。 既然 std::auto_ptr 的析构函数会调用 delete 操作符,它所包含的对象的内存会确保释放掉。智能指针确保在任何情况下,动态分配的内存都能得到正确释放。用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。
因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。windows_handle h(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId())); SetPriorityClass(h.handle(), HIGH_PRIORITY_CLASS); 通过 OpenProcess() 打开的资源不需要显示的调用 CloseHandle() 来关闭。 当然,应用程序终止时资源也会随之关闭。 然而,在更加复杂的应用程序里, windows_handle 类确保当一个资源不再使用时就能正确的关闭。 某个资源一旦离开了它的作用域——上例中 h 的作用域在 main()函数的末尾——它的析构函数会被自动的调用,相应的资源也就释放掉了。一个作用域指针独占一个动态分配的对象,对应的类名为 boost::scoped_ptr。一个作用域指针不能传递它所包含的对象的所有权到另一个作用域指针。
一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。因为一个作用域指针只是简单保存和独占一个内存地址,所以 boost::scoped_ptr的实现就要比 std::auto_ptr 简单。 在不需要所有权传递的时候应该优先使用 boost::scoped_ptr 。 在这些情况下,比起 std::auto_ptr 它是一个更好的选择,因为可以避免不经意间的所有权传递。#include <boost/scoped_ptr.hpp> boost::scoped_ptr<int> i(new int); 一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。get() 和 reset() 方法:前者返回所含对象的地址,后者用一个新的对象来重新初始化智能指针。
在这种情况下,新创建的对象赋值之前会先自动释放所包含的对象。boost::scoped_ptr 的析构函数中使用delete 操作符来释放所包含的对象。 这对 boost::scoped_ptr所包含的类型加上了一条重要的限制。作用域数组的使用方式与作用域指针相似。 关键不同在于,作用域数组的析构函数使用 delete[]操作符来释放所包含的对象。 因为该操作符只能用于数组对象,所以作用域数组必须通过动态分配的数组来初始化。对应的作用域数组类名为 boost::scoped_array#include <boost/scoped_array.hpp> boost::scoped_array<int> i(new int[2]); 可以通过 operator[]() 操作符访问数组中特定的元素,boost:scoped_array 也提供了 get() 和 reset() 方法,用来返回和重新初始化所含对象的地址。智能指针 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> 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 能够共享它所含对象的所有权,所以保存在容器中的拷贝(包括容器在需要时额外创建的拷贝)都是和原件相同的。共享数组的行为类似于共享指针。 关键不同在于共享数组在析构时,默认使用 delete[]操作符来释放所含的对象。 因为这个操作符只能用于数组对象,共享数组必须通过动态分配的数组的地址来初始化。共享数组对应的类型是 boost::shared_array#include <boost/shared_array.hpp> boost::shared_array<int> i1(new int[2]); boost::shared_array<int> i2(i1); std::cout << i2[0] << std::endl; 就像共享指针那样,所含对象的所有权可以跟其他共享数组来共享。 这个例子中定义了2个变量i1 和 i2,它们引用到同一个动态分配的数组。i1 通过 operator[]() 操作符保存了一个整数1——这个整数可以被 i2 引用,比如打印到标准输出。boost::shared_array 也同样提供了 get() 和 reset() 方法。 另外还重载了 operator bool()。弱指针只有在配合共享指针一起使用时才有意义。 弱指针 boost::weak_ptr#include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> DWORD WINAPI reset(LPVOID p) boost::shared_ptr<int> *sh =
static_cast<boost::shared_ptr<int>*>(p); DWORD WINAPI print(LPVOID p) boost::weak_ptr<int> *w =
static_cast<boost::weak_ptr<int>*>(p); boost::shared_ptr<int> sh = w->lock(); std::cout << *sh << std::endl; boost::shared_ptr<int> sh(new int(99)); boost::weak_ptr<int> w(sh); 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::intrusive_ptr的工作方式和共享指针完全一样。boost::shared_ptr 在内部记录着引用到某个对象的共享指针的数量,可是对介入式指针来说,程序员就得自己来做记录。
对于框架对象来说这就特别有用,因为它们记录着自身被引用的次数。#include <boost/intrusive_ptr.hpp> void intrusive_ptr_add_ref(IDispatch *p) void intrusive_ptr_release(IDispatch *p) void check_windows_folder() CLSIDFromProgID(CComBSTR("Scripting.FileSystemObject"),
&clsid); CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch),
&p); boost::intrusive_ptr<IDispatch> disp(static_cast<IDispatch*>(p));CComDispatchDriver dd(disp.get()); CComVariant arg("C:\\Windows"); dd.Invoke1(CComBSTR("FolderExists"), &arg, &ret); std::cout << (ret.boolVal != 0) << std::endl; COM 对象是使用 boost::intrusive_ptr的绝佳范例,因为 COM 对象需要记录当前有多少指针引用着它。 通过调用 AddRef() 和 Release() 函数,内部的引用计数分别增 1 或者减 1。当引用计数为 0 时,COM 对象自动销毁。在 intrusive_ptr_add_ref() 和 intrusive_ptr_release() 内部调用 AddRef() 和Release() 这两个函数,来增加或减少相应 COM 对象的引用计数。#include
<boost/ptr_container/ptr_vector.hpp> boost::ptr_vector<int> v; 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++标准里提供的那些。
浏览
24点赞
评论
收藏
分享
手机扫一扫分享
分享
举报
点赞
评论
收藏
分享
手机扫一扫分享
分享
举报