C++语法学习笔记三十: 可调用对象、std function、std bind

实例代码


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

using namespace std;


void myfunc(int tv){
	cout << "myfunc()函数执行了, tv" << tv << endl;
}

class TC{
public:
	void operator()(int tv){
		cout << "TC::operator() 执行了 tv = " << tv << endl;
	}
};

class TC2{

public:
	using tfpoint = void(*)(int); //定义一个函数指针类型
	static void myfunc(int tv){  //静态成员函数
		cout << "TC2::myfunc() 静态成员函数执行了 tv = " << tv << endl;
	}

	operator tfpoint(){ return myfunc; }
};

class TC3{
public:
	void operator()(int tv){
		cout << "TC3::operator() 执行了 tv = " << tv << endl;
	}
	void ptfunc(int tv){
		cout << "TC3::ptfunc() 执行了 tv = " << tv << endl;
	}


	static int stcfunc(int tv){
		cout << "TC3::stcfunc()静态函数执行了, tv= " << tv << endl;
		return tv;
	}

	int m_a;
};


class CB{
	std::function<void()> fcallback;
public:
	CB(const std::function<void()>& f) :fcallback(f){
		int i = 1;
	}

	void runcallback(void){
		fcallback();
	}
};

class CT{
public:
	void operator()(void){
		cout << "operator()执行" << endl;
	}

};


void mycallback(int cs, const std::function<void(int)> &f){
	f(cs);
}

void runfunc(int x){
	cout << x << endl;
}


void myfunc1(int x, int y, int z){
	cout << "x = " << x << ", y = " << y << ", z = " << z << endl;
}

void myfunc2(int&x, int&y){
	x++;
	y++;
}

class CD{
public:

	CD(){
		cout << "CD构造函数" << endl;
	}

	CD(const CD&tm){
		cout << "CD拷贝构造函数" << endl;
	}
	~CD(){
		cout << "CD析构函数" << endl;
	}
	void myfunpt(int x, int y){ //普通成员函数
		cout << "x = " << x << ", y = " << y << endl;
		m_a = x;
	}

	int m_a = 0;
};

class CF{
public:

	CF(){
		cout << "CF构造函数" << endl;
	}

	CF(const CF&tm){
		cout << "CF拷贝构造函数" << endl;
	}
	~CF(){
		cout << "CF析构函数" << endl;
	}
	
	void operator()(){
		cout << "operator()被调用" << this << endl;
	}

	int m_a = 0;
};

