C++ STL Smart Pointer 智能指针(一)shared_ptr和weak_ptr
C++标准库提供了两大类型的smart pointer
一、class shared_ptr。共享式拥有。多个shared_ptr可以指向同一个对象,该对象和起相关资源会在最后一个指针被销毁时释放。
二、class unique_ptr。独占式拥有。同一时间只有一个smart pointer可以指向该对象。对于避免资源泄露特别有用。
1.Class shared_ptr
1.1 使用shared_ptr
可以像使用其他指针一样使用shared_ptr。可以赋值、拷贝、比较,也可以使用*和->操作符。
#include <iostream> #include <string> #include <memory> #include <vector> using namespace std; int main(){ shared_ptr<string> pNico(new string("nico")); shared_ptr<string> pJutta(new string("jutta")); cout<<*pNico<<endl; cout<<*pJutta<<endl; (*pNico)[0]='N'; pJutta->replace(0,1,"J"); cout<<*pNico<<endl; cout<<*pJutta<<endl; vector<shared_ptr<string>> whoMadeCoffee; whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); cout<<"vector whoMadeCoffee:"<<endl; for(auto ptr:whoMadeCoffee){ cout<<*ptr<<" "; } cout<<endl; *pNico="Nicolai"; cout<<"vector whoMadeCoffee:"<<endl; for(auto ptr:whoMadeCoffee){ cout<<*ptr<<" "; } cout<<endl; cout<<"use_count:"<<whoMadeCoffee[0].use_count()<<endl; whoMadeCoffee.clear(); cout<<"use_count:"<<whoMadeCoffee[0].use_count()<<endl; return 0; }
输出:
nico jutta Nico Jutta vector whoMadeCoffee: Jutta Jutta nico Jutta nico vector whoMadeCoffee: Jutta Jutta Nicolai Jutta Nicolai use_count:4 use_count:1
1.2 析构策略
1 我们可以声明属于自己的deleter。
2 对于Array 我们可以用delete[]
#include <iostream> #include <string> #include <memory> #include <vector> using namespace std; int main(){ //定义一个Deleter shared_ptr<string> pNico(new string("nico"),[](string* p){ cout<<"delete "<<*p<<endl; delete p; }); shared_ptr<string> pJutta(new string("jutta")); vector<shared_ptr<string>> whoMadeCoffee; whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); whoMadeCoffee.push_back(pJutta); whoMadeCoffee.push_back(pNico); //delete all pNico pNico=nullptr; whoMadeCoffee.resize(2); cout<<"vector whoMadeCoffee:"<<endl; for(auto ptr:whoMadeCoffee){ cout<<*ptr<<" "; } cout<<endl; //对付Array shared_ptr<int> p(new int[10],[](int* p){ delete[] p; }); shared_ptr<int> p2(new int[10],default_delete<int[]>()); unique_ptr<int[]> p3(new int[10]); unique_ptr<int,void(*)(int*)> p4(new int[10],[](int* p){ delete[] p; }); return 0; }
运行结果:
delete nico vector whoMadeCoffee: jutta jutta
3 其他析构策略
你可以制定属于自己的析构策略。比如当最后一个ptr被析构时,删除文件,你可以:
#include <string> #include <memory> #include <fstream> #include <cstdio> using namespace std; class FileDeleter { public: FileDeleter(const string& fn):filename(fn){ } void operator ()(ofstream* fp){ fp->close(); //close file remove(filename.c_str()); //delete file cout<<"remove file"<<endl; } private: string filename; };
int main(){ shared_ptr<ofstream> fp(new ofstream("tmpfile.txt"),FileDeleter("tmpfile.txt")); fp->write("aaa\n",4); fp->write("aaa\n",4); fp=nullptr; }
2.Class weak_ptr
weak_ptr允许你“共享但不拥有”对象,这个class会建立起一个shared_pointer。一旦最末一个拥有该对象的shared pointer失去了拥有权,任何weak pointer 都会自动成空。因此除了default和copy构造函数之外,weak_ptr只提供“接受一个shared_ptr”的构造函数。
你不能使用*和->访问weak_ptr所指的对象。而是必须另外建立一个shared pointer。
person.h
#include <iostream> #include <string> #include <vector> #include <memory> using namespace std; class Person : public enable_shared_from_this<Person> { public: Person(const string& n,shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr); ~Person(); void setParentsAndTheirKids(shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr); string name; shared_ptr<Person> mother; shared_ptr<Person> father; vector<shared_ptr<Person>> kids; }; shared_ptr<Person> initFamily(const string& name);
person.cpp
#include "person.h" Person::Person(const string& n,shared_ptr<Person> m,shared_ptr<Person> f): name(n),mother(m),father(f) { } Person::~Person(){ cout<<"delete "<<name<<endl; } void Person::setParentsAndTheirKids(shared_ptr<Person> m, shared_ptr<Person> f){ mother=m; father=f; if(m!=nullptr){ m->kids.push_back(shared_from_this()); } if(f!=nullptr){ f->kids.push_back(shared_from_this()); } } shared_ptr<Person> initFamily(const string& name){ shared_ptr<Person> mom(new Person(name+"'s mom")); shared_ptr<Person> dad(new Person(name+"'s dad")); shared_ptr<Person> kid(new Person(name,mom,dad)); mom->kids.push_back(kid); dad->kids.push_back(kid); return kid; }
main.cpp
int main(){ shared_ptr<Person> p=initFamily("nico"); cout<<"nico's family exists"<<endl; cout<<"- nico is shared "<<p.use_count()<<" times"<<endl; //使用weak_ptr时,必须请问改变被指对象的访问方式,在式子内加上lock() cout<<"- name of 1st kid of nico's mom: "<<p->mother->kids[0].lock()->name<<endl; p=initFamily("jim"); cout<<"jim's family exists"<<endl; }
输出:
nico's family exists - nico is shared 3 times - name of 1st kid of nico's mom: nico jim's family exists
将person.h改为:
#include <iostream> #include <string> #include <vector> #include <memory> using namespace std; class Person : public enable_shared_from_this<Person> { public: Person(const string& n,shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr); ~Person(); void setParentsAndTheirKids(shared_ptr<Person> m=nullptr,shared_ptr<Person> f=nullptr); string name; shared_ptr<Person> mother; shared_ptr<Person> father; vector<weak_ptr<Person>> kids; }; shared_ptr<Person> initFamily(const string& name);
使用weak_ptr时,我们必须改变一下被指对象的访问方式,必须在式子内加入lock()。
这会导致新产生一个得自于kids容器内含之weak_ptr的shared_ptr。如果无法进行这样的改动,lock()会产生一个empty的shared_ptr。
main.cpp改为
int main(){ shared_ptr<Person> p=initFamily("nico"); cout<<"nico's family exists"<<endl; cout<<"- nico is shared "<<p.use_count()<<" times"<<endl; //使用weak_ptr时,必须请问改变被指对象的访问方式,在式子内加上lock() cout<<"- name of 1st kid of nico's mom: "<<p->mother->kids[0].lock()->name<<endl; p=initFamily("jim"); cout<<"jim's family exists"<<endl; return 0; }
输出:
nico's family exists - nico is shared 1 times - name of 1st kid of nico's mom: nico delete nico delete nico's dad delete nico's mom jim's family exists delete jim delete jim's dad delete jim's mom
weak_ptr是shared_ptr的帮手,用来共享对象但不拥有对象。使用use_count()返回的是对象被shared_ptr拥有的次数,至于weak_ptr对它的共享次数是不计的。而且weak_ptr可以为空。
一般而言,shared_ptr并非线程安全的。因此为避免data race造成的不明确行为,当你在多个线程中以shared_pointer指向同一个对象,你必须使用诸如mutex或lock等技术。