这个系列我们主要来复习C++ STL相关的基础面试题,这些是C++基础面试中经常会问到的一些基础知识,每个问题我都整理了完整的参考答案,希望对各位面试和学习有所帮助。
C++ STL高频面试题[1-10]
1.请解释vector容器和它的特点。
在C++中,vector
是标准模板库(STL)的一部分,它是一个动态数组。与普通数组相比,它的大小可以在运行时动态改变。下面是vector
的一些主要特点和应用场景:
-
动态大小:与传统的数组不同,
vector
可以根据需要动态地扩展或缩减大小。这意味着你不需要事先知道数据的数量。 -
随机访问:就像数组一样,
vector
支持随机访问,这意味着你可以通过索引直接访问任何元素,访问时间是常数时间复杂度(O(1))。 -
内存管理:
vector
在内部管理其存储的内存。当元素被添加到vector
中,并且当前分配的内存不足以容纳它们时,它会自动重新分配更多的内存。 -
灵活性:你可以在
vector
的末尾添加或删除元素,而且效率很高。但在中间或开始位置插入或删除元素可能会比较慢,因为这可能需要移动现有的元素。
应用场景
-
动态数据集合:当你需要一个可以根据数据量动态调整大小的数组时,
vector
是一个很好的选择。例如,处理用户输入的数据集,其中输入数量事先未知。 -
需要快速访问的数据:由于
vector
支持随机访问,它非常适合于需要频繁读取元素的情况,比如查找或排序算法中。 -
性能敏感的应用:由于其元素紧密排列在连续的内存块中,
vector
通常提供高效的内存访问性能,适合用于性能敏感的应用。
总之,vector
是一个非常灵活且强大的容器,适合用于多种不同的编程场景。在实际应用中,选择正确的数据结构往往是优化程序性能的关键。
2.vector如何保证元素的连续存储?
vector
在 C++ STL 中保证元素连续存储的方式主要体现在它的内部实现上。具体来说,vector
使用动态分配的数组来存储其元素。这意味着在内存中,vector
的所有元素都被放置在一个连续的内存块中。以下是这种实现的几个关键点:
-
动态数组:
vector
的底层是一个动态数组。当创建一个vector
时,它会在堆上分配一块连续的内存来存储元素。 -
自动扩容:当向
vector
添加元素,而当前的内存空间不足以容纳更多元素时,vector
会自动进行扩容。这个过程包括分配一个更大的内存块、将现有元素复制到新的内存块中,并释放旧的内存块。 -
内存管理策略:
vector
通常使用“倍增”策略来扩容,即每次扩容时将容量增加到当前的两倍(或者按照特定的增长因子增加)。这样做可以平衡内存使用和性能,尽管可能会导致一定程度的内存浪费。 -
连续性的好处:由于所有元素都存储在连续的内存块中,
vector
能够提供快速的随机访问。这对于需要经常访问元素的场景特别有用,例如在循环或算法中。
应用场景示例
- 图形处理:在处理图像或图形时,像素或顶点数据可以存储在
vector
中,以利用其快速随机访问的优势。 - 科学计算:在科学计算中,大量数值数据(如矩阵的元素)通常需要连续存储,以便高效处理。
连续存储的设计使得 vector
在很多情况下都是一个高效且灵活的选择。
3.当vector空间不足时,如何扩容?
当 vector
的空间不足以容纳更多元素时,它会进行扩容操作以提供更多的存储空间。这个过程涉及以下步骤:
-
确定新容量:首先,
vector
需要确定新的容量。这通常是当前容量的两倍(或其他预定义的增长因子)。这种倍增策略是为了在扩容次数和每次扩容的成本之间找到平衡。 -
分配新内存:接着,
vector
会在堆上分配一块新的、更大的连续内存空间来存放元素。 -
复制元素:将现有的所有元素从旧内存区域复制到新分配的内存区域。这一步通常使用拷贝构造函数或移动构造函数(如果元素类型支持移动语义)。
-
释放旧内存:一旦所有元素都被成功复制到新内存区域,
vector
会释放原来的内存空间。 -
更新内部指针:最后,
vector
更新其内部数据结构,如指向元素数组的指针、大小和容量。
扩容的影响和考虑因素
-
性能成本:扩容是一个相对昂贵的操作,因为它涉及到内存分配和元素的复制或移动。这就是为什么合理选择初始容量或使用
reserve()
方法预留足够空间可以提高性能。 -
迭代器失效:扩容会导致之前所有指向
vector
元素的迭代器、指针和引用失效,因为元素已经被移动到了新的内存位置。
应用场景示例
-
数据收集:在不断收集数据的应用场景中(如日志记录或实时数据采集),
vector
可以动态扩容以应对数据量的不断增长。 -
动态数组功能:在需要动态数组功能的场景中,如游戏开发中的动态实体列表,
vector
提供了自动扩容的便利。
总的来说,vector
的自动扩容机制使其成为一个非常灵活和强大的容器,适用于多种需要动态数组功能的场景。
4.vector的push_back和emplace_back有什么区别?
vector
的 push_back
和 emplace_back
函数都是用来在 vector
的末尾添加新元素的,但它们之间有几个关键的区别:
-
构造方式:
push_back
函数会复制或移动已经构造好的对象到vector
的末尾。emplace_back
函数则是直接在vector
的末尾构造新元素,它接受的是构造函数的参数,而不是对象本身。
-
性能:
- 使用
push_back
时,如果传入的是一个临时对象,它首先会被构造,然后再被复制或移动到vector
中(C++11起,会尝试使用移动构造减少开销)。 emplace_back
则可以避免这些额外的复制或移动操作,因为它直接在容器的内存中构造对象,从而可能提供更好的性能。
- 使用
-
例子:
- 使用
push_back
添加一个复杂对象时:myVector.push_back(MyClass(a, b, c));
这里a, b, c
是传递给MyClass
构造函数的参数,首先在外部构造一个临时的MyClass
对象,然后将其添加到vector
。 - 使用
emplace_back
相同的操作:myVector.emplace_back(a, b, c);
这里直接将参数a, b, c
- 使用