点击关注了解更多精彩内容!!
智能指针是C++中一项很常用的技术,合理的使用智能指针可以更方便的管理内存,降低内存泄漏的风险,这里只介绍C++11后官方的智能指针。01智能指针的种类
关于智能指针使用以及区别可以自行查找资料,这里主要介绍智能指针的实现原理。
shared_ptr的实现
我们平时看文档都知道shared_ptr内部是使用引用计数来记录托管指针被引用的次数,当托管指针的引用计数为0时会释放托管的内存,这里通过gcc源码探究shared_ptr内部究竟是如何实现的内存引用计数功能。非标准类图如下:
点击查看大图 如上图,shared_ptr类几乎什么都没有做,它是继承了shared_ptr, shared_ptr内部有一个类型为shared_count类型的成员_M_refcount, shared_count内部有类型为_Sp_counted_base*的_M_pi的成员, _Sp_counted_base才是整个shared_ptr功能的核心,通过_Sp_counted_base控制引用计数来管理托管的内存,由图可见_Sp_counted_base内部不持有托管内存的指针,这里__shared_count内部的成员其实是一个继承自_Sp_counted_base的_Sp_counted_ptr类型,_Sp_counted_ptr类型内部持有托管内存的指针_M_ptr, _M_pi是一个_Sp_counted_base基类对象指针,指向_Sp_counted_ptr子类对象内存,这样_M_pi内部就既可以控制引用计数,又可以在最后释放托管内存。这里称_M_pi为管理对象,它内部的_M_ptr为托管对象,管理同一块托管对象的多个shared_ptr内部共用一个管理对象(_M_pi), 这里的多个shared_ptr可能是通过第一个shared_ptr拷贝或者移动而来,管理对象内部有两个成员变量_M_use_count和_M_weak_count, _M_use_count表示托管对象的引用计数,控制托管对象什么时候析构和释放,大概就是有N个shared_ptr的拷贝那引用计数就是N,当引用计数为0时调用托管对象的析构函数且释放内存。_M_weak_count表示管理对象的引用计数,引用管理对象也是一个内存指针,这块指针是初始化第一个shared_ptr时new出来的,到最后也需要delete,所以使用_M_weak_count来控制管理对象什么时候析构,我们平时用到的weak_ptr内部其实持有的就是这个管理对象的指针,当weak_ptr拷贝时,管理对象的引用计数_M_weak_count就会增加,当_M_weak_count为0时,管理对象_M_pi就会析构且释放内存。 _M_use_count是如何加减的
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer"><span class="code-snippet__keyword">template <<span class="code-snippet__keyword">typename _Yp>
shared_ptr(const __shared_ptr<_Yp, _Lp>& r,
<span class="code-snippet_outer"> element_type* __p) <span class="code-snippet__keyword">noexcept
: _M_ptr(p), _M_refcount(r._M_refcount) // never throws
<span class="code-snippet_outer">{
}
<span class="code-snippet_outer"><br></br>
shared_count(const __shared_count& r) noexcept : _M_pi(__r._M_pi)
<span class="code-snippet_outer">{
if (_M_pi != 0) _M_pi->_M_add_ref_copy();
<span class="code-snippet_outer">}
<span class="code-snippet_outer"><span class="code-snippet__keyword">template <>
inline void _Sp_counted_base<_S_single>::_M_add_ref_copy()
<span class="code-snippet_outer">{
++_M_use_count;
<span class="code-snippet_outer">}
```
```
shared\_ptr拷贝时,内部**shared\_count类型的\_M\_refcount会进行拷贝,**shared\_count的拷贝构造函数会调用\_M\_add\_ref\_copy()方法,\_M\_add\_ref\_copy()方法中会将\_M\_use\_count加1。这里再看下shared\_ptr的赋值构造函数:-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
```
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer"><span class="code-snippet__keyword">template <<span class="code-snippet__keyword">typename _Yp>
_Assignable<const shared_ptr<_Yp>&> operator=(
<span class="code-snippet_outer"> <span class="code-snippet__keyword">const <span class="code-snippet__built_in">shared_ptr<_Yp>& __r) <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">this->__shared_ptr<_Tp>::<span class="code-snippet__keyword">operator=(__r);
return *this;
<span class="code-snippet_outer">}
<span class="code-snippet_outer"><span class="code-snippet__keyword">template <<span class="code-snippet__keyword">typename _Yp>
_Assignable<_Yp> operator=(const __shared_ptr<_Yp, _Lp>& r) <span class="code-snippetkeyword">noexcept
<span class="code-snippet_outer">{
_M_ptr = __r._M_ptr;
<span class="code-snippet_outer"> _M_refcount = __r._M_refcount; <span class="code-snippet__comment">// __shared_count::op= doesn't throw
return *this;
<span class="code-snippet_outer">}
<span class="code-snippet_outer">__shared_count& <span class="code-snippet__keyword">operator=(<span class="code-snippet__keyword">const __shared_count& __r) <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> _Sp_counted_base<_Lp>* __tmp = __r._M_pi;
if (__tmp != _M_pi) {
<span class="code-snippet_outer"> <span class="code-snippet__keyword">if (__tmp != <span class="code-snippet__number">0) __tmp->_M_add_ref_copy();
if (_M_pi != 0) _M_pi->_M_release();
<span class="code-snippet_outer"> _M_pi = __tmp;
}
<span class="code-snippet_outer"> <span class="code-snippet__keyword">return *<span class="code-snippet__keyword">this;
}
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer">~__shared_count() <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">if (_M_pi != <span class="code-snippet__literal">nullptr) _M_pi->_M_release();
}
<span class="code-snippet_outer"><br></br>
template <>
<span class="code-snippet_outer"><span class="code-snippet__keyword">inline <span class="code-snippet__keyword">void _Sp_counted_base<_S_single>::_M_release() <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">if (--_M_use_count == <span class="code-snippet__number">0) {
_M_dispose();
<span class="code-snippet_outer"> <span class="code-snippet__keyword">if (--_M_weak_count == <span class="code-snippet__number">0) _M_destroy();
}
<span class="code-snippet_outer">}
<span class="code-snippet_outer"><span class="code-snippet__keyword">virtual <span class="code-snippet__keyword">void _M_dispose() <span class="code-snippet__keyword">noexcept { <span class="code-snippet__keyword">delete _M_ptr; }
```
```
在shared\_ptr生命周期结束析构时会将引用计数减1,如果引用引用计数为0,会调用\_M\_dispose()函数进而释放托管对象内存。
\_M\_weak\_count是如何加减的
上面的代码中可以看见—\_M\_weak\_count为0时,会调用\_M\_destroy()函数,这里看看—\_M\_weak\_count是如何加减的。管理对象初始化时\_M\_weak\_count的初始值为1
-
```
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer">_Sp_counted_base() <span class="code-snippet__keyword">noexcept : _M_use_count(<span class="code-snippet__number">1), _M_weak_count(<span class="code-snippet__number">1) {}
```
```
注意当shared\_ptr拷贝或者移动时\_M\_weak\_count是不会增加的,它表示的是管理对象的计数,只有当\_\_M\_use\_count为0时\_M\_weak\_count才会减1,除此之外\_M\_weak\_count的数值是由weak\_ptr控制的。
由上面类图可以看见weak\_ptr内部其实和shared\_ptr内部持有的是同一个管理对象指针,即\_Sp\_counted\_base的指针,当weak\_ptr拷贝析构时候,\_Sp\_counted\_base内部的\_M\_weak\_count会相应加减。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
```
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer">__weak_count(<span class="code-snippet__keyword">const __weak_count& __r) <span class="code-snippet__keyword">noexcept : _M_pi(__r._M_pi)
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">if (_M_pi != <span class="code-snippet__literal">nullptr) _M_pi->_M_weak_add_ref();
}
<span class="code-snippet_outer"><br></br>
template <>
<span class="code-snippet_outer"><span class="code-snippet__keyword">inline <span class="code-snippet__keyword">void _Sp_counted_base<_S_single>::_M_weak_add_ref() <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> ++_M_weak_count;
}
<span class="code-snippet_outer"><br></br>
~__weak_count() noexcept
<span class="code-snippet_outer">{
if (_M_pi != nullptr) _M_pi->_M_weak_release();
<span class="code-snippet_outer">}
<span class="code-snippet_outer"><span class="code-snippet__keyword">template <>
inline void _Sp_counted_base<_S_single>::_M_weak_release() noexcept
<span class="code-snippet_outer">{
if (--_M_weak_count == 0) _M_destroy();
<span class="code-snippet_outer">}
<span class="code-snippet_outer"><span class="code-snippet__keyword">virtual <span class="code-snippet__keyword">void _M_destroy() <span class="code-snippet__keyword">noexcept { <span class="code-snippet__keyword">delete <span class="code-snippet__keyword">this; }
```
```
从代码中可以看出,weak\_ptr拷贝时\_M\_weak\_count加1,析构时\_M\_weak\_count减1,当\_M\_weak\_count为0时,表示不再需要管理对象来控制托管对象,调用\_M\_destroy()的delete this来释放管理对象内存。关于delete this,我们之后的文章会讲到。
weak\_ptr的expired()和lock()做了什么
-
-
-
-
```
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">bool <span class="code-snippet__title">expired<span class="code-snippet__params">() <span class="code-snippet__keyword">const <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">return _M_refcount._M_get_use_count() == <span class="code-snippet__number">0;
}
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer">__shared_ptr<_Tp, _Lp> lock() <span class="code-snippet__keyword">const <span class="code-snippet__keyword">noexcept
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">return __shared_ptr<element_type, _Lp>(*<span class="code-snippet__keyword">this, <span class="code-snippet__built_in">std::nothrow);
}
<span class="code-snippet_outer"><br></br>
shared_ptr(const __weak_ptr<_Tp, _Lp>& r, std::nothrow_t)
<span class="code-snippet_outer"> : _M_refcount(__r._M_refcount, <span class="code-snippet__built_in">std::nothrow)
{
<span class="code-snippet_outer"> _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : <span class="code-snippet__literal">nullptr;
}
weak_ptr的lock()函数是打算返回一个shared_ptr对象来延长托管对象的生命周期,这里返回后需要判断返回值是否为nullptr。 shared_from_this()是如何实现的
<pre class="code-snippet__js" data-lang="cpp">```
<span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class <span class="code-snippet__title">enable_shared_from_this
{
<span class="code-snippet_outer"> <span class="code-snippet__built_in">shared_ptr<<span class="code-snippet__keyword">const _Tp> shared_from_this() <span class="code-snippet__keyword">const
{
<span class="code-snippet_outer"> <span class="code-snippet__keyword">return <span class="code-snippet__built_in">shared_ptr<<span class="code-snippet__keyword">const _Tp>(<span class="code-snippet__keyword">this->_M_weak_this);
}
<span class="code-snippet_outer"><br></br>
mutable weak_ptr<_Tp> _M_weak_this;
<span class="code-snippet_outer">};
```
```
使用shared\_from\_this()的类需要继承enable\_shared\_from\_this类,enable\_shared\_from\_this类中持有一个类型为weak\_ptr的成员\_M\_weak\_this,调用shared\_from\_this()就是将内部持有的weak\_ptr转成了shared\_ptr。
总结
shared\_ptr内部使用\_\_shared\_count中的\_Sp\_counted\_base对象来控制托管指针,\_Sp\_counted\_base内部有\_M\_use\_count和\_M\_weak\_count,\_M\_use\_count表示托管指针的引用计数,\_M\_weak\_count表示\_Sp\_counted\_base的引用计数,\_M\_use\_count为0时候释放托管指针指向的内存,\_M\_weak\_count为0时释放\_Sp\_counted\_base指向的内存,这里\_Sp\_counted\_base的生命线一般不会短于shared\_ptr的生命线。
注意:文中所说的释放指针指向的内存不太准确,这里表示delete \*ptr,既调用析构函数又释放相应内存。
▼ 更多精彩推荐,请关注我们 ▼**·END·**
**代码精进之路**
![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2020-06-14/761d8ad26efe2f9657b1b5ae06e40489)
代码精进之路,我们一起成长!
![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2020-06-14/ec59ef4f4fb00257b346e11c98202626)
[RAII妙用之计算函数耗时](/jump/aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1NekkzTmpBMU9URXpNZz09Jm1pZD0yMjQ3NDgzODUyJmlkeD0xJnNuPTM0ZGRlODUzZDUzMDRmOGExNjVlODJlZmZlMjg3ZjNjJmNoa3NtPWViN2EwNzVmZGMwZDhlNDk3MWY1NWRjM2M0YWQ0Mzg4YTNjNzQ2NmM2ZGY1YTQ3YmNmYTgxNTEwZGUyMzBjZjIyMDY1Mjk4ZmVlMjYmc2NlbmU9MjEjd2VjaGF0X3JlZGlyZWN0)
[每日一题:堆箱子](/jump/aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1NekkzTmpBMU9URXpNZz09Jm1pZD0yMjQ3NDgzODUyJmlkeD0yJnNuPTllNGRkM2RiYWNmMTU4YTkxMTI2YTc0MWExN2JlYjkwJmNoa3NtPWViN2EwNzVmZGMwZDhlNDljNDI2YzFmNWYzNTkxMzQxNGI4MzZkZmMxMjAwMTZlOWJjMmQyOTQ1ZjEyZTdjZDQ0ZTQ4ZGZmZTg1ZTMmc2NlbmU9MjEjd2VjaGF0X3JlZGlyZWN0)
[深入浅出虚拟内存](/jump/aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1NekkzTmpBMU9URXpNZz09Jm1pZD0yMjQ3NDgzODQwJmlkeD0xJnNuPTQ4YTViYTE4MGM2MTg3NzEzNDM0NDA4NGUyNWQyN2Y0JmNoa3NtPWViN2EwNzUzZGMwZDhlNDUwZGNmZTNhNmQ2YjA0MDVjMDQzYjYwOTQ0MTczNTEwODM2MDQ5ODAyZDQ4ODI4ZTM2NmQ0N2Q4ODY3NGUmc2NlbmU9MjEjd2VjaGF0X3JlZGlyZWN0)
[深入浅出虚拟内存(二)绘制虚拟内存排布图](/jump/aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1NekkzTmpBMU9URXpNZz09Jm1pZD0yMjQ3NDgzODQwJmlkeD0yJnNuPWZmNDk4YmFkZDEzOGQ4OTdjM2QxOTI4NGI0ZGRjOGY5JmNoa3NtPWViN2EwNzUzZGMwZDhlNDUxNGM2MTMxOTljMzNmZDFjNjA0Y2I4ZTJmMjdmNmU0MzYyZDA5ZmE1Mzc2YTZkY2RlYTJkNmI3M2Q1Mzgmc2NlbmU9MjEjd2VjaGF0X3JlZGlyZWN0)
[深入浅出虚拟内存(三)堆内存分配及malloc实现原理](/jump/aHR0cDovL21wLndlaXhpbi5xcS5jb20vcz9fX2Jpej1NekkzTmpBMU9URXpNZz09Jm1pZD0yMjQ3NDgzODQwJmlkeD0zJnNuPTM4OGVjZTdkMjc0YmJmZDY5ZTQ4ZDAxNzk1ZTA0ZDZmJmNoa3NtPWViN2EwNzUzZGMwZDhlNDUwODhhYTViNDAyZDJhZjY3OGRkZDVkOTFlYzQxYzg5NDU0OTg2YzA3NjAyZTk5MjVkYmYyZTA2ZGZjMTkmc2NlbmU9MjEjd2VjaGF0X3JlZGlyZWN0)
<a class="weapp_text_link js_weapp_entry" data-miniprogram-appid="wx3acabfc252730572" data-miniprogram-nickname="粉丝社" data-miniprogram-path="pages/index/index?id=117092" data-miniprogram-servicetype="" data-miniprogram-type="text" href="" style="font-size:17px;">点此留言</a>
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。