C/C++你必须知道的小知识(31)

142 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情

\


1.4.9 说说 STL 中 resize 和 reserve 的区别

参考回答

  1. 首先必须弄清楚两个概念:

    (1)capacity:该值在容器初始化时赋值,指的是容器能够容纳的最大的元素的个数。还不能通过下标等访问,因为此时容器中还没有创建任何对象。

    (2)size:指的是此时容器中实际的元素个数。可以通过下标访问0-(size-1)范围内的对象。

  1. resize和reserve区别主要有以下几点:

    (1)resize既分配了空间,也创建了对象;reserve表示容器预留空间,但并不是真正的创建对象,需要通过insert()或push_back()等创建对象。

    (2)resize既修改capacity大小,也修改size大小;reserve只修改capacity大小,不修改size大小。

    (3)两者的形参个数不一样。 resize带两个参数,一个表示容器大小,一个表示初始值(默认为0);reserve只带一个参数,表示容器预留的大小。

答案解析

​ 问题延伸:

​ resize 和 reserve 既有差别,也有共同点。两个接口的共同点它们都保证了vector的空间大小(capacity)最少达到它的参数所指定的大小。 下面就他们的细节进行分析。

​ 为实现resize的语义,resize接口做了两个保证:

​ (1)保证区间[0, new_size)范围内数据有效,如果下标index在此区间内,vector[indext]是合法的;

​ (2)保证区间[0, new_size)范围以外数据无效,如果下标index在区间外,vector[indext]是非法的。

​ reserve只是保证vector的空间大小(capacity)最少达到它的参数所指定的大小n。在区间[0, n)范围内,如果下标是index,vector[index]这种访问有可能是合法的,也有可能是非法的,视具体情况而定。

​ 以下是两个接口的源代码:

void resize(size_type new_size)

   { 
           resize(new_size, T());
   }
  void resize(size_type new_size, const T& x)
   {
        if (new_size < size()) 
              erase(begin() + new_size, end()); // erase区间范围以外的数据,确保区间以外的数据无效
           else
              insert(end(), new_size - size(), x); // 填补区间范围内空缺的数据,确保区间内的数据有效
   }


#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> a;
    cout<<"initial capacity:"<<a.capacity()<<endl;
    cout<<"initial size:"<<a.size()<<endl;

    /*resize改变capacity和size*/
    a.resize(20);
    cout<<"resize capacity:"<<a.capacity()<<endl;
    cout<<"resize size:"<<a.size()<<endl;


    vector<int> b;
     /*reserve改变capacity,不改变resize*/
    b.reserve(100);
    cout<<"reserve capacity:"<<b.capacity()<<endl;
    cout<<"reserve size:"<<b.size()<<endl;
return 0;
}

/*    运行结果:
          initial capacity:0
        initial size:0
        resize capacity:20
        resize size:20
        reserve capacity:100
        reserve size:0
*/    

​ 注意: 如果n大于当前的vector的容量(是容量,并非vector的size),将会引起自动内存分配。所以现有的pointer,references,iterators将会失效。而内存的重新配置会很耗时间。

1.4.10 说说 STL 容器动态链接可能产生的问题?

参考回答

  1. 可能产生 的问题

    ​ 容器是一种动态分配内存空间的一个变量集合类型变量。在一般的程序函数里,局部容器,参数传递容器,参数传递容器的引用,参数传递容器指针都是可以正常运行的,而在动态链接库函数内部使用容器也是没有问题的,但是给动态库函数传递容器的对象本身,则会出现内存堆栈破坏的问题。

  2. 产生问题的原因
    容器和动态链接库相互支持不够好,动态链接库函数中使用容器时,参数中只能传递容器的引用,并且要保证容器的大小不能超出初始大小,否则导致容器自动重新分配,就会出现内存堆栈破坏问题。

1.4.11 说说 map 和 unordered_map 的区别?底层实现

参考回答

​ map和unordered_map的区别在于他们的实现基理不同

  1. map实现机理

    ​ map内部实现了一个红黑树(红黑树是非严格平衡的二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树有自动排序的功能,因此map内部所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找、删除、添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉树(又名二叉查找树、二叉排序树)存储的,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值。使用中序遍历可将键值按照从小到大遍历出来。

  2. unordered_map实现机理

    unordered_map内部实现了一个哈希表(也叫散列表),通过把关键码值映射到Hash表中一个位置来访问记录,查找时间复杂度可达O(1),其中在海量数据处理中有着广泛应用。因此,元素的排列顺序是无序的。