函数指针和std::function、可调用对象包装器和绑定器

一、可调用对象

在C++中存在“可调用对象”这么一个概念。

1、函数指针

普通函数指针方式

#include <iostream>
using namespace std;

void func(int a) {
	cout << "func pointer " << a << endl;
}

int main() {
	void (*ptr)(int) = func;	// 函数指针
	void(&ref)(int) = func;		// 函数引用	c++11

	ptr(1);						// 或者写成(*ptr)(1);
	ref(2);						// 指针和引用调用方式相同

	int(*ptr_lambda)(int) = [](int b) {		// lamdba表达式
		cout << "lambda function" << endl;
		return b;
	};

	cout << "lambda " << ptr_lambda(3) << endl;
	return 0;
}

执行结果:

func pointer 1
func pointer 2
lambda function
lambda 3

using方式:

#include <iostream>
using namespace std;

void func(int a) {
	cout << "func pointer " << a << endl;
}

int main() {
	using ptr = void(*)(int);
	ptr p = func;					// 或写为ptr p = &func;
	(*p)(1);						// 或者写为(*p)(1);

	return 0;
}

执行结果:

func pointer 1

2、仿函数

函数指针不支持指向仿函数!!!

#include <iostream>
using namespace std;

class Myclass {
public:
	void operator()() {
		cout << "operator()" << endl;
	}
};

int main() {
	Myclass myclass;
	myclass();

	return 0;
}

执行结果:

operator()

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

#include <iostream>

using func_ptr = void(*)(int);

class MyClass {
public:
    // 示例成员函数
    static void print(int value) {
        std::cout << "Value: " << value << std::endl;
    }

    // 转换运算符,将类对象转换为函数指针
    operator func_ptr() {
        return print;
    }
};

int main() {
    MyClass myObject;

    // 使用转换运算符获取成员函数指针
    void (*functionPointer)(int) = myObject;

    // 使用函数指针调用成员函数
    functionPointer(42);

    return 0;
}

chatGPT版本:

#include <iostream>

class MyClass {
public:
    // 示例成员函数
    void print(int value) {
        std::cout << "Value: " << value << std::endl;
    }

    // 转换运算符,将类对象转换为函数指针
    operator void(*)(int)() {
        return &MyClass::print;
    }
};

int main() {
    MyClass myObject;

    // 使用转换运算符获取成员函数指针
    void (*functionPointer)(int) = myObject;

    // 使用函数指针调用成员函数
    functionPointer(42);

    return 0;
}

4、类成员函数指针、者类成员指针及类的成员指针(变量)

指向类成员函数时,需要加上"类名::"区分普通函数和类成员函数。

普通函数指针方式

#include <iostream>
using namespace std;

class Base {
public:
	int m_a;
	int m_b;
public:
	Base(int a, int b) : m_a(a), m_b(b) {}

	void display() {
		cout << "Base display" << endl;
	}
	static void count() {
		cout << "Base Count" << endl;
	}
};

int main() {
	// 类成员函数
	void (Base:: * ptr_display)() = &Base::display;	// 必须加'&'符号
	Base b(1, 2);
	(b.*ptr_display)();			// 调用时需要绑定对象

	// 类静态函数
	void (*ptr_count)() = &Base::count;		// 可替换为Base::count,无需加'&'符号
	ptr_count();				// 调用时无需绑定对象

	// 类成员指针指向类成员变量
	int Base::* obj_ptr = &Base::m_a;
	(b.*obj_ptr) = 10;
	cout << "number is: " << b.m_a << endl;

	return 0;
}

using方式:

#include <iostream>
using namespace std;

class Base {
public:
	int m_a;
	int m_b;
public:
	Base(int a, int b) : m_a(a), m_b(b) {}

	void display() {
		cout << "Base display" << endl;
	}
	static void count() {
		cout << "Base Count" << endl;
	}
};

int main() {
	// 类成员函数
	using ptr = void (Base::*)();
	ptr ptr_display = &Base::display;	// 必须加'&'符号
	Base b(1, 2);
	(b.*ptr_display)();			// 调用时需要绑定对象

	// 类静态函数
	using ptr_static = void (*)();
	ptr_static ptr_count = &Base::count;		// 可替换为Base::count,无需加'&'符号	
	ptr_count();				// 调用时无需绑定对象
	
    // 类成员指针指向类成员变量
	using obj_ptr = int Base::*;
	obj_ptr p = &Base::m_a;

	b.*p = 10;
	cout << "number is: " << b.m_a << endl;
	return 0;
}

执行结果:

Base display
Base Count
number is: 10

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

在C++中,std::function是一个可调用对象的包装器,允许您将各种可调用实体(函数、函数指针、成员函数指针、Lambda 表达式等)存储在一个对象中,然后通过该对象进行调用。

注意:

  1. std::function必须要包含一个叫做functional的头文件;

  2. 类的成员函数不能直接通过std::function进行打包,它还需要std::bind进行协助

格式:

#include <functional>
std::function<返回值类型(参数类型列表)> 对象名称 = 可调用对象;

1、常见用法

(1)包装普通函数

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

void func(int a) {
	cout << "func pointer " << a << endl;
}

