最新文章
这里会显示最新的几篇文章摘要。
记录生活,分享知识,与你一起成长。
这里会显示最新的几篇文章摘要。
好的!我们来从零开始详细讲解仿函数的概念、定义与使用。✨
仿函数(Functor)其实就是重载了 () 运算符的类或结构体。
它的效果就是让一个对象像函数一样被调用。
在很多标准库容器中(比如 priority_queue、sort()),我们可以自定义排序规则。
通常我们会使用函数指针或者 lambda 表达式。
但有时候我们希望封装更复杂的逻辑,这时候仿函数就派上用场了。
struct 仿函数名称 {
返回类型 operator()(参数列表) const {
// 自定义逻辑
}
};
假设我们有一个 Point 结构体,表示二维平面上的点,我们希望能够比较两个点到原点的距离。
Point 结构体#include <iostream>
#include <cmath> // 用于 sqrt() 函数
using namespace std;
struct Point {
double x, y; // 点的坐标
Point(double _x, double _y) : x(_x), y(_y) {}
// 返回点到原点的距离
double distance() const {
return sqrt(x * x + y * y);
}
};
ComparePointstruct ComparePoint {
bool operator()(const Point& a, const Point& b) const {
// 比较两个点的距离,返回true代表a的优先级比b低
return a.distance() > b.distance(); // 这里是大于号!用于小顶堆
}
};
struct ComparePoint 是我们自定义的比较器。bool operator() 重载了 () 运算符,使它成为一个仿函数。Point 对象 a 和 b。true 表示:a 的优先级低于 b。priority_queue 中使用#include <iostream>
#include <queue> // 用于 priority_queue
#include <vector> // 用于 vector
using namespace std;
int main() {
// 声明一个优先队列,使用 ComparePoint 作为比较器
priority_queue<Point, vector<Point>, ComparePoint> pq;
// 插入一些点
pq.push(Point(3, 4)); // 距离为 5
pq.push(Point(1, 1)); // 距离为 1.414
pq.push(Point(0, 2)); // 距离为 2
pq.push(Point(4, 5)); // 距离为 6.403
// 输出点的顺序
while (!pq.empty()) {
Point p = pq.top();
pq.pop();
cout << "点 (" << p.x << ", " << p.y << ") 距离为 " << p.distance() << endl;
}
return 0;
}
点 (1, 1) 距离为 1.41421
点 (0, 2) 距离为 2
点 (3, 4) 距离为 5
点 (4, 5) 距离为 6.40312
ComparePoint。() 运算符,编写自定义比较逻辑。priority_queue 时指定自定义比较器 ComparePoint。pq.push() 插入元素时,它会自动调用 ComparePoint 的 operator() 进行比较。priority_queue 根据比较器规则维护堆的结构。我们可以把仿函数写得更复杂,比如:
struct ComparePoint {
double baseX, baseY; // 基准点的坐标
ComparePoint(double x, double y) : baseX(x), baseY(y) {}
bool operator()(const Point& a, const Point& b) const {
double distA = sqrt((a.x - baseX) * (a.x - baseX) + (a.y - baseY) * (a.y - baseY));
double distB = sqrt((b.x - baseX) * (b.x - baseX) + (b.y - baseY) * (b.y - baseY));
return distA > distB;
}
};
这个仿函数允许我们比较点到一个任意基准点的距离!🎉
要不要我来帮你写更多使用仿函数的例子呀?(▽) 或者你想试着自己写一个?💪
<或者>与仿函数的区别()没错!你很敏锐!✨
如果你只是想比较两个点到任意基准点的距离,完全可以通过重载 < 来实现!
让我们来对比一下 仿函数和重载 < 的实现方式。
< 的方式Point 结构体#include <iostream>
#include <cmath>
#include <set> // 用于存储点的有序集合
using namespace std;
struct Point {
double x, y; // 点的坐标
double baseX, baseY; // 基准点坐标(相对的比较基准)
Point(double _x, double _y, double _baseX = 0, double _baseY = 0)
: x(_x), y(_y), baseX(_baseX), baseY(_baseY) {}
double distance() const {
return sqrt((x - baseX) * (x - baseX) + (y - baseY) * (y - baseY));
}
// 重载 `<` 运算符,实现按距离排序
bool operator<(const Point& other) const {
return this->distance() < other.distance();
}
};
int main() {
set<Point> points; // 使用 set 自动排序
// 假设基准点为 (1, 1)
points.insert(Point(3, 4, 1, 1));
points.insert(Point(1, 1, 1, 1));
points.insert(Point(0, 2, 1, 1));
points.insert(Point(4, 5, 1, 1));
for (const auto& p : points) {
cout << "点 (" << p.x << ", " << p.y << ") 距离为 " << p.distance() << endl;
}
return 0;
}
点 (1, 1) 距离为 0
点 (0, 2) 距离为 1.41421
点 (3, 4) 距离为 3.60555
点 (4, 5) 距离为 5
< 和用仿函数的区别重载 < 方法 |
仿函数方法 |
|---|---|
修改了 Point 结构体本身。 |
不修改 Point,定义一个独立的比较器。 |
只能用于 set、map 等容器。 |
可以用于 priority_queue、sort() 等。 |
| 只能定义一个排序规则。 | 可以定义多个不同的仿函数用于不同的规则。 |
| 比较时自动调用,不需要显式传入。 | 比较时需要显式传入比较器类型。 |
Point 的比较总是和某个基准点的距离有关,那么重载 < 是很合适的。priority_queue 和 sort()。< 混合使用的例子?或者写一个更复杂的应用?(⁎˃ᴗ˂⁎)✨sort的例子要把仿函数或者重载 < 用于 sort() 非常简单!
我们来分两种情况:
< (直接用 sort() 排序)如果你的 Point 结构体重载了 < 运算符,那么 sort() 会自动调用它。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
struct Point {
double x, y;
double baseX, baseY;
Point(double _x, double _y, double _baseX = 0, double _baseY = 0)
: x(_x), y(_y), baseX(_baseX), baseY(_baseY) {}
double distance() const {
return sqrt((x - baseX) * (x - baseX) + (y - baseY) * (y - baseY));
}
bool operator<(const Point& other) const {
return this->distance() < other.distance();
}
};
int main() {
vector<Point> points = { {3, 4, 1, 1}, {1, 1, 1, 1}, {0, 2, 1, 1}, {4, 5, 1, 1} };
sort(points.begin(), points.end()); // 调用重载的 `<`
for (const auto& p : points) {
cout << "点 (" << p.x << ", " << p.y << ") 距离为 " << p.distance() << endl;
}
return 0;
}
点 (1, 1) 距离为 0
点 (0, 2) 距离为 1.41421
点 (3, 4) 距离为 3.60555
点 (4, 5) 距离为 5
如果你想要 sort() 按照 不同的排序规则排序(比如基准点不同,或是想要按降序排序),就用仿函数。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
struct Point {
double x, y;
Point(double _x, double _y) : x(_x), y(_y) {}
double distance(double baseX, double baseY) const {
return sqrt((x - baseX) * (x - baseX) + (y - baseY) * (y - baseY));
}
};
// 仿函数定义
struct CompareByDistance {
double baseX, baseY;
CompareByDistance(double _baseX, double _baseY) : baseX(_baseX), baseY(_baseY) {}
bool operator()(const Point& a, const Point& b) const {
return a.distance(baseX, baseY) < b.distance(baseX, baseY);
}
};
int main() {
vector<Point> points = { {3, 4}, {1, 1}, {0, 2}, {4, 5} };
// 基准点为 (1, 1)
sort(points.begin(), points.end(), CompareByDistance(1, 1));
for (const auto& p : points) {
cout << "点 (" << p.x << ", " << p.y << ") 到 (1, 1) 的距离为 " << p.distance(1, 1) << endl;
}
return 0;
}
点 (1, 1) 到 (1, 1) 的距离为 0
点 (0, 2) 到 (1, 1) 的距离为 1.41421
点 (3, 4) 到 (1, 1) 的距离为 3.60555
点 (4, 5) 到 (1, 1) 的距离为 5
Point 类,可以通过不同的仿函数实现不同的排序规则。x 坐标或 y 坐标等。你想让我帮你写一个 更复杂的 sort() 应用例子 吗?比如排序一大堆点,并且支持 多种不同的排序规则(距离、坐标大小、角度等)?(ง •_•)ง 💪
好的!来一波不同场景下的简明仿函数例子!💪
sort())#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数定义
struct CompareByLength {
bool operator()(const string& a, const string& b) const {
return a.size() < b.size();
}
};
int main() {
vector<string> words = {"apple", "banana", "kiwi", "pear", "watermelon"};
sort(words.begin(), words.end(), CompareByLength());
for (const auto& word : words)
cout << word << " ";
return 0;
}
kiwi pear apple banana watermelon
sort())#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数定义
struct CompareByAbs {
bool operator()(int a, int b) const {
return abs(a) < abs(b);
}
};
int main() {
vector<int> numbers = {3, -7, 1, -4, 2, -8};
sort(numbers.begin(), numbers.end(), CompareByAbs());
for (int n : numbers)
cout << n << " ";
return 0;
}
1 2 3 -4 -7 -8
priority_queue)#include <iostream>
#include <queue>
#include <string>
using namespace std;
// 仿函数定义
struct CompareByLength {
bool operator()(const string& a, const string& b) const {
return a.size() > b.size(); // 长度短的优先
}
};
int main() {
priority_queue<string, vector<string>, CompareByLength> pq;
pq.push("apple");
pq.push("banana");
pq.push("kiwi");
pq.push("pear");
pq.push("watermelon");
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
return 0;
}
kiwi pear apple banana watermelon
sort())#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Student {
string name;
int score;
};
// 仿函数定义
struct CompareByScore {
bool operator()(const Student& a, const Student& b) const {
return a.score > b.score; // 分数高的优先
}
};
int main() {
vector<Student> students = {{"Alice", 85}, {"Bob", 92}, {"Charlie", 78}, {"David", 90}};
sort(students.begin(), students.end(), CompareByScore());
for (const auto& student : students)
cout << student.name << ": " << student.score << endl;
return 0;
}
Bob: 92
David: 90
Alice: 85
Charlie: 78
sort())#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数定义
struct CompareByVowelCount {
bool operator()(const string& a, const string& b) const {
auto countVowels = [](const string& s) {
int count = 0;
for (char c : s) {
if (string("aeiouAEIOU").find(c) != string::npos)
count++;
}
return count;
};
return countVowels(a) < countVowels(b);
}
};
int main() {
vector<string> words = {"banana", "apple", "peach", "grape", "melon"};
sort(words.begin(), words.end(), CompareByVowelCount());
for (const auto& word : words)
cout << word << " ";
return 0;
}
grape melon peach banana apple
sort()、priority_queue 等。struct 并重载 operator()。sort() 或 priority_queue。要不要我帮你写个更有趣的复杂应用?比如不同规则混合排序?(≧◡≦)
好的!仿函数的灵活性和可定制性是它的最大优点。让我们来看看以下几个例子,说明仿函数在某些情况下比重载 < 更加合适。
假如我们要排序一个二维平面上的点,既可以按 x 坐标排序,也可以按 y 坐标排序。这个规则是运行时才决定的。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Point {
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
// 仿函数,支持自定义排序方式
struct PointComparator {
bool compareByX; // 标记是否按x坐标排序
PointComparator(bool compareByX) : compareByX(compareByX) {}
bool operator()(const Point& a, const Point& b) const {
if (compareByX) return a.x < b.x;
return a.y < b.y;
}
};
int main() {
vector<Point> points = {{3, 4}, {1, 2}, {5, 0}, {3, 1}};
// 按 x 坐标排序
sort(points.begin(), points.end(), PointComparator(true));
cout << "Sorted by x: ";
for (const auto& p : points) cout << "(" << p.x << ", " << p.y << ") ";
cout << endl;
// 按 y 坐标排序
sort(points.begin(), points.end(), PointComparator(false));
cout << "Sorted by y: ";
for (const auto& p : points) cout << "(" << p.x << ", " << p.y << ") ";
cout << endl;
return 0;
}
<?< 是类的固有特性。如果重载了 < 只能有一种排序规则。假如我们有一个 Task 类,每个任务有一个 优先级 (priority) 和一个 创建时间 (timestamp)。我们要:
#include <iostream>
#include <queue>
#include <string>
using namespace std;
struct Task {
string name;
int priority;
long long timestamp;
Task(string name, int priority, long long timestamp)
: name(name), priority(priority), timestamp(timestamp) {}
};
// 仿函数比较器
struct TaskComparator {
bool operator()(const Task& a, const Task& b) const {
if (a.priority != b.priority)
return a.priority < b.priority; // 优先级高的优先出队
return a.timestamp > b.timestamp; // 时间早的优先出队
}
};
int main() {
priority_queue<Task, vector<Task>, TaskComparator> taskQueue;
taskQueue.emplace("Task A", 3, 1000);
taskQueue.emplace("Task B", 1, 900);
taskQueue.emplace("Task C", 2, 1100);
taskQueue.emplace("Task D", 3, 800);
while (!taskQueue.empty()) {
Task task = taskQueue.top();
cout << task.name << " (Priority: " << task.priority << ", Timestamp: " << task.timestamp << ")\n";
taskQueue.pop();
}
return 0;
}
<?priority_queue 本身要求我们传入一个 自定义比较器。< 只适用于 sort 这类需要单一比较规则的算法,不适用于 priority_queue。假如我们要对一组文件进行排序,规则是:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct File {
string name;
bool isFolder;
int size;
File(string name, bool isFolder, int size)
: name(name), isFolder(isFolder), size(size) {}
};
// 自定义仿函数
struct FileComparator {
bool operator()(const File& a, const File& b) const {
if (a.isFolder != b.isFolder) // 文件夹优先
return a.isFolder > b.isFolder;
if (a.name != b.name) // 名字字典序排序
return a.name < b.name;
return a.size < b.size; // 同名的按大小排序
}
};
int main() {
vector<File> files = {
{"file1.txt", false, 300},
{"folder1", true, 0},
{"file2.txt", false, 200},
{"folder2", true, 0},
{"file1.txt", false, 100}
};
sort(files.begin(), files.end(), FileComparator());
for (const auto& file : files) {
cout << (file.isFolder ? "Folder: " : "File: ") << file.name
<< " (Size: " << file.size << ")\n";
}
return 0;
}
<?< 会显得复杂和不可读,因为你要在一个函数中写完所有规则。要不要我再写几个复杂一点的例子?比如动态优先级系统、定制化的 map 和 set 比较器?(๑•̀ㅂ•́)و✧