shared_ptr:弱回调技术

           场景:公司company存有公司员工的信息,现在给定一个查询接口,给定员工姓名在公司中查找若找到返回员工信息,若没有找到在公司录入这个员工信息。

     1 若简单在公司company中用一个容器保存staff信息,那么staff离职了需要清理容器否则staff信息一直存在。

     2 又涉及了对象生命周期的管理,想到shared_ptr/weak_ptr,那么company用weak_ptr管理员工对象,即容器存放weak_ptr,给定员工姓名找到相应的weak_ptr尝试提升这个weak_ptr,若提升成功则表明公司有这个员工,否则需要录入这个员工。这样即使员工离职了,公司只保存了一个weak_ptr对象。但是这还是存在内存泄露,毕竟离职的weak_ptr没用了。

      3 利用shared_ptr的订制析构功能:

            3.1 定制删除器:

 shared_ptr(Y *p, D d)的第一个参数是要被管理的指针,它的含义与其他构造函数的参数相同,而第二个删除器参数d则告诉shared_ptr在析构时不是使用delete在操作指针p,而要用d来操作,即把delete p 换成d(p);在这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d(p)成立即可。对删除器的要求是它必须是可拷贝的,行为必须也像delete那样,不能抛出异常。有了删除器的概念,就可以用shared_ptr实现管理任意资源,只要这种资源提供了它自己的释放操作。

template<class Y,class D> shread_ptr::shared_ptr(Y* p,D d)

template<class Y,class D> shared_ptr::reset(Y* p,D d)//这里Y与shared_ptr本身的模板参数T可以不同,只要Y*能隐式转换为T*  //shared_ptr<T> x//x本身的模板参数T

           3.2 利用shared_ptr的订制析构功能,传入一个仿生函数或函数指针d在析构时不用delete而用d。结合2,weak_ptr提升为shared_ptr后若没有提升成功,则新建员工信息并用shared_ptr订制析构功能注册个回调函数d作为删除器,这个删除器不再简单的delete对象而是再执行一个将公司容器中相应的weak_ptr删除,这样解决了2的内存泄露问题

           3.3 3.2company中创建员工信息时,订制析构器是利用boost::bind(&DeleteCallback,this,_1)这里假设DeleteCallback是删除器,this是company的this指针。这就将company对象的this指针暴漏给了析构员工对象的删除器。假设company在员工对象前销毁,那么删除器持有那个this找谁呢?core dump了。

     4  前面3中出现的又是company的对象声明周期的问题,回到shared_ptr/weak_ptr管理对象声明周期的问题上,那么company也要用shared_ptr来管理。利用enable_shared_from_this可以将this指针转化为shared_ptr对象。那么company的生命期不会短于删除器。

      下面看代码:

