函数对象:可变函数和参数——回调函数——如何取代虚函数
- 一、可变函数和参数
- 1、参数——拷贝语义
- 2、参数——移动语义
- 3、std::forward
- 二、回调函数
- 三、如何取代虚函数
-
一、可变函数和参数
- Args:可以作为一个整体传给bind,
- 然后把可调用对象的实参一个个绑定,
- 调用不同的函数对象。
可变参数也可以使用va_start、va_arg、va_end、va_list来实现,我们后面单独讲解。此处重点的bind的使用。
1、参数——拷贝语义
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
void show0() {
cout << "第0个,show0,长安归故里。\n";
}
void show1(const string& message) {
cout << "第1个,show1," << message << endl;
}
struct CC
{
void show2(int bh, const string& message) {
cout << "第" << bh << "个," << message << endl;
}
};
template<typename Fn, typename...Args>
auto show(Fn fn, Args...args)
{
cout << "before show......\n";
auto f = bind(fn, args...);
cout << "after show。\n";
return f;
}
int main()
{
auto f0 = show(show0);
f0();
cout << endl;
auto f1 = show(show1, "长安归故里。");
f1();
cout << endl;
CC cc;
auto f2 = show(&CC::show2, &cc, 2, "长安归故里。");
f2();
return 0;
}
输出
before show…
after show。
第0个,show0,长安归故里。
before show…
after show。
第1个,show1,长安归故里。
before show…
after show。
第2个,长安归故里。
2、参数——移动语义
#include <iostream>
#include <thread>
#include <functional>
using namespace std;
void show0() {
cout << "第0个,show0,长安归故里。\n";
}
void show1(const string& message) {
cout << "第1个,show1," << message << endl;
}
struct CC
{
void show2(int bh, const string& message) {
cout << "第" << bh << "个," << message << endl;
}
};
template<typename Fn, typename...Args>
auto show(Fn&& fn, Args&&...args)
{
cout << "before show......\n";
auto f = bind(forward<Fn>(fn), forward<Args>(args)...);
cout << "after show。\n";
return f;
}
int main()
{
auto f0 = show(show0);
f0();
cout << endl;
auto f1 = show(show1, "长安归故里。");
f1();
cout << endl;
CC cc;
auto f2 = show(&CC::show2, &cc, 2, "长安归故里。");
f2();
return 0;
}
输出
before show…
after show。
第0个,show0,长安归故里。
before show…
after show。
第1个,show1,长安归故里。
before show…
after show。
第2个,长安归故里。
3、std::forward
forward详细的,我们放到左值和右值时讲解
- std::forward —— (C++11)转发一个函数实参
- std::move —— (C++11)获得右值引用
二、回调函数
- 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
我们仅仅讲解下把一个类的一个函数注册到另外一个类中,仅仅在有需要时调用。(其实以下代码完全可以使用虚函数来实现,比如NetClient提供虚函数的注册接口,class BB来负责虚函数的具体实现,在此我们不再扩展)
- class NetClient:代表接受消息的客户端
- class BB:代表接受客户端消息,处理业务的类
#include <iostream>
#include <string>
#include <thread>
#include <functional>
using namespace std;
class BB {
public:
void show(const string& message) {
cout << "处理业务的类:展示——" << message << endl;
}
};
class NetClient
{
function<void(const string&)> m_callback;
public:
template<typename Fn, typename ...Args>
void register_callback(Fn&& fn, Args&&...args) {
m_callback = bind(forward<Fn>(fn), forward<Args>(args)..., std::placeholders::_1);
}
void receive() {
while (true) {
this_thread::sleep_for(chrono::seconds(2));
string message = "收到消息是:小明在濮阳工作。";
if (m_callback)
{
m_callback(message);
}
}
}
};
int main()
{
NetClient client;
BB bb;
client.register_callback(&BB::show, &bb);
client.receive();
}
三、如何取代虚函数
C++虚函数在执行过程中会跳转两次(先查找对象的函数表,再次通过该函数表中的地址找到真正的执行地址),这样的话,CPU会跳转两次,而普通函数只跳转一次。
CPU每跳转一次,预取指令要作废很多,所以效率会很低。(百度)
为了管理的方便(基类指针可指向派生类对象和自动析构派生类),保留类之间的继承关系。
1、虚函数
#include <iostream>
#include <functional>
using namespace std;
struct Hero {
virtual void show() { cout << "英雄释放了技能。\n"; }
};
struct XS :public Hero {
void show() { cout << "西施释放了技能。\n"; }
};
struct HX :public Hero {
void show() { cout << "韩信释放了技能。\n"; }
};
int main()
{
int id = 0;
cout << "请输入英雄(1-西施;2-韩信。):";
cin >> id;
Hero* ptr = nullptr;
if (id == 1) {
ptr = new XS;
}
else if (id == 2) {
ptr = new HX;
}
if (ptr != nullptr) {
ptr->show();
delete ptr;
}
}
输出
请输入英雄(1-西施;2-韩信。):1
西施释放了技能。
2、包装器和绑定器:取代虚函数
- 包装器和绑定器不要求有继承关系
- 如果有继承关系,基类释放会自动调用子类的析构函数,如果没有继承关系,则不会调用
#include <iostream>
#include <functional>
using namespace std;
struct Hero {
function<void()> m_callback;
template<typename Fn, typename ...Args>
void callback(Fn&& fn, Args&&...args) {
m_callback = bind(forward<Fn>(fn), forward<Args>(args)...);
}
void show() { m_callback(); }
};
struct XS :public Hero {
void show() { cout << "西施释放了技能。\n"; }
};
struct HX :public Hero {
void show() { cout << "韩信释放了技能。\n"; }
};
int main()
{
int id = 0;
cout << "请输入英雄(1-西施;2-韩信。):";
cin >> id;
Hero* ptr = nullptr;
if (id == 1) {
ptr = new XS;
ptr->callback(&XS::show, static_cast<XS*>(ptr));
}
else if (id == 2) {
ptr = new HX;
ptr->callback(&HX::show, static_cast<HX*>(ptr));
}
if (ptr != nullptr) {
ptr->show();
delete ptr;
}
}
输出
请输入英雄(1-西施;2-韩信。):1
西施释放了技能。
参考
1、C++ STL 容器库 中文文档
2、STL教程:C++ STL快速入门
3、https://www.apiref.com/cpp-zh/cpp/header.html
4、https://en.cppreference.com/w/cpp/container
5、WIKI教程_C ++标准库_C++ Library - <iterator>
6、哔哩哔哩_系统化学习C++_C++11神器之可调用对象包装器和绑定器
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)