C++: STL 适配器
第38课我们学了函数对象。
现在,我们要学函数适配器——把现有的函数"改造"成需要的样子。
就像乐高积木,用小零件拼出大作品。
1. 适配器概述
(1) 1.1 什么是适配器?
适配器(Adapter)是修改函数对象行为的模板。
常见适配器:
std::bind(绑定参数)std::ref(按引用传递)std::negate(取反)std::mem_fn(成员函数指针)
2. std::bind——参数绑定
(1) 2.1 基本用法
std::bind 用于绑定函数参数,创建新的函数对象。
示例:绑定参数(难度⭐⭐)
▶ 示例 2:代码示例(难度⭐)
CPP
#include <iostream>
#include <functional>
int add(int a, int b) {
return a + b;
}
int main() {
// 绑定add的第一个参数为10
auto add10 = std::bind(add, 10, std::placeholders::_1);
std::cout << "add10(5) = " << add10(5) << std::endl; // 输出:15
std::cout << "add10(20) = " << add10(20) << std::endl; // 输出:30
return 0;
}
输出:
TEXT
📖 仅展示
add10(5) = 15
add10(20) = 30
💡 提示:
std::placeholders::_1表示"第一个参数留着后面填"
(2) 2.2 调整参数顺序
示例:交换参数顺序(难度⭐⭐)
CPP
#include <iostream>
#include <functional>
int subtract(int a, int b) {
return a - b;
}
int main() {
// 交换参数顺序
auto reverse_subtract = std::bind(subtract,
std::placeholders::_2,
std::placeholders::_1);
std::cout << "subtract(10, 3) = " << subtract(10, 3) << std::endl; // 7
std::cout << "reverse(10, 3) = " << reverse_subtract(10, 3) << std::endl; // -7
return 0;
}
3. std::ref——引用包装
(1) 3.1 问题:值传递
默认情况下,STL算法按值传递函数对象,导致状态无法共享。
示例:用std::ref解决(难度⭐⭐)
CPP
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
struct Counter {
int count = 0;
void operator()(int) { count++; }
};
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
Counter counter;
// ❌ 错误!按值传递,counter的副本被调用
std::for_each(v.begin(), v.end(), counter);
std::cout << "计数:" << counter.count << std::endl; // 输出:0
// ✅ 正确!用std::ref按引用传递
std::for_each(v.begin(), v.end(), std::ref(counter));
std::cout << "计数:" << counter.count << std::endl; // 输出:5
return 0;
}
4. std::not_fn——取反
(1) 4.1 基本用法
std::not_fn 用于取反函数对象的返回值。
示例:取反谓词(难度⭐⭐)
CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 查找第一个偶数
auto it1 = std::find_if(v.begin(), v.end(),
(int x) { return x % 2 == 0; });
std::cout << "第一个偶数:" << *it1 << std::endl; // 2
// 查找第一个奇数(取反)
auto it2 = std::find_if(v.begin(), v.end(),
std::not_fn((int x) { return x % 2 == 0; }));
std::cout << "第一个奇数:" << *it2 << std::endl; // 1
return 0;
}
5. std::mem_fn——成员函数指针
(1) 5.1 问题:成员函数指针难用
成员函数指针语法复杂,用 std::mem_fn 可以简化。
示例:调用成员函数(难度⭐⭐⭐)
CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>
struct Student {
std::string name;
void display() const {
std::cout << "学生:" << name << std::endl;
}
};
int main() {
std::vectorStudent students = {{"张三"}, {"李四"}};
// 用std::mem_fn调用成员函数
std::for_each(students.begin(), students.end(),
std::mem_fn(&Student::display));
return 0;
}
6. 综合示例
▶ 示例 1:灵活的成绩处理器(难度⭐⭐⭐)
CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
std::vector<int> scores = {85, 92, 78, 90, 88};
int threshold = 90;
// 统计不低于threshold的人数
int count = std::count_if(scores.begin(), scores.end(),
std::bind(std::greater_equalint(),
std::placeholders::_1,
threshold));
std::cout << "不低于" << threshold << "分的人数:" << count << std::endl;
return 0;
}
输出:
TEXT
📖 仅展示
不低于分的人数:
❓ 常见问题
Q:C++11还有std::bind吗? A:有,但Lambda更推荐。Lambda更简洁,性能更好。
CPP
// 用std::bind
auto f1 = std::bind(add, 10, std::placeholders::_1);
// 用Lambda(推荐)
auto f2 = (int x) { return add(10, x); };
Q stack和queue可以用其他容器作为底层吗?
A 可以!适配器通过模板参数指定底层容器:
stack<int, vector<int>>用vector实现;queue<int, list<int>>用list实现。默认stack用deque,queue也用deque。▶ 示例 3:queue队列(难度⭐)
CPP
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
q.push(10);
q.push(20);
q.push(30);
while (!q.empty()) {
std::cout << "队头:" << q.front() << std::endl;
q.pop();
}
return 0;
}
输出:
TEXT
📖 仅展示
队头:
💡 提示:队列是先进先出(FIFO)结构,用
push() 入队,front() 访问队头,pop() 出队。
📖 小节
std::stack:栈适配器,后进先出(LIFO)std::queue:队列适配器,先进先出(FIFO)std::priority_queue:优先队列,按优先级出队std::bind:函数参数绑定
📝 作业
-
**基础题 (Difficulty ⭐):创建一个 stackint,依次 push 1、2、3,然后循环 pop 并输出所有元素。观察输出顺序。
-
**进阶题 (Difficulty ⭐⭐):用 queue 实现一个"打印任务队列"——模拟多个打印任务按顺序处理,每次处理一个后输出队列剩余长度。
-
**挑战题 (Difficulty ⭐⭐⭐):用 priority_queue 实现一个"任务调度器"——每个任务有优先级(1-10),队列按优先级从高到低处理,相同优先级按插入顺序。
- 适配器:stack/queue/priority_queue 封装底层容器
- stack 后进先出:push/pop/top
- queue 先进先出:push/pop/front/back
- priority_queue 优先级队列:最大堆
- 适配器通过模板参数指定底层容器
下一课:异常处理(#40)