c++ vector 的坑
一个空的vector执行pop_back操作会发生什么
由于之前看STL源码剖析的时候,发现所执行的操作如下:
只是简单的将末尾的finish迭代器减1后destroy。这让人产生一个疑问:假如这个vector为空了,finish=start了,finish再减1不就不在vector的内存控制范围了么。于是,我打算看一下vs2013编译器和g++编译器的源码。
vs2013的编译器源码如下:
#if _ITERATOR_DEBUG_LEVEL == 2
void pop_back()
{ // erase element at end
if (empty())
_DEBUG_ERROR("vector empty before pop");
else
{ // erase last element
_Orphan_range(this->_Mylast - 1, this->_Mylast);
this->_Getal().destroy(this->_Mylast - 1);
--this->_Mylast;
}
}
#else /* _ITERATOR_DEBUG_LEVEL == 2 */
void pop_back()
{ // erase element at end
this->_Getal().destroy(this->_Mylast - 1);
--this->_Mylast;
}
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */</span>
该源码的意思就是,在debug模式下运行,是会检测vector是否empty的,但在release模式下不会检测。经过测试,debug下pop_back一个空的vector会报错,但release没有,但是release下面,pop_back后这个vector基本上就废了,你不能再push_back了,会报错。因为pop_back显然已经将vector的对象的结构破坏。
在g++编译器下测试,其源码如下:
void pop_back() _GLIBCXX_NOEXCEPT
{
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
}</span>
结果也是一样的,pop_back一个空的vector会破坏整个vector对象,具体的作用就是finish迭代器失效。但有趣的是,下面的代码可以运行:
vec.push_back(0);
vec.pop_back();
vec.pop_back();
vec[0] = 1;
cout << vec[0];</span>
原因就是,push_back插入对象是 分为申请空间和构造对象两步,pop_back的destroy只有析构对象的作用,没有deallocate的回收空间的作用。所以,pop_back之后,空间是没有释放的,vec[0]能够执行赋值行为并输出,但这个时候因为finish迭代器的损毁,vec已经不能执行push_back操作了。另外提一下,destroy函数在析构对象的时候,做了进一步的判断,如果该对象存在有效的析构函数,则调用改析构函数析构之,否则什么都不做,比如int类型的对象。
总的来说,pop_back的准确调用需要程序员来保证,在执行pop_back时候最好能预先做一些判断。
vector clear() 方法 内存释放问题
自己查到的三处说法的对比:
一、转自知道的答案:https://zhidao.baidu.com/question/323662520.html?qq-pf-to=pcqq.c2c#
vector,clear()并不真正释放内存(这是为优化效率所做的事),clear实际所做的是为vector中所保存的所有对象调用析构函数(如果有的话),然后初始化size这些东西,让觉得把所有的对象清除了。
真正释放内存是在vector的析构函数里进行的,所以一旦超出vector的作用域(如函数返回),首先它所保存的所有对象会被析构,然后会调用allocator中的deallocate函数回收对象本身的内存。
所以,某些编译器clear后还能访问到对象数据(因为它根本没清除),在一些比较新的C++编译器上(例如VS2008),当进行数组引用时(例如a[2]这种用法),STL库中会有一些check函数根据当前容器的size值来判断下标引用是否超出范围,如果超出,则会执行这样一句:
_THROW(out_of_range, "invalid vector<T> subscript");
即抛出一个越界异常,clear后没有捕获异常,程序在新编译器编译后就会崩溃掉。
-------------------------分割线--------------------------------------------------------------
二、转自博客:https://www.cnblogs.com/summerRQ/articles/2407974.html
vector : C++ STL中的顺序容器,封装数组
1. vector容器的内存自增长
与其他容器不同,其内存空间只会增长,不会减小。先来看看"C++ Primer"中怎么说:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储。设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间、拷贝元素、撤销旧空间,这样性能难以接受。因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。
关于vector的内存空间,有两个函数需要注意:size()成员指当前拥有的元素个数;capacity()成员指当前(容器必须分配新存储空间之前)可以存储的元素个数。reserve()成员可以用来控制容器的预留空间。vector另外一个特性在于它的内存空间会自增长,每当vector容器不得不分配新的存储空间时,会以加倍当前容量的分配策略实现重新分配。例如,当前capacity为50,当添加第51个元素时,预留空间不够用了,vector容器会重新分配大小为100的内存空间,作为新连续存储的位置。
2. vector内存释放
由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。
如果需要空间动态缩小,可以考虑使用deque。如果非vector不可,可以用swap()来帮助你释放内存。具体方法如下:
vector<int> nums;
nums.push_back(1);
nums.push_back(1);
nums.push_back(2);
nums.push_back(2);
vector<int>().swap(nums); //或者nums.swap(vector<int> ())
或者如下所示,使用一对大括号,意思一样的:
//加一对大括号是可以让tmp退出{}的时候自动析构
{
std::vector<int> tmp = nums;
nums.swap(tmp);
}
swap()是交换函数,使vector离开其自身的作用域,从而强制释放vector所占的内存空间,总而言之,释放vector内存最简单的方法是vector<int>.swap(nums)。当时如果nums是一个类的成员,不能把vector<int>.swap(nums)写进类的析构函数中,否则会导致double free or corruption (fasttop)的错误,原因可能是重复释放内存。标准解决方法如下:
template < class T >
void ClearVector( vector< T >& vt )
{
vector< T > vtTemp;
veTemp.swap( vt );
}
3. 利用vector释放指针
如果vector中存放的是指针,那么当vector销毁时,这些指针指向的对象不会被销毁,那么内存就不会被释放。如下面这种情况,vector中的元素时由new操作动态申请出来的对象指针:
#include <vector>
using namespace std;
vector<void *> v;
每次new之后调用v.push_back()该指针,在程序退出或者根据需要,用以下代码进行内存的释放:
for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++)
if (NULL != *it)
{
delete *it;
*it = NULL;
}
v.clear();
三、转自博客:https://blog.csdn.net/hk_john/article/details/72463318
最近经常用到vector容器,发现它的clear()函数有点意思,经过验证之后进行一下总结。
clear()函数的调用方式是,vector<datatype> temp(50);//定义了50个datatype大小的空间。temp.clear();
作用:将会清空temp中的所有元素,包括temp开辟的空间(size),但是capacity会保留,即不可以以temp[1]这种形式赋初值,只能通过temp.push_back(value)的形式赋初值。
同样对于vector<vector<datatype> > temp1(50)这种类型的变量,使用temp1.clear()之后将会不能用temp1[1].push_back(value)进行赋初值,只能使用temp1.push_back(temp);的形式。
下面的代码是可以运行的。
#include <iostream>
#include<vector>
using namespace std;
int main(){
vector<vector<int>> test(50);
vector<int> temp;
test[10].push_back(1);
cout<<test[10][0]<<endl;
test.clear();
for(int i=0;i<51;i++)
test.push_back(temp);
system("pause");
return 0;
}
但是这样是会越界错误的。
#include <iostream>
#include<vector>
using namespace std;
int main(){
vector<vector<int>> test(50);
vector<int> temp;
test[10].push_back(1);
cout<<test[10][0]<<endl;
test.clear();
for(int i=0;i<50;i++)
test[i].push_back(1);
system("pause");
return 0;
}
并且即使我们使用
for(int i=0;i<100;i++)
test[i].push_back(1);
都是可以的,因为size已经被清除了。
转自:https://blog.csdn.net/tangle001/article/details/47026989
https://blog.csdn.net/acoolgiser/article/details/81018296
c++ vector 的坑的更多相关文章
- vector的坑——C++primer练习6.33总结
说来惭愧,一道简单的对vector递归的题目写了一个多小时,最后还是请教了大神才改出来. 首先贴上原代码: void return_vector(vector<int>::iterator ...
- PAT 甲级 1053 Path of Equal Weight (30 分)(dfs,vector内元素排序,有一小坑点)
1053 Path of Equal Weight (30 分) Given a non-empty tree with root R, and with weight Wi assigne ...
- vector 踩过的坑
今天,做LeetCode上的一道题,198题:Rob House,在纸上画了画,发现了重复的结构,就使用了递归的方式实现的 #include<iostream> #include<v ...
- std::vector<bool>中的坑
http://www.cplusplus.com/reference/vector/vector/?kw=vector C++中,vector<bool>为了达到节省内存的目的,专门做了特 ...
- C++踩坑——用memset对vector进行初始化
在一段程序中,使用memset对vector进行了初始化,然后得到了错误的结果.找这个bug花费了很长时间. vector中有其自身的结构,不能单纯的按字节进行初始化.使用memset对vector进 ...
- STL vector容器需要警惕的一些坑
从迭代器中取值切记需要判断是否为空 例如: vector<int> vtTest; vtTest.clear(); if (vtTest.empty()){ ; } ]; 如果没有忘了判断 ...
- c++ 踩坑大法好 复合数据类型------vector
1,vector是啥? 是具有动态大小的数组,具有顺序.能够存放各种类型的对象.相比于固定长度的数组,运行效率稍微低一些,不过很方便. 2,咋用? 声明: vector <int> vi; ...
- 被vector动态扩容给坑了!
大家好,我是东北码农.记录一下工作中事. 前几天,运维同事给我反馈了一个问题: 通过监控发现,线上的一个服务,业务线程时不时会出现卡顿,卡顿大约持续几秒. 我们做金融系统后台开发的,对性能要求很严格的 ...
- 【python2/3坑】从gensim的Word2Vec.load()的中文vector模型输出时显示unicode码
服务器上python2.7 打印出的e[0]对应的是 unicode码 于是分别尝试了用e[0].encode('utf-8')转码 和 e[0].decode('unicode-escape')依然 ...
随机推荐
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十章:混合
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十章:混合 代码工程地址: https://github.com/j ...
- 01-常见Dos命令、Java历史、Java跨平台、配置Path环境变量、第一个HelloWorld例子
常见Dos命令 dir: 列出当前目录下的文件以及文件夹 md: 创建目录 rd: 删除目录 cd: 进入指定目录 del: 删除文件 copy: 复制文件 xcopy: 复制目录 tree: 列出目 ...
- Unity3dShader学习合集暂存
http://www.cnblogs.com/flappy/archive/2012/08/10/2631348.html 1. Unity3d的參考手冊, 里面涵盖Unity3d官方的描写叙述 ht ...
- Tcp之双向通信
TestServer.java package com.sxt.tcp; /* * 服务端 */ import java.io.DataInputStream; import java.io.Data ...
- SSH基本原理
SSH原理与运用:远程登录 作者: 阮一峰 年12月21日 SSH是每一台Linux电脑的标准配置. 随着Linux设备从电脑逐渐扩展到手机.外设和家用电器,SSH的使用范围也越来越广.不仅程序员离不 ...
- 「BZOJ2510」弱题
「BZOJ2510」弱题 这题的dp式子应该挺好写的,我是不会告诉你我开始写错了的,设f[i][j]为操作前i次,取到j小球的期望个数(第一维这么大显然不可做),那么 f[i][j]=f[i-1][j ...
- linux内核分析笔记----中断和中断处理程序
中断还是中断,我讲了很多次的中断了,今天还是要讲中断,为啥呢?因为在操作系统中,中断是必须要讲的.. 那么什么叫中断呢, 中断还是打断,这样一说你就不明白了.唉,中断还真是有点像打断.我们知道linu ...
- 如何用phpmyadmin导入大容量.sql文件,直接使用cmd命令进行导入
很多使用php+mysql建站的站长朋友们,经常要用到phpMyAdmin数据库管理工具备份和恢复数据库,当站点运行很久的时候,MySQL数据库会非常大,当站点碰到问题时,需要使用phpMyAdmin ...
- H3C SSH配置例子
- 前后端结合的 WAF
前言 之前介绍了一些前后端结合的中间人攻击方案.由于 Web 程序的特殊性,前端脚本的参与能大幅弥补后端的不足,从而达到传统难以实现的效果. 攻防本为一体,既然能用于攻击,类似的思路同样也可用于防御. ...