C++ 智能指针
A smart pointer is a class object that acts like a pointer but has addiction features. --《C++ Primer》第六版
一、使用普通指针的问题
1) 每次调用 remodel,动态分配内存,最后没有释放该资源,将导致memory leak.
1 void remodel(std::string& str) 2 { 3 std::string* ps = new std::string(str);//动态分配内存 4 ... 5 str = ps; 6 return; 7 }
2) 即使最后释放资源,但是在中间抛出异常,则delete 语句无法执行,仍导致memory leak.
1 void remodel(std::string& str) 2 { 3 std::string* ps = new std::string(str);//动态分配内存 4 ... 5 if(weird_thing())//抛出异常 6 throw_exception(); 7 str = *ps; 8 delete ps;//释放动态分配的内存 9 return; 10 }
二、智能指针使用实例
主要有三种智能指针,auto_ptr/unique_ptr/shared_ptr。定义一个指向类A的智能指针pA:auto_ptr<A> pA(new A)。
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 using namespace std; 5 class Report 6 { 7 public: 8 Report(const string s):str(s){ 9 cout<<"Object created!\n"; 10 } 11 ~Report(){ 12 cout<<"Object deleted!\n"; 13 } 14 void comment() const{ 15 cout<<str<<"\n"; 16 } 17 private: 18 string str; 19 }; 20 21 int main() 22 { 23 {//auto_ptr 24 auto_ptr<Report> ps (new Report("using auto_ptr")); 25 ps->comment(); // use -> to invoke a member function 26 } 27 cout<<"\n"; 28 {//shared_ptr 29 shared_ptr<Report> ps (new Report("using shared_ptr")); 30 ps->comment(); 31 } 32 cout<<"\n"; 33 {//unique_ptr 34 unique_ptr<Report> ps (new Report("using unique_ptr")); 35 ps->comment(); 36 } 37 return 0; 38 }
结果:当指针生命周期结束时,会自动调用所指向对象的析构函数
三、使用智能指针的注意
1) 普通指针和智能指针间需要通过显示转换
智能指针有显示构造函数,它的参数是指针。普通指针需要显示转换成智能指针:Each of these classes has an explicit constructor taking a pointer as an argument. Thus, there is no automatic type cast from a pointer to a smart pointer object.
1 shared_ptr<double> pd; 2 double *p_reg = new double; 3 pd = p_reg; // not allowed (implicit conversion) 4 pd = shared_ptr<double>(p_reg); // allowed (explicit conversion 5 shared_ptr<double> pshared = p_reg; // not allowed (implicit conversion) 6 shared_ptr<double> pshared(p_reg); // allowed (explicit conversion)
2) 智能指针不能非动态分配的对象作为构造函数的参数
因为它最后会用delete 操作来释放该资源。When pvac expires, the program would apply the delete operator to non-heap memory, which is wrong.
1 string vacation("I wandered lonely as a cloud."); 2 shared_ptr<string> pvac(&vacation); // NO!
四、为什么少使用auto_ptr?
1) auto_ptr and unique_ptr 策略
多个智能指针不能同时指向同一个对象。假设 A 指向 C, 当使用 B=A 赋值后,原来的智能指针A就失去了对C的所有权,即不能通过A来访问对象C。
Institute the concept of ownership, with only one smart pointer allowed to own a particular object. Only if the smart pointer owns the object will its destructor delete the object.Then have assignment transfer ownership.This is the strategy used for auto_ptr and for unique_ptr, although unique_ptr is somewhat more restrictive.
2) shared_ptr 策略
引用计数:每次赋值会使计数器递增;指针过期会使计数器递减;当最后一个指针过期(计数器为0)将调用 delete。
Create an even smarter pointer that keeps track of how many smart pointers refer to a particular object.This is called reference counting.Assignment, for example, would increase the count by one, and the expiration of a pointer would decrease the count by one. Only when the final pointer expires would delete be invoked.
3)auto_ptr 实例
多个auto_ptr指向同一个对象,会使原来的指针失效,非法访问
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 using namespace std; 5 6 int main() 7 { 8 //files:一个包含5个string 对象的数组 9 auto_ptr<string> files[5] = 10 { 11 auto_ptr<string> (new string("Fowl balls")), 12 auto_ptr<string> (new string("Duck Walks")), 13 auto_ptr<string> (new string("Chicken Runs")), 14 auto_ptr<string> (new string("Turkey Errors")), 15 auto_ptr<string> (new string("Goose Eggs")) 16 }; 17 18 auto_ptr<string> pwin; 19 pwin = files[2]; //files[2] 失去对象的ownership 20 cout << "The nominees for best avian baseball film are\n"; 21 for (int i = 0; i < 5; i++) 22 cout<<*files[i]<<endl; 23 cout<<"The winner is "<<*pwin<<"!\n"; 24 return 0; 25 }
4) unique_ptr
当多个unique_ptr指向同一个对象,会产生编译错误
1 unique_ptr< string> pu1(new string "Hi ho!"); 2 unique_ptr< string> pu2; 3 pu2 = pu1; //#1 not allowed 4 unique_ptr<string> pu3; 5 pu3 = unique_ptr<string>(new string "Yo!"); //#2 allowed
unique_ptr可以使用new[](delete[])。The auto_ptr template uses delete, not delete [], so it can only be used with new, not with new []. But unique_ptr has a new[], delete[] version: std::unique_ptr< double[]>pda(new double(5)); // will use delete []
五、如何选择智能指针
1) shared_ptr
程序里需要用多个指针指向同一个对象。比如:拥有一个数组指针,且需要标识出一些特别的元素(最大/最小);有两个对象都需要指向第三个类的对象;使用STL容器,容器的对象为指针。
2) unique_ptr
程序里不需要多个指针指向同一个对象。比如:把unique_ptr 作为函数的返回值,它指向一块动态分配的内存。
unique_ptr<int> make_int(int n){ return unique_ptr<int>(new int(n)); }