关于C++的函数指针以及它在C++11中的变化

本文介绍了C++中的函数指针和类成员函数指针的使用,并探讨了在C++11中如何通过std::function统一封装各种可调用对象,包括一般函数、静态成员函数、非静态成员函数、仿函数和匿名函数。通过std::function,可以简化代码并提高代码的灵活性。
摘要由CSDN通过智能技术生成

关于C++的函数指针以及它在C++11中的变化


在对函数指针赋值时,就结果而言,加或不加&的结果是一样的。比如:

void fcn();

void (*fp1) = fcn;		// 两者在vscode上的结果一致
void (*fp2) = &fcn;

1. 可调用对象(Callable Objects)

可调用对象有如下几种:

  • 函数指针

    // 1.函数指针
    void add(int a, int b) {
        cout << "a + b = " << a + b;
        return;
    }
    void (*func)(int, int) = add;						// 函数指针赋值
    func(1, 2);											// 函数指针调用
    
  • 类成员(函数)指针非静态成员函数指针前必须加上调用类的作用域class_name::,但是静态成员函数指针不需要。

    void (*func)(int, int);								// 静态成员函数指针、函数指针
    void (class_name::*func)(int, int);					// 非静态成员函数指针
    
    // 4.类成员函数指针
    class Object {
    public:
        // 静态成员函数
        static void add_static(int a, int b) {
            cout << "a + b = " << a + b;
        	return;
        }
        // 非静态成员函数
        void add(int a, int b) {
            cout << "a + b = " << a + b;
        	return;
        }
    }
    
    // (1) 静态成员函数
    
    void (*func)(int, int) = Object::add;				// 与一般函数的赋值相同
    func(1, 2);
    
    // (2) 非静态成员函数
    void (Object::*func)(int, int) = Object::add;		// 非静态类成员函数指针赋值
    // 通过对象调用
    Object obj;
    (obj.*func)(1, 2);									// 非静态类成员函数指针调用(必须加括号)
    // 通过对象指针调用
    Object* obj = new Object;
    (obj->*func)(1, 2);									// 非静态类成员函数指针调用(必须加括号)
    
  • 具有operator()成员函数的类对象(仿函数)

    // 2.仿函数
    struct Add {
        void operator() (int a, int b) {
            cout << "a + b = " << a + b;
        	return;
        }
    };
    Add add;											// 实例化的类对象(object), 即仿函数
    add(3, 4);					 						// 仿函数调用
    
  • 可被转换为函数指针的类对象

以上涉及的对象可以像一个函数一样做调用操作,统称为可调用对象。现在,C++11通过提供std::functionstd::bind统一了可调用对象的各种操作

2. std::function

以上可见,C++中的可调用对象的定义方法有很多种。为了统一泛化函数对象、函数指针、引用函数、成员函数的指针的各种操作,为了统一形式,C++11推出了std::function

std::function是可调用对象的包装器。它是一个类模板,可以包装:函数指针类成员函数指针任意类型的函数对象。以上使用的可调用对象都可以使用std::function<void(int, int)>类型来包装。

#include <functional>
typedef std::function<void(int, int)>	Fcn;	// 函数封装类型

2.1.封装一般函数

// 函数
void add1(int a, int b) {
    cout << "a + b = " << a + b;
    return;
}

Fcn func1 = add1;						// 封装
func1(1, 2);							// 调用

2.2.封装类成员函数

// 类成员函数指针
class Object {
public:
    // 静态成员函数
    static void add_static(int a, int b) {
        cout << "a + b = " << a + b;
    	return;
    }
    
    // 非静态成员函数 (参数列表中有隐藏的this指针)
    void add(int a, int b) {
        cout << "a + b = " << a + b;
    	return;
	}
}

2.2.1.静态成员函数

静态成员函数的封装方法与一般函数相同。

// (1) 静态成员函数 (以下两种均可)
Fcn func21 = Object::add_static;			// 封装	
Fcn func22 = &Object::add_static;			// 封装
func21(3, 4);								// 调用
func22(3, 4);
2.2.2.非静态成员函数

非静态成员函数比较特殊,因为含有this指针,需要通过bind()来实现封装

// (2) 非静态成员函数
/*
	error: conversion from 'void (Object::*)(int, int)' to non-scalar type 'std::function<void(int, 		int)>' requested function<void(int, int)> fcn3 = Object::add;
*/
Fcn func3 = Object::add;														// 编译出错	

Object obj;
// &obj的位置就是类的this指针
Fcn func31 = std::bind(Object::add, &obj, placeholders::_1, placeholders::_2);	// 编译成功
Fcn func32 = std::bind(&Object::add, &obj, placeholders::_1, placeholders::_2);	// 编译成功
func31(1, 2);
func32(1, 2);

2.3.封装仿函数

struct Functor {
    void operator() (int a, int b) {
        cout << "a + b = " << a + b;
    	return;
    }
}

Functor fcn;
Fcn fcn4 = fcn;
fcn4(1, 2);

2.4.封装匿名函数

auto fcn = [](int a, int b) {
    cout << "a + b = " << a + b;
    return;
};

Fcn fcn5 = fcn;			// 可以发现, lambda函数对象fcn可以被转换为Fcn类型
fcn5(1, 2);

3.应用实例

这是我最近遇到的一个场景,介绍如下:

若一个类A中有一个成员变量是一个函数对象,它调用的是另一个类B中的非静态成员函数。这应该如何实现对类A中的函数对象的赋值操作?

#include <functional>

typedef std::function<void()> callBackFcn;

class A {
private:
    callBackFcn cb;
public:
    A();
    ~A();
    
    void doCallBack() {
        return cb();
    }
}

class B {
public:
    void doTask() {
        cout << "this is B." << endl;
    }
}

这里在A的构造函数中实现对函数对象cb的赋值操作。

3.1.做法一

// 定义A的构造函数
A::A(callBackFcn fcn) {				// 区别: 值传递
    cb = fcn;
}

// 实例化A
B b;
A a(std::bind(B::doTask, &b));
a.doCallBack();

3.2.做法二

与做法二的唯一区别在于,这里是通过引用传递来进行函数对象的传参的。但是,重点来了!这里的引用传递必须是常量引用传递,也就是说,必须加上关键字const否则,无法通过编译。

若不加const关键字,就会得到如下的报错。翻译一下就是:不能将非常量的左值引用(这里是指fcn)绑定到一个右值类型(这里是指std::bind(B::doTask, &b))上。加上关键字const,将函数的形参类型变成常量的左值引用,就可以绑定右值类型了。

error: cannot bind non-const lvalue reference of type 'callBackFcn&' {aka 'std::function<void()>&'} to an rvalue of type 'callBackFcn' {aka 'std::function<void()>'}
// 定义A的构造函数
A::A(const callBackFcn& fcn) {		// 区别: 引用传递
    cb = fcn;
}

// 实例化A
B b;
A a(std::bind(B::doTask, &b));
a.doCallBack();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值