您的位置:首页 > 编程语言 > C语言/C++

【C++】vector与构造、析构函数的一点小发现

2013-11-28 10:11 537 查看
今天偶然要用到vector存储一个自定义的类,但用起来心里有点发虚,构造和析构到底什么时候调用的,于是写了一点代码测试一下,发现了点有意思的东西,分享下(本文所有代码在dev c++ 4.9.9.2下运行):

Version1:


这段代码很简单,构造了一个A的对象,把这个对象放到vector里边,输出也应该很容易猜到,就是构造函数,拷贝构造函数和析构函数依次被调用,但结果出乎我的意料:


析构函数竟然没有被调用!

小小的震惊过后,静下心来看代码,发现不只vector里边的对象的析构没有被调用,连正常生成的对象a的析构都没有被调用,于是发现自己犯了个低级错误,由此便产生了版本2:

Version2:


版本2与1的差别仅仅在于我把18,19,20这三行的代码放到了一个大括号里边,这次的运行结果就正常了:


是的,版本1中的析构没有被打印的原因是变量的寿命在主函数死亡后才终结,那时候已经没法打印析构函数这几个字了,换种方式,我用命令行来打开版本1的程序,就可以看到析构函数在“请按任意键继续…”的后边被调用了:


好的,继续,这次我们多放一个对象进去,

Version3:


其他还是一样,只是这次我又放了个b进去,它的运行结果也应该很容易猜到,首先是a和b的构造函数被调用,然后是a和b的拷贝构造函数被调用,再然后是四个析构函数被调用,可是结果又出乎我的意料了:


可以看到首先是两个构造函数,这没问题,可接下来,我们看到拷贝构造函数被调用了三次!而最终的析构也是5次!搞什么?从哪多出来一个对象?

为了搞清楚这个问题,我再来加一个对象:

Version4:


再看它的运行结果:


看到这我才醒悟过来,这是vector在自动扩容!

我们来一行一行的看版本4的运行结果:

前3行的构造函数没得说,是a,b,c三个对象的生成;

第4行,是push_back(a)在起作用,调用了一次拷贝构造。

第5,6,7行是一个vector自动扩容的过程,vector首先申请了新的空间,然后通过两次push_back将a,b放进去(当然,也可能是push_front,总之是个放进去的过程),这个过程要调用两次拷贝构造,也就是第5,6行的拷贝构造,新空间构建完成后,立马就释放掉原来的空间(好狠心~~!),也就是第7行的析构函数的调用。

第8,9,10,11,12是一个与5,6,7相同的过程,c来的时候先申请新空间,然后把a,b,c放进去,然后调用a,b的析构函数。

最后的13,14,15,16,17,18六行,则是现存对象的释放,分别是变量a,b,c及他们在vec中的“替身”的释放。

把程序小改一下就可以证明我的推测:

Version5:


运行结果:


看到这,着实让我很吃惊,默认情况下vector执行的是一种“用时间换空间”的做法!默认大小为1,每次仅增长1!这样做可以保证vector尽可能的小,但却需要耗费大量的时间去不停的释放旧空间,开辟新空间。

由此我们可以看出,要想利用好vector,最好还是预估一下数据大小,给它指定一个大小出来,尽可能的减少释放和析构的次数,如果你的数据量实在不好估算而你的对象构造和析构又比较耗资源,那么还是不要选择vector的好,还是试试list吧。

话说回来,一定要使用vector也没什么问题,只需要在恰当的时候利用好reserve函数给它指定一个新的大小就可以了。

注意,vector还有个函数叫resize的,也会改变vector的空间大小,不过个人不太建议使用,看我的这个例子:

Version6:


原因我就不再解释了,关于这两个函数的区别在http://blog.csdn.net/shuilan0066/article/details/3588478这篇文章中讲的挺清楚,我也会随后转载过来,有兴趣的朋友可以了解一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: