C++ 可调用对象

概念

在C++中,存在“可调用对象”这么一个概念,准确来说,可调用对象有如下几种定义:

  1. 是一个函数指针
  2. 是一个具有operator()成员函数的类对象(仿函数)
  3. 是一个可被转换为函数指针的类对象
  4. 是一个类成员(函数)指针
void func() 
{
	cout << "func" << endl;
}
struct Foo
{
	void operator()()
	{
		cout << "Foo::operator()" << endl;
	}
};
struct Bar
{
	using fr_t = void(*)(void); //函数指针类型
	static void func()
	{
		cout << "Bar::func" << endl;
	}
	operator fr_t() //将对象通过该方法,强转为一个指针
	{
		return func;
	}
};
struct A
{
	int a_;
	void mem_func()
	{
		cout << "A::meme_func" << endl;
	}
};

int main()
{
	void(*func_ptr)() = &func;  //1 指向函数指针
	func();
	
	Foo foo;
	foo();   //2 定义对象 重载()

	Bar bar;
	bar();   //3 调动重载成为函数指针,返回一个静态函数地址,加()进行调用

	void (A:: * mem_func_ptr)() = &A::mem_func; //函数指针指向A作用域下mem_func函数地址
	int A::* mem_obj_ptr = &A::a_;//仅仅是一个偏移量

	A aa;  //普通成员方法需要使用对象 进行激活,静态成员方法不需要
	(aa.*mem_func_ptr)();   // .* 调用类成员函数
	(aa.*mem_obj_ptr) = 10;

	return 0;
}

从上述可以看到,除了类成员指针之外,上面定义涉及的对象均可以像一个函数那样做调用操作

在C++11中,像上面例子中的这些对象都被称作可调用对象,相对应的,这些对象的类型被统称为“可调用类型”

C++中的可调用对象虽然具有比较统一的操作形式(除了类成员指针之外,都是后面加括号进行调用),但定义方法五花八门,这样在我们试图使用统一的方式保存,或传递一个可调用对象时,会十分繁琐

现在,C++11 通过提供std::function 和 std::bind 统一了可调用对象的各种操作

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

std::function 是可调用对象的包装器,它是一个模板类,可以容纳除了类的成员(函数)指针外的所有可调用对象,通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟执行它们

void fun(void)
{
	cout << __FUNCTION__ << endl; 
}
class Foo
{
public:
	static int foo_func(int a)
	{
		cout << __FUNCTION__ << "(" << a << ")" << endl;
		return 0;
	}
};
class Bar
{
	int val;
public:
	Bar(int x = 0):val(x){}
	int operator()(int a) const
	{
		cout << __FUNCTION__ << "(" << val << ")" << endl;
		return a;
	}
};
int main()
{
	std::function<void(void)> fr1 = fun;
	fr1();

	std::function<int(int)> fr2 = Foo::foo_func;
	fr2(10);

	Bar bar(12);
	Bar bac(23);
	fr2 = bar;

	fr2(12);

	fr2 = bac;
	fr2(34);
}

在这里插入图片描述

回调形式

int func(int a = 10)
{
	cout << __FUNCTION__ << endl;
	a += 10;
	cout << a << endl;
	return a;
}
int main()
{

	std::function<int(int)> fr = func;
	int x = fr();  //error!!! 需要参数
  	return 0;
}

绑定器并不认为可以得到一个默认值

void fun(int a, int b, int (*pfun)(int, int))
{
	if (pfun != nullptr)
	{
		pfun(a, b); //通过函数指针,进行回调方案
	}
}
class A
{
	std::function<void()> callback_;
public:
	A(const std::function<void()>& f) :callback_(f) {}
	void notify(void)
	{
		callback_(); //执行
	}
};
class Foo
{
public:
	void operator()()
	{
		cout << __FUNCTION__ << endl;
	}
};
int main()
{
	Foo foo;
	A aa(foo);   //传入foo
	aa.notify(); //通知
	return 0;
}

打印偶数

