C++STL面试详解

1.什么是C++STL?

C++ STL从广义来讲包括了三类:算法,容器和迭代器。

  • 算法包括排序,复制等常用算法,以及不同容器特定的算法。
  • 容器就是数据的存放形式,包括序列式容器和关联式容器,序列式容器就是listvector等,关联式容器就是setmap等。
  • 迭代器就是在不暴露容器内部结构的情况下对容器的遍历。

2.什么时候需要用hash_map?

总体来说,hash_map 查找速度会比 map 快,而且查找速度基本和数据数据量大小无关,属于常数级别; map 的查找速度是 log(n) 级别。

并不一定常数就比 log(n) 小,hash 还有 hash 函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑 hash_map。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map 可能会让你陷入尴尬,特别是当你的 hash_map 对象特别多时,你就更无法控制了。而且 hash_map 的构造速度较慢。

现在知道如何选择了吗?权衡三个因素: 查找速度, 数据量, 内存使用

3.STL中的hashtable的底层实现?

STL中的hashtable使用的是开链法解决hash冲突问题,如下图所示。

hashtable中的bucket所维护的list既不是list也不是slist,而是其自己定义的由hashtable_node数据结构组成的linked-list,而bucket聚合体本身使用vector进行存储。hashtable的迭代器只提供前进操作,不提供后退操作

hashtable设计bucket的数量上,其内置了28个质数[53, 97, 193,…,429496729],在创建hashtable时,会根据存入的元素个数选择大于等于元素个数的质数作为hashtable的容量(vector的长度),其中每个bucket所维护的linked-list长度也等于hashtable的容量。如果插入hashtable的元素个数超过了bucket的容量,就要进行重建table操作,即找出下一个质数,创建新的buckets vector,重新计算元素在新hashtable的位置。

4.vector的底层原理

1vector的底层原理

vector底层是一个动态数组,包含三个迭代器,startfinish之间是已经被使用的空间范围,end_of_storage是整块连续空间包括备用空间的尾部。

当空间不够装下数据(vec.push_back(val))时,会自动申请另一片更大的空间(1.5倍或者2倍),然后把原来的数据拷贝到新的内存空间,接着释放原来的那片空间【vector内存增长机制】。

当释放或者删除(vec.clear())里面的数据时,其存储空间不释放,仅仅是清空了里面的数据。

因此,对vector的任何操作一旦引起了空间的重新配置,指向原vector的所有迭代器会都失效了。

2vector中的reserveresize的区别

reserve是直接扩充到已经确定的大小,可以减少多次开辟、释放空间的问题(优化push_back),就可以提高效率,其次还可以减少多次要拷贝数据的问题。reserve只是保证vector中的空间大小(capacity)最少达到参数所指定的大小nreserve()只有一个参数。

resize()可以改变有效空间的大小,也有改变默认值的功能。capacity的大小也会随着改变。resize()可以有多个参数。

3vector中的sizecapacity的区别

size表示当前vector中有多少个元素(finish – start),而capacity函数则表示它已经分配的内存中可以容纳多少元素(end_of_storage – start)。

4vector的元素类型可以是引用吗?

vector的底层实现要求连续的对象排列,引用并非对象,没有实际地址,因此vector的元素类型不能是引用。

5vector迭代器失效的情况

当插入一个元素到vector中,由于引起了内存重新分配,所以指向原内存的迭代器全部失效。

当删除容器中一个元素后,该迭代器所指向的元素已经被删除,那么也造成迭代器失效。erase方法会返回下一个有效的迭代器,所以当我们要删除某个元素时,需要it=vec.erase(it);

6)正确释放vector的内存(clear(), swap(), shrink_to_fit())

vec.clear():清空内容,但是不释放内存。

vector().swap(vec):清空内容,且释放内存,想得到一个全新的vector

vec.shrink_to_fit():请求容器降低其capacitysize匹配。

vec.clear();vec.shrink_to_fit();:清空内容,且释放内存。

7vector 扩容为什么要以1.5倍或者2倍扩容?

根据查阅的资料显示,考虑可能产生的堆空间浪费,成倍增长倍数不能太大,使用较为广泛的扩容方式有两种,以2倍的方式扩容,或者以1.5倍的方式扩容。

