c++ 可调用对象的绑定器和包装器

本文详细介绍了C++中可调用对象的各种形式,包括普通函数、静态成员函数、仿函数、lambda函数和类的非静态成员函数,以及如何通过函数指针、包装器如function和bind进行操作,同时讨论了回调函数和替代虚函数的用法及性能优化。
摘要由CSDN通过智能技术生成

可调用对象

在C++中,可以像函数一样调用的有:

普通函数、类的静态成员函数、仿函数、lambda 函数、类的成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象

可调用对象有类型,可以用指针存储他们的地址,可以被引用

普通函数

普通函数类型可以声明函数、定义函数指针和函数引用,但是,不能定义函数的实体

#include<iostream>
#include<string>
using namespace std;

using Fun = void(int,const string&);
Fun show;//声明普通函数show  不能用函数类型定义函数

void show(int x,const string&s){
	cout<< x <<"--"<< s <<endl;
}
int main(){
	show(1,"我是一个鸟");
	
	void (*f1)(int,const string&) = show;//函数指针指向函数
	void (&f2)(int,const string&) = show;//函数引用指向函数
	f1(2,"hello");
	f2(3,"world");

	Fun *f3 = show;//函数指针指向函数
	Fun &f4 = show;//函数引用指向函数
	f3(4,"hello");
	f4(5,"world");
    return 0;
}

类的静态成员函数

类的静态成员函数和普通函数本质上是一样的,把普通函数放在类中而已

#include<iostream>
#include<string>
using namespace std;

using Fun = void(int,const string&);
Fun show;//声明普通函数show  不能用函数类型定义函数
struct AA{
	static void show(int x,const string&s){
		cout<< x <<"--"<< s <<endl;
	}
};

int main(){
	AA::show(1,"我是一个鸟");
	
	void (*f1)(int,const string&) = AA::show;//函数指针指向函数
	void (&f2)(int,const string&) = AA::show;//函数引用指向函数
	f1(2,"hello");
	f2(3,"world");

	Fun *f3 = AA::show;//函数指针指向函数
	Fun &f4 = AA::show;//函数引用指向函数
	f3(4,"hello");
	f4(5,"world");
    return 0;
}

仿函数

本质是类

class TT{
public:
    void operator()(int b,const string&s){
        cout<< b<<"=="<<s <<endl;
    }
};


int main() {
    TT t;
    t(0,"dad");//对象调用
    TT()(1,"dasd");//匿名对象调用仿函数

    TT&t0 = t;
    t0(2,"dy");//对象的引用调用
	return 0;
}

lambda函数

本质是仿函数

auto fd = [](int x,const string&s){
    cout<< x<<"=="<<s<<endl;
};
int main() {
    fd(1,"du");

    auto &ff = fd;
    ff(2,"yxx");
}

类的非静态成员函数 最重要的

只有指针类型,没有引用类型

class CC{
public:
    void show(int b,const string&s){
        cout<< b<<"=="<<s <<endl;
    }
};
int main() {
    CC c;
    c.show(1,"dxx");

    //定义类的成员函数指针C
    void (CC::*f1)(int,const string&) = &CC::show;
    (c.*f1)(2,"nihao");

    //定义类的成员函数指针C++
    using Fun = void(CC::*)(int,const string&);
    Fun f2 = &CC::show;
    (c.*f2)(3,"xxxxx");
}

可被转换为函数指针的类对象

类可以重载类型转换运算符 operator 数据类型() 如果数据类型是函数指针或函数引用,那么该类的实例也将成为可调用对象

本质是类 调用的代码像函数
实际开发意义不大

void show(int x){
    cout<< x <<endl;
}
class CC{
public:
    using F = void(*)(int);
    operator F(){
        return show;//返回普通函数
        //只能返回普通全局函数和类的静态成员函数
    }
};
int main() {
    CC c;
    c(10);
}

包装器 function

包含头文件 functional

#include<iostream>
#include<functional>
#include<string>
using namespace std;

//普通函数
void show(int x, const string& s) {
	cout << "你好" << x << "==" << s << endl;
}

