10道C++ STL高频面试题[21-30](附带完整参考答案)

21. lower_bound()和upper_bound()有什么用处?

lower_bound()upper_bound() 是 C++ STL 中用于在已排序的范围内进行二分搜索的两个函数。它们的作用是找到一个范围内不小于(或不大于)某个给定值的第一个元素的位置。这两个函数通常用于有序序列,尤其是在处理有重复元素时非常有用。

lower_bound():

  • 返回一个迭代器,指向在不破坏顺序的情况下,可以插入给定值的第一个位置,而不让任何原有的元素小于给定值。
  • 如果序列中存在与给定值相等的元素,lower_bound() 会返回指向这些元素中第一个的迭代器。
  • 如果所有元素都小于给定值,则返回指向序列尾部的迭代器。

upper_bound():
-返回一个迭代器,指向在不破坏顺序的情况下,可以插入给定值的最后一个位置,而不让任何原有的元素小于或等于给定值。
-如果序列中存在与给定值相等的元素,upper_bound() 会返回指向这些元素中最后一个之后的迭代器。
-如果所有元素都小于或等于给定值,则返回指向序列尾部的迭代器。

应用场景示例

假设你有一个已排序的 vector<int>,其中包含重复元素,并且你想找到一个特定值的范围:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 4, 4, 4, 5, 6};

    // 寻找不小于4的第一个元素的位置
    auto lower = std::lower_bound(v.begin(), v.end(), 4);
    std::cout << "Lower bound for 4 is at index: " << (lower - v.begin()) << std::endl;

    // 寻找大于4的第一个元素的位置
    auto upper = std::upper_bound(v.begin(), v.end(), 4);
    std::cout << "Upper bound for 4 is at index: " << (upper - v.begin()) << std::endl;

    return 0;
}

在这个例子中,lower_bound() 将返回指向第一个 4 的迭代器,而 upper_bound() 将返回指向最后一个 4 之后的位置的迭代器。这样你就可以得到等于 4 的所有元素的范围,即 [lower, upper)。这在统计有序序列中等于某个值的元素数量时非常有用。

22. STL中的allocator有什么作用?

STL(Standard Template Library,标准模板库)中的 allocator 类是用于管理内存分配的。它是一种泛型编程的组成部分,主要用于容器类,如 vectorlist 等,来分配和管理它们的内存。

allocator 的作用主要包括:

  1. 内存分配与回收:allocator 提供了分配和回收对象内存的方法。例如,allocate 方法用于分配内存,而 deallocate 用于释放内存。

  2. 对象构造与析构:除了管理内存,allocator 还可以在分配的内存上构造对象(使用 construct 方法)和析构对象(使用 destroy 方法)。

  3. 类型独立:由于 allocator 是模板化的,它可以用于任何类型的对象,这使得 STL 容器可以存储任何类型的元素。

  4. 性能优化:有些 allocator 实现可能提供优于默认内存分配器的性能。比如,它们可能有特殊的策略来减少内存碎片或提高内存分配效率。

应用场景举例:假设你正在使用一个 std::vector<int>,这个向量在内部会使用 allocator 来分配存储整数的内存空间。当向量需要增长时,allocator 会分配更大的内存区域,并帮助将现有元素移到新的内存位置。

简而言之,allocator 在 STL 中扮演着内存管理者的角色,确保容器能够高效地分配和管理内存。

23.什么是RAII原则,它在STL中如何应用?

RAII(Resource Acquisition Is Initialization,资源获取即初始化)原则是C++中的一种编程理念,旨在通过对象生命周期管理资源,如内存、文件句柄、网络连接等。这个原则的核心思想是,资源的分配(获取)应该在对象的构造函数中完成,而资源的释放(释放)应该在对象的析构函数中完成。

RAII在STL中的应用:

  1. 智能指针:如 std::unique_ptrstd::shared_ptr。这些智能指针在构造时获取资源(例如分配内存),在其析构时自动释放资源。这简化了资源管理,防止了内存泄漏。
  2. 容器:STL容器(如 std::vector, std::map 等)也遵循RAII原则。容器在构造时分配必要的内存资源,并在析构时自动释放这些资源。
  3. 锁:如 std::lock_guardstd::unique_lock。这些用于同步的对象在构造时自动获取锁,并在析构时释放锁,从而避免了死锁和确保了异常安全。