2倍的方式扩容,导致下一次申请的内存必然大于之前分配内存的总和,导致之前分配的内存不能再被使用,所以最好倍增长因子设置为(1,2)之间:

8vector的常用函数

vector<int>vec(10,100);创建10个元素,每个元素值为100vec.resize(r,vector<int>(c,0));二维数组初始化reverse(vec.begin(),vec.end())将元素翻转sort(vec.begin(),vec.end());排序,默认升序排列vec.push_back(val);尾部插入数字vec.size();向量大小find.vec.begin(),vec.end(),1);查找元素
iterator =vec.erase(iterator)删除元素

5.list 底层原理及其相关面试题

1list的底层原理

list的底层是一个双向链表,以结点为单位存放数据,结点的地址在内存中不一定连续,每次插入或删除一个元素,就配置或释放一个元素空间。

list不支持随机存取,适合需要大量的插入和删除,而不关心随即存取的应用场景。

2list的常用函数

list.push_back(elem)在尾部加入一个数据
list.pop_back()删除尾部数据
list.push_front(elem)在头部插入一个数据list.pop_front()删除头部数据list.size()返回容器中实际数据的个数list.sort() 排序,默认由小到大list.unique()移除数值相同的连续元素list.back() 取尾部迭代器list.erase(iterator)删除一个元素,参数是迭代器,返回的是删除迭代器的下一个位置

6.deque底层原理面试题

deque的底层原理

deque是一个双向开口的连续线性空间(双端队列),在头尾两端进行元素的插入跟删除操作都有理想的时间复杂度。

2)什么情况下用vector,什么情况下用list,什么情况下用deque

vector可以随机存储元素(即可以通过公式直接计算出元素地址,而不需要挨个查找),但在非尾部插入删除数据时,效率很低,适合对象简单,对象数量变化不大,随机访问频繁。除非必要,我们尽可能选择使用vector而非deque,因为deque的迭代器比vector迭代器复杂很多。

list不支持随机存储,适用于对象大,对象数量变化频繁,插入和删除频繁,比如写多读少的场景。

需要从首尾两端进行插入或删除操作的时候需要选择deque

3deque的常用函数

deque.push_back(elem)在尾部加入一个数据。deque.pop_back()删除尾部数据。deque.push_front(elem)在头部插入一个数据。deque.pop_front()删除头部数据。deque.size() 返回容器中实际数据的个数。deque.at(idx)传回索引idx所指的数据,如果idx越界,抛出out_of_range。

7.vector怎么分配空间?

由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear()vector所占用的内存空间依然如故,无法保证内存的回收。

如果需要空间动态缩小,可以考虑使用deque。如果vector,可以用swap()来帮助你释放内存。

vector(Vec).swap(Vec);//将Vec的内存清除; 
vector().swap(Vec);//清空Vec的内存;

8.如何在共享内存下使用STL标准库?

1) 想像一下把STL容器,例如map, vector, list等等,放入共享内存中,IPC一旦有了这些强大的通用数据结构做辅助,无疑进程间通信的能力一下子强大了很多。

我们没必要再为共享内存设计其他额外的数据结构,另外,STL的高度可扩展性将为IPC所驱使。STL容器被良好的封装,默认情况下有它们自己的内存管理方案。

当一个元素被插入到一个STL列表(list)中时,列表容器自动为其分配内存,保存数据。考虑到要将STL容器放到共享内存中,而容器却自己在堆上分配内存。

一个最笨拙的办法是在堆上构造STL容器,然后把容器复制到共享内存,并且确保所有容器的内部分配的内存指向共享内存中的相应区域,这基本是个不可能完成的任务。

2) 假设进程A在共享内存中放入了数个容器,进程B如何找到这些容器呢?

一个方法就是进程A把容器放在共享内存中的确定地址上(fixed offsets),则进程B可以从该已知地址上获取容器。另外一个改进点的办法是,进程A先在共享内存某块确定地址上放置一个map容器,然后进程A再创建其他容器,然后给其取个名字和地址一并保存到这个map容器里。

