用 std::function 函数指针的形式实现多态

        假如有这样一种需求:有一个模块A会被很多模块使用;而不同模块在使用时,需要A中的一个函数完成不同的功能。

        首先,我们可以使用虚函数来实现:A中定义一个基类,基类中有一个虚函数;不同模块对这个虚函数进行重写,以实现不同的功能。

       其次,也可以用函数指针来实现,C++11的新特性std::fcuntion可更方便地实现。A中定义一个 std::function的成员变量,一个public的注册函数,用于其他模块向A模块申请注册不同的函数实现。

      std::function 是一个模板,需要在<>中给出模板参数:函数类型,包括返回值类型、参数类型。

      std::function的使用方法可参考

       https://en.cppreference.com/w/cpp/utility/functional/function

       http://www.cplusplus.com/reference/functional/function/function/ 

       对一个std::function变量赋值时,不同的函数需要不同的方式。

      std::function<void(int)> f_display = print_num;  // print_num是一个全局函数,非 类内成员函数

      std::function<void(const Foo&, int)> f_add_display = &Foo::print_add //print_add是Foo类的成员函数

      如果想实现上述需求,class B的函数 需要传递注册给class A,就需要用到std::bind,使用如下这种形式对std::function进行赋值。这里的std::placeholders::_1 是占位符,表示foo 函数的第一个参数。如果foo没有参数,则std::bind中的_1需要删除。

    // store a call to a member function and object

    using std::placeholders::_1;

    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

    f_add_display2(2);

    以下是一个实现上述需求的一个示例,但这个示例编译ok,但执行时会报错。

#include <functional>
#include <iostream>
using namespace std;
// g++ -g -Wall -std=c++11 main_error.cpp  -o sim_error
class MyTest
{
public:
	MyTest()
	{}
	void PrintFunc()
	{
		cout<<"MyTest::PrintFunc "<<endl;
		if (m_callback_0_not_ptr != NULL)
        {
			cout<<"MyTest::PrintFunc call m_callback_0_not_ptr "<<endl;
            m_callback_0_not_ptr();// can get right result
        }
		if (m_callback_0 != NULL)
        {
            (*m_callback_0)(); //throwing an instance of 'std::bad_function_call'
        }
	}	

	inline void register_callback_0(std::function<void()>* _fun) 
	{
		cout<<"MyTest::register_callback_0 "<<endl;
		m_callback_0 =  _fun;
		(*m_callback_0)(); 		// can get right result

		m_callback_0_not_ptr = *_fun ;
	}

private:
	std::function<void()> * m_callback_0 = NULL;
	std::function<void()>   m_callback_0_not_ptr = NULL;
};

class Platform
{
public:
	Platform()
	{
		std::function<void()> fun_ptr_0 =  std::bind(&Platform::fun_0, this  )  ;
		m_test.register_callback_0( &fun_ptr_0 );
	};
	void fun_0()
	{
		cout<<"Platform::fun_0 "<<endl;
	}

	MyTest   m_test;
};

int main(int argc, char** argv)
{
    Platform * m_platform = new Platform();
	m_platform->m_test.PrintFunc();
    return 0;
}

           std::bad_function_call 提示这个函数对象是空的。个人分析原因如下,调用register_callback_0(std::function<void()>* _fun)时,&fun_ptr_0 传递给了_fun, 在这个函数的作用域内,* _fun 确实是可以得到正确的结果:Platform::fun_0函数。但由于_fun 是临时的,_fun这个指针指向的内容在register_callback_0结束时就消亡了,所以再次(*m_callback_0)()的时候出错。这个跟普通指针在函数调用时的传递还是不一样的。

         推荐使用以下示例中的方法,抛弃掉 std::function*, 直接用 std::function。

#include <functional>
#include <iostream>
using namespace std;
// g++ -g -Wall -std=c++11 main_right.cpp  -o sim_right
class MyTest
{
public:
	MyTest()
	{}
	void PrintFunc()
	{
		cout<<"MyTest::PrintFunc "<<endl;
		if (m_callback_0 != NULL)
        {
            m_callback_0();
        }
		if (m_callback_1 != NULL)
        {
			int b = 100;
            m_callback_1(b);
        }		
	}	

	inline void register_callback_0(std::function<void()> _fun ) 
	{
		cout<<"MyTest::register_callback_0 "<<endl;
		m_callback_0 = _fun;
	}
	inline void register_callback_1(std::function<void(int)> _fun) 
	{
		cout<<"MyTest::register_callback_1 "<<endl;
		m_callback_1 = _fun;
	}
private:
	std::function<void()> 	 m_callback_0 = NULL;
	std::function<void(int)> m_callback_1 = NULL;
};

class Platform
{
public:
	Platform()
	{
		std::function<void()> fun_ptr_0 =  std::bind(&Platform::fun_0, this  )  ;
		m_test.register_callback_0( fun_ptr_0 );

		using std::placeholders::_1;
		std::function<void(int)> fun_ptr_1 =  std::bind(&Platform::fun_1, this, _1  )  ;
		m_test.register_callback_1( fun_ptr_1 );		
	};
	void fun_0()
	{
		cout<<"Platform::fun_0 "<<endl;
	}
	void fun_1(int a)
	{
		cout<<"Platform::fun_1, a = "<<a<<endl;
	}	

	MyTest   m_test;
};

int main(int argc, char** argv)
{
    Platform * m_platform = new Platform();
	m_platform->m_test.PrintFunc();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zgcjaxj/article/details/109150077