函数对象能存状态、可内联、能重载、可被标准算法直接使用;普通函数无法自带状态,需依赖全局/静态变量,且不易内联、不支持重载、传入stl算法时易出问题。

函数对象能存状态、可内联、能重载、可被标准算法直接用;普通函数做不到这几点,除非靠全局/静态变量硬凑。
函数对象必须重载 operator(),且只能是类成员
普通函数直接写 void foo(int x) { ... } 就完事;函数对象得先定义一个类,再在类里声明 operator()。比如:
struct Adder {
int offset = 0;
int operator()(int x) const { return x + offset; }
};
注意三点:
-
operator()只能是类的非静态成员函数,不能是全局函数或静态成员 - 可以重载多个不同参数签名的
operator(),比如operator()(int)和operator()(int, int) - 如果不需要修改对象状态,记得加
const—— 否则传给std::for_each这类只接受 const callable 的算法时会编译失败
普通函数无法自带状态,函数对象天然支持
想让一个“加法器”每次调用都累加计数,普通函数只能靠 static 或全局变量:
int counter = 0;
int add_with_count(int x) {
counter++;
return x + counter;
}
这在多线程下不安全,也无法同时存在多个独立计数器。而函数对象把状态封装进实例里:
struct CountingAdder {
int count = 0;
int operator()(int x) {
count++;
return x + count;
}
};
CountingAdder a1, a2;
a1(10); // count=1
a1(20); // count=2
a2(5); // count=1(完全独立)
每个对象有自己的一份成员变量,互不影响。
函数对象传给 STL 算法更自然,普通函数常需包装
std::sort、std::transform 这些算法模板参数要求的是 “callable”,函数对象直接传类型或实例就行:
std::vector<int> v = {3, 1, 4};
std::sort(v.begin(), v.end(), std::greater<int>{}); // 函数对象实例
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // lambda(本质也是函数对象)</int></int>
但普通函数名(如 my_compare)传进去会触发函数指针隐式转换,有时模板推导失败,尤其带重载时。更稳妥的做法是显式转成函数指针:static_cast<bool>(my_compare)</bool>,或者干脆用 lambda 包一层。
性能差异:函数对象更容易被编译器内联
普通函数调用有真实跳转开销(哪怕很小),而函数对象的 operator() 是类成员函数,只要定义在头文件里、没跨编译单元、且未禁用优化,编译器大概率直接内联。特别是像 std::less 这种空类函数对象,调用开销趋近于零。
不过要注意:如果函数对象体积大、含复杂逻辑、或定义在 .cpp 里,内联优势就没了——这时候和普通函数性能基本没差别。
真正容易被忽略的是 const 正确性:很多初学者写的函数对象没加 const,结果在只读上下文(如 std::find_if 配合 const 容器)里直接编译不过,报错类似 no matching function for call to 'xxx::operator()' —— 不是语法错,是 const 限定符不匹配。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











