场景:公司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>程序还是执行了,互斥锁还是管用了....这点似乎以前遇见过.....