假如有这样一种需求:有一个模块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;
}