【C++ STL】适配器简单介绍

发布时间:2026/7/27 12:10:29

【C++ STL】适配器简单介绍 适配器适配器Adapter本质上是一种“包装器”它对已有的容器vector、list、deque或迭代器、函数对象进行封装改变其接口形式以用来满足特定场景的使用需求。特点依赖底层容器/迭代器/函数对象自身不提供独立的存储结构只修改接口不改变底层对象的核心功能属于模板类具备高度的通用性和灵活性。分类容器适配器将顺序容器如 vector、deque、list封装为栈、队列、优先队列等抽象数据结构。迭代器适配器对迭代器进行包装改变其遍历行为或生成新的迭代器类型如反向迭代器、插入迭代器、流迭代器等。函数适配器将函数或函数对象进行组合、绑定参数或转换使其能够适配到算法所需的接口现代 C 中多由 lambda 和 std::bind 替代。核心思想是复用通过组合现有组件快速构建出新的功能而不需要重新实现底层逻辑。容器适配器容器适配器是对STL基础容器如deque、vector、list进行封装隐藏其部分接口只暴露符合特定数据结构逻辑的接口实现栈、队列、优先级队列等常用数据结构。STL提供了三种常用容器适配器stack栈适配器遵循“后进先出”LIFO的原则queue队列适配器遵循“先进先出”FIFO的原则priority_queue优先级队列适配器元素按优先级排序优先级最高的元素先出队。[! 注意]容器适配器的底层容器可指定部分适配器有默认底层容器但需满足特定条件如stack需要支持push_back、pop_back、back等操作。栈适配器stack默认使用deque作为底层容器也可指定vector或list作为底层容器需支持push_back、pop_back、back、empty、size操作。stack隐藏了底层容器的除“尾部操作”外的所有接口只允许从尾部插入push、尾部删除pop、访问尾部元素top。常用操作push(const T val)在栈顶插入元素pop()删除栈顶元素无返回值需先判断非空top()返回栈顶元素的引用非空时使用empty()判断栈是否为空返回boolsize()返回栈中元素个数。// 1、默认底层使用deque容器 stackint s1; s1.push(1); s1.push(2); s1.push(3); cout 栈顶元素 s1.top() endl; // 3 cout 栈大小 s1.size() endl; // 3 // 删除栈顶元素,无返回值 s1.pop(); // 2、指定栈底层容器 stackint, vectorint s2; s2.push(10); s2.push(20); s2.push(30); cout 栈顶元素 s2.top() endl; // 30 cout 栈大小 s2.size() endl; // 3使用时注意点stack的pop()操作只删除元素不返回元素若需获取栈顶元素需先调用top()不能直接遍历stack若需遍历需先将元素弹出并暂存遍历后再重新入栈会破坏原栈结构底层容器若为vectorpop_back()效率较低需移动后续元素适合元素个数较少的场景底层为deque时效率更均衡。队列适配器queue默认使用deque作为底层容器也可指定list作为底层容器需支持push_back、pop_front、front、back、empty、size操作。queue遵循“先进先出”原则隐藏底层容器的其他接口只允许从队尾插入push、队头删除pop、访问队头front和队尾back元素。[! 注意]vector头部删除效率不高因此队列不采用常用操作push(const T val)在队尾插入元素pop()删除队头元素无返回值需先判断非空front()返回队头元素的引用back()返回队尾元素的引用empty()判断队列是否为空size()返回队列中元素个数。// 队列适配器 queueint q; // 队尾插入元素 q.push(10); q.push(20); q.push(30); cout 队尾元素 q.back() endl; // 30 cout 队首元素 q.front() endl; // 10 // 删除队首元素 q.pop(); cout 队首元素 q.front() endl; // 20使用注意queue的pop()操作只删除队头元素不返回元素需先调用front()获取不能直接遍历queue遍历需弹出元素暂存会破坏原队列结构底层容器不能使用vector因为vector不支持pop_front()效率极低STL不推荐。优先级队列适配器priority_queuepriority_queue默认使用vector作为底层容器也可指定deque作为底层容器需支持随机访问迭代器、push_back、pop_back、front等操作。其核心是“堆排序”底层容器会被维护成一个大根堆默认队头元素始终是优先级最高的元素数值最大的元素。优先级可自定义通过指定比较函数仿函数可实现小根堆优先级最低的元素先出队。常用操作push(const T val)插入元素自动调整堆结构维持优先级pop()删除队头优先级最高元素自动调整堆结构top()返回队头元素的引用非空时使用empty()判断队列是否为空size()返回队列中元素个数。使用注意priority_queue的top()返回优先级最高的元素默认是大根堆数值大的优先插入和删除元素时底层会自动调整堆结构时间复杂度为O(log n)自定义优先级时需重载运算符默认或使用仿函数确保堆结构能正确维护不能直接遍历priority_queue遍历需弹出元素破坏原堆结构。容器适配器的总结不提供迭代器不能使用 STL 算法直接操作。底层容器必须支持某些操作stack要求back()、push_back()、pop_back()queue要求front()、back()、push_back()、pop_front()priority_queue要求随机访问迭代器和push_back()、pop_back()以及堆算法。通过封装实现了数据结构的隔离使代码意图更清晰。迭代器适配器迭代器适配器是对已有的迭代器如vector::iterator、list::iterator进行封装改变其遍历方向、访问方式使其满足特定的遍历需求。STL提供的迭代器适配器不改变迭代器的底层遍历逻辑只修改其接口行为。常用的迭代器适配器有3种reverse_iterator反向迭代器将正向迭代器的遍历方向反转从尾到头insert_iterator插入迭代器将赋值操作转化为插入操作用于向容器中插入元素stream_iterator流迭代器将输入/输出流如cin、cout封装为迭代器实现流与容器的快速交互。反向迭代器reverse_iteratorreverse_iterator基于正向迭代器实现通过重载、–运算符将“正向遍历”转化为“反向遍历”。使迭代器反向遍历容器。通过rbegin()和rend()获得。[! 注意]反向迭代器的操作等价于正向迭代器的–操作反向迭代器的–操作等价于正向迭代器的操作。vectorint v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 反向遍历 for (vectorint::reverse_iterator it v.rbegin(); it ! v.rend(); it) { cout *it ; // 9 8 7 6 5 4 3 2 1 0 } cout endl;插入迭代器insert_iteratorinsert_iterator将赋值操作*it val转化为容器的插入操作避免直接使用insert()方法的繁琐常用于算法如copy中向容器插入元素。根据插入位置的不同insert_iterator分为3种back_insert_iterator在容器尾部插入元素要求容器支持push_back()如vector、deque、listfront_insert_iterator在容器头部插入元素要求容器支持push_front()如deque、listinsert_iterator在容器指定位置插入元素要求容器支持insert()方法所有顺序容器均可。辅助函数back_inserter()、front_inserter()、inserter()方便创建对应迭代器vectorint src { 0,1,2,3 }; vectorint dst; // back_insert_iterator copy(src.begin(), src.end(), back_inserter(dst)); for (int i : dst) { cout i endl; }流迭代器stream_iteratorstream_iterator将输入流如cin或输出流如cout封装为迭代器实现“迭代器操作”与“流操作”的无缝衔接。输入流迭代器istream_iterator用于从流中读取数据输出流迭代器ostream_iterator用于向流中写入数据。函数适配器函数适配器也叫函数对象适配器是对STL函数对象仿函数进行封装、组合或修改改变其参数个数、参数类型或返回值使其满足算法如sort、find_if的参数要求。STL提供的函数适配器主要依赖于functional头文件常用的有绑定适配器bind1st、bind2ndC11前bindC11及以后推荐使用否定适配器not1、not2用于否定函数对象的返回值函数指针适配器ptr_fun将普通函数指针转化为函数对象。[! 注意]C11及以后bind适配器功能更强大可替代bind1st、bind2nd绑定适配器bind适配器的核心是“绑定函数对象/普通函数的参数”可以固定部分参数的值或将参数的顺序调换返回一个新的函数对象。其语法格式为 bind(函数对象/函数指针, 参数1, 参数2, …, 参数n)参数中可以使用placeholders::_1、placeholders::_2、…占位符表示新函数对象的参数位置_1表示第一个参数_2表示第二个参数以此类推。// 判断元素是否大于threshold bool greaterTan(int val, int threshold) { return val threshold; } int main() { vectorint v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int threshold 5; // 函数适配器绑定greaterTan函数的第二个参数生成一元谓词 // using namespace std::placeholders; // _1, _2 auto bind_fun bind(greaterTan, placeholders::_1, threshold); // 统计元素值大于5的个数 int ret count_if(v.begin(), v.end(), bind_fun); cout ret endl; // 4 return EXIT_SUCCESS; };否定适配器not1和not2用于否定函数对象的返回值其中 - not1用于否定“一元函数对象”接收1个参数返回bool值 - not2用于否定“二元函数对象”接收2个参数返回bool值。常与STL内置函数对象如less、greater、equal_to、less_equal配合使用改变算法的判断条件。注意部分环境中not1可能存在兼容问题C11及以后更推荐使用bind适配器替代not1代码兼容性更强、可读性更好。vectorint v { 5, 12, 8, 15, 3, 20 }; // 替代not1用bind实现“找不小于10的元素大于等于10” // 等价于否定 x 10即 x 10用bind绑定lessint的参数顺序实现 auto it1 find_if(v.begin(), v.end(), bind(less_equalint(), 10, placeholders::_1)); cout 第一个不小于10的元素 *it1 endl; // 输出12函数指针适配器ptr_fun用于将“普通函数指针”转化为“函数对象”以便适配STL算法中对“函数对象”的要求部分算法只接受函数对象不直接接受函数指针。C11及以后bind可替代ptr_fun但了解其用法有助于理解函数适配器的核心逻辑。// 普通函数判断一个数是否为偶数 bool isEven(int x) { return x % 2 0; } int main() { vectorint v {1, 2, 3, 4, 5, 6}; // ptr_fun将isEven函数指针转化为函数对象配合find_if找第一个偶数 auto it find_if(v.begin(), v.end(), ptr_fun(isEven)); if (it ! v.end()) { cout 第一个偶数 *it endl; // 输出2 } return 0; }使用lambda表达式Lambda 可以直接捕获变量、绑定参数简洁灵活。vectorint v { 5, 12, 8, 15, 3, 20 }; // 统计元素大于10的个数 int threshold 10; int ret count_if(v.begin(), v.end(), [threshold](int val) {return val threshold; }); std::cout 大于10的元素个数: ret std::endl; // 3

相关新闻