智能指针的基本用法 笔记

转载请注明出处,谢谢.

智能指针可以通过引用计数的策略自动销毁对象,其作用不仅仅是偷了一点点懒,而是可以解决一些大型工程难题(尤其是多线程环境下对象的销毁问题).最近开始看<<Linux多线程服务端编程 使用muduo c++网络库>>(陈硕 著),感觉以上来它就讲了我写项目正在遇到的问题,提到了使用智能指针解决问题.

#include <memory>

一.shared_ptr<Type>

强引用.一个强引用类型的智能指针能增加目标对象的引用计数.

先举个栗子大概看一下怎么用:

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    node()
    {
        cout<<"node.create"<<endl;
    }
    node(int a):node()
    {
        v=a;
    }
    ~node()
    {
        cout<<"node.destroy"<<endl;
    }
};
int main()
{
    {
        shared_ptr<node>p(new node);
        p->v=1;
        cout<<    p.get()->v   <<endl;
    }
    cout<<"end"<<endl;

}

运行一下:

文件名单词拼错了,不要在意这些细节.

注意看,对象被自动销毁了.

first.先看看怎么构造

第一种方法,可以跟上述例子一样:

使用shared_ptr(Type *p)构造方法:

shared_ptr<node>p(new node);

第二种,用make_shared<Type>(...)创建一个共享指针实例;  参数的填写方法取决于该对象的构造方法.比如:

shared_ptr<node>p=make_shared<node>();    //调用 node()构造方法
shared_ptr<node>q=make_shared<node>(5);    //调用 node(int a)构造方法

当然也可以用万能的auto简化书写  个人觉得这里auto特好用(反正共享指针IDE的补全也是很操蛋)

auto p=make_shared<node>();

second.看看成员函数

摆一下成员函数:

use_count 返回引用计数的个数
unique 返回是否是独占所有权( use_count 为 1)
swap 交换两个 shared_ptr 对象(即交换所拥有的对象)
reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr<int> sp(new int(1)); sp 与 sp.get()是等价的

(成员函数的介绍来自阿Hai的博客)

然后大概举例子看看这些函数怎么用:

use_count, reset和unique:

        shared_ptr<node> p=make_shared<node>(5);
        cout<<"use_count="<<p.use_count()<<endl;
        cout<<"unique="<<p.unique()<<endl;
        auto p2=p;
        cout<<"use_count="<<p.use_count()<<endl;
        cout<<"unique="<<p.unique()<<endl;

我们发现赋值之后引用计数+1了,然后p指针也不是独占对象了.而如果加一句reset:

{
        shared_ptr<node> p=make_shared<node>(5);
        cout<<"use_count="<<p.use_count()<<endl;
        cout<<"unique="<<p.unique()<<endl;
        auto p2=p;
        p.reset();
        cout<<"use_count="<<p.use_count()<<endl;
        cout<<"unique="<<p.unique()<<endl;
    }
    cout<<"end"<<endl;

然后就可以释放掉引用:

如果是取reset之后的指针,则use_count=0,unique=0;(亲测)

get函数

简介里很清楚了,第一个例子里已经演示过了就不重复了.

swap函数

swap就是交换指向的对象了,如:

{
        shared_ptr<node> p=make_shared<node>(5);
        shared_ptr<node> p2=make_shared<node>(3);
        cout<< p->v   <<endl;
        cout<< p2->v  <<endl;
        p.swap(p2);
        cout<< p->v   <<endl;
        cout<< p2->v  <<endl;
    }
    cout<<"end"<<endl;

third.自定义释放器

我们可以给智能指针提供一个释放对象的函数,这样就可以释放掉对象之后再做一些收尾工作,比如:

我们在销毁掉node类的一个示例后,跟它说声"再见":

void sayGoodBye(node *obj)
{
    delete obj;
    cout<<"GoodBye"<<endl;
}
int main()
{
    {
        shared_ptr<node> p(new node(5),sayGoodBye);
    }
    cout<<"end"<<endl;
}

当然,也可以在销毁前说再见,具体怎么做可以在释放器中自己定义.

这个功能很实用.

fourth.小心循环引用

在此之前,提一句:智能指针的复制构造函数在传参的时候会使引用计数+1,参数销毁时-1.

举个循环引用的栗子:

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    shared_ptr<node>ptr;
    void set(shared_ptr<node> &p) //no copy
    {
        ptr=p;
        cout<<ptr.use_count()<<endl;
    }
    node()
    {
        cout<<"node.create"<<endl;
    }
    node(int a):node()
    {
        v=a;
    }
    ~node()
    {
        cout<<"node.destroy"<<endl;
    }
};

int main()
{
    {
        shared_ptr<node> p(new node(5));
        shared_ptr<node> p1(new node(3));
        p->set(p1);
        p1->set(p);
    }
    cout<<"end"<<endl;
}

两个对象只被创建了,都没被销毁,造成了内存泄露.

解决方案就是在类成员中使用weak_ptr代替shared_ptr.至于weak_ptr是什么下面解释.

二.weak_ptr<Type>

weak_ptr(弱引用)是为了配合shared_ptr(强引用)而设计的.它不会增加对象的引用计数,但是可以判断该对象是否有效(有没有被销毁),以及提供对目标对象的访问.

成员函数

weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象. 注意, weak_ptr 在使用前需要检查合法性.

expired 用于检测所管理的对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
lock 用于获取所管理的对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr, 其内部对象指向与 weak_ptr 相同.
use_count 返回与 shared_ptr 共享的对象的引用计数.
reset 将 weak_ptr 置空.
weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.

  weak_ptr 在功能上类似于普通指针, 然而一个比较大的区别是, 弱引用能检测到所管理的对象是否已经被释放, 从而避免访问非法内存。

(成员函数的介绍来源于阿Hai的博客)

也就是说,weak_ptr完全是shared的一个助手,完全没有人权,只用来解决shared_ptr的循环引用问题.

上面循环引用的例子里,只需要把成员变量shared_ptr换成weak_ptr就可以了.不过我们为了看一下weak_ptr怎么用,给上面的例子里增加一丢丢操作.

改正后的代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
    int v;
    weak_ptr<node>ptr;
    void set(shared_ptr<node> &p) //no copy
    {
        ptr=p;
        cout<<ptr.use_count()<<endl;
    }
    void printFrinedv()
    {
        shared_ptr<node>p=ptr.lock();
        if(p)   //if p.get() exists ?
        {
            cout<< "friend.v=" << p->v  <<endl;
        }
        else
        {
            cout<<"friend has already been destroyed"<<endl;
        }
    }
    node()
    {
        cout<<"node.create"<<endl;
    }
    node(int a):node()
    {
        v=a;
    }
    ~node()
    {
        cout<<"node.destroy"<<endl;
    }
};

int main()
{
    {
        shared_ptr<node> p(new node(5));
        shared_ptr<node> p1(new node(3));
        p->set(p1);
        p1->set(p);
        p->printFrinedv();
        p1.reset();
        p->printFrinedv();
    }
    cout<<"end"<<endl;
}

其中lock函数是原子操作.不需要额外加锁.

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值