1.什么是C++STL?

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

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

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的底层原理

(1)vector的底层原理

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

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

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

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

(2)vector中的reserve和resize的区别

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

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

(3)vector中的size和capacity的区别

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

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

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

(5)vector迭代器失效的情况

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

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

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

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

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

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

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

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

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

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

(8)vector的常用函数

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 底层原理及其相关面试题

(1)list的底层原理

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

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

(2)list的常用函数

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。

(3)deque的常用函数

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 底层原理及其相关面试题

(1)map 、set、multiset、multimap的底层原理

map 、set、multiset、multimap的底层实现都是红黑树,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存在。

(2)map 、set、multiset、multimap的特点

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

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

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

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

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

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

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

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

(5)map 、set、multiset、multimap的常用函数

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_map、unordered_set 底层原理及其相关面试题

(1)unordered_map、unordered_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;}}}

(3)unordered_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 的构造速度较慢。

(4)unordered_map、unordered_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 + n、it – n、it += n、 it -= n、it1 – it2和it[n]等操作。

3、迭代器失效的问题

(1)插入操作

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

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

对于list和forward_list,所有的iterator,pointer和refercnce有效。

(2)删除操作

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

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

对于list和forward_list,所有的iterator,pointer和refercnce有效。

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

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

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

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

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

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

最后,vector的resize(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) 顺序容器(序列式容器,比如vector、deque)

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

It = c.erase(it);

2) 关联容器(关联式容器,比如map、set、multimap、multiset等)

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

c.erase(it++)

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

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

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

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

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

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

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

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

19. STL内存优化?

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

(1) 第一级配置器:

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

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

(2) 第二级配置器:

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

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

如果分配的区块小于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添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移到新的空间。

C++STL面试详解相关推荐

  1. C++中的STL算法详解

    1.STL算法详解 STL提供能在各种容器中通用的算法(大约有70种),如插入.删除.查找.排序等.算法就是函数模板,算法通过迭代器来操纵容器中的元素.许多算法操作的是容器上的一个区间(也可以是整个容 ...

  2. 面试详解之Java8为什么用红黑树来实现HashMap

    面试详解之Java8为什么用红黑树来实现HashMap 文章目录 面试详解之Java8为什么用红黑树来实现HashMap 一.背景简介 二.为什么要用红黑树 2.1 红黑树概述 2.2 红黑树性质 2 ...

  3. Java面试详解(2020版):500+ 面试题和核心知识点详解

    与其在网上拼命的找面试题,不如加入我们畅快的阅读. 为了写好这些面试题,我先后拜访了一二十家互联网公司,与不同的面试官和面试者进行面对面探讨,深入了解了企业对于面试者的要求和常见的 Java 面试题型 ...

  4. 2019全网最全面试详解.

    前言 在投递简历之前,就是所谓的寒冬将至,开个年会都是守望寒冬,然后我身边的准备跳槽的大佬们,都是有几分凉意,不过我还好,总感觉一个人吃饱,全家不饿,?O(∩_∩)O哈!没想那么多,直接就全身投入,找 ...

  5. (建议收藏)万字长文,帮你一招搞定产品经理面试-详解产品经理面试大全

    ​ 前言 产品经理这个岗位由于其特殊性,在面试当中往往不能够以技术性的题目来衡量,也因其涉猎的范围广泛,所以在准备面试时必须面面俱到,才能够胸有成竹. 博主从个人方面.公司方面.项目方面.产品基础知识 ...

  6. java面试详解-总有你能碰得到的

    1.  hibernate中离线查询去除重复项怎么加条件? dc.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 2.  http协议及端口, ...

  7. 京东面试详解(渣渣吐血整理版)

    面试题链接:https://www.nowcoder.com/discuss/91413 3.java与c++的区别,知道多少答多少 JAVA中有异常机制,C++没有 C++有指针,java没有 C+ ...

  8. 【详解】Linux面试详解

    目录 1 Linux 概述 1.1 什么是Linux 1.2 Unix和Linux有什么区别? 1.3 什么是 Linux 内核? 1.4 Linux的基本组件是什么? 1.5 Linux 的体系结构 ...

  9. redis 面试详解

    Linux版本Redis的安装 1.目标 掌握Redis的安装 2.讲解 在Linux虚拟机中安装c++环境(C的编译运行环境) yum -y install gcc-c++ 下载Redis 上传到L ...

最新文章

  1. 细胞因子风暴与新冠肺炎
  2. 互联网产品设计的“马化腾法则”
  3. Android官方文章翻译之管理设备苏醒状态(Managing Device Awake State)(二)
  4. 类与类之间关系,用C#和JavaScript体现
  5. ubuntu16.04下wifi上网速度很慢的解决方案
  6. 项目升级-加密的参数传递到后台然后解密(相当于重新封装下request)
  7. URAL 1346. Intervals of Monotonicity(DP)
  8. Pulsar较Kafka的优势
  9. HDU 1260: Tickets
  10. 2.4.1 ALU-串行加法器和并行加法器
  11. python读取nc文件并转换成csv_python - 在python 3中读取Crystal Report .rpt文件并将其转换为.csv或.xlsx - 堆栈内存溢出...
  12. “挖矿2.0”:资本方不是只能割区块链的韭菜
  13. Vim常用操作,normal模式、insert模式和visual模式
  14. 点星PBX(DotAsterisk)和讯时MX100G数字中继网关外呼去掉本地被叫号码区号的方法
  15. protractor测试框架6----报告、公共方法、公共变量
  16. 丢人了,我都不知道4D啊
  17. 人在旅途——》张家界之旅:20190422
  18. VC 2017 redist 包到最后提示安装失败,0x80240037
  19. 中国古代兵器与兵书·铜兵生辉
  20. day03_控制语句

热门文章

  1. PTA 名人堂与代金券
  2. 编程小知识:文件扩展名的作用是什么?通俗易懂的文件扩展名详解
  3. 表面粗糙度的基本评定参数是_表面粗糙度最常用评定参数是什么?
  4. 2019 年 2 月份文章汇总
  5. asm.js 和 WebAssembly 有什么区别?
  6. HTML5/HTML中input标签用法解读
  7. 黑马程序员——Set集合概述及特点
  8. JavaScript中的多种进制与进制转换
  9. tigergraph_TigerGraph启动图形数据库即服务
  10. 一文读懂cpu cache