面试中遇到几个跟STL相关的问题,自己在平时工作中用的不多,很多基础都比较欠缺。回来后查了一下,记录以供后续学习。
问题1、vector的clear函数会释放内存空间吗?如何主动释放被占用的内存空间?
当vector、string大量插入数据后,即使删除了大量数据(或者全部都删除,即clear)并没有改变容器的容量(capacity),所以仍然会占用着内存,只析构元素。
《Effective STL》给出的解决方案是:
vector<type>v;
//.... 这里添加许多元素给v
//.... 这里删除v中的许多元素
vector<type>(v).swap(v);
//此时v的容量已经尽可能的符合其当前包含的元素数量
//对于string则可能像下面这样
string(s).swap(s);
即先创建一个临时拷贝与原先的vector一致,值得注意的是,此时的拷贝其容量是尽可能小的符合所需数据的。紧接着将该拷贝与原先的vector v进行交换。执行交换后,临时变量会被销毁,内存得到释放。此时的v即为原先的临时拷贝,而交换后的临时拷贝则为容量非常大的vector(不过已经被销毁)。如果要全部清除,则可以和一个空容器临时对象交换。
问题2:、vector、deque、map、set、list等容器迭代器的正确使用方式,尤其在erase方法调用时。
STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector、deque(需要再次确定));另一类是以不连续的节点形式存储的容器(如:list、set、map)。在使用erase方法来删除元素时,需要注意一些问题。
在使用list、set或map遍历删除某些元素时可以这样使用:
方法一:
std::list<int> List;
std::list<int>::iterator itList;
for( itList =List.begin(); itList != List.end(); )
{
if( WillDelete( *itList))
{
itList = List.erase( itList);
}
else
itList++;
}
方法二:
std::list<int> List;
std::list<int>::iterator itList;
for( itList =List.begin(); itList != List.end(); )
{
if(WillDelete( *itList) )
{
List.erase( itList++); // 删除的是临时对象,迭代器已经后移
}
else
itList++;
}
错误使用方法一:
for( itList =List.begin(); itList != List.end(); itList++)
{
if( WillDelete( *itList) )
{
List.erase( itList); // 迭代器失效,不能进行++操作定位到下一个元素。
}
}
错误使用方法二:
for( itList =List.begin(); itList != List.end(); )
{
if(WillDelete( *itList) )
{
List.erase(++itList); // 迭代器先移,删除后失效
}
else
itList++;
}
vector、deque只能使用第一种方法遍历删除。顺序容器删除元素后会将后面的元素前移。
问题三:vector迭代器在插入或删除元素时为什么会有失效的情况?
当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原空间的迭代器就成了类似于“悬垂指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。
迭代器失效的类型:
1. 由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。
2. 由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。
vector迭代器的几种失效的情况:
1. 当插入(push_back)一个元素后,end操作返回的迭代器肯定失效。
2. 当插入(push_back)一个元素后,capacity返回值与没有插入元素之前相比有改变,则需要重新加载整个容器,此时first和end操作返回的迭代器都会失效。
3. 当进行删除操作(erase,pop_back)后,指向删除点的迭代器全部失效;指向删除点后面的元素的迭代器也将全部失效。
问题四:vector、map、set、list、deque容器的底层实现机制。
vector封装了数组,连续存储,空间不足时重新分配内存。
map、set 封装了红黑树
list封装了双向链表
deque封装双端队列
问题五:unsortedmap。
map是基于红黑树实现的,可以快速查找一个元素是否存在,是关系型容器map中的元素是按照健,有序的。unordered_map是基于hash实现的。unordered_map的插入、删除、查找性能都优于hash_map优于map,所以首选为unordered_map。它的缺陷是元素是无序的,当使用时候需要元素是有序的时候,不可以用它。