shared_ptr.是c++为了提高指针安全性而添加的智能指针,方便了内存管理。功能非常强大,非常强大,非常强大(不单单是shared_ptr,配合week_ptr以及enable_share_from_this()以及share_from_this())!!!对于支持智能指针的c++版本编程,能用智能指针就用智能指针!

shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。使得指针可以共享对象,并且不用考虑内存泄漏问题
shared_ptr 可以支持普通指针的所有操作,完全可以像操作普通指针一样操作智能指针。
shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
2.通过make_shared函数得到
3.通过另外一个智能指针初始化
#include <memory>
#include <iostream>
int main()
{
int *p = new int(30);
std::shared_ptr<int> bptr(p);//方式1
std::shared_ptr<int> aptr = std::make_shared<int>(20);//方式
std::shared_ptr<int> cptr(aptr);
std::cout << "aptr.use_count() = " << aptr.use_count() <<" value = "<<*aptr<<std::endl;//use_count 是引用计数器
std::cout << "bptr.use_count() = " << bptr.use_count() <<" value = "<<*bptr<<std::endl;
std::cout << "cptr.use_count() = " << cptr.use_count() <<" value = "<<*cptr<<std::endl;
//输出是:2,20
// 1,30
// 2,20
}

下面是使用shared_ptr 的一些注意事项:

0. 禁止纯指针给智能指针赋值或者拷贝构造。

int* a=new int(2);
shared_ptr<int>sp=a;// error
sp=a;// error

  

1. shared_ptr多次引用同一数据,会导致两次释放同一内存。如下:
{
int* pInt = new int[100];
shared_ptr<int> sp1(pInt);
// 一些其它代码之后…
shared_ptr<int> sp2(pInt);
}

2.使用shared_ptr包装this指针带来的问题,如下:

class tester 
{
public:
  tester()
  ~tester()
  {
    std::cout << "析构函数被调用!\n"; 
  }
public:
  shared_ptr<tester> sget()
  {
    return shared_ptr<tester>(this);
  }
};

int main()
{
  tester t;
  shared_ptr<tester> sp =  t.sget(); // …
  return 0;
}

也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码。

class tester : public enable_shared_from_this<tester>
{
public:
  tester()
  ~tester()
  {
  std::cout << "析构函数被调用!\n"; 
  }
public:
 shared_ptr<tester> sget()
  {
  return shared_from_this();
  }
};

int main()
{
  shared_ptr<tester> sp(new tester);
  // 正确使用sp 指针。
  sp->sget();
  return 0;
}

3. shared_ptr循环引用导致内存泄露,代码如下:

class parent;
class child;

typedef shared_ptr<parent> parent_ptr;
typedef shared_ptr<child> child_ptr;

class parent
{
public:
       ~parent() { 
              std::cout <<"父类析构函数被调用.\n"; 
       }
public:
       child_ptr children;
};

class child
{
public:
       ~child() { 
              std::cout <<"子类析构函数被调用.\n"; 
       }
public:
       parent_ptr parent;
};

int main()
{
  parent_ptr father(new parent());
  child_ptr son(new child);
  // 父子互相引用。
  father->children = son;
  son->parent = father;
  return 0;
}

如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。

4.在多线程程序中使用shared_ptr应注意的问题。代码如下:

class tester 
{
public:
  tester() {}
  ~tester() {}
  // 更多的函数定义…
};

void fun(shared_ptr<tester> sp)
{
  // !!!在这大量使用sp指针.
  shared_ptr<tester> tmp = sp;
}

int main()
{
  shared_ptr<tester> sp1(new tester);
  // 开启两个线程,并将智能指针传入使用。
  thread t1(bind(&fun, sp1));
  thread t2(bind(&fun, sp1));
  t1.join();
  t2.join();
  return 0;
}

这个代码带来的问题很显然,由于多线程同时访问智能指针,并将其赋值到其它同类智能指针时,很可能发生两个线程同时在操作引用计数(但并不一定绝对发生),而导致计数失败或无效等情况,从而导致程序崩溃,如若不知根源,就无法查找这个bug,那就只能向上帝祈祷程序能正常运行。

引入weak_ptr可以解决这个问题,将fun函数修改如下:

void fun(weak_ptr<tester> wp)
{

//这个方案只解决了多线程对引用计数同时访问的读写问题,并没有解决对share_ptr指向的数据的多线程安全问题,因此weak_ptr只是安全的获得share_ptr的一种方式,因为可以确保在获得share_ptr的时候的多线程安全
  shared_ptr<tester> sp = wp.lock;
  if (sp)
  {
    // 在这里可以安全的使用sp指针.
  }
  else
  {
    std::cout << “指针已被释放!” << std::endl;
  }
}

5.weak_ptr不仅可以解决多线程访问带来的安全问题,而且还可以解决上面第三个问题循环引用。Children类代码修改如下,即可打破循环引用:

class child
{
public:
  ~child() { 
   std::cout <<"子类析构函数被调用.\n"; 
  }
public:
  weak_ptr<parent> parent;
};