RAII的好处:

  • 异常安全:由于资源释放是自动的,即使发生异常,也能保证资源的正确释放。
  • 内存管理简化:自动管理内存,减少内存泄漏和资源泄漏的风险。
  • 代码清晰:资源的生命周期与对象的生命周期挂钩,使得资源管理更加直观。

应用场景例子:

  • 使用 std::vector 管理一组元素。当这个 vector 对象离开其作用域时,它所管理的内存会被自动释放,无需手动清理。
  • 在一个函数中使用 std::lock_guard 来保护临界区。当 lock_guard 对象被销毁时,它会自动释放锁,即使函数因为异常而提前退出。

RAII原则在C++中非常重要,它通过自动管理资源的生命周期,减轻了程序员的负担,同时提高了代码的可维护性和安全性。

24.什么是智能指针,它有什么作用?

智能指针是C++标准模板库(STL)中的一种类模板,用于管理动态分配的内存,以确保资源的正确释放,防止内存泄漏。在C++中,动态分配内存是通过new操作符完成的,而释放内存则需要使用delete操作符。但在复杂的程序中,确保每次new后都有对应的delete调用是一项挑战,尤其是在出现异常或早期返回时。

智能指针通过封装原始指针,并在其析构函数中自动调用delete,帮助程序员自动管理内存。C++提供了几种类型的智能指针,主要包括:

  1. std::unique_ptr:它是一种独占式智能指针,意味着它对其所管理的对象具有唯一的所有权。一旦unique_ptr被销毁,它所指向的对象也会被删除。它不支持复制,但可以进行移动,从而转移所有权。
  2. std::shared_ptr:这是一种共享所有权的智能指针。多个shared_ptr可以指向同一个对象,内部使用引用计数来追踪有多少个shared_ptr指向同一个对象。当最后一个这样的指针被销毁时,对象才会被删除。
  3. std::weak_ptr:它是shared_ptr的伴侣,提供了一种不控制对象生命周期的智能指针。它主要用于解决shared_ptr可能引起的循环引用问题。

应用场景举例:

  1. unique_ptr:当你在一个函数中创建了一个对象,并且想在函数结束时自动销毁它,可以使用unique_ptr。例如,在一个图形应用中创建一个图像对象,当处理完毕后自动释放。

  2. shared_ptr:在需要多个指针共享同一个对象时,例如在实现一个树结构,多个节点可能共享相同的子节点。

  3. weak_ptr:在创建复杂的数据结构如图或树时,weak_ptr可以用来避免循环引用,从而避免内存泄漏。比如在树的节点中,父节点使用shared_ptr指向子节点,而子节点使用weak_ptr指向父节点。

25.unique_ptr、shared_ptr和weak_ptr有什么区别?

unique_ptrshared_ptrweak_ptr都是C++11引入的智能指针,它们自动管理内存,帮助防止内存泄漏。它们的区别主要在于它们如何管理所指向对象的生命周期和所有权。

  1. unique_ptr
  • 所有权唯一:unique_ptr拥有它所指向的对象,保证同一时间只有一个unique_ptr指向特定对象。
  • 不可复制:unique_ptr不能被复制,避免了不小心产生两个指向同一资源的指针。
  • 可移动:unique_ptr可以被移动,这意味着所有权可以转移给另一个unique_ptr,而原来的unique_ptr会变为空。
  • 用途:当你想要确保一个对象有且只有一个所有者时使用unique_ptr
  1. shared_ptr
  • 共享所有权:shared_ptr允许多个指针共享同一个对象的所有权。
  • 引用计数:shared_ptr使用引用计数机制来跟踪有多少个shared_ptr共享同一个资源。当最后一个shared_ptr被销毁时,对象会被自动删除。
  • 用途:当你想要多个所有者共享同一个对象时,可以使用shared_ptr
  1. weak_ptr
  • 非拥有的观察者:weak_ptr是一种非拥有的智能指针,它指向由shared_ptr管理的对象。
  • 不影响引用计数:weak_ptr不会增加对象的引用计数,这意味着它不会阻止所指向的对象被销毁。
  • 用途:weak_ptr常用于解决shared_ptr相互引用时可能产生的循环引用问题。

