shared_ptr智能指针总结

智能指针

new在动态内存中为对象分配空间并返回一个指向该对象的指针,delete,接收一个动态对象的指针,销毁对象,并释放与之相关联的内存。

 为了更容易更安全的使用动态内存,新标准提供出了两种智能指针类型来管理动态对象。智能指针的类型类似于常规指针,重要的区别是它负责自动释放所指向的对象。

 而这两种指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr “则独占” 所指向的对象。
 标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。

为什么要使用智能指针:
智能指针的作用是在管理一个指针,因为申请的内存空间在函数结束时忘记了释放,就造成了内存泄露。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类就会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

shared_ptr

 类似于 vector,智能指针也是模板。所以创建智能指针时,必须提供额外信息(智能指针可以指向的类型)。创建方法如下所示:

shared_ptr<string>p1;		//shared_ptr,可以指向string

shared_ptr<list<int>> p2;	//shared_ptr,可以指向int的list

shared_ptr 和 unique_ptr 都支持的操作

shared_ptr<T> sp , unique_ptr<T> up空智能指针,可以指向类型为T的对象
p将p用作一个条件判断,若p指向一个对象,则为 true
*p解引用p,获得它指向的对象
p->mem等价于(*p).mem
p.get()返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了
swap(p,q) ,p.swap(q)交换 p 和 q 中的指针

shared_ptr独有的操作

make_shared<T> (args)返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化对象
shared_ptr<T>p(q)p是shared_ptr q 的拷贝;此操作会递增q中的计数器。q中的指针必须能转换为 T*
p = qp 和 q 都是 shared_ptr,所保存的指针必须能相互互换。此操作会递减 p 中的引用计数,递增q的引用计数;若 p 的引用计数变为0,则将会管理的原内存释放。
p.unique()若 p.use_count() 为1,返回 true;否则返回 false
p.use_count()返回与 p 共享对象的智能指针数量;可能很慢,主要用于调试
p.reset()放弃内部对象所有权拥有对象的变更,会引起原有对象的引用计数的减少

make_shared函数
 最安全的分配和使用动态内存的方法是调用一个名为 make_shared 的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的 shared_ptr。也定义在memory头文件中。
 当要用 make_shared 时,必须指定想要创建的对象的类型。定义方式如下所示:

//指向一个值为42的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);
//p4指向一个值为“9999999999”的string
shared_ptr<string>p4 = make_shared<string>(10,'9');
//p5指向一个值初始化的int,即,值为0
shared_ptr<int> p5 = make_shared<int>();

 make_shared 用其参数来构造给定类型的对象。例如,调用make_shared<string>时传递的参数必须与string的某个构造函数相匹配。
我们通常用auto定义一个对象来保存make_shared的结果。

//p6指向一个动态分配的空vector<string>
auto p6 = make_shared<vector<string>>();

shared_ptr 的拷贝和赋值

 进行拷贝和赋值时,每个shared_ptr 都会记录有多少个其他 shared_ptr 指向相同的对象:

auto p = make_shared<int>(42);	//p指向的对象只有p一个引用者
auto q(p);						//p和q指向相同对象,此对象有两个引用者

 每个shared_ptr 都有一个关联的计数器,通常称其为引用计数。无论何时我们拷贝一个 shared_ptr,计数器都会递增例如,当一个shared_ptr 初始化另一个 shared_ptr ,或将它作为参数传递给一个函数以及作为函数返回值时,它所关联的计数器就会递增。而当我们给 shared_ptr 赋予一个新值或是shared_ptr 被销毁时,计数器就会递减。
 一旦一个 shared_ptr 的计数器变为0,他就会自动释放自己所管理的对象:

引用计数可以看这篇文章文章:

https://blog.csdn.net/qq_29108585/article/details/78027867**

auto r = make_shared<int>(42);	//r指向的int只有一个引用者
r = q;	//给r赋值,令它指向另一个地址
		//递增q指向的对象的引用计数
		//递减r原来指向的对象的引用计数
		//r原来指向的对象已没有引用者,会自动释放

此例中我们分配了一个int,将指针保存在 r 中。接下来,我们将一个新值赋予r,在此情况下,r是唯一一个指向此int 对象的shared_ptr,再把q赋给r的过程中,此int被自动释放

shared_ptr自动销毁所管理的对象

 当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象。由析构函数完成。

shared_ptr还会自动释放相关联的内存
 当动态对象不再被使用时,shared_ptr类会自动释放动态对象。

如下例子所示:
 它返回一个shared_ptr,指向一个Foo类型的动态分配的对象,对象是通过一个类型为T的参数进行初始化的

//factory返回一个shared_ptr,指向一个动态分配的对象。
shared_ptr<Foo>factory(T arg)
{
	//shared_ptr负责释放内存
	return make_shared<Foo>(arg);
}

由于factory返回一个shared_ptr,所以我们可以确保它分配的对象会在恰当的时刻被释放。

我们再来看一个例子:
 如果有其他shared_ptr也指向这块内存,他就不会被释掉:

shared_ptr<Foo> use_factory(T arg)
{
	shared_ptr<Foo> p = factory(arg);
	//使用p
	return p;
}//p离开了作用域,但它指向的内存不会被释放

如果你将shared_ptr存放于一个容器中,而后不在需要全部元素,而只使用其中一部分,要记得用erase删除不在需要的那些元素。

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。

void test_weak_ptr()
    {
        string* s = new string("hello");
        shared_ptr<string> p1(s);
        weak_ptr<string> p2(p1);
        cout<<p1.use_count()<<endl;//1
        cout<<p2.use_count()<<endl;//1

        if(!p2.expired())//被观测的资源存在
        {
            shared_ptr<string> p3 = p2.lock();
            *p3 = "hi";//改变s的值
            cout<<*s<<endl;//hi
            cout<<p2.use_count()<<endl;//2
            cout<<p1.use_count()<<endl;//2
        }
    }
}

namespace test02
{
    //循环引用
    //考虑一个简单的对象建模——家长与子女:a Parent has a Child, a Child knowshis/her Parent。
    class Parent;
    class Child
    {
    public:
//        shared_ptr<Parent> p;//错误
        weak_ptr<Parent> p;//正确方法
        ~Child(){cout << "child delete\n";};
    };

    class Parent
    {
    public:
        shared_ptr<Child> c;
        ~Parent(){cout << "Parent delete\n";};
    };

    //发生了循环引用
    // 当要跳出函数时,智能指针child,parent析构时两个资源引用计数会减1,但是两者引用计数还是为1,
    // 导致跳出函数时资源没有被释放(A、B的析构函数没有被调用)运行结果没有输出析构函数的内容,造成内存泄露
    void test02_test_circle()
    {
        shared_ptr<Child> child(new Child());
        shared_ptr<Parent> parent(new Parent());
        cout << child.use_count() << endl;	//1
        cout << parent.use_count() << endl;	//1
        child->p=parent;//孩子的家长
        parent->c = child;//家长的孩子
        cout << child.use_count() << endl;	//2 
        cout << parent.use_count() << endl;	//2 
    }
}
//改weak后parent.use_count()为1,parent析构,导致child引用-1,child析构在-1,child也析构了,不会产生循环引用

参考书籍:《c++primer》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值