C++高级开发之可调用对象functionbind

Posted dearQiHao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++高级开发之可调用对象functionbind相关的知识,希望对你有一定的参考价值。

目录

可调用对象

  以前函数调用总是离不开一堆圆括号,没错“()”就是函数调用的一个明显标记,这个 “()”有一个称呼叫函数调用运算符
  那么在类中重载了这个函数调用运算符“()”,就可以像使用函数一样是用那个该类的对象,或者换句话说就可以像函数调用一样来“调用”该类的对象

1.函数指针

  创建一个.cpp文件然后加入以下代码

void myfunc(int tv)

	cout << "myfunc()函数执行了,tv=" << tv << endl;


  在 main函数中加入如下代码

	void(*pmf)(int) = myfunc;
	pmf(20);

2.具有 operator() 成员函数的类对象(仿函数/函数对象)

  仿函数的定义:仿函数又称为函数对象,是一个能行使函数功能的类所定义的对象。防寒输得语法几乎和普通的函数调用一样。
  在 cpp文件前面增加如下 TC类定义

class TC

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

  在main函数中加入如下代码(可以选择将之前的main函数注释掉)

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

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

  可被转换为函数值指针的类对象也可以叫做 仿函数或函数对象
  在 cpp文件中增加如下 TC2类定义

class TC2

public:
	using tfpoint = void(*)(int);

	static void mtsfunc(int tv)	//静态成员函数
	
		cout << "TC2::mysfunc()静态成员函数执行了,tv=" << tv << endl;
	
	operator tfpoint()  return myfunc; //类型转换运算符/类型转换函数
;

在main主函数中,加入如下代码

	TC2 tc2;
	tc2(30);

4.类成员函数指针

  在前面的 TC类中增加一个 public修饰的成员函数和一个成员变量

public:
	void ptfunc(int tv)
	
		cout << "TC::ptfunc()执行了,tv=" << tv << endl;
	;
	int m_a;

在 main主函数中加入如下代码

	TC tc3;
	void(TC::*myfpoint)(int) = &TC::ptfunc;//类成员函数指针变量 myfpoint定义并被给初值
	(tc3.*myfpoint)(999);	//要调用你成员函数,就必须要用到对象 tc3

6.总结

  可调用对象首先被看做一个对象。那么有没有一种方法能够把这些可调用对象的调用形式统一起来呢?有,那就是使用 std::function 把这些可调用对象包装起来。

std::function 可调用对象包装器

  要使用这个类模板,在 cpp文件中要添加头文件 #include <functional>
  这个蕾姆办理装的是各种可调用对象,比较遗憾的是不能装类成员函数指针。因为类成员函数指针是需要类对象参与才能完成调用的。
   #include <functional>类模板的特点是:**通过指定模板参数,它能够用统一的方式来处理各种可调用对象。

1.绑定普通函数

  在 main 主函数中加入如下代码

				//返回类型(参数列表)
	std::function<void(int)>f1 = myfunc;//绑定一个普通函数,注意<>中的格式
	f1(100);//调用普通函数

2绑定类的静态成员函数

  在前面的TC类中增加一个 public修饰的静态成员函数

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

在 main函数中加入如下代码

						//返回类型(参数列表)
	std::function<int(int)> fs2 = TC::stcfunc;
	fs2(110);

3绑定仿函数

  在 main函数中,加入如下代码

	TC tc3;
	std::function<void(int)> f3 = tc3;//提示使用了未初始化的局部变量“tc3”,因为类TC里有成员变量没被初始化,需要增加一个构造函数,还应该初始化一下成员变量才好


  在 TC类中增加 public修饰的构造函数并初始化成员变量 m_a

TC()  //构造函数
	
		m_a = 1;
	//加入之后不会再报错了

在 main主函数中,继续增加代码

	f3(120); //TC::operator()执行了, tv = 120

	TC2 tc4;
	std::function<void(int)> f4 = tc4;
	f4(150); //TC2::mysfunc()静态成员函数执行了, tv = 150

4范例演示

在 cpp文件 前面增加如下 CB类和 CT类定义

class CB

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

	void runcallback(void)
	
		fcallback();
	
;

class CT

public:	
	CT()//构造函数
	
		cout << "CT::CT()执行" << endl;
	
	CT(const CT&)//拷贝构造函数
	
		cout << "CT::CT(const CT&)执行" << endl;
	
	~CT()
	
		cout << "CT::~CT()执行" << endl;
	
	void operator()(void)
	
		cout << "CT::opertor()执行" << endl;
	
;

在 main函数中,加入如下代码:

	CT ct; //可调用对象,这行导致CT构造函数的执行
	CB cb(ct); //cb需要可调用对象做参数来构造,ct因为有operator()所以可以转为const std::function<void (void)> &对象。
					 //这行导致CT拷贝构造函数被执行多次
	cb.runcallback(); //CT::opertor()执行


再来一个范例,在 cpp前面增加如下代码

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

	f(cs);


void runfunc(int x)

	cout << x << endl;

在 main中函数中,加入如下代码:

		for (int i = 0; i < 10; i++)
		
			mycallback(i, runfunc);//0,1,2,3,4,5,6,7,8,9
		


  从范例中可以看出 std::function 的灵活性非常高,能容纳的可调用对象种类也非常多。

