c++中的四种智能指针

写惯了python,golang再来写c++总觉得头大,很大一个原因就是他没有一个GC机制。

不过c++中提供了智能指针,也不是不能用,李姐万岁!

auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用。

1.auto_ptr

这个指针采用了所有权的方式,

#include<memory>
#include <iostream>
using namespace std;
int main() {
    auto_ptr<string> p_s1(new string("this is  a string"));
    cout << *p_s1.get() << endl;
    auto_ptr<string> p_s2 = p_s1;
    cout << *p_s2.get() << endl;
    //cout << *p_s1.get() << endl;报错,此时s1已经没有所指向的内存的所有权
}

auto_ptr的缺点是:存在潜在的内存崩溃问题!

2.unique_ptr

unique_ptr同样是独占的,但unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做。

实在想赋值的话,可以用std::move()实现。

#include<memory>
#include <iostream>
using namespace std;
int main() {
    unique_ptr<string> p_s1(new string("this is  a string"));//临时右值是可以用来赋值的
    cout << *p_s1.get() << endl;
    //unique_ptr<string> p_s2 =p_s1;无法直接赋值
    unique_ptr<string> p_s2 = std::move(p_s1);
    cout << *p_s2.get() << endl;
}

3.shared_ptr

shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制(和python的垃圾回收机制也很像)来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造,也可以通过make_shared函数或者通过构造函数传入普通指针。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针,其可以检测到所管理的对象是否已经被释放,从而避免非法访问。

4. weak_ptr

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

class B;
class A
{
public:
    shared_ptr<B> pb_;
    ~A()
    {
        cout << "A delete"<<pb_.use_count()<<endl;
    }
};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete"<<pa_.use_count()<<endl;
    }
};
void fun()
{
    shared_ptr<B> pb(new B());
    shared_ptr<A> pa(new A());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout << pb.use_count() << endl;
    cout << pa.use_count() << endl;
}
int main()
{
    fun();
    return 0;
}

可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb_; 运行结果如下,

1
2
B delete1
A delete0

这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。

注意的是我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb_->print(); 英文pb_是一个weak_ptr,应该先把它转化为shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

改后如下

class B;
class A
{
public:
    weak_ptr<B> pb_;
    ~A()
    {
        cout << "A delete"<<pb_.use_count()<<endl;
    }

};
class B
{
public:
    shared_ptr<A> pa_;
    ~B()
    {
        cout << "B delete"<<pa_.use_count()<<endl;
    }
    void func() {
        cout << "this is print" << endl;
    }
};
void fun()
{

    shared_ptr<A> pa(new A());
    shared_ptr<B> pb(new B());
    pb->pa_ = pa;
    pa->pb_ = pb;
    cout << pb.use_count() << endl;
    cout << pa.use_count() << endl;
    (*(*pa).pb_.lock()).func();
}
int main()
{
    fun();
    return 0;
}

输出如下

1
2
this is print
B delete2
A delete

最新文章

  1. 【zepto学习笔记02】零碎点
  2. MySQL入门(四)
  3. MySQL数据库小实验
  4. poj 算法 分类
  5. SQL触发器,数据库
  6. [css] haslayout
  7. pyenv
  8. 利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档
  9. Path类
  10. osg学习笔记2, 命令行参数解析器ArgumentParser
  11. HDU 1010Tempter of the Bone(奇偶剪枝回溯dfs)
  12. [LeetCode]题解(python):140-Word Break II
  13. HTML5 画一张图
  14. C语言 动态创建二维数组
  15. Shodan的http.favicon.hash语法详解与使用技巧
  16. java中List按指定大小分割
  17. Python爬虫的学习经历
  18. net core 解除上传大附件的限制
  19. 转:npm安装教程
  20. BZOJ 1113 海报 单调栈

热门文章

  1. 【转】稳定婚姻问题(Stable Marriage Problem)
  2. Link Script 学习
  3. 原生Js_使用setInterval() 方法实现图片轮播功能
  4. SpringMVC参数传递 HttpServletRequest,HttpServletResponse和HttpSession
  5. C++入门经典-例3.5-判断某一年是否是闰年之嵌套判断
  6. docker启动redis并使用java连接
  7. 1.zookeeper原理解析-数据存储之Zookeeper内存结构
  8. 一、基础篇--1.1Java基础-hashCode和equals方法的区别和联系
  9. Git 合并两个分支内容
  10. 下载excle文件之工具