应用场景例子:

  • unique_ptr:当你创建一个对象,并且需要确保这个对象在离开作用域时会被自动销毁,同时防止其他对象的访问,可以使用unique_ptr
  • shared_ptr:如果你正在写一个库,其中的对象需要被多个客户端代码共享,那么shared_ptr是一个好选择。
  • weak_ptr:在实现缓存时,可以使用weak_ptr来监控对象是否仍然存在,而不妨碍对象在不再需要时被销毁。

26.在什么情况下会选择使用智能指针?

智能指针通常在以下情况下使用:

  1. 资源管理:当你需要确保在资源(如动态分配的内存)不再需要时能够自动释放时,智能指针是很好的选择。这样可以防止内存泄漏和资源未释放的问题。

  2. 异常安全:在异常可能抛出的代码中,智能指针可以保证在异常发生时资源能够被正确清理。

  3. 共享资源:当资源需要被多个对象共享,并且需要明确资源的所有权和生命周期时,shared_ptr 是理想的选择。

  4. 避免资源泄露:在复杂的函数或程序中,智能指针确保即使在多个返回点或复杂的控制流程中,资源也能被适时释放。

  5. 所有权语义:使用 unique_ptr 表明资源的唯一所有权,而 shared_ptrweak_ptr 则用于实现复杂的所有权关系,如循环引用或临时所有权。

  6. 多线程程序:在多线程环境中,智能指针可以帮助安全地管理资源,防止竞争条件和死锁。

  7. 工厂函数:当你有一个工厂函数需要创建一个对象并返回给调用者时,返回一个智能指针可以保证即使不再需要这个对象时,它也会被自动销毁。

  8. RAII原则:当你想要应用RAII原则以简化资源管理时,智能指针提供了一种简单有效的方式。

  9. 动态多态:当使用动态多态时,使用智能指针可以在不需要类型信息的情况下安全地删除对象。

例子:

  • 在构建复杂数据结构如树或图时,智能指针可以帮助管理节点之间的关系,并在不再需要节点时自动清理它们。
  • 在GUI应用程序中,控件的生命周期可能由用户交互决定,智能指针可以用来管理控件对象,确保它们在不需要时被适当销毁。

27.什么是adapter容器?

在C++ STL中,适配器容器(Container Adapters)是一种特殊的容器,它提供了特定的接口和行为,并在内部使用其他容器作为其底层数据结构。适配器容器通常改变了某个现有容器的接口以满足特定的需求。STL中包含三种适配器容器:

  1. stack
  • 行为:后进先出(LIFO)。
  • 底层容器:默认使用deque,但也可以用listvector
  • 应用场景:用于解决需要后进先出访问元素的问题,如在递归算法、解析表达式和回溯算法中常用。
  1. queue
  • 行为:先进先出(FIFO)。
  • 底层容器:默认使用deque,但也可以用list
  • 应用场景:适用于需要按顺序处理元素的场景,比如任务调度、缓冲处理等。
  1. priority_queue
  • 行为:元素按优先级出列。
  • 底层容器:通常使用vector并配合make_heappush_heappop_heap算法使用。
  • 应用场景:适合于需要快速访问最“重要”元素的场合,比如调度系统中的任务优先级调度、图算法中的最短路径搜索等。

28.priority_queue有什么应用场景?

priority_queue 是 C++ STL 中的一个容器适配器,它提供了严格的顺序概念,确保每次取出的元素都是当前队列中优先级最高的。这种特性使得 priority_queue 在多种场景中非常有用,特别是在需要按特定顺序处理元素的地方。以下是一些 priority_queue 的应用场景:

  1. 任务调度:在操作系统中,任务(进程或线程)可能有不同的优先级,priority_queue 可以用来管理待执行的任务队列,确保优先级高的任务先被执行。

  2. Dijkstra算法:在图形算法中,比如Dijkstra求最短路径算法,priority_queue 可以用来持续追踪下一个最短路径候选节点。

  3. 哈夫曼编码:在构建哈夫曼树进行数据压缩时,priority_queue 用于确保最低频率的节点先被处理。

  4. 数据流的中值查找:在处理数据流时,priority_queue 可以用来快速访问中值数据,比如维护两个优先队列来跟踪当前读取的所有值的中位数。

  5. 模拟系统:在模拟系统中,如事件驱动的模拟,priority_queue 可以管理事件的优先级,确保按正确的顺序处理事件。

  6. A*路径寻找算法:在游戏编程和AI中,priority_queue 可以用于A*算法,这是一种寻找从一个点到另一个点的最短路径的算法。

  7. 实时数据处理:在实时系统中,可能需要处理多个数据源发来的数据包,priority_queue 可以按照数据包的重要性或紧急程度来处理它们。