void call_when_even(int x, const std::function<void(int)>& f)
{
	if (!(x & 1))
	{
		f(x);
	}
}
void output(int x)
{
	cout << x << " " << endl;
}
int main()
{
	for (int i = 0; i < 10; ++i)
	{
		call_when_even(i, output);
	}
	return 0;
}

std::bind 绑定器

std::bind 用来将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候

通俗来讲,它主要有两大作用:

  1. 将可调用对象与其参数一起绑定成一个仿函数
  2. 将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1) 元可调用对象,即只绑定部分参数
void call_when_even(int x, const std::function<void(int)>& f)
{
	if (!(x & 1))
	{
		f(x);
	}
}
void output(int x)
{
	cout << x << " " ;
}
void output_add_2(int x)
{
	cout << x + 2 << " " ;
}

int main()
{
	{
		auto fr = std::bind(output, std::placeholders::_1);
		//                  函数    占位符
		for (int i = 0; i < 10; ++i)
		{
			call_when_even(i, fr);
			//          参数  
		}
	}
	cout << endl;
	{
		auto fr = std::bind(output_add_2, std::placeholders::_1);
		for (int i = 0; i < 10; ++i)
		{
			call_when_even(i, fr);
		}
	}
}

在这里,我们使用了std::bind,在函数外部通过绑定不同的函数,控制了最后的执行结果

我们使用auto fr 保存 std::bind 的返回结果,是因为我们并不关心 std::bind 真正的返回类型(实际上std::bind 的返回类型是一个stl内部定义的仿函数类型),只需要知道它是一个仿函数,可以直接赋值给一个std::function,当然,这里直接使用std::function类型来保存std::bind 的返回值也是可以的

std::placeholders::_1 是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代

void output(int x, int y)
{
	cout << x << " " << y << endl;
}
void fun()
{
	cout << __FUNCTION__ << endl;
}
void func(int x)
{
	cout << __FUNCTION__ << " " << x << endl;
}
int main()
{
	int a = 10;
	//std::bind(output)(1, 2);
	std::bind(func,std::placeholders::_1)(2); //使用占位符,后面给出了参数
	std::bind(func, 12)(); //直接给出了参数
	std::bind(func, a)();
	return 0;
}

可以将bind看作是一个仿函数,并且若在bind中给出了参数,后面则不需要再给定参数,而若给出的是一个占位符,则需要后面给出参数

class Alloc
{
	size_t sz;
	int*& ptr;
public:
	Alloc(size_t s,int*& p):sz(s),ptr(p){}
	void operator()()
	{
		ptr = (int*)malloc(sz);
	}
};

int main()
{
	int* p = nullptr;
	int sz = sizeof(int) * 10;
	auto fr = std::bind(alloc, sz, std::ref(p));
	fr();

	if (p != nullptr)
	{
		cout << p << endl;
	}
}
void output(int x, int y)
{
	cout << x << " " << y << endl;
}
int main()
{
	std::bind(output, std::placeholders::_1, std::placeholders::_2)(1, 2);
	std::bind(output, std::placeholders::_2, std::placeholders::_1)(1, 2);
	return 0;
}

在这里插入图片描述

void output(int x, int y)
{
	cout << x << " " << y << endl;
}
int main()
{
	auto fr1 = std::bind(output, std::placeholders::_1, 23);
	auto fr2 = std::bind(output, 12, std::placeholders::_2);
	fr1(23);
	fr2(12);  //error!!! 这样是错误的

	return 0;
}

bind 函数要么指明参数要么通过占位符进行关联

void output(int x, int y,int& z)
{
	cout << "x: " << x << "y: " << y << endl;
	z = x + y;
}

using Task = std::function<void(void)>;

std::list<Task> g_tasklist;
void thread_func()
{
	cout << "thread begin" << endl;
	while (!g_tasklist.empty())
	{
		Task task = g_tasklist.front();
		g_tasklist.pop_front();
		task(); //调动事务
		//这是一个方法与数据的集合
	}
}
int main()
{
	int a = 10, b = 20;
	int z = 0;
	Task t1 = std::bind(output, a, b, std::ref(z));
	g_tasklist.push_back(t1);

	std::thread tha(thread_func);
	tha.join();
	cout << z << endl;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值