因为weak_ptr不增加引用计数,所以可以在退出函数域时,正确的析构。

weak_ptr 介绍:

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.
用法:
 weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
 使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。
 weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr.

share_ptr 的 aliasing constructor:

可以使得share_ptr 拥有某个对象控制权的时候,存储指针(storage pointer,shared_ptr 还有一个概念叫owned ptr 指向存储当前控制对象相关信息)指向另外一个对象,模板如下:

template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

该构造函数是的shared_ptr 管理 x 指针指向对象,会将计数器加一,并且在释放控制权时减一,如有需要还会调用析构函数,但是不同的是它storage ptr 指向的是p,调用 share_ptr.get() 会返回 p ,一般的用途在 p所指对象是x所指对象的成员或者别名(alias)[这段是翻译的,总之,aliasing constructor 还是慎用吧]

本文转载于:https://blog.csdn.net/man_sion/article/details/77196766

c++ shared_ptr的使用的更多相关文章

  1. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  2. 智能指针shared_ptr的用法

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

  3. shared_ptr

    省去对象指针的显示delete typedef tr1::shared_ptr<int> IntPtr; IntPtr fun() { IntPtr p = new int(3); ret ...

  4. 如何用shared_ptr减少锁的争用

    在并发环境下锁的使用是家常便饭, 如何减少锁的使用是优化程序性能的一个方面. c++11里面新增了智能指针std::shared_ptr, 这个东西也许能给我们带来些启发. shared_ptr的一个 ...

  5. C++ 之 auto_ptr and shared_ptr

    1.auto_ptr 这个所谓的只能指针有点鸡肋!  没有引用计数,而且还有一个所有权转移的情况! 当所有权转移后,以前的auto_ptr将会成为null 2.shared_ptr 增加了引用计数,没 ...

  6. shared_ptr和多线程

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

  7. shared_ptr:资源管理利器

    如果你还在使用传统的C++,那么可以肯定堆内存的管理让你头痛过!在传统的C++领域,堆内存管理上我们能借用的现成工具就只有auto_ptr.但是很不幸用auto_ptr管理堆内存简直就是个错误.aut ...

  8. shared_ptr 线程安全

    Then what's really happening is TWO different sections of memory are being allocated. It's done at o ...

  9. shared_ptr 和 unique_ptr

    c++11标准废除乐auto_ptr, C++ 标准库智能指针 使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项. unique_ptr 只允许基础指针的一个所有者. 除非你 ...

  10. c++ shared_ptr 使用注意事项. 2

    1.抛弃临时对象,让所有的智能指针都有名字. 2.类向外传递 this 的  shared_ptr 让类继承   enable_shared_from_this. 然后返回  shared_from_ ...

随机推荐

  1. HDU 4821 String(BKDRHash)

    http://acm.hdu.edu.cn/showproblem.php?pid=4821 题意:给出一个字符串,现在问你可以找出多少个长度为M*L的子串,该子串被分成L个段,并且每个段的字符串都是 ...

  2. Python 全集变量

    1.添加关键字: global 在要给变量从新赋值的时候添加. 全局变量都大写,局部变量都小写.

  3. #ifndef/#define/#endif使用详解

    问题: 想必很多人都看过"头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用".但是是否能理解"被重复引用"是什么意思?是不能在不 ...

  4. Java中String型与Date型数据的互相转换

    /** * Date类型转为指定格式的String类型 * * @param source * @param pattern * @return */ public static String Dat ...

  5. mac终端不好用?用brew神器代替

    一.概念 Brew是一款Mac OS平台下的软件包管理工具,拥有安装.卸载.更新.查看.搜索等很多实用的功能.简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷. 官 ...

  6. 常见的Java面试题及答案整理

    1. 基础篇 1. 面向对象特征:封装,继承,多态和抽象 封装封装给对象提供了隐藏内部特性和行为的能力.对象提供一些能被其他对象访问的方法来改变它内部的数据.在 Java 当中,有 3 种修饰符: p ...

  7. art-template

    art-template 输出 标准语法 {{value}} {{data.key}} {{data['key']}} {{a ? b : c}} {{a || b}} {{a + b}} 原始语法 ...

  8. Android开发代码规范总结

    本篇开始总结Android开发中的一些注意事项,提高代码质量(仅供参考): 1.  Activity间的数据通信,对于数据量比较大的,避免使用 Intent + Parcelable 的方式,可以考虑 ...

  9. python + lisp hy的新手注记1

    想在python里用lisp方言hy的目的: 1 用lisp去parse 包含 “数据+简单if控制流(代码.AST)”的配置文件,或者说用包含s-exp的.hy文件作为这类配置文件的实现(而不是用y ...

  10. vue element-ui 日期选择器组件 日期时间格式化

    vue element-ui 组件开发大大提高了我们的效率,但有时候并不能满足我们的需求,例如时间,日期组件: element-ui 日期返回的格式是这样的,看下图: 但我们要的是另一个格式 , 如下 ...