C++中的仿函数

仿函数是什么

仿函数(Functor)在C++中和函数对象是一个概念,它们都是重载了函数调用运算符的类的对象,Functor是STL六大模块之一,其余模块分别是Container, Algorithm, Iterator, Adaptor, Allocator.

仿函数存在的意义

仿函数的作用和C语言中的函数指针很像,但它扩展性更好。当我们需要为某种算法提供特定“操作”(比如说排序方式)时,我们可以传入函数指针,但如果将来这个函数的签名(参数个数,参数类型,参数顺序,不包括返回值类型)发生了变化,就需要做很大调整了。其次,函数之间可能会共用变量,维护全局变量也很麻烦。仿函数就没有这些缺点,它可以被依赖、组合和继承,在自己的类中维护变量,功能更加强大。

比如在用到unordered_map或者unordered_set的时候,我们可能会需要将自定义类用作key,这时我们需要做两件事

  1. 为这个类重载等于运算符
  2. 提供用于hash的仿函数

举个例子,对于下面这个磁盘页面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct PageId {
int fd;
page_id_t page_no = INVALID_PAGE_ID;
friend bool operator==(const PageId& x, const PageId& y) {
return x.fd==y.fd&&x.page_no==y.page_no;
}
bool operator<(const PageId &x) const {
if (fd < x.fd)
return true;
return page_no < x.page_no;
}
std::string toString() {
return "{fd: " + std::to_string(fd) +
" page_no: " + std::to_string(page_no) + "}";
}
inline int64_t Get() const {
return (static_cast<int64_t>(fd << 16) | page_no);
}
};

我们需要提供一个hash函数对象,从而构建一个unordered_map,使得磁盘中的页和内存中的帧可以一一对应

1
2
3
struct PageIdHash {
size_t operator()(const PageId &x) const { return (x.fd << 16) | x.page_no; }
};

这样我们就可以获得std::unordered_map<PageId,frame_id_t,PageIdHash>了,如果我们想要提供默认实现,可以
再使用模板特化的方法

1
2
3
4
5
template <> struct std::hash<PageId> {
size_t operator()(const PageId &obj) const {
return std::hash<int64_t>()(obj.Get());
}
};

有时候仿函数的存在是为了让函数具有类的性质,通过仿函数,我们可以在拥有函数功能的同时,拥有状态,依据函数生成对象,让函数能彼此继承。仿函数主要在STl中使用,lambda的内部实现也是用的仿函数。


C++中的仿函数
http://example.com/2024/04/08/C-中的仿函数/
作者
Jinming Zhang
发布于
2024年4月8日
许可协议