进程B知道如何获取该保存了地址映射的map容器,然后同样再根据名字取得其他容器的地址。

9.map的插入方式有几种?

1) 用insert函数插入pair数据,

mapStudent.insert(pair<int, string>(1, "student_one"));

2) 用insert函数插入value_type数据

mapStudent.insert(map<int, string>::value_type (1, "student_one"));

3) 在insert函数中使用make_pair()函数

mapStudent.insert(make_pair(1, "student_one"));

4) 用数组方式插入数据

mapStudent[1] = "student_one";

10. map 、set、multiset、multimap 底层原理及其相关面试题

1map setmultisetmultimap的底层原理

map setmultisetmultimap的底层实现都是红黑树epoll模型的底层数据结构也是红黑树,linux系统中CFS进程调度算法,也用到红黑树。

红黑树的特性:

  1. 每个结点或是红色或是黑色;
  2. 根结点是黑色;
  3. 每个叶结点是黑的;
  4. 如果一个结点是红的,则它的两个儿子均是黑色;
  5. 每个结点到其子孙结点的所有路径上包含相同数目的黑色结点。

对于STL里的map容器,count方法与find方法,都可以用来判断一个key是否出现,mp.count(key) > 0统计的是key出现的次数,因此只能为0/1,而mp.find(key) != mp.end()则表示key存在。

2map setmultisetmultimap的特点

setmultiset会根据特定的排序准则自动将元素排序,set中元素不允许重复,multiset可以重复。

mapmultimapkeyvalue组成的pair作为元素,根据key的排序准则自动将元素排序(因为红黑树也是二叉搜索树,所以map默认是按key排序的),map中元素的key不允许重复,multimap可以重复。

mapset的增删改查速度为都是logn,是比较高效的。

3)为何mapset的插入删除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不会失效?

因为存储的是结点,不需要内存拷贝和内存移动。

因为插入操作只是结点指针换来换去,结点内存没有改变。而iterator就像指向结点的指针,内存没变,指向内存的指针也不会变。

4)为何mapset不能像vector一样有个reserve函数来预分配数据?

因为在mapset内部存储的已经不是元素本身了,而是包含元素的结点。也就是说map内部使用的Alloc并不是map<Key, Data, Compare, Alloc>声明的时候从参数中传入的Alloc

5map setmultisetmultimap的常用函数

it map.begin() 返回指向容器起始位置的迭代器(iterator) 
it map.end()         返回指向容器末尾位置的迭代器
bool map.empty()      若容器为空,则返回true,否则false
it map.find(k)寻找键值为k的元素,并用返回其地址
int map.size()返回map中已存在元素的数量
map.insert({int,string})插入元素for(itor = map.begin(); itor != map.end();){   
        if (itor->second =="target")
                 map.erase(itor++); // erase之后,令当前迭代器指向其后继。          else        
                ++itor;
}

 11. unordered_map、unordered_set 底层原理及其相关面试题

unordered_mapunordered_set 底层原理及其相关面试题

1unordered_mapunordered_set的底层原理

unordered_map的底层是一个防冗余的哈希表(采用除留余数法)。哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,时间复杂度为O(1);而代价仅仅是消耗比较多的内存。

使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数(一般使用除留取余法),也叫做散列函数),使得每个元素的key都与一个函数值(即数组下标,hash值)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照key为每一个元素分类,然后将这个元素存储在相应所对应的地方,称为桶。

但是,不能够保证每个元素的key与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了冲突,换句话说,就是把不同的元素分在了相同的之中。 一般可采用拉链法解决冲突:

2)哈希表的实现

#include <iostream>
#include <vector>
#include <list>
#include <random>
#include <ctime>
using namespace std;
const int hashsize =12;//定一个节点的结构体
template<typename T,typename U>
struct HashNode{
  T _key; U _value;};