std::bind 绑定器

  要使用这个函数模板,在 cpp文件前面要包含如下头文件#include<funcitonal>
  std::bind能够将对象以及相关的参数绑定到一起,绑定完成后可以直接调用,也可以用 std::function进行保存,在需要的时候调用。该函数模板的一般使用格式如下:

std::bind(待绑定的函数对象/函数指针/成员函数指针,绑定参数1...绑定参数n);

std::bind有两个意思:

  • 将可调用对象和参数绑定到一起,构成一个仿函数,所以可以直接调用
  • 如果函数有多个参数,可以绑定部分参数,其他的参数在调用的时候指定
      具体演示:在main函数中加入如下代码:
	void myfunc1(int x, int y, int z)

	cout << "x=" << x << ",y=" << y << ",z=" << z << endl;

在主函数中,加入如下代码

//表示绑定函数myfunc1的第一,二,三个参数值为:10 20 30,返回值auto表示我们不关心它返回的是啥类型,
//实际它返回的也是个仿函数类型对象,可以直接调用,也可以赋给std::function
	auto bf1 = std::bind(myfunc1, 10, 20, 30);
	bf1();


  上述范例非常简单,在 std::bind中,就可以直接给 myfunc1指定各参数。
  具体演示2:

//表示绑定函数myfunc1的第三个参数为30,而myfunc1的第一,二个参数分别由调用bf2时的第一,二个参数指定, _1、_2...、_20这种是标准库里定义的,占位符的含义,类似这样的参数有20个,
//够我们用了,这里这个placeholders::_1表示这个位置(当前该placeholders::_1所在的位置)将在函数调用时,被传入的第一个参数所代替。
	auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30);
	bf2(5, 15);


直接调用也可以

	std::bind(myfunc1, placeholders::_1, placeholders::_2,30)(10, 20);


再看一例:
在 cpp文件前面增加 myfun2函数的定义。

void myfunc2(int& x, int& y)

	x++;
	y++;

在main函数中加入如下代码。注释很关键

		int a = 2;
		int b = 3;
		auto bf4 = std::bind(myfunc2, a, placeholders::_1);
		bf4(b); //执行后a=2,b=4。这说明:bind对于预先绑定的函数参数是通过值传递的,所以这个a实际上是值传递的。
				//bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的,所以这个b实际上是引用传递的
		cout << "a: "<<a<<"b:"<<b << endl;


在看一例:
在 cpp文件中增加 CQ类定义

class CQ

public:
	CQ()
	
		printf("CQ::CQ()构造函数执行,this=%p\\n", this);
	
	CQ(const CQ&)
	
		printf("CQ::CQ(const CQ&)拷贝构造函数执行,this=%p\\n", this);
	
	~CQ()
	
		printf("CQ::~CQ()析构函数执行,this=%p\\n", this);
	
public:
	void myfunpt(int x, int y)
	
		cout << "x=" << x << ",y=" << y << endl;
		m_a = x;
	
	int m_a = 0; //成员变量
;

在main函数中,加入如下代码:

	CQ cq; //一个类对象
	auto bf5 = std::bind(&CQ::myfunpt, cq, placeholders::_1, placeholders::_2); //类函数有绝对地址,和对象无关,但要被调用必须有类对象参数
	bf5(10, 20);//对成员函数的调用


  注意上面代码中,std::bind的第二个参数 cq会导致生成一个临时的 CQ对象std::bind是将该临时对象和相关的成员函数以及多个参数绑定到一其,后续对 myfunpt成员函数的调用修改的是这个临时的 CQ对象的额m_a,并不影响真实的 cq对象的 m_a值
  如果将 std::bind的第二个参数 cq前面增加 &,这样就不会导致生成一个临时的CQ对象,后续的修改就是直接对cq对象的修改了。

在main函数中继续计入代码,观察 bind和 function的配合使用

	//bind 和 function配合使用(bind返回值直接赋给 std::function类型)
	CQ cq;
	std::function<void(int, int)> bfc6 = std::bind(&CQ::myfunpt, cq, std::placeholders::_1, std::placeholders::_2);
	bfc6(10, 20);


  cq->bind->function(构造->拷贝->拷贝)
加入饮用之后能够明显看到效率的提高

总结

  因为有了占位符,所以 std::bind的使用就变得非常灵活。可以直接绑定函数的所有参数,也可以使用 std::placeholders来决定bind所在位置的参数将会属于调用发生时的第几个参数。

  • std::bind 的思想实际上是一种延迟计算的思想,将可调用对象保存起来,然后后在需要的时候在调用。
  • std::function 一般要绑定一个可调用对象,类成员函数不能被绑定。而 std::bind更加强大,成员函数、成员变量等都能绑定。现在通过 std::function和std::bind的配合,所有的可调用对象都有了统一的操作方法。

以上是关于C++高级开发之可调用对象functionbind的主要内容,如果未能解决你的问题,请参考以下文章

C++高级开发之可调用对象functionbind

c++11之functionbind

什么是 C++ 中的可调用对象?

转载C++ functionbind和lambda表达式

查明 C++ 对象是不是可调用

C++可调用对象学习笔记