int main() {
	// 普通函数
	function<void(int)> f1 = func;
	f1(5);

	// lamdba
	function<void(void)> f2 = []() {
		cout << "lamdba" << endl;
	};
	f2();

	return 0;
}

执行结果:

func pointer 5
lamdba

(2)包装类的静态函数

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

class Base {
public:
	static void count() {
		cout << "Base Count" << endl;
	}
};

int main() {
	// 类静态成员函数
	Base b;
	function<void(void)> f1 = Base::count;
	f1();

	return 0;
}

执行结果:

Base Count

(3)包装仿函数

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

class Func {
public:
	void operator()(int a) {
		cout << "operator: " << a << endl;
	}
};

int main() {
	// 仿函数
	Func func;
	function<void(int)> f1 = func;
	f1(5);

	return 0;
}

执行结果:

operator: 5

2、回调函数

使用对象包装器取代函数指针进行回调函数。

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

class Myclass
{
public:
    // 构造函数参数是一个包装器对象
    Myclass(const function<void()>& f) : callback(f)
    {
    }

    void print()
    {
        callback(); // 调用通过构造函数得到的函数指针
    }
private:
    function<void()> callback;
};

// 仿函数
class A
{
public:
    void operator()() {
        cout << "仿函数示例" << endl;
    }

    static void hello() { 
        cout << "类静态成员函数" << endl;
    }
};

// 普通函数
void func() {
    cout << "普通函数示例" << endl;
}

int main(void)
{
    // 仿函数通过包装器对象进行包装
    A a;
    Myclass myclass1(a);         
    myclass1.print();

    // 普通函数
    Myclass myclass2(func);
    myclass2.print();

    Myclass myclass3(A::hello);
    myclass3.print();

    return 0;
}


执行结果:

仿函数示例
普通函数示例
类静态成员函数

三、绑定器std::bind

std::bind 是 C++ 标准库中 <functional> 头文件提供的一个函数模板。它用于将参数绑定到一个函数,创建一个表示对原始函数调用的函数对象。在需要将特定参数固定或重新排列的情况下,std::bind 可以很有用,特别是在处理算法或回调函数等场景时。

std::bind主要作用:

  1. 将可调用对象与其参数一起绑定成一个仿函数;
  2. 将多元(参数个数为n,n>1)可调用对象转换为一元或者(n-1)元可调用对象,即只绑定部分参数。

std::bind语法:

// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);

1、绑定器绑定参数得到仿函数

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

void print(int x, int y)
{
    cout << x << " " << y << endl;
}

int main(void)
{
    // 使用绑定器绑定可调用对象和参数并得到仿函数
    bind(print, 3, 4)();
    bind(print, placeholders::_1, 2)(5);
    bind(print, 2, placeholders::_1)(5);
   
    // bind(print, 2, placeholders::_2)(5); // 错误, 参数列表中没有第二个参数,placeholders::_2与参数列表无法对应
    bind(print, 2, placeholders::_2)(3, 4); // 调用时第一个参数3不会被使用

    bind(print, placeholders::_1, placeholders::_2)(7, 8);  // 占位符与参数列表要相对应
    bind(print, placeholders::_2, placeholders::_1)(7, 8);

    return 0;
}

执行结果:

3 4
5 2
2 5
2 4
7 8
8 7

placeholders::_1是一个占位符,代表这个位置将在函数调用时被传入的第一个参数所替代。同样还有其他的占位符placeholders::_2、placeholders::_3、placeholders::_4、placeholders::_5等依次类推…

2、std::function和std::bind结合使用的例子

std::bind绑定器返回的是一个仿函数类型,得到的返回值可以直接赋值给一个std::function,我们不需要关心绑定器的返回值类型,可以方便的使用auto进行自动类型推导。

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

void callFunc(int x, const function<void(int)>& f) {
    f(x);
}

void print(int x)
{
    cout << "x = " << x << endl;
}

void add100(int x)
{
    cout << "x +100 = " << x + 100 << " ";
}

int main(void)
{
    // 使用绑定器绑定可调用对象和参数
    auto f1 = bind(print, placeholders::_1);
    callFunc(10, f1);
   
    auto f2 = bind(add100, placeholders::_1);
    callFunc(10, f2);

    return 0;
}

执行结果:

x = 10
x +100 = 110

2、std::bind实现类成员函数指针、类成员指针的包装

可调用对象包装器std::function是不能实现对类成员函数指针或者类成员指针的包装的,但是通过绑定器std::bind的配合之后,就可以解决这个问题了。

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

class Myclass
{
public:
    void print(int x, int y)
    {
        cout << "x: " << x << ", y: " << y << endl;
    }
    int m_a = 100;
};

int main(void)
{
    Myclass c;
    // 绑定类成员函数
    function<void(int, int)> f1 = bind(&Myclass::print, &c, placeholders::_1, placeholders::_2);
    // 绑定类成员变量(公共)
    function<int& (void)> f2 = bind(&Myclass::m_a, &c);	// 类成员需要可读可写,因此使用引用&(否则不可改变变量值),int代表m_a的类型,指定void则是因为可调用类成员变量没有参数

    f1(1, 2);
    f2() = 10;
    cout << "t.m_a: " << c.m_a << endl;

    return 0;
}

执行结果:

x: 1, y: 2
t.m_a: 10
  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值