#include<iostream>
#include<string>
#include<map>
#include<boost/shared_ptr.hpp>
#include<boost/weak_ptr.hpp>
#include<boost/bind.hpp>
#include<boost/enable_shared_from_this.hpp>
#include<boost/noncopyable.hpp>
using namespace std;
using namespace boost;
class company;//公司
class staff{//员工
    public:
        staff(string x):name(x){}
        void check(shared_ptr<company> x);//员工要查看公司所有员工信息
        string& key(){//员工标识符就是姓名
            return name;
        }
        ~staff(){
            cout<<"~staff "<<name<<endl;
        }
    private:
        string name;//员工姓名
};
class company :public boost::enable_shared_from_this<company>,boost::noncopyable//enable_shared_from_this使this指针变身为shared_ptr
{
    public:
        shared_ptr<staff> get(const string name){//给定一个员工姓名查找公司中是否有,若有则返回,否则创建一个新员工,注意这里不要返回临时对象的引用,shared_ptr对象就是一个普通的对象
            shared_ptr<staff> member;
            lock();//互斥量保护
            weak_ptr<staff>& wk_member=vec[name];//注意这里是引用方便修改
            member=wk_member.lock();//weak_ptr提升操作原子操作线程安全
            if(!member){
                member.reset(new staff(name),boost::bind(&company::WeakDeleteCallback,boost::weak_ptr<company>(shared_from_this()),_1));//订制析构,当shared_ptr管理的对象析构时就执行订制的仿生函数
                //这里解决了很多问题:1保证了员工会被销毁 2若只采用简单weak_ptr管理员工则会造成轻微内存泄露即weak_ptr本身在公司map里不会被清理 3采用shared_from_this解决了公司的this指针泄露,若公司先于员工倒闭,那么员工访问公司core dump。注意这里的弱回调技术是weak_ptr<company>
                wk_member=member;
            }
            unlock();
            return member;
        }
        void traverse(){//遍历公司员工
            lock();
            for(map<string,weak_ptr<staff> >::iterator it=vec.begin();it!=vec.end();it++){
                shared_ptr<staff> temp(it->second.lock());
                if(temp)
                    temp->check(shared_from_this());//遍历时每个员工执行一个请求公司互斥锁的操作时死锁了
            }
            unlock();
        }
        void lock() const{//加锁
            pthread_mutex_lock(&mutex);
        }
        void unlock() const{//解锁
            pthread_mutex_unlock(&mutex);
        }
        company(){//shared_from_this()不要在构造函数中调用,在构造的时候还没有被shared_ptr接管
            pthread_mutex_init(&mutex,NULL);
        }
        ~company(){
            cout<<"~company"<<endl;
        }
    private:
        static void WeakDeleteCallback(const boost::weak_ptr<company>& Com,staff* member){//弱回调函数
            shared_ptr<company> Com_(Com.lock());
            if(Com_){
                if(member){
                    Com_->remove(member);
                }
            }
            delete member;//别忘了这句,这是delelte真正的操作,前面那几句相当于附加操作罢了
        }
        void remove(staff* member){
            if(member){
                lock();
                vec.erase(member->key());
                unlock();
            }
        }
        mutable pthread_mutex_t mutex;
        map<string,weak_ptr<staff> > vec;
};
void staff::check(shared_ptr<company> x){//遍历员工时用互斥锁保护,然后员工又调用此函数去申请公司的互斥锁,死锁了
    x->lock();
    cout<<"Can I get there? I think I can't"<<endl;
    x->unlock();
}
void LongLifeCompany(){
    cout<<"LongLifeCompany"<<endl;
    shared_ptr<company> google(new company);
    {
        shared_ptr<staff> LiLei=google->get("LiLei");
        //LiLei在这里析构
    }
    //google在这里析构
}
void LongLifeStaff(){
    cout<<"LongLifeStaff"<<endl;
    shared_ptr<staff> HanMeimei;
    {
        shared_ptr<company> google(new company);
        HanMeimei=google->get("HanMeimei");
        //google->traverse();//若执行这句则死锁
    }
}
int main(){
    LongLifeCompany();
    LongLifeStaff();
    return 0;
}

输出:

LongLifeCompany
~staff LiLei
~company
LongLifeStaff
~company
~staff HanMeimei


若执行了google->traverse()那句则输出是:

LongLifeCompany
~staff LiLei
~company
LongLifeStaff
^C                                             //死锁了,只好中断了....

最后说明:

      注意35行的 member.reset(new staff(name),boost::bind(&company::WeakDeleteCallback,boost::weak_ptr<company>(shared_from_this()),_1));这行代码信息量略大啊!weak_ptr<company>真正实现了弱回调:若对象还活着则执行相关操作,否则忽略之。即员工对象发现company对象已经不存在(通过weak_ptr提升判断)则不执行从company删除自己的信息。

     分析这个bind函数:执行bind对象的是shared_ptr计数为0要析构对象,此时传入订制析构时delte A换成d(p),参数 shared_ptr(Y *p, D d)中的p而这正是员工(A)staff对象。那么boost::function<void()> f=bind(&WeakDelteCallback,weak_ptr<company>(shared_from_this(),_1)处的_1就是调用时传入的第一个参数。假设调用时是f(p)其中p是第一个参数赋给_1的位置,实质是WeakDeleteCallback(const boost::weak_ptr<company>& Com,staff* member)//_1的位置换成了执行时的第一个参数。

我发现了个问题:没有#include<pthread.h>程序还是执行了,互斥锁还是管用了....这点似乎以前遇见过.....

你可能感兴趣的:(shared_ptr弱回调技术)