//类内有静态成员函数
struct AA{
	static void show(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//仿函数
struct BB{
	void operator()(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//lambda函数
auto f = [](int x, const string& s) {
	cout << "你好" << x << "==" << s << endl;
	};

//类内有普通成员函数
struct CC{
	void show(int x, const string& s) {
		cout << "你好" << x << "==" << s << endl;
	}
};

//可被转换为函数指针的类
struct DD {
	using Fun = void(*)(int, const string&);
	operator Fun() {
		return show;
	}
};

int main() {
	//普通函数
	//<函数返回类型(参数列表)>
	function<void(int, const string&)>fn1 = show;//包装普通全局函数show
	fn1(1, "function包装器对全局函数");

	//类内有静态成员函数
	function<void(int, const string&)>fn2 = AA::show;//包装普通全局函数show
	fn2(2, "function包装器对类内静态成员函数");

	//仿函数
	function<void(int, const string&)> fn3= BB();
	fn3(3, "function包装器对仿函数");

	//lambda函数
	function<void(int, const string&)> fn4 = f;
	fn4(4, "function包装器对lambda");

	//类内普通函数
	CC c;
	function<void(CC&c,int, const string&)>fn5 = &CC::show;
	fn5(c, 5, "function包装器对类内普通函数");

	//可被转换为函数指针的类
	DD d;
	function<void(int, const string&)>fn6 = d;
	fn6(6, "function包装器对可被转换为函数指针的类");
	return 0;
}

注意:

  • 重载了bool运算符,用于判断是否包装了可调用对象
  • 如果std::function对象未包装可调用对象,使用std::function对象将抛出std::bad_function_call异常

适配器bind

std::bind()模板函数是一个通用的函数适配器(绑定器),它用一个可调用对象及其参数,生成一个新的可调用对象,以适应模板

在这里插入图片描述

#include<iostream>
#include<functional>
#include<string>

using namespace std;

void show(int x, const string &s) {
    cout << x << "--" << s << endl;
}

struct AA {
    void show(int x) {
        cout << x << endl;
    }
};

int main() {
    function<void(int, const string &)> f1 = show;
    f1(1, "按照顺序");
    // placeholders::_n <>中的第n个和其所在位置的绑定
    // 传参的顺序按照 <> 的顺序
    function<void(const string &, int)> f2 = bind(show, placeholders::_2, placeholders::_1);
    f2("不按照顺序", 2);

    int x{5};
    // 绑定的时候默认是x值传递,要是引用要使用ref(x)
    function<void(const string &)> f3 = bind(show, ref(x), placeholders::_1);
    x = 500;
    f3("缺省");

    function<void(int, const string &, int)> f4 = bind(show, placeholders::_1, placeholders::_2);
    f4(4, "多参数", 111);


    //类的非静态成员函数
    AA a;
    function<void(int)> f5 = bind(&AA::show, &a, placeholders::_1);
    f5(45);

    return 0;
}

可变函数和参数实现

#include<iostream>
#include<functional>

using namespace std;

void print() {}

template<typename T, typename ...Args>
void print(T arg, Args...args) {
    cout << arg << endl;
    print(args...);
}

template<typename ...Args>
void biaobai(Args...arg) {
    print(arg...);
}
//万能表白函数
/**
 * @tparam Fn 可调用函数
 * @tparam Args 可变参数
 * @return bind(fn, args...)
 *
 * 如果传递的值是右值 移动赋值,&&和forward完美转发
 */
template<typename Fn, typename ...Args>
auto show_love(Fn fn, Args...args)-> decltype(bind(fn, args...)) {
    auto f = bind(fn, args...);
    f();
    return f;
}

void show0(const string &s) {
    cout << "亲爱的" << s << ",爱你" << endl;
}
void show1(){
    cout << "亲爱的"<< ",爱你" << endl;
}
auto f = [](const string&s){
    cout << "亲爱的" << s << ",爱你" << endl;
};

struct AA{
    void operator()(const string &s) {
        cout << "亲爱的" << s << ",爱你" << endl;
    }
};


int main() {
    show_love(show0,"ying");
    show_love(show1);
    show_love(AA(),"ying");

    return 0;
}

回调函数实现

在消息队列和网络库的框架中,当接收到消息(报文)时,回调用户自定义的函数对象,把消息(报文)参数传给它,由它决定如何处理

#include<iostream>
#include<functional>

using namespace std;

template<typename CallBackType>
void performCallback(const string &s, const CallBackType &callback) {
    callback(s);// 调用回调函数并传递消息
}

class MyCallBack {
public:
    void myFunc(const string &message) {
        cout << "我的信息是:" << message << endl;
    }
};

int main() {
    MyCallBack m;

    auto boundFunc = bind(&MyCallBack::myFunc,&m,placeholders::_1);

    performCallback("你好啊",boundFunc);
    return 0;
}

替代虚函数

C++虚函数在执行过程中会跳转两次(先查找对象的函数表,再次通过该函数表中的地址找到真正的执行地址),这样的话,CPU会跳转两次,而普通函数只跳转一次

CPU 每跳转一次,预取指令要作废很多,所以效率会很低(百度)

为了管理的方便 (基类指针可指向派生类对象和自动析构派生类) 保留类之间的继承关系。

包装器和绑定器可以代替虚函数的功能并且无性能损失

#include<iostream>
#include<functional>

using namespace std;

struct Hero {
    /*virtual void show(){
        cout<< "英雄释放了技能" <<endl;
    }*/
    function<void()> m_callback;  // 用于绑定子类的成员函数

    //注册子类成员函数,子类成员函数没有参数
    template<class Fn, class ...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 << "西施释放了技能" << endl;
    }
};

struct HX : public Hero {
    void show() {
        cout << "韩信释放了技能" << endl;
    }
};

int main() {
    int id;
    cout << "请输入英雄id:";
    cin >> id;

    Hero *p = nullptr;
    if (id == 1) {
        p = new XS;
        p->callback(&XS::show, static_cast<XS *>(p));
    } else if (id == 2) {
        p = new HX;
        p->callback(&HX::show, static_cast<HX *>(p));
    }
    if (p != nullptr) {
        p->show();
        delete p;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值