引入

shared_ptr 是c++为了提高安全性而添加的智能指针,方便了内存管理。

特点

shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。使得指针可以共享对象,并且不用考虑内存泄漏问题

shared_ptr 可以支持普通指针的所有操作,完全可以像操作普通指针一样操作智能指针。
shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
2.通过make_shared函数得到
3.通过另外一个智能指针初始化

    int *a = new int();
std::shared_ptr<int> p1(a); auto b = std::make_shared<int>(); auto c(b);

注意事项

1. 使用原生指针多次初始化

class Base
{
public:
Base() {
printf("con\n");
}
~Base() {
printf("decon\n");
}
}; int main()
{
Base *a = new Base(); std::shared_ptr<Base> p1(a);
std::shared_ptr<Base> p2(a); return ;
}

这段代码,导致调用一次构造函数,两次析构函数

2. 使用一个 shared_ptr 的 get() 初始化另一个 shared_ptr

    Base *a = new Base();

    std::shared_ptr<Base> p1(a);
std::shared_ptr<Base> p2(p1.get());

cppreference 指明这是未定义行为

3. 使用 shared_ptr 包装 this 指针

class Base
{
public:
Base() {
printf("con\n");
}
~Base() {
printf("decon\n");
} std::shared_ptr<Base> sget() {
return std::shared_ptr<Base>(this);
}
}; int main()
{
Base b;
std::shared_ptr<Base> p = b.sget(); return ;
}

这会调用两次析构函数。一次是b对象析构时,一次是智能指针销毁时

正确的使用方法是:

class Base : public std::enable_shared_from_this<Base>
{
public:
Base() {
printf("con\n");
}
~Base() {
printf("decon\n");
} std::shared_ptr<Base> sget() {
return shared_from_this();
}
}; int main()
{
std::shared_ptr<Base> b = std::make_shared<Base>();
std::shared_ptr<Base> a = b->sget(); return ;
}

enable_shared_from_this 能让其一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。

4. shared_ptr 包装数组

shared_ptr 不能直接包装数组,需要指定数组的析构函数。不过 shared_ptr 不支持取下标操,unique_ptr 是支持的

class Base
{
public:
Base() {
printf("con\n");
}
~Base() {
printf("decon\n");
}
}; int main()
{
std::shared_ptr<Base> a(new Base[], [] (Base* p) {
delete[] p;
}); return ;
}

5. 循环引用

class A;
class B; using sa = std::shared_ptr<A>;
using sb = std::shared_ptr<B>; class A
{
public:
A() {
printf("A con\n");
}
~A() {
printf("A decon\n");
} sb b_;
}; class B
{
public:
B() {
printf("B con\n");
}
~B() {
printf("B decon\n");
} sa a_;
}; int main(int argc, char const *argv[])
{
sa a(new A);
sb b(new B); a->b_ = b;
b->a_ = a; return ;
}

对象 a 和 b 都未被析构

正确的方法是使用 weak_ptr 代替 shared_ptr

class A;
class B; using sa = std::shared_ptr<A>;
using sb = std::shared_ptr<B>; class A
{
public:
A() {
printf("A con\n");
}
~A() {
printf("A decon\n");
} std::weak_ptr<B> b_;
}; class B
{
public:
B() {
printf("B con\n");
}
~B() {
printf("B decon\n");
} std::weak_ptr<A> a_;
}; int main(int argc, char const *argv[])
{
sa a(new A);
sb b(new B); a->b_ = b;
b->a_ = a; return ;
}

6. 多线程中使用 shared_ptr

shared_ptr的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:

  • 一个 shared_ptr 对象实体可被多个线程同时读取
  • 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作
  • 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁

shared_ptr 用法的更多相关文章

  1. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  2. std::shared_ptr 和 std::weak_ptr的用法以及引用计数的循环引用问题

    在std::shared_ptr被引入之前,C++标准库中实现的用于管理资源的智能指针只有std::auto_ptr一个而已.std::auto_ptr的作用非常有限,因为它存在被管理资源的所有权转移 ...

  3. C++ shared_ptr的用法

    一. http://www.cnblogs.com/welkinwalker/archive/2011/10/20/2218804.html 二.http://www.cnblogs.com/Tian ...

  4. 智能指针unique_ptr的用法

    unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...

  5. shared_ptr和多线程

    前一篇文章写得实在太挫,重新来一篇. 多线程环境下生命周期的管理 多线程环境下,跨线程对象的生命周期管理会有什么挑战?我们拿生产者消费者模型来讨论这个问题. 实现一个简单的用于生产者消费者模型的队列 ...

  6. [5] 智能指针boost::shared_ptr

    [1]boost::shared_ptr简介 boost::shared_ptr属于boost库,定义在namespace boost中,包含头文件#include<boost/shared_p ...

  7. std::shared_ptr

    在std::shared_ptr被引入之前,C++标准库中实现的用于管理资源的智能指针只有std::auto_ptr一个而已.std::auto_ptr的作用非常有限,因为它存在被管理资源的所有权转移 ...

  8. (六)boost库之内存管理shared_ptr

    (六)boost库之内存管理shared_ptr 1.shared_ptr的基本用法 boost::shared_ptr<int> sp(new int(10)); //一个指向整数的sh ...

  9. 智能指针 shared_ptr 解析

    近期正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦.写篇blog整理一下. LinJM   @HQU s ...

随机推荐

  1. pycharm进行个性化设置

    1. 设置主题:Monokai Ctrl+Alt+S: Editor->Color&Fonts->Python 2. 修改[选中内容]颜色 因为用了Monokai主题后,选中内容的 ...

  2. 60: noi.ac #69

    $code$ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring&g ...

  3. bzoj3745: [Coci2015]Norma 分治,单调队列

    链接 bzoj 思路 首先\(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=i}^{j}max(a_k)\)可以用单调队列求解.参见 ...

  4. Linux expect详解

    随处可见的expect第一次见expect这个命令还是我第一次参加全量上线的时候,那是公司的一个牛人用Shell脚本写的一套自动部署.MD5 比对.发布的全量上线工具,没事的时候,看了下其中的几个脚本 ...

  5. saltstack自动化运维工具搭建个人笔记

    至于为什么选择saltstack,因为Puppet.Chef基于Ruby开发,而ansible.saltstack基于python开发,便于后期二次,良好的可移植性. 又,ansible基于SSH协议 ...

  6. plsql tables 表存在,但是看不到所有的表信息

      1.情景展示 tables目录存在,但是看不到该数据库下的表信息. 2.解决方案 对比同事的发现,原来是选错用户了. 将用户切换为当前用户就行了,不知道什么时候搞成所有用户了. 写在最后 哪位大佬 ...

  7. 南开大学2020年数学分析高等代数考研试题回忆版TeX排版

    南开大学2020年数学分析高等代数考研试题回忆版TeX排版 220328[南开大学2020年高等代数考研试题回忆版] 220329[南开大学2020年数学分析考研试题回忆版]

  8. 微信小程序图片宽度100%,高度自适应

    实现图片自适应,按照一般情况只需设置: img { width: 100%; height: auto; } 但是微信小程序里是特例,需要image标签上设置属性mode=widthFix,就是hei ...

  9. linux , nginx: 封禁IP的办法【转】

    今天,我们的一台服务器出了问题: 被若干IP地址访问某个接口,该接口会发送短信. 所以,我们可以做两件事: 1. nginx的层面封IP .  2  linux server的层面封IP 先看ngin ...

  10. mysql8.0:SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client

    忽然注意到的情况: 2018/7/19至2018/9/13之间发布的7.1.20.7.1.21.7.1.22和7.2.8.7.2.9.7.2.10这六个版本提供的对caching_sha2_passw ...