int main() {

	//一:可调用对象
	//两种:1.函数。 2.重载了()运算符的类的对象
	//(1.1) 函数指针

	void(*pmf)(int) = &myfunc; //定义一个函数指针pmf并给了初值
	//void(*pmf)(int) = myfunc; //这种写法和上面相同 (加不加& 符号 在这里无所谓)
	pmf(15); //调用函数, 这就是个可调用对象

	//(1.2) 具有operator()成员函数的类对象(仿函数)
	//仿函数的定义:仿函数(functor),它的行为类似于函数的东西
	//C++仿函数是通过在类中重载()运算符实现,又称为函数对象(function object),能行驶函数功能的类

	TC tc;
	tc(20); //调用的是()操作符,这也是个可调用对象。等价于tc.operator()(20);

	//(1.3) 可被转换为函数指针的类对象
	TC2 tc2;
	tc2(50); //先调用tfpoint, 再调用myfunc: 这也是个可调用对象。等价于 tc2.operator TC2::tfpoint()(50);
	
	//(1.4) 类成员函数指针
	TC3 tc3;
	void (TC3::*myfpointpt)(int) = &TC3::ptfunc; //(TC3::*myfpointpt) 中的TC3::表示一个范围(域)
	//类成员函数指针变量 myfpointpt 定义并给初值

	//成员函数是独立存在类中,不依赖具体对象
	//使用成员函数时,依赖具体对象
	(tc3.*myfpointpt)(68); //也是一个可调对象, 等价于 tc3.ptfunc(68);

	//上面演示了多种可调用对象调用方式,如何把各种不同的可调用对象的形式统一一下,
	//统一的目的是方便咱们调用。下面引入std::fuction。

	//二、std::fuction(可调用对象包装器) C++ 11中引入
	//std::fuction是个类模板, 用来装各种可调用对象, (不能装类成员函数指针,因为调用类成员函数指针需要一个具体的对象参与)
	//std::fuction类模板的特点:就是能够通过给它指定模板参数,它就能够 用统一的方式来处理函数。


	//(2.1) 绑定普通函数
	std::function<void(int)> f1 = myfunc;
	f1(100);

	//(2.2) 绑定类的静态成员函数
	std::function<int(int)> fs2 = TC3::stcfunc;//绑定一个类的静态成员函数
	cout << fs2(110) << endl;


	//(2.3) 绑定仿函数
	TC3 tc4;
	std::function<void(int)> f4 = tc4;
	f4(120);

	TC2 tc5;
	std::function<void(int)> f5 = tc5;
	f5(150);

	//(2.4) 范例演示
	CT ct; //可调用对象
	CB cb(ct); //cb需要可调用对象参数来构造,ct因为有operator() 所以可以转为 std::function<void()>&f 对象;
	cb.runcallback(); //执行 CT里的operator();

	for (int i = 0; i < 10; ++i) {
		mycallback(i, runfunc); //相当于 runfunc(i)
	}
	
	//三、std::bind绑定器 ,也是个类模板 C++ 11引入的
	//std::bind能够将对象以及相关的参数绑定到一起,绑定完后可以直接调用,
	//也可以用std::function进行保存,在需要的时候调用,

	//格式:
	//std::bind(待绑定的函数对象/ 函数指针/ 成员函数指针,参数绑定值1, 参数绑定值2.....,参数绑定值n);
	//a). 将可调用对象和参数绑定到一起,构成一个仿函数,所以可以直接调用;
	//b). 如果函数有多个参数,可以绑定一部分参数,其他参数在调用的时候指定;
	auto bf1 = std::bind(myfunc1, 10, 20, 30); //auto表示不关心bind的返回类型,其实bind它返回的是一个仿函数类型对象,可以直接调用,也可以赋给std::function
	bf1(); //执行myfunc1函数
	
	//表示绑定函数的myfunc1的第三个参数为30,而myfunc1的第一个和第二个参数分别由调用bf2时的第一,二个参数指定。
	//placeholders::_1 是标准库里定义的,占位符的含义,类似这样的占位符有20个,足够我们使用。
	//这里这个 placeholders::_1 表示这个位置(当前placeholders::_1所在的位置)将在函数调用时,被传入的第一个参数代替。
	auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30);
	bf2(5, 15); // 输出: 5 15 30

	auto bf3 = std::bind(myfunc1, placeholders::_1, placeholders::_1, 30);
	bf3(5, 15); // 输出: 5 5 30

	std::bind(myfunc1, placeholders::_1, placeholders::_2, 30)(5, 15);//这种调用也可以

	auto bf4 = std::bind(myfunc1, placeholders::_2, placeholders::_1, 30);
	bf4(5, 15); // 输出: 15 5 30

	
	int a = 2;
	int b = 3;

	auto bf5 = std::bind(myfunc2, a, placeholders::_1);
	bf5(b);

	cout << "a = " << a << "b = " << b << endl; //输出: a = 2, b = 4  
	//上面输出说明:bind对于预先绑定的函数参数是通过值传递的,所以这个a实际是值传递的
	//bind对于不事先绑定的参数,通过std::placeholders传递的参数,是通过引用传递的,
	//所以b实际上是引用传递,这要注意。
	
	CD cd;// 一个CD类对象
	auto bf6 = std::bind(&CD::myfunpt, cd, std::placeholders::_1, std::placeholders::_2);
	bf6(10, 20); //上面的第二个参数cd,会导致调用CD的拷贝构造函数来生成一个CD类型的临时对象,作为std::bind的返回值(bind返回仿函数类型对象)
					//后续的myfunpt调用 修改的是这个临时对象的m_a 值,并不影响真实cd对象的m_a值。

	auto bf7 = std::bind(&CD::myfunpt, &cd, std::placeholders::_1, std::placeholders::_2);
	bf7(10, 20);//上面的第二个参数cd前边如果加了& ,就不生成临时的CD对象,后续myfunpt调用修改的事cd对象m_a值
				//这说明此时bind返回的这个对象其实是cd对象本身(仿函数类型对象)
	
	//std::bind和std::function的配合使用
	CD cd1;
	std::function<void(int, int)> bfc6 = std::bind(&CD::myfunpt, cd1, std::placeholders::_1, std::placeholders::_2);
	bfc6(10, 20);

	//把成员变量地址当函数一样绑定,绑定的结果放在std::function<int &(void)>里保存;说白了就是用一个可调用对象的方式来
	//表示这个变量
	//std::function<int &()> bf8 = std::bind(&CD::m_a, &cd);

	std::function<int &()> bf9 = std::bind(&CD::m_a, cd1);// 如果bind第二个参数用的不是&,
	//那么这个bind会导致产生两次CD拷贝构造函数的执行。
	//第一次拷贝构造函数的执行是因为系统利用cd1来产生一个临时的CD对象。
	//第二次拷贝构造函数的执行是因为std::bind本身返回一个CD对象,要返回的这个CD对象(仿函数)拷贝自临时CD对象
	//但是std::bind执行完毕后,临时CD对象会被释放(第一次拷贝构造生成的对象),返回的这个CD对象(仿函数)就弄到bf9里
	bf9() = 60;

	
	auto rt = std::bind(CF()); //CF()是构造临时对象,然后又调用了拷贝构造函数生成了一个可调用对象,作为std::bind的返回内容
							//bind 返回仿函数类型对象,就是用拷贝构造函数构造起来的对象
	
	rt(); //调用的是operator() 

	auto bf10 = std::bind(runfunc, std::placeholders::_1); //runfunc的一个参数由调用时的第一个参数指定

	for (int i = 0; i < 10; i++) {
		mycallback(i, bf10); //调用的是runfunc
	}

	//四:总结
	//a) bind思想:延迟调用,将可调用对象统一格式,保存起来,需要的时候在调用
	//b) 我们有用std::function绑定一个可调用对象,类型成员不能绑。但std::bind 可以绑定成员函数、成员变量等等都能绑。
	

	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值