关于shared_ptr

简单总结,详见参考资料。

引用计数的实现方式

引用计数是与对象绑定的,并且可能有多个shared_ptr绑定同一对象,为了达到同时更新多个shared_ptr的引用计数,所以需要让他们指向同一个引用计数,所以只能在堆上另外分配空间来存放引用计数。
为了保证多线程读同一个shared_ptr是安全的,引用计数的增减是原子操作。

线程安全性

shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:
一个 shared_ptr 对象实体可被多个线程同时读取;
两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;
如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁;

为什么shared_ptr不能并发读写:因为 shared_ptr 有两个数据成员(引用计数、对象指针),读写操作不能原子化。
具体发生多线程问题的场景可以自己推导,更详细的说明可以看陈硕写的文章:
为什么多线程读写 shared_ptr 要加锁?

enable_shared_from_this的实现

一个主要的场景是保证异步回调函数中操作的对象仍然有效。
std::enable_shared_from_this<T> 有一个std::weak_ptr<T>的成员,实际上在构造std::enable_shared_from_this<T>时,并没有初始化std::weak_ptr<T>成员,而是在用这个std::enable_shared_from_this<T>去构造std::shared_ptr的时候,去构造并初始化这个std::weak_ptr<T>成员。所以这也就是为什么cppreference中说的这个对象必须是std::shared_ptr管理的,因为这个对象不是通过std::shared_ptr来管理,那么std::weak_ptr是未初始化的,无法通过其提升为std::shared_ptr对象。
如何判断是否继承了shared_from_this?模板类型推导:根据是否有继承特定类型实例化不同的模板实现;
多继承场景下使用虚继承解决问题;

make_shared/new

与直接使用new相比,make函数不存在代码重复且具备异常安全性,std::make_shared和std::allocate_shared可以生成更快更短的目标码。
对象和控制块分配在一块内存上,减少了内存分配的次数。但是这并不一定是好事,因为这导致对象和控制块占用的内存也要一次回收掉。即,如果还有std::weak_ptr存在,控制块就要在,对象占用的内存也没办法回收。如果对象比较大,且std::weak_ptr在对象析构后还可能长期存在,那么这种开销是不可忽视的。
在需要自定义删除器及以大括号初始化时make函数无能为力。
更详细可以阅读《Effective Moden C++》Item 21。

pointer_cast的实现

智能指针的类型转换无法通过static_cast等实现,需要使用static_pointer_cast方法(想一下如果取裸指针出来static_cast会发生什么)。static_pointer_cast是通过shared_ptr一个特殊的构造函数实现的。

template< class T, class U > 
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept
{
    auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
    return std::shared_ptr<T>(r, p);
}

shared_ptr与deleter

shared_ptr<Foo> sp(new Foo)在构造sp的时候捕获了Foo的析构行为,意味着:

  1. shared_ptr<void> 可以指向并安全地管理(析构或防止析构)任何对象,派生类没有虚析构函数也能被正确析构

    shared_ptr<Foo> sp1(new Foo); // ref_count.ptr 的类型是 Foo*
    shared_ptr<void> sp2 = sp1; // 可以赋值,Foo* 向 void* 自动转型
    sp1.reset(); // 这时 Foo 对象的引用计数降为 1

此后 sp2 仍然能安全地管理 Foo 对象的生命期,并安全完整地释放 Foo,不会出现 delete void* 的情况,因为 delete 的是 ref_count.ptr,不是 sp2.ptr。
std::shared_ptr<void>的工作原理

  1. shared_ptr进行资源销毁时,总会调用创建智能指针的那个DLL中的delete,这意味着shared_ptr可以随意地在DLL间传递而不需担心跨DLL的问题。(Effective C++ Item18)

参考资料:
为什么多线程读写 shared_ptr 要加锁
std::shared_ptr<void>的工作原理
《Effective C++》
《Effective Modern C++》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,345评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,494评论 3 416
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,283评论 0 374
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,953评论 1 309
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,714评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,186评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,255评论 3 441
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,410评论 0 288
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,940评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,776评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,976评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,518评论 5 359
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,210评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,642评论 0 26
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,878评论 1 286
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,654评论 3 391
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,958评论 2 373