“想要忘记一段感情,方法永远只有一个——时间和新欢。要是时间和新欢也不能让你忘记一段感情,原因只有一个:时间不够长,新欢不够好。” - 《面包树上的女人》
在 C++ 中,可调用对象是可以像函数一样使用的对象。可调用对象包括以下几种类型:
【像函数一样的调用对象】
普通函数
这里来个最简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<bits/stdc++.h>
using namespace std; int add(int a,int b){ return a+b; } signed main(){ int num1,num2; cin>>num1>>num2; int sum=add(num1,num2); cout<<sum<<endl; return 0; }
|
类成员函数
类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量⼀样。类成员函数是类的⼀个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
来看下面这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include<bits/stdc++.h>
using namespace std;
class Person {
public: Person(string name,int age):name_(name),age_(age){ } int getAge(){ return age_; } string getName(){ return name_; } private: string name_; int age_; }; signed main(){ Person p("penge666",18); cout<<p.getAge()<<endl; return 0; }
|
类静态函数
类中的静态成员函数作⽤在整个类的内部,对应类的所有实例是共享静态成员函数的,在调⽤静态成员函数的时候跟调⽤⾮静态成员函数是有区别的。另外,静态成员函数只能访问对应类内部的静态数据成员,否则会出现编译错误。因为没有this指针!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std;
class Person {
public: Person(string name) : name_(name) { } static void printAge() { cout << "age:" << age_ << endl; return; } string getName() { return name_; }
private: string name_; static int age_; }; int Person::age_ = 18; signed main() { Person p("penge666"); p.printAge(); return 0; }
|
Note:
静态成员函数
静态成员变量:类内声明,类外定义【无需写上staitic关键字】。
仿函数
仿函数其实就是重载了operator()运算符的类对象。可以视为⼀个⼀般的函数,只不过这个函数功能是在⼀个类中的运算符operator()中实现,是⼀个函数对象,它将函数作为参数传递的⽅式来使⽤。(⾏为类似函数,故称仿函数)。实际上就是创建⼀个类,该类重载了operator()运算符,使得类的实例可以像函数⼀样被调⽤。这允许你在函数对象内部保存状态,并在调⽤时执⾏操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std;
class Person {
public: void operator()() { cout << __FUNCTION__ << '\n'; }
private: };
signed main() { Person p; p.operator()(); p(); Person()(); return 0; }
|
在类中重载了⼀个括号运算符,有⼏种⽅法可以调⽤这个函数呢?
-
定义⼀个结构体对象,然后显式访问这个函数:operator(),需要注意的是,后⾯得再加上⼀个括号。
-
通过结构体对象直接访问函数。
-
通过结构体匿名对象,访问并调⽤函数
可以方便计算!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std;
template <class Fun> int count_if(const std::vector<int>::iterator &a, const std::vector<int>::iterator &b, Fun fun) { int sum = 0; for (auto it = a; it != b; it++) { if (fun(*it)) sum++; } return sum; } bool equal_fun(int num) { return num == 3; } signed main() { vector<int> a{1, 2, 3, 3, 3, 3, 3, 4, 4}; int ans = count_if(a.begin(), a.end(), equal_fun); cout << ans << endl; return 0; }
|
写成仿函数的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std; template <class Fun> int count_if(const std::vector<int>::iterator &a, const std::vector<int>::iterator &b, Fun fun) { int sum = 0; for (auto it = a; it != b; it++) { if (fun(*it)) sum++; } return sum; } struct Equal_fun { int val_; Equal_fun(const int &val) : val_(val) {} bool operator()(const int a) { return a == val_; } }; signed main() { vector<int> a{1, 2, 3, 3, 3, 3, 3, 4, 4}; int ans = count_if(a.begin(), a.end(), Equal_fun(3)); cout << ans << endl; return 0; }
|
1.优点
-
仿函数⽐函数指针的执⾏速度快,函数指针是通过地址调⽤,⽽仿函数是对运算符operator进⾏⾃定义来提⾼调⽤的效率。
- 内联优化。同时编译器会对对象进行优化
- 函数指针调用的间接性,因为函数地址是在运行时确定的。每次调用函数指针时,CPU 必须通过指针跳转到实际的函数地址,这增加了额外的开销。
-
仿函数⽐⼀般函数灵活,可以同时拥有两个不同的状态实体,⼀般函数不具备此种功能。
-
仿函数可以作为模板参数使⽤,因为每个仿函数都拥有⾃⼰的类型。
2.缺点
函数指针
double proto(int);
的函数指针是double (*pf)(int);
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<bits/stdc++.h>
using namespace std;
void print(int x) { std::cout << "Value: " << x << std::endl; }
int main() { void (*funcPtr)(int) = print; funcPtr(5); return 0; }
|
Lambda函数
1 2 3 4 5 6 7 8 9 10
| #include <iostream>
int main() { auto print = [](int a) { std::cout << "Value: " << a << std::endl; };
print(5); return 0; }
|
std::function与std::bind
std::function是⼀个可调⽤对象包装器,是⼀个类模板,可以容纳除了类成员函数指针之外的所有可调⽤对象,它可以⽤统⼀的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
std::function
std::function的实例可以存储、复制和调用任何可调⽤对象,存储的可调⽤对象称为std::function的⽬标,若std::function不含目标,则称它为空,调⽤空的std::function的⽬标会抛出std::bad_function_call异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <functional>
void print(int a) { std::cout << "Function: " << a << std::endl; } class Printer { public: void operator()(int a) const { std::cout << "Functor: " << a << std::endl; } }; int main() { std::function<void(int)> func; func = print; func(5); func = [](int a) { std::cout << "Lambda: " << a << std::endl; }; func(10); func = Printer(); func(15); return 0; }
|
std::bind
std::bind
是一个函数适配器,用于将函数与其参数绑定在一起,从而创建一个新的可调用对象。std::bind
允许部分应用函数参数(即“柯里化”),从而创建新的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <functional>
void add(int a, int b) { std::cout << "Sum: " << a + b << std::endl; }
int main() { auto add_five = std::bind(add, 5, std::placeholders::_1); add_five(10);
auto add_ten = std::bind(add, 5, 5); add_ten();
return 0; }
|
示例:std::bind
和 std::function
结合使用
std::bind
生成的可调用对象可以存储在 std::function
中,从而增强了灵活性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std;
void sum(int a, int b) { cout << "sum:" << a + b << '\n'; } signed main() { function<void()> func; func = bind(sum, 1, 2); func(); function<void(int)> func1; func1 = bind(sum, 1, std::placeholders::_1); func1(2); return 0; }
|
使用 std::function
和 std::bind
来实现一个通用的事件系统:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <iostream> #include <vector> #include <thread> #include <queue> #include <functional> #include <condition_variable> #include <mutex> #include <future> using namespace std;
class EventSystem { public: void registerEvent(const function<void(int)> &func) { listeners.push_back(func); } void triggerEvent(int val) { for (const auto &listener : listeners) { listener(val); } }
private: vector<function<void(int)>> listeners; }; void printValue(int a) { std::cout << "Value: " << a << std::endl; }
class Handler { public: void handleEvent(int a) { std::cout << "Handled Value: " << a << std::endl; } }; signed main() { EventSystem eventSystem; eventSystem.registerEvent(printValue); eventSystem.registerEvent([](int a) { std::cout << "Lambda Handled Value: " << a << std::endl; }); Handler handler; eventSystem.registerEvent(std::bind(&Handler::handleEvent, &handler, std::placeholders::_1));
eventSystem.triggerEvent(42); return 0; }
|
夯实基础
例子1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> #include <functional>
using namespace std;
void fun(function<void(int)>&& f){ f(10); } signed main(){ fun(move([](int num){ cout<<"debug:"<<num<<endl; })); return 0; }
|
例子2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <vector> #include <functional>
class EventHandler { public: using EventCallback = std::function<void(const std::string&)>;
void registerCallback(EventCallback&& callback) { callbacks_.push_back(std::move(callback)); }
void triggerEvent(const std::string& eventData) { for (const auto& callback : callbacks_) { callback(eventData); } }
private: std::vector<EventCallback> callbacks_; };
int main() { EventHandler handler;
handler.registerCallback(std::move([](const std::string& eventData) { std::cout << "Event received: " << eventData << std::endl; }));
handler.triggerEvent("Hello, World!");
return 0; }
|
例子3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> #include <thread> #include <functional>
class AsyncTask { public: using TaskCallback = std::function<void(int)>;
void start(TaskCallback&& callback) { std::thread([callback = std::move(callback)]() mutable { std::cout << "Starting async task..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); int result = 42; callback(result); }).detach(); } };
int main() { AsyncTask task;
task.start(std::move([](int result) { std::cout << "Async task complete. Result: " << result << std::endl; }));
std::cout << "Main thread continues..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
return 0; }
|