C++智能指针 auto_ptr、shared_ptr、weak_ptr和unique_ptr
手写代码是理解C++的最好办法,以几个例子说明C++四个智能指针的用法,转载请注明出处。
一、auto_ptr
auto_ptr这是C++98标准下的智能指针,现在常常已经被C++标准的其他智能指针取代。它的缺点是在转移所有权后会使运行期不安全。C++11新标准,用unique_ptr来代替auto_ptr原有功能,其用法介绍见第四部分unique_ptr。
#include <iostream> #include <memory> #include <string> using namespace std; void main(){ auto_ptr<string> country[5] = { auto_ptr<string>(new string("USA")), auto_ptr<string>(new string("CHN")), auto_ptr<string>(new string("RUS")), auto_ptr<string>(new string("FRA")), auto_ptr<string>(new string("GRB")) }; auto_ptr<string> pwin; pwin = country[2]; //将所有权从country[2]转让给pwin,此时country[2]不再引用该字符串从而变成空指针,在运行到循环时就会崩溃 for (int i = 0; i < 5; ++i) cout << *country[i] << endl; cout << "The best is " << *pwin << endl; system("pause"); }
二、share_ptr
share_ptr是C++11新添加的智能指针,它限定的资源可以被多个指针共享。
用法见下例:
#include <iostream> #include <memory> #include <string> using namespace std; void fun(){ shared_ptr<string> pa(new string("CHN")); shared_ptr<string> pb(new string("USA")); cout << "*pa " << *pa << endl;//CHN cout << "pa.use_count " << pa.use_count() << endl;//1 cout << "*pb " << *pb << endl;//USA cout << "pb.use_count " << pb.use_count() << endl;//1 pa = pb; cout << *pa << endl;//USA cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2 cout << "pb.use_count " << pb.use_count() << endl;//2 pa.reset(); pb.reset(); cout << "pa.use_count " << pa.use_count() << endl;//0 cout << "pb.use_count " << pb.use_count() << endl;//0 } void main() { fun(); system("pause"); }
与数组相结合应用,见另一个例子:
#include <iostream> #include <memory> #include <string> using namespace std; void main(){ shared_ptr<string> country[5] = { shared_ptr<string>(new string("USA")), shared_ptr<string>(new string("CHN")), shared_ptr<string>(new string("RUS")), shared_ptr<string>(new string("FRA")), shared_ptr<string>(new string("GRB")) }; shared_ptr<string> pwin; cout << pwin.use_count() << endl;//输出0 pwin = country[2]; /*使用shared_ptr时运行正常,因为shared_ptr采用引用计数,pwin和films[2]都指向同一块内存, 在释放空间时因为事先要判断引用计数值的大小因此不会出现多次删除一个对象的错误。 从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。 可以通过成员函数use_count()来查看资源的所有者个数。 */ cout << pwin.use_count() << endl;//输出2 for (int i = 0; i < 5; ++i) cout << *country[i] << endl; cout << "The best is " << *pwin << endl; system("pause"); }
三、weak_ptr
weak_ptr是一种用于解决shared_ptr相互引用时产生死锁问题的智能指针。如果有两个shared_ptr相互引用,那么这两个shared_ptr指针的引用计数永远不会下降为0,资源永远不会释放。weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。
先看一下两个shared_ptr指针互相引用导致的资源释放失败的例子:
#include <iostream> #include <memory> #include <string> using namespace std; class B; class A { public: shared_ptr<B> pb_; ~A() { cout << "A delete\n"; } }; class B { public: shared_ptr<A> pa_; ~B() { cout << "B delete\n"; } }; void fun(){ shared_ptr<B> pb(new B()); cout << "pb.use_count " << pb.use_count() << endl;//1 shared_ptr<A> pa(new A()); cout << "pa.use_count " << pa.use_count() << endl;//1 pb->pa_ = pa; cout << "pb.use_count " << pb.use_count() << endl;//1 cout << "pa.use_count " << pa.use_count() << endl;//2 pa->pb_ = pb; cout << "pb.use_count " << pb.use_count() << endl;//2:由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1 cout << "pa.use_count " << pa.use_count() << endl;//2 }//程序结束时,没有调用A和B的析构函数 void main() { fun(); system("pause"); }
而使用weak_ptr:把A中的shared_ptr<B> pb_改为weak_ptr<B> pb_weak,这样改为了弱引用,传递时不会增加pb引用计数use_count()的值,所以最终能够使A、B资源正常释放:
#include <iostream> #include <memory> #include <string> using namespace std; class B; class A { public: weak_ptr<B> pb_weak; ~A() { cout << "A delete\n"; } }; class B { public: shared_ptr<A> pa_; ~B() { cout << "B delete\n"; } void print(){ cout << "This is B" << endl; } }; void fun(){ shared_ptr<B> pb(new B()); cout << "pb.use_count " << pb.use_count() << endl;//1 shared_ptr<A> pa(new A()); cout << "pa.use_count " << pa.use_count() << endl;//1 pb->pa_ = pa; cout << "pb.use_count " << pb.use_count() << endl;//1 cout << "pa.use_count " << pa.use_count() << endl;//2 pa->pb_weak = pb; cout << "pb.use_count " << pb.use_count() << endl;//1:弱引用不会增加所指资源的引用计数use_count()的值 cout << "pa.use_count " << pa.use_count() << endl;//2 shared_ptr<B> p = pa->pb_weak.lock(); p->print();//不能通过weak_ptr直接访问对象的方法,须先转化为shared_ptr cout << "pb.use_count " << pb.use_count() << endl;//2 cout << "pa.use_count " << pa.use_count() << endl;//2 }//函数结束时,正确的情况下,应该调用A和B的析构函数 /*资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放, B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。 */ void main() { fun(); system("pause"); }
四、unique_ptr
unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权。它取代了C++98中的auto_ptr。
用法和auto_ptr类似,详情见一下代码:
#include <iostream> #include <memory> #include <string> using namespace std; unique_ptr<string> fun2(){ return unique_ptr<string>(new string("RUS")); } void fun(){ unique_ptr<string> pa(new string("CHN")); //unique_ptr没有use_count()方法 unique_ptr<string> pb(new string("USA")); pb = move(pa); //p2=p1;//错误,不能直接用等于号 if (pa == nullptr) cout << "pa现在为空" << endl; cout << "*pb " << *pb << endl;//pb变成了“CHA” string* ps = pb.release();//清空当前智能指针所指的资源对象,并返回指针 cout << "*ps " << *ps << endl;//ps变成了“CHA” pa.reset(ps);//重置指向另一个对象 cout << "*pa " << *pa << endl;//pa变成了“CHA” pb = fun2();//接收函数的返回值可以用等于号,因为使用了移动构造函数 cout << "*pb " << *pb << endl;//pb变成了“RUS” } void main() { fun(); system("pause"); }