这些只是priority_queue 应用的一些例子,它的使用场景非常广泛,几乎涵盖了所有需要优先级排序的算法和系统设计。

29. string和stringstream有什么区别?

stringstringstream 在 C++ 中都用于处理文本,但它们的用途和功能有所不同:

  1. string:
    基本概念:string 是标准模板库(STL)中的一种基础数据类型,用于表示和操作字符串。
    主要用途:用于存储和操作简单的字符序列。例如,拼接字符串、访问单个字符、查找子字符串等。
    性能:对于基本的字符串操作,string 提供了高效的方法。
    直接操作:你可以直接对 string 对象进行读写操作,例如 string s = "hello";
  2. stringstream
    基本概念:stringstream 是输入/输出库(I/O)的一部分,是一个流(stream)对象,用于字符串的读写操作。
    主要用途:用于复杂的字符串处理,如字符串的格式化、从字符串中解析出不同类型的数据、将多种类型的数据转换为字符串。
    灵活性:stringstream 提供了类似于文件流的接口,可以像处理文件一样处理字符串。
    使用方式:通过插入(<<)和提取(>>)操作符进行读写,例如 stringstream ss; ss << 100; int x; ss >> x;

应用场景例子:

  • string:如果你只需要存储一段文本或进行简单的字符串拼接,比如用户名或者地址,string 是最合适的。
  • stringstream:在需要解析字符串中的多种数据类型或进行复杂的格式化时使用 stringstream。例如,从一行文本中提取并转换成整数、浮点数和字符串的组合,stringstream 就非常有用。

总的来说,string 适合于基本的字符串操作,而 stringstream 更适用于复杂的字符串处理和数据转换任务。

30.如何使用stringstream进行字符串的格式化输出?

在C++中,stringstream 是一个非常有用的类,它属于 <sstream> 头文件。它主要用于字符串的格式化和解析。使用 stringstream 进行字符串的格式化输出非常简单,主要涉及以下几个步骤:

  1. 包含必要的头文件:
    首先,你需要包含 sstream 头文件:
#include <sstream>
  1. 创建一个 stringstream 对象:
    你可以创建一个 std::stringstream 对象来进行操作:
std::stringstream ss;
  1. 使用流插入操作符:
    通过流插入操作符 <<,你可以将各种类型的数据插入到 stringstream 中,类似于如何使用 cout 进行输出:
int number = 100;
double pi = 3.14;
std::string text = "Example";

ss << "Number: " << number << ", Pi: " << pi << ", Text: " << text;
  1. 转换为字符串:
    完成数据插入后,你可以使用 str() 方法将 stringstream 的内容转换为字符串:
std::string formattedString = ss.str();
  1. 输出或使用格式化的字符串:
    现在,你可以将格式化的字符串用于输出或其他目的:
std::cout << formattedString << std::endl;

应用场景:

  • stringstream 在格式化复杂字符串时非常有用,尤其是当字符串包含多种不同数据类型时。
  • 它也常用于将数值数据类型(如 int, float)转换为字符串。
  • 在解析字符串时,stringstream 也很有用,比如从字符串中提取和转换数据。

这种方法提供了一种灵活的方式来构建和操作字符串,使得代码既清晰又容易维护。

  • 32
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在提供的引用内容中,有一个关于STL面试题的代码示例。这个示例展示了如何使用STL中的allocator类来进行内存分配和对象构造销毁的操作。在这个示例中,使用了一个Test类作为示例对象。首先,使用allocator的allocate方法来申请三个单位的Test内存,并将其赋值给指针pt。然后,使用allocator的construct方法来构建三个Test对象,并使用默认值或拷贝构造函数来初始化这些对象。最后,使用allocator的destroy方法来销毁这些对象,并使用deallocate方法释放之前分配的内存。这个示例展示了如何使用allocator来实现自定义内存管理和对象构造销毁的操作。 关于C++ STL面试题,根据提供的引用内容,我无法找到具体的面试题。请提供更具体的问题或者引用内容,以便我能够给出更准确的答案。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ STL程序员面试题](https://download.csdn.net/download/kpxingxing/3697052)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++面试题 STL篇](https://blog.csdn.net/qq_31442743/article/details/109575971)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值