//使用拉链法实现哈希表类
template<typename T,typename U>
class HashTable{
public:        HashTable();       
        vec(hashsize); {}//类中的容器需要通过构造函数来指定大小             ~HashTable(){}
bool insert_data(const T &key, const U &value);  
int hash(const T &key);bool hash_find(const T &key);private:
    vector<list<HashNode<T, U>>> vec;//将节点存储到容器中
};
//哈希函数(除留取余)
template<typenameT, typename U>
int HashTable<T, U>::hash(const T &key)
{   
return key % 13;
}
//哈希查找
template<typename T, typename U>
bool HashTable<T, U>::hash_find(const T &key)
{    int index =hash(key);//计算哈希值  
    for(auto it = vec[index].begin(); it != vec[index].end(); ++it){               if(key == it -> _key)//如果找到则打印其关联值        {
                  cout<< it->_value << endl;//输出数据前应该确认是否包含相应类型                   return true;        }       
 }      
 return false;}//插入数据
template<typename T,typename U>
boolHashTable<T, U>::insert_data(const T &key, const U &value){//初始化数据
HashNode<T, U> node;
node._key = key;node._value = value;for(int i =0; i< hashsize; ++i){        
        
    if(i ==hash(key))//如果溢出则把相应的键值添加进链表        
    {
            vec[i].push_back(node);
            return true;
     }}}

3unordered_map map的区别?使用场景?

构造函数:unordered_map 需要hash函数,等于函数;map只需要比较函数(小于函数).

存储结构:unordered_map 采用hash表存储,map一般采用红黑树(RB Tree) 实现。因此其memory数据结构是不一样的。

总体来说,unordered_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;map的查找速度是log(n)级别。并不一定常数就比log(n)小,hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑unordered_map 。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,unordered_map 可能会让你陷入尴尬,特别是当你的unordered_map 对象特别多时,你就更无法控制了,而且unordered_map 的构造速度较慢。

4unordered_mapunordered_set的常用函数

unordered_map.begin()返回指向容器起始位置的迭代器(iterator)unordered_map.end()返回指向容器末尾位置的迭代器unordered_map.cbegin()返回指向容器起始位置的常迭代器(const_iterator) unordered_map.cend()返回指向容器末尾位置的常迭代器unordered_map.size()返回有效元素个数 unordered_map.insert(key)插入元素unordered_map.find(key)查找元素,返回迭代器unordered_map.count(key) 返回匹配给定主键的元素的个数

12. 迭代器的底层机制和失效的问题

1、迭代器的底层原理

迭代器是连接容器和算法的一种重要桥梁,通过迭代器可以在不了解容器内部原理的情况下遍历容器。它的底层实现包含两个重要的部分:萃取技术和模板偏特化。

萃取技术(traits)可以进行类型推导,根据不同类型可以执行不同的处理流程,比如容器是vector,那么traits必须推导出其迭代器类型为随机访问迭代器,而list则为双向迭代器。

例如STL算法库中的distance函数,distance函数接受两个迭代器参数,然后计算他们两者之间的距离。显然对于不同的迭代器计算效率差别很大。比如对于vector容器来说,由于内存是连续分配的,因此指针直接相减即可获得两者的距离;而list容器是链式表,内存一般都不是连续分配,因此只能通过一级一级调用next()或其他函数,每调用一次再判断迭代器是否相等来计算距离。vector迭代器计算distance的效率为O(1),list则为O(n),n为距离的大小。

使用萃取技术(traits)进行类型推导的过程中会使用到模板偏特化。模板偏特化可以用来推导参数,如果我们自定义了多个类型,除非我们把这些自定义类型的特化版本写出来,否则我们只能判断他们是内置类型,并不能判断他们具体属于是个类型。

2、迭代器的种类

输入迭代器:是只读迭代器,在每个被遍历的位置上只能读取一次。例如上面find函数参数就是输入迭代器。

输出迭代器:是只写迭代器,在每个被遍历的位置上只能被写一次。

前向迭代器:兼具输入和输出迭代器的能力,但是它可以对同一个位置重复进行读和写。但它不支持operator–,所以只能向前移动。

双向迭代器:很像前向迭代器,只是它向后移动和向前移动同样容易。

随机访问迭代器:有双向迭代器的所有功能。而且,它还提供了迭代器算术,即在一步内可以向前或向后跳跃任意位置, 包含指针的所有操作,可进行随机访问,随意移动指定的步数。支持前面四种Iterator的所有操作,并另外支持it + nit – nit += n it -= nit1 – it2it[n]等操作。

3、迭代器失效的问题

1)插入操作

对于vectorstring,如果容器内存被重新分配,iterators,pointers,references失效;如果没有重新分配,那么插入点之前的iterator有效,插入点之后的iterator失效;

对于deque,如果插入点位于除frontback的其它位置,iterators,pointers,references失效;当我们插入元素到frontback时,deque的迭代器失效,但referencepointers有效;

对于listforward_list,所有的iterator,pointerrefercnce有效。

2)删除操作

对于vectorstring,删除点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的;

对于deque,如果删除点位于除frontback的其它位置,iterators,pointers,references失效;当我们插入元素到frontback时,off-the-end失效,其他的iterators,pointers,references有效;

对于listforward_list,所有的iterator,pointerrefercnce有效。

对于关联容器map来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用,否则会导致程序无定义的行为。


 13. 为什么vector的插入操作可能会导致迭代器失效?

vector动态增加大小时,并不是在原空间后增加新的空间,而是以原大小的两倍在另外配置一片较大的新空间,然后将内容拷贝过来,并释放原来的空间。由于操作改变了空间,所以迭代器失效。

14. vector的reserve()和resize()方法之间有什么区别?

首先,vector的容量capacity()是指在不分配更多内存的情况下可以保存的最多元素个数,而vector的大小size()是指实际包含的元素个数;

其次,vectorreserve(n)方法只改变vector的容量,如果当前容量小于n,则重新分配内存空间,调整容量为n;如果当前容量大于等于n,则无操作;

最后,vectorresize(n)方法改变vector的大小,如果当前容量小于n,则调整容量为n,同时将其全部元素填充为初始值;如果当前容量大于等于n,则不调整容量,只将其前n个元素填充为初始值。

 15. 标准库中有哪些容器?分别有什么特点?

    标准库中的容器主要分为三类:顺序容器、关联容器、容器适配器。

  • 顺序容器包括五种类型:
    • array<T, N>数组:固定大小数组,支持快速随机访问,但不能插入或删除元素;
    • vector<T>动态数组:支持快速随机访问,尾位插入和删除的速度很快;
    • deque<T>双向队列:支持快速随机访问,首尾位置插入和删除的速度很快;(可以看作是vector的增强版,与vector相比,可以快速地在首位插入和删除元素)
    • list<T>双向链表:只支持双向顺序访问,任何位置插入和删除的速度都很快;
    • forward_list<T>单向链表:只支持单向顺序访问,任何位置插入和删除的速度都很快。
  • 关联容器包含两种类型:
    • map容器:
    • map<K, T>关联数组:用于保存关键字-值对;
    • multimap<K, T>:关键字可重复出现的map
    • unordered_map<K, T>:用哈希函数组织的map
    • unordered_multimap<K, T>:关键词可重复出现的unordered_map
    • set容器:
    • set<T>:只保存关键字;
    • multiset<T>:关键字可重复出现的set
    • unordered_set<T>:用哈希函数组织的set
    • unordered_multiset<T>:关键词可重复出现的unordered_set
  • 容器适配器包含三种类型:
    • stack<T>栈、queue<T>队列、priority_queue<T>优先队列。


16. 容器内部删除一个元素

1) 顺序容器(序列式容器,比如vectordeque

erase迭代器不仅使所指向被删除的迭代器失效,而且使被删元素之后的所有迭代器失效(list除外),所以不能使用erase(it++)的方式,但是erase的返回值是下一个有效迭代器;

It = c.erase(it);

2) 关联容器(关联式容器,比如mapsetmultimapmultiset)

erase迭代器只是被删除元素的迭代器失效,但是返回值是void,所以要采用erase(it++)的方式删除迭代器;

c.erase(it++)

17. vector越界访问下标,map越界访问下标?vector删除元素时会不会释放空间?

    1) 通过下标访问vector中的元素时会做边界检查,但该处的实现方式要看具体IDE,不同IDE的实现方式不一样,确保不可访问越界地址。

2) map的下标运算符[]的作用是:将key作为下标去执行查找,并返回相应的值;如果不存在这个key,就将一个具有该keyvalue的某人值插入这个map

3) erase()函数,只能删除内容,不能改变容量大小;

erase成员函数,它删除了itVect迭代器指向的元素,并且返回要被删除的itVect之后的迭代器,迭代器相当于一个智能指针;clear()函数,只能清空内容,不能改变容量大小;如果要想在删除内容的同时释放内存,那么你可以选择deque容器。


18. map中[ ]与find的区别?

    1) map的下标运算符[ ]的作用是:将关键码作为下标去执行查找,并返回对应的值;如果不存在这个关键码,就将一个具有该关键码和值类型的默认值的项插入这个map

2) mapfind函数:用关键码执行查找,找到了返回该位置的迭代器;如果不存在这个关键码,就返回尾迭代器。

19. STL内存优化?

STL内存管理使用二级内存配置器。

(1) 第一级配置器:

第一级配置器以malloc()free()realloc()C函数执行实际的内存配置、释放、重新配置等操作,并且能在内存需求不被满足的时候,调用一个指定的函数。一级空间配置器分配的是大于128字节的空间,如果分配不成功,调用句柄释放一部分内存,如果还不能分配成功,抛出异常。

第一级配置器只是对malloc函数和free函数的简单封装,在allocate内调用malloc,在deallocate内调用free。同时第一级配置器的oom_malloc函数,用来处理malloc失败的情况。

(2) 第二级配置器:

第一级配置器直接调用mallocfree带来了几个问题:

  • 内存分配/释放的效率低
  • 当配置大量的小内存块时,会导致内存碎片比较严重
  • 配置内存时,需要额外的部分空间存储内存块信息,所以配置大量的小内存块时,还会导致额外内存负担

如果分配的区块小于128bytes,则以内存池管理,第二级配置器维护了一个自由链表数组,每次需要分配内存时,直接从相应的链表上取出一个内存节点就完成工作,效率很高

自由链表数组:自由链表数组其实就是个指针数组,数组中的每个指针元素指向一个链表的起始节点。数组大小为16,即维护了16个链表,链表的每个节点就是实际的内存块,相同链表上的内存块大小都相同,不同链表的内存块大小不同,从8一直到128。如下所示,obj为链表上的节点,free_list就是链表数组。

内存分配:allocate函数内先判断要分配的内存大小,若大于128字节,直接调用第一级配置器,否则根据要分配的内存大小从16个链表中选出一个链表,取出该链表的第一个节点。若相应的链表为空,则调用refill函数填充该链表。默认是取出20个数据块。

填充链表 refill:若allocate函数内要取出节点的链表为空,则会调用refill函数填充该链表。refill函数内会先调用chunk_alloc函数从内存池分配一大块内存,该内存大小默认为20个链表节点大小,当内存池的内存也不足时,返回的内存块节点数目会不足20个。接着refill的工作就是将这一大块内存分成20份相同大小的内存块,并将各内存块连接起来形成一个链表。

内存池:chunk_alloc函数内管理了一块内存池,当refill函数要填充链表时,就会调用chunk_alloc函数,从内存池取出相应的内存。

  • chunk_alloc函数内首先判断内存池大小是否足够填充一个有20个节点的链表,若内存池足够大,则直接返回20个内存节点大小的内存块给refill
  • 若内存池大小无法满足20个内存节点的大小,但至少满足1个内存节点,则直接返回相应的内存节点大小的内存块给refill
  • 若内存池连1个内存节点大小的内存块都无法提供,则chunk_alloc函数会将内存池中那一点点的内存大小分配给其他合适的链表,然后去调用malloc函数分配的内存大小为所需的两倍。若malloc成功,则返回相应的内存大小给refill;若malloc失败,会先搜寻其他链表的可用的内存块,添加到内存池,然后递归调用chunk_alloc函数来分配内存,若其他链表也无内存块可用,则只能调用第一级空间配置器。

20. 频繁对vector调用push_back()对性能的影响和原因?

在一个vector的尾部之外的任何位置添加元素,都需要重新移动元素。而且,向一个vector添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移到新的空间。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会编程喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值