四个智能指针

C语言指针存在的缺陷

  1. 裸指针在声明的时候没有指出,裸指针指向的是单个对象还是一个数组
  2. 裸指针在声明中没有提示在使用完其指向的对象后,是否需要析构它
  3. 如果确定需要析构,应该采用怎样的析构方法仍然是一个问题,是使用delete呢?还是需要特定函数来完成呢?
  4. 如果确定应该使用delete的方式进行析构,是使用delete呢,还是使用delete []呢?
  5. 如果确定指针拥有其指向的对象,并且也知道如何将对象析构,如何保证析构操作在所有的代码路径执行且只执行一次呢?
  6. 没有什么正规的方法可以可测出指针是否空悬(dangle)

auto_ptr介绍

  1. 永远不要使用auto_ptr,因为在auto_ptr的年代没有移动语义,作为变通,auto_ptr使用赋值操作完成移动任务,这种设计往往会引起BUG
  2. unique_ptr可以做auto_ptr的任何功能,并且他的执行效率和auto_ptr一样高,最后unique_ptr使用移动操作代替复制操作,减少BUG产生,因此应该完全使用unique_ptr替换auto_ptr

unique_ptr介绍

  1. 每当你需要使用智能指针的时候,unique_ptr基本上应该是首选,可以认为unique_ptr和裸指针有着一样的大小和速度
  2. std::unique_ptr是小巧、高速、具备只移型别的智能指针,对托管资源实施专属所有权语义
  3. 默认情况下,资源析构采用delete运算符实现,但可以指定自定义删除器。有状态的函数对象和采用函数指针实现的删除器会增加unique_ptr的对象大小,无状态的函数对象(包括无捕获的lambda表达式)不会增加unique_ptr的大小
  4. 将std::unique_ptr转换为std::shared_ptr可以使用shared_ptr的构造函数,非常方便。即返回unique_ptr的工厂函数既可以提供专属所有权语义,又可以提供共享所有权语义。
  5. 常用于工厂模式以及Pimpl惯用法

shared_ptr介绍

  1. shared_ptr实现了任意资源在共享所有权语义下进行生命周期管理的垃圾回收
  2. 与unique_ptr相比,shared_ptr的尺寸通常是裸指针尺寸的两倍,他还会带来控制块的开销。
  3. 控制块带来的开销包括:引用计数的内存必须动态分配(使用make_shared可以避免)、引用计数的递增和递减必须是原子操作、使用到了继承以及虚函数
  4. 默认的资源析构通过delete运算符进行,但同时也支持定制删除器。删除器的型别对shared_ptr的型别没有影响
  5. 避免使用裸指针变量来创建shared_ptr
  6. shared_ptr的构造函数并不会一定导致引用计数的增加,比如移动构造函数以及移动赋值函数
  7. shared_ptr支持自定义析构器,自定义析构器不是shared_ptr类型的一部分,而对于unique_ptr而言,析构器是其类型的一部分
  8. 自定义析构器不会改变shared_ptr的尺寸,无论析构器是怎样的类型,shared_ptr的大小都是裸指针的两倍
  9. 控制块内容包括:引用计数、自定义析构器、自定义内存分配器以及弱引用计数等
    10.创建控制块的时机:
    a. std::make_shared总是构建一个控制块
    b. 从具备专属所有权的指针出发构造一个shared_ptr
    c. 当shared_ptr构造函数使用裸指针作为实参时
  10. 在使用默认析构器和内存分配器的前提下,并且shared_ptr是由make_shared创建的前提下,控制块的尺寸只有三个字长,并且分配操作实质上没有任何成本,提领一个shared_ptr并不比提领一个裸指针话费更多,当进行引用计数操作的时候需要一个或者两个原子操作,但这些操作通常会映射到单个机器指令,尽管与非原子化指令相比成本高点,但仍然是单指令。控制块中的虚函数机制通常只被每个托管给shared_ptr的对象使用一次:在该对象被析构的时候。
  11. shared_ptr不支持对应的数组类型
  12. shared_ptr支持派生类到基类的转换

weak_ptr介绍

  1. weap_ptr不是一种独立的智能指针而是shared_ptr的辅助工具
  2. expired方法可以判断是否失效
  3. 从weak_ptr获取shared_ptr
    a. weak_ptr::lock()会返回一个shared_ptr,如果此时weak_ptr已经失效,则shared_ptr为空
    b. 以weak_ptr为参数调用shared_ptr的构造方法,如果此时weak_ptr已经失效,则会抛出异常
  4. weak_ptr和shared_ptr从本质上讲是一致的,他们有相同的尺寸,他们使用同一控制块,weak_ptr的构造、析构以及赋值操作都包含了对引用计数的原子操作
  5. 控制块中有两个引用计数,其中一个为所有shared_ptr共享,一个为所有weak_ptr共享

weak_ptr应用

1.设计提供缓存功能的简单工厂模式(缓存有过期功能,缓存的有效期取决于用户在获取到智能指针后的作用范围)
2. 观察者列表(观察者被销毁后,主题能够有方法获取此信息)
3. 使用weak_ptr来打破shared_ptr的循环引用(在类似树这种有着严格继承谱式的数据结构中使用unique_ptr和普通指针就好)
4. 总结起来:weak_ptr的使用场景适合为“类似shared_ptr但是可能为空的场景”

make_unique以及make_shared的优势

  1. 避免代码重复(避免重复撰写类型)
  2. 保证异常安全
  3. make系列函数有性能提升,原因是make函数会让编译器有机会使用更简洁的数据结构产生更小更快的代码
    a.使用new会产生两次内存分配,而使用make系列函数只会产生一次内存分配,减小程序的静态尺寸,同时增加可执行代码的运行速度,犹有进者,使用make系列函数能够避免控制块中一些薄记信息的必要性,这样也能潜在地减少程序的内存痕迹总量。

make_unique以及make_shared的劣势

  1. 所有make函数不允许使用自定义析构器
  2. 在make系列函数中对形参进行完美转发的代码使用的是圆括号而不是花括号, 即不会优先使用列表初始化

使用make_shared额外劣势

  1. 如果类自定义类operator new以及operator delete,那么不适合使用make系列函数
  2. 内存紧张的系统、非常大的对象并且存在比指向到相同对象的shared_ptr生存期更久的weak_ptr
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值