众所周知,auto_ptr是c++自动释放指针,能确保new出来的内存能够delete释放,无论是正常退出还是异常退出。
博主做某个项目把new后的指针放在vector,感觉之后挨个delete有点麻烦,便想到能否vector存入auto_ptr,不用去操心释放内存的事情。事情往往不如人愿。g++一遍果断报错了。
博主写了段测试代码,如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <memory> 4 #include <vector> 5 using namespace std; 6 class B{ 7 public: 8 B(){} 9 virtual ~B(){cout<<"B discontructed"<<endl;} 10 virtual void print(){ 11 cout<<"This is B"<<endl; 12 } 13 }; 14 class A:public B{ 15 public: 16 A(){} 17 ~A(){ 18 cout<<"A discontructed"<<endl; 19 } 20 virtual void print(){ 21 cout<<"This is A"<<endl; 22 } 23 }; 24 void main () 25 { 26 auto_ptr<B> a(new A()); 27 vector<auto_ptr<B> > v; 28 v.push_back(a); 29 v[0]->print(); 30 }
g++一遍,报错了,博主百思不得其解。遂百度了一下,感觉这个帖子讲的还不错:
http://bbs.youkuaiyun.com/topics/10178049
后面经过资料的查找,发现auto_ptr之所以不能用作容器的元素,是因为它的转移语义,不符合容器对元素的要求。关于转移语义,在此做个小小的解释:
转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。通过转移语义,临时对象中的资源能够转移其它的对象里。
详细请参考该网页:http://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/
为了验证该论点,作者写了一下代码进行验证:
1 #include <iostream> 2 #include <memory> 3 using namespace std; 4 class B{ 5 public: 6 B(){} 7 virtual ~B(){cout<<"B is discontructed"<<endl;} 8 }; 9 class A{ 10 public: 11 B b; 12 A(){} 13 ~A(){cout<<"A is discontructed"<<endl;} 14 }; 15 int main () 16 { 17 auto_ptr<A> p(new A()); 18 { 19 auto_ptr<A> p2(p);//将p内的指针所有权转移给p2,注意此时是可以拷贝的。 20 }//p2为局部变量,出栈时执行起析构函数,其中将A类对象释放掉。 21 cout<<"after p2"<<endl; 22 { 23 auto_ptr<A> p3(p);//由于p中已经没有A对象指针所有权了,所以即使p3拷贝p后,也无需释放new A()时分配的内存 24 } 25 cout<<"after p3"<<endl; 26 return 0; 27 }
运行结果如下:
A is discontructed B is discontructed after p2 after p3
可以看出,在p转移给p2的时候,p本身已经不存有new A()指针的所有权了。释放内存的责任也丢给了p2。
那么如何在容器中使用智能指针呢?可以使用boost库中的shared_ptr智能指针,它会记录有多少个shared_ptr共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。
以下是在vector中使用shared_ptr代码:
1 #include <boost/shared_ptr.hpp> 2 #include<iostream> 3 #include <vector> 4 using namespace std; 5 using namespace boost; 6 class A{ 7 public: 8 A(){} 9 virtual ~A(){cout<<"A is discontructed"<<endl;} 10 }; 11 12 int main(){ 13 typedef vector<shared_ptr<A> > VectorA; 14 VectorA va; 15 for(int i=0;i<5;i++){ 16 shared_ptr<A> tmp(new A()); 17 va.push_back(tmp);//将share_ptr<A>指针放入容器 18 } 19 cout<<"after for function"<<endl; 20 }
运行结果如下:
after for function A is discontructed A is discontructed A is discontructed A is discontructed A is discontructed
分析运行结果可以得知,容器中的对象是在19行运行结束后执行析构函数的。而不是在for函数中。
同样,读者可以试下发生异常能否执行析构函数。
最后总结:shared_ptr智能指针给了我们很多方便的地方,不用去考虑去delete释放内存。尽可能使用智能指针,对内存泄露say bye~
Wind
2013-11-21