《The Boost C++ Libraries》 第一章 智能指针
Boost.SmartPointers中提供了多种智能指针,它们采用在智能指针析构时释放内存的方式,帮助管理动态分配的对象。由于析构函数在智能指针生命周期结束时被执行,所以由它管理的动态分配对象可以保证被释放。这样则不会出现内存泄漏,即使你忘记了手动delete。
从C++98开始,标准库中开始提供智能指针std::auto_ptr,但是std::auto_ptr在C++11中被废弃。C++11标准库中引入了更好的智能指针。std::shared_ptr和std::weak_ptr源于Boost.SmartPoints的boost::shared_ptr和boost::weak_ptr。boost中没有std::unique_ptr的对应实现。但是Boost.SmartPointers提供了额外四种标准库中没有的智能指针:boost::scope_ptr,boost::scoped_array,boost::shared_array和boost::intrusive_ptr。
独占所有权
boost::scope_ptr是一种独占某个动态分配对象的智能指针。boost::scope_ptr不可被拷贝或者转移。该智能指针被定义在头文件boost/scoped_ptr.hpp。
Example 1.1. 使用boost::scoped_ptr
#include <boost/scoped_ptr.hpp>
#include <iostream>
int main()
{
boost::scoped_ptr<int> p{new int{1}};
std::cout << *p << '\n';
p.reset(new int{2});
std::cout << *p.get() << '\n';
p.reset();
std::cout << std::boolalpha << static_cast<bool>(p) << '\n';
}
boost::scoped_ptr类型的智能指针不可以转移它所管理的对象的所有权。boost::scoped_ptr在用一个地址初始化后,动态分配的对象将在scoped_ptr被析构时或者在调用reset()方法时被释放。
Example1.1 使用了一个boost::scoped_ptr类型的智能指针p,p用一个指向动态分配的数字1的指针来初始化。通过*操作符,p被解引用并且1被打印到标准输出。
可以使用reset()方法将一个新的地址存入智能指针,例子中使用reset()方法向p传入了一个新的动态分配的数字2,原本存储于p的对象会自动被释放。
get()方法返回智能指针中存储的对象的裸地址,例子中对get()返回的地址进行解引用,并将得到的2打印至标准输出。
boost::scoped_ptr重载了bool操作符。当智能指针存储了某个对象的引用时(也即不为空),bool操作符返回true。在例子中,标准输出将输出false,因为p调用reset()被清空了。
boost::scoped_ptr的析构函数使用delete释放它引用的对象,而动态分配的数组应该用delete[]来释放,所以boost::scoped_ptr不可以用动态分配的数组地址来初始化。对于数组,Boost.SmartPointers提供了boost::scoped_array类。
Example 1.2. 使用boost::scoped_array
#include <boost/scoped_array.hpp>
int main()
{
boost::scoped_array<int> p{new int[2]};
*p.get() = 1;
p[1] = 2;
p.reset(new int[3]);
}
智能指针boost::scoped_array使用方法和boost::scoped_ptr类似,最主要的区别在于boost::scoped_array在析构函数中使用delete[]来释放它所包含的对象。由于该操作只适用于数组,所以boost::scoped_array必须使用一个指向动态分配的数组的指针来初始化。
boost::scoped_array在头文件boost/scoped_array.hpp中定义。
boost::scoped_array重载了[]操作符,可以使用[]操作符访问数组中的指定元素。所以boost::scoped_array类型的对象的行为与其保存的数组指针类似。例子1.2中在p中所保存的数组中的第二个元素中存入数字2。
和boost::scoped_ptr一样,成员函数get()可以被用于获取裸指针,reset()可以被用于重置包含的对象。
共享所有权
智能指针boost::shared_ptr和boost::scoped_ptr类似,它们的主要区别在于boost::shared_ptr不一定是某个对象的唯一所有者。所有权可以被其它boost::shared_ptr类型的智能指针所共享。在这种情况下,被共享的动态对象只有在最后一个引用它的共享指针结束生命周期后才会被释放。由于boost::shared_ptr可以共享所有权,所以该智能指针可以被拷贝,这对于boost::scoped_ptr是不可能的。
boost::shared_ptr在头文件boost/shared_ptr.hpp中定义。
Example1.3. 使用boost::shared_ptr
#include <boost/shared_ptr.hpp>
#include <iostream>
int main()
{
boost::shared_ptr<int> p1{new int{1}};
std::cout << *p1 << '\n';
boost::shared_ptr<int> p2{p1};
p1.reset(new int{2});
std::cout << *p1.get() << '\n';
p1.reset();
std::cout << std::boolalpha << static_cast<bool>(p2) << '\n';
}
例1.3使用了两个boost::shared_ptr类型的智能指针p1和p2,p2使用p1来进行初始化,这意味着两个智能指针共享int类型对象的所有权。当p1调用reset()方法时,一个新的int类型对象被绑定入p1中,但这并不会使得原有的int类型对象被销毁,因为该对象也被p2所持有,所以该对象仍然存在。在reset()被调用后,p1是值为2的int类型对象的唯一所有者,p2是值为1的int类型对象的唯一所有者。
boost::shared_ptr内部使用引用计数。只有在boost::shared_ptr检测到最后一个引用某个动态对象的只能指针被销毁后,该对象才会被delete释放。
与boost::scoped_ptr类似,boost::shared_ptr重载了bool()运算符、*运算符和->运算符。也提供成员函数get()来获取裸指针,reset()函数来绑定一个新的对象.
boost::shared_ptr在构造时可以传入一个删除器作为第二个参数。这个删除器必须是一个函数或者一个函数对象,并且接受该实例化的boost::shared_ptr为唯一参数。在boost::shared_ptr析构时,该删除器取代delete被调用。这个特性使得boost::shared_ptr可以用于管理动态分配对象以外的其它资源。
Example 1.4. boost::shared_ptr和自定义删除器
#include <boost/shared_ptr.hpp>
#include <Windows.h>
int main()
{
boost::shared_ptr<void> handle(OpenProcess(PROCESS_SET_INFORMATION, FALSE,
GetCurrentProcessId()), CloseHandle);
}
在例1.4中,使用void实例化boost::shared_ptr。OpenProcess()的返回结果作为第一个参数被传递给构造函数。OpenProcess()是一个Windows中获取进程句柄的函数。在例子中,OpenProcess()返回指向当前进程的句柄,也就是该例子本身。
Windows中使用句柄来引用资源。当某个资源不再需要使用时,句柄必须使用CloseHandle()来关闭。CloseHandle()的唯一参数就是要被关闭的句柄。在例子中,CloseHandle()作为第二个参数被传入boost::shared_ptr的构造函数,也就是CloseHandle()是handle的删除器。当handle在main()函数结束被销毁时,智能指针的析构函数调用CloseHandle()来关闭作为第一个参数被传入智能指针构造函数的句柄。
注意:例1.4可以正常工作是因为Windows中将句柄定义为void类型。如果OpenProcess()不返回一个void类型的值或者CloseHandle()不接受一个void*类型的参数,那么在该例中就不能使用boost::shared_ptr。删除器并不能使boost::shared_ptr成为管理任意资源的万能工具。
Example 1.5. 使用boost::make_shared
#include <boost/make_shared.hpp>
#include <typeinfo>
#include <iostream>
int main()
{
auto p1 = boost::make_shared<int>(1);
std::cout << typeid(p1).name() << '\n';
auto p2 = boost::make_shared<int[]>(10);
std::cout << typeid(p2).name() << '\n';
}
Boost.SmartPointers在boost/make_shared.hpp中提供了一个工具函数boost::make_shared()。使用boost::make_shared()可以不直接调用boost::shared_ptr的构造函数来创建一个boost::shared_ptr类型的的只能指针。
使用boost::make_shared()的好处在于保存对象所用的动态内存和保存智能指针内部的引用计数器的内存可以被保存在一起。使用boost::make_shared()比先使用new来创建一个动态对象而后又在boost::shared_ptr的构造函数中使用new来创建引用计数器效率更高。
也可以对数组使用boost::make_shared()。在例1.5中,第二次调用boost::make_shared将一个有十个元素的数组绑定入p2中。
boost::shared_ptr从1.53.0版本开始开始支持数组。就像boost::scoped_ptr和boost::scoped_array的关系一样,boost::shared_array是一个类似于boost::shared_ptr的智能指针。如果使用Visual C++2013和Boost1.53.0或者更新版本来进行构建的话,例1.5中对于p2的类型会打印出class boost::shared_ptr<int [0]>。
从Boost1.53.0版本开始,boost::shared_ptr同时支持单个对象和数组,并且会自动检测需要用delete还是用delete[]来释放资源。因为boost::shared_ptr也重载了[]操作符(1.53.0版本后),所以它也可以作为boost::shared_array的替代品来使用。
特殊的智能指针
到目前为止介绍的每种智能指针都可以被独立地用在不同的场景中。但是boost::weak::ptr只有在和boost::shared_ptr一起使用是才有意义。boost::weak_ptr被定义在boost/weak_ptr.hpp中。
Example 1.8. 使用boost::weak_ptr
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <thread>
#include <functional>
#include <iostream>
void reset(boost::shared_ptr<int> &sh)
{
sh.reset();
}
void print(boost::weak_ptr<int> &w)
{
boost::shared_ptr<int> sh = w.lock();
if (sh)
std::cout << *sh << '\n';
}
int main()
{
boost::shared_ptr<int> sh{new int{99}};
boost::weak_ptr<int> w{sh};
std::thread t1{reset, std::ref(sh)};
std::thread t2{print, std::ref(w)};
t1.join();
t2.join();
}
boost::weak::ptr必须用一个boost::shared_ptr来进行初始化。它最重要的成员函数是lock()。lock()返回一个与该weak指针初始化时的传入的shared指针共享所有权的一个boost::shared_ptr。如果shared指针是空的,那么返回的指针也会是空的。
boost::weak_ptr可以使用的场景为:当某个函数中需要用到某个由shared指针管理的对象,但是该对象的生命周期却不应该取决于该函数本身。这个函数只有在程序中别的地方至少还有一个shared指针享有该对象的所有权时才能使用该对象。这样可以使得如果shared指针被reset了,被管理的对象不会由于在函数中还有一个额外的shared指针的存在而继续存活。
例1.8中在main函数创建了两个线程,第一个线程接收一个shared指针的引用作为参数,并在线程中调用了reset()。第二个线程中接收一个weak指针的引用作为参数,并在线程调用了print()。这个weak指针在此前用上述的shared指针初始化。
程序开始运行时,reset()和print()时同时执行的,但是它们的执行顺序是不确定的。这就带来了在print()被调用时,对象刚好被reset()销毁的潜在问题。
而weak指针可以解决这一问题:调用lock()时,如果对象仍然存活,则返回一个可用的shared指针。如果对象已经被销毁,则返回的shared指针被置为0,与一个空指针等价。
boost::weak::ptr本身不会对对象的生命周产生影响。为了安全地在print()函数中访问该对象,lock()返回一个boost::shared_ptr。这保证了即使另一个线程中试图释放该对象,这个对象也会因为这个返回的shared指针而继续存活。
《The Boost C++ Libraries》 第一章 智能指针的更多相关文章
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- [4] 智能指针boost::scoped_ptr
[1]boost::scoped_ptr简介 boost::scoped_ptr属于boost库,定义在namespace boost中,包含头文件#include <boost/scoped_ ...
- 智能指针剖析(下)boost::shared_ptr&其他
1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...
- C++智能指针剖析(下)boost::shared_ptr&其他
1. boost::shared_ptr 前面我已经讲解了两个比较简单的智能指针,它们都有各自的优缺点.由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不 ...
- Boost中的智能指针(转)
这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...
- Boost智能指针使用总结
内存管理是一个比较繁琐的问题,C++中有两个实现方案: 垃圾回收机制和智能指针.垃圾回收机制因为性能等原因不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案. 1. 智能指针定义 ...
- boost智能指针总结
智能指针是一种具备指针类似行为的对象,当不在需要它的时候自动删除其引用的c++对象.直接点说就是自动析构C++对象. boost提供了6种智能指针,如下所示: scoped_ptr <boost ...
- C++智能指针详解
本文出自http://mxdxm.iteye.com/ 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最 ...
- 【转】C++ 智能指针详解
一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...
随机推荐
- 【RAC】 RAC For W2K8R2 安装--创建ASM磁盘组(六)
[RAC] RAC For W2K8R2 安装--创建ASM磁盘组(六) 一.1 BLOG文档结构图 一.2 前言部分 一.2.1 导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以 ...
- Linux 各系统目录作用及内容
- C#-使用GoogleAPI读写spreadsheets
https://docs.google.com/spreadsheets/在线使用一些常用办公工具,比如excel. 如需要C#代码自动读写这些excel,则需要使用GoogleAPI. 封装的公用类 ...
- Termux和Ubuntu建立ssh连接
1 本机环境 Android:Termux v0.77 作为客户端 Linux:Ubuntu 19.10 作为服务器 两者处于同一局域网下 2 ssh安装 2.1 Termux pkg install ...
- ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. Poor Ramzi -dp+记忆化搜索
ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. ...
- Kotlin高阶函数与函数式编程详解
函数可变参数: 在上一次https://www.cnblogs.com/webor2006/p/11518425.html中学到了可变参考,关于可变参数有如下规则说明: “一个方法中,只允许一个参数为 ...
- 【Beta】Scrum meeting3
第三天:2019/6/26 前言: 第3次会议于6月26日在教9-501召开. 对每个人负责撰写的文档进行分配,并讨论其中模糊的问题,时长30min. 本日任务完成情况 成员 今日完成任务情况 成员贡 ...
- 【CLAA系列】CLAA 通讯过程
名词: MSP:中兴服务器 CS:客户服务器,也就是我们的服务器 GW:网关,这里默认是中兴的网关 Chip:芯片,这里特指包含了Lora标准通讯模块,且针对CLAA做过特殊优化的芯片. Lora:L ...
- 异常检测(Anomaly detection): 异常检测算法(应用高斯分布)
估计P(x)的分布--密度估计 我们有m个样本,每个样本有n个特征值,每个特征都分别服从不同的高斯分布,上图中的公式是在假设每个特征都独立的情况下,实际无论每个特征是否独立,这个公式的效果都不错.连乘 ...
- 如何使用project制定项目计划?(附详细步骤截图)
使用project制定项目计划可以分为六个步骤,如下图(1): 图(1)-project制定项目计划步骤 下面我们就以project2010为例,按上图所示步骤对如何制定项目计划进行详细说明: 一.创 ...