由vector中无法加入auto_ptr所想到的

众所周知,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.csdn.net/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

 

posted @ 2013-11-15 21:44  the wind's words  阅读(1033)  评论(0编辑  收藏  举报