C++: STL 适配器

第38课我们学了函数对象。

现在,我们要学函数适配器——把现有的函数"改造"成需要的样子。

就像乐高积木,用小零件拼出大作品。


1. 适配器概述

(1) 1.1 什么是适配器?

适配器(Adapter)是修改函数对象行为的模板

常见适配器:



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

💡 提示:


(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() 出队。


📖 小节


📝 作业

  1. **基础题 (Difficulty ⭐):创建一个 stackint,依次 push 1、2、3,然后循环 pop 并输出所有元素。观察输出顺序。

  2. **进阶题 (Difficulty ⭐⭐):用 queue 实现一个"打印任务队列"——模拟多个打印任务按顺序处理,每次处理一个后输出队列剩余长度。

  3. **挑战题 (Difficulty ⭐⭐⭐):用 priority_queue 实现一个"任务调度器"——每个任务有优先级(1-10),队列按优先级从高到低处理,相同优先级按插入顺序。



下一课:异常处理(#40)

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