Pix*_*ist 400
注意:大多数答案都涵盖了函数指针,这是在C++中实现"回调"逻辑的一种可能性,但是到目前为止并不是我认为最有利的一种.
回调是类或函数接受的可调用(参见下文),用于根据该回调自定义当前逻辑.
使用回调的一个原因是编写通用代码,该代码与被调用函数中的逻辑无关,并且可以与不同的回调一起使用.
标准算法库的许多功能都<algorithm>使用回调.例如,该for_each算法对一系列迭代器中的每个项应用一元回调:
template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
for (; first != last; ++first) {
f(*first);
}
return f;
}
Run Code Online (Sandbox Code Playgroud)
可以通过传递适当的callables来首先递增然后打印矢量,例如:
std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });
Run Code Online (Sandbox Code Playgroud)
打印
5 6.2 8 9.5 11.2
Run Code Online (Sandbox Code Playgroud)
回调的另一个应用是向某些事件的调用者发出通知,这些事件启用了一定量的静态/编译时间灵活性.
就个人而言,我使用一个使用两个不同回调的本地优化库:
Thus, the library designer is not in charge of deciding what happens with the information that is given to the programmer via the notification callback and he needn't worry about how to actually determine function values because they're provided by the logic callback. Getting those things right is a task due to the library user and keeps the library slim and more generic.
Furthermore, callbacks can enable dynamic runtime behaviour.
Imagine some kind of game engine class which has a function that is fired, each time the users presses a button on his keyboard and a set of functions that control your game behaviour. With callbacks you can (re)decide at runtime which action will be taken.
void player_jump();
void player_crouch();
class game_core
{
std::array<void(*)(), total_num_keys> actions;
//
void key_pressed(unsigned key_id)
{
if(actions[key_id]) actions[key_id]();
}
// update keybind from menu
void update_keybind(unsigned key_id, void(*new_action)())
{
actions[key_id] = new_action;
}
};
Run Code Online (Sandbox Code Playgroud)
这里函数key_pressed使用存储的回调actions来获得按下某个键时所需的行为.如果玩家选择更改跳跃按钮,则引擎可以呼叫
game_core_instance.update_keybind(newly_selected_key, &player_jump);
Run Code Online (Sandbox Code Playgroud)
因此,下一次游戏时按下此按钮后,会改变呼叫的行为key_pressed(调用player_jump).
请参阅C++概念:在cppreference上可调用以获得更正式的描述.
回调功能可以通过C++(11)中的几种方式实现,因为有几种不同的东西可以调用*:
std::function 对象operator())*注意:指向数据成员的指针也是可调用的,但根本不调用任何函数.
注意:从C++ 17开始,f(...)可以编写类似的调用,std::invoke(f, ...)它也可以处理指向成员大小写的指针.
函数指针是回调可以具有的"最简单"(就通用性而言,可读性最差)类型.
Let's have a simple function foo:
int foo (int x) { return 2+x; }
Run Code Online (Sandbox Code Playgroud)
A function pointer type has the notation
return_type (*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to foo has the type:
int (*)(int)
Run Code Online (Sandbox Code Playgroud)
where a named function pointer type will look like
return_type (* name) (parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. f_int_t is a type: function pointer taking one int argument, returning int
typedef int (*f_int_t) (int);
// foo_p is a pointer to function taking int returning int
// initialized by pointer to function foo taking int returning int
int (* foo_p)(int) = &foo;
// can alternatively be written as
f_int_t foo_p = &foo;
Run Code Online (Sandbox Code Playgroud)
The using declaration gives us the option to make things a little bit more readable, since the typedef for f_int_t can also be written as:
using f_int_t = int(*)(int);
Run Code Online (Sandbox Code Playgroud)
Where (at least for me) it is clearer that f_int_t is the new type alias and recognition of the function pointer type is also easier
And a declaration of a function using a callback of function pointer type will be:
// foobar having a callback argument named moo of type
// pointer to function returning int taking int as its argument
int foobar (int x, int (*moo)(int));
// if f_int is the function pointer typedef from above we can also write foobar as:
int foobar (int x, f_int_t moo);
Run Code Online (Sandbox Code Playgroud)
The call notation follows the simple function call syntax:
int foobar (int x, int (*moo)(int))
{
return x + moo(x); // function pointer moo called using argument x
}
// analog
int foobar (int x, f_int_t moo)
{
return x + moo(x); // function pointer moo called using argument x
}
Run Code Online (Sandbox Code Playgroud)
A callback function taking a function pointer can be called using function pointers.
Using a function that takes a function pointer callback is rather simple:
int a = 5;
int b = foobar(a, foo); // call foobar with pointer to foo as callback
// can also be
int b = foobar(a, &foo); // call foobar with pointer to foo as callback
Run Code Online (Sandbox Code Playgroud)
A function ca be written that doesn't rely on how the callback works:
void tranform_every_int(int * v, unsigned n, int (*fp)(int))
{
for (unsigned i = 0; i < n; ++i)
{
v[i] = fp(v[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
where possible callbacks could be
int double_int(int x) { return 2*x; }
int square_int(int x) { return x*x; }
Run Code Online (Sandbox Code Playgroud)
used like
int a[5] = {1, 2, 3, 4, 5};
tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
tranform_every_int(&a[0], 5, square_int);
// now a == {4, 16, 36, 64, 100};
Run Code Online (Sandbox Code Playgroud)
A pointer to member function (of some class C) is a special type of (and even more complex) function pointer which requires an object of type C to operate on.
struct C
{
int y;
int foo(int x) const { return x+y; }
};
Run Code Online (Sandbox Code Playgroud)
A pointer to member function type for some class T has the notation
// can have more or less parameters
return_type (T::*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to C::foo has the type
int (C::*) (int)
Run Code Online (Sandbox Code Playgroud)
where a named pointer to member function will -in analogy to the function pointer- look like this:
return_type (T::* name) (parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a type `f_C_int` representing a pointer to member function of `C`
// taking int returning int is:
typedef int (C::* f_C_int_t) (int x);
// The type of C_foo_p is a pointer to member function of C taking int returning int
// Its value is initialized by a pointer to foo of C
int (C::* C_foo_p)(int) = &C::foo;
// which can also be written using the typedef:
f_C_int_t C_foo_p = &C::foo;
Run Code Online (Sandbox Code Playgroud)
Example: Declaring a function taking a pointer to member function callback as one of its arguments:
// C_foobar having an argument named moo of type pointer to member function of C
// where the callback returns int taking int as its argument
// also needs an object of type c
int C_foobar (int x, C const &c, int (C::*moo)(int));
// can equivalently declared using the typedef above:
int C_foobar (int x, C const &c, f_C_int_t moo);
Run Code Online (Sandbox Code Playgroud)
The pointer to member function of C can be invoked, with respect to an object of type C by using member access operations on the dereferenced pointer.
Note: Parenthesis required!
int C_foobar (int x, C const &c, int (C::*moo)(int))
{
return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
// analog
int C_foobar (int x, C const &c, f_C_int_t moo)
{
return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
Run Code Online (Sandbox Code Playgroud)
Note: If a pointer to C is available the syntax is equivalent (where the pointer to C must be dereferenced as well):
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
if (!c) return x;
// function pointer meow called for object *c using argument x
return x + ((*c).*meow)(x);
}
// or equivalent:
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
if (!c) return x;
// function pointer meow called for object *c using argument x
return x + (c->*meow)(x);
}
Run Code Online (Sandbox Code Playgroud)
A callback function taking a member function pointer of class T can be called using a member function pointer of class T.
Using a function that takes a pointer to member function callback is -in analogy to function pointers- quite simple as well:
C my_c{2}; // aggregate initialization
int a = 5;
int b = C_foobar(a, my_c, &C::foo); // call C_foobar with pointer to foo as its callback
Run Code Online (Sandbox Code Playgroud)
std::function objects (header <functional>)The std::function class is a polymorphic function wrapper to store, copy or invoke callables.
std::function object/type notationThe type of a std::function object storing a callable looks like:
std::function<return_type(parameter_type_1, parameter_type_2, parameter_type_3)>
// i.e. using the above function declaration of foo:
std::function<int(int)> stdf_foo = &foo;
// or C::foo:
std::function<int(const C&, int)> stdf_C_foo = &C::foo;
Run Code Online (Sandbox Code Playgroud)
The class std::function has operator() defined which can be used to invoke its target.
int stdf_foobar (int x, std::function<int(int)> moo)
{
return x + moo(x); // std::function moo called
}
// or
int stdf_C_foobar (int x, C const &c, std::function<int(C const &, int)> moo)
{
return x + moo(c, x); // std::function moo called using c and x
}
Run Code Online (Sandbox Code Playgroud)
The std::function callback is more generic than function pointers or pointer to member function since different types can be passed and implicitly converted into a std::function object.
3.3.1 Function pointers and pointers to member functions
A function pointer
int a = 2;
int b = stdf_foobar(a, &foo);
// b == 6 ( 2 + (2+2) )
Run Code Online (Sandbox Code Playgroud)
or a pointer to member function
int a = 2;
C my_c{7}; // aggregate initialization
int b = stdf_C_foobar(a, c, &C::foo);
// b == 11 == ( 2 + (7+2) )
Run Code Online (Sandbox Code Playgroud)
can be used.
3.3.2 Lambda expressions
An unnamed closure from a lambda expression can be stored in a std::function object:
int a = 2;
int c = 3;
int b = stdf_foobar(a, [c](int x) -> int { return 7+c*x; });
// b == 15 == a + (7*c*a) == 2 + (7+3*2)
Run Code Online (Sandbox Code Playgroud)
3.3.3 std::bind expressions
The result of a std::bind expression can be passed. For example by binding parameters to a function pointer call:
int foo_2 (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
int a = 2;
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
// c == 49 == 2 + ( 9*5 + 2 )
Run Code Online (Sandbox Code Playgroud)
Where also objects can be bound as the object for the invocation of pointer to member functions:
int a = 2;
C const my_c{7}; // aggregate initialization
int b = stdf_foobar(a, std::bind(&C::foo, my_c, _1));
// b == 1 == 2 + ( 2 + 7 )
Run Code Online (Sandbox Code Playgroud)
3.3.4 Function objects
Objects of classes having a proper operator() overload can be stored inside a std::function object, as well.
struct Meow
{
int y = 0;
Meow(int y_) : y(y_) {}
int operator()(int x) { return y * x; }
};
int a = 11;
int b = stdf_foobar(a, Meow{8});
// b == 99 == 11 + ( 8 * 11 )
Run Code Online (Sandbox Code Playgroud)
Changing the function pointer example to use std::function
void stdf_tranform_every_int(int * v, unsigned n, std::function<int(int)> fp)
{
for (unsigned i = 0; i < n; ++i)
{
v[i] = fp(v[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
gives a whole lot more utility to that function because (see 3.3) we have more possibilities to use it:
// using function pointer still possible
int a[5] = {1, 2, 3, 4, 5};
stdf_tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
// use it without having to write another function by using a lambda
stdf_tranform_every_int(&a[0], 5, [](int x) -> int { return x/2; });
// now a == {1, 2, 3, 4, 5}; again
// use std::bind :
int nine_x_and_y (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
// calls nine_x_and_y for every int in a with y being 4 every time
stdf_tranform_every_int(&a[0], 5, std::bind(nine_x_and_y, _1, 4));
// now a == {13, 22, 31, 40, 49};
Run Code Online (Sandbox Code Playgroud)
Using templates, the code calling the callback can be even more general than using std::function objects.
Note that templates are a compile-time feature and are a design tool for compile-time polymorphism. If runtime dynamic behaviour is to be achieved through callbacks, templates will help but they won't induce runtime dynamics.
Generalizing i.e. the std_ftransform_every_int code from above even further can be achieved by using templates:
template<class R, class T>
void stdf_transform_every_int_templ(int * v,
unsigned const n, std::function<R(T)> fp)
{
for (unsigned i = 0; i < n; ++i)
{
v[i] = fp(v[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
with an even more general (as well as easiest) syntax for a callback type being a plain, to-be-deduced templated argument:
template<class F>
void transform_every_int_templ(int * v,
unsigned const n, F f)
{
std::cout << "transform_every_int_templ<"
<< type_name<F>() << ">\n";
for (unsigned i = 0; i < n; ++i)
{
v[i] = f(v[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
Note: The included output prints the type name deduced for templated type F. The implementation of type_name is given at the end of this post.
The most general implementation for the unary transformation of a range is part of the standard library, namely std::transform,
which is also templated with respect to the iterated types.
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op)
{
while (first1 != last1) {
*d_first++ = unary_op(*first1++);
}
return d_first;
}
Run Code Online (Sandbox Code Playgroud)
The compatible types for the templated std::function callback method stdf_transform_every_int_templ are identical to the above mentioned types (see 3.4).
Using the templated version however, the signature of the used callback may change a little:
// Let
int foo (int x) { return 2+x; }
int muh (int const &x) { return 3+x; }
int & woof (int &x) { x *= 4; return x; }
int a[5] = {1, 2, 3, 4, 5};
stdf_transform_every_int_templ<int,int>(&a[0], 5, &foo);
// a == {3, 4, 5, 6, 7}
stdf_transform_every_int_templ<int, int const &>(&a[0], 5, &muh);
// a == {6, 7, 8, 9, 10}
stdf_transform_every_int_templ<int, int &>(&a[0], 5, &woof);
Run Code Online (Sandbox Code Playgroud)
Note: std_ftransform_every_int (non templated version; see above) does work with foo but not using muh.
// Let
void print_int(int * p, unsigned const n)
{
bool f{ true };
for (unsigned i = 0; i < n; ++i)
{
std::cout << (f ? "" : " ") << p[i];
f = false;
}
std::cout << "\n";
}
Run Code Online (Sandbox Code Playgroud)
The plain templated parameter of transform_every_int_templ can be every possible callable type.
int a[5] = { 1, 2, 3, 4, 5 };
print_int(a, 5);
transform_every_int_templ(&a[0], 5, foo);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, muh);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, woof);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, [](int x) -> int { return x + x + x; });
print_int(a, 5);
transform_every_int_templ(&a[0], 5, Meow{ 4 });
print_int(a, 5);
using std::placeholders::_1;
transform_every_int_templ(&a[0], 5, std::bind(foo_2, _1, 3));
print_int(a, 5);
transform_every_int_templ(&a[0], 5, std::function<int(int)>{&foo});
print_int(a, 5);
Run Code Online (Sandbox Code Playgroud)
The above code prints:
1 2 3 4 5
transform_every_int_templ <int(*)(int)>
3 4 5 6 7
transform_every_int_templ <int(*)(int&)>
6 8 10 12 14
transform_every_int_templ <int& (*)(int&)>
9 11 13 15 17
transform_every_int_templ <main::{lambda(int)#1} >
27 33 39 45 51
transform_every_int_templ <Meow>
108 132 156 180 204
transform_every_int_templ <std::_Bind<int(*(std::_Placeholder<1>, int))(int, int)>>
975 1191 1407 1623 1839
transform_every_int_templ <std::function<int(int)>>
977 1193 1409 1625 1841
Run Code Online (Sandbox Code Playgroud)
type_name implementation used above#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>
template <class T>
std::string type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(abi::__cxa_demangle(typeid(TR).name(), nullptr,
nullptr, nullptr), std::free);
std::string r = own != nullptr?own.get():typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += " &";
else if (std::is_rvalue_reference<T>::value)
r += " &&";
return r;
}
Run Code Online (Sandbox Code Playgroud)
Ram*_* B. 156
还有C方式做回调:函数指针
//Define a type for the callback signature,
//it is not necessary, but makes life easier
//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*CallbackType)(float);
void DoWork(CallbackType callback)
{
float variable = 0.0f;
//Do calculations
//Call the callback with the variable, and retrieve the
//result
int result = callback(variable);
//Do something with the result
}
int SomeCallback(float variable)
{
int result;
//Interpret variable
return result;
}
int main(int argc, char ** argv)
{
//Pass in SomeCallback to the DoWork
DoWork(&SomeCallback);
}
Run Code Online (Sandbox Code Playgroud)
现在,如果要将类方法作为回调传递,那些对这些函数指针的声明具有更复杂的声明,例如:
//Declaration:
typedef int (ClassName::*CallbackType)(float);
//This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
//Class instance to invoke it through
ClassName objectInstance;
//Invocation
int result = (objectInstance.*callback)(1.0f);
}
//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
//Class pointer to invoke it through
ClassName * pointerInstance;
//Invocation
int result = (pointerInstance->*callback)(1.0f);
}
int main(int argc, char ** argv)
{
//Pass in SomeCallback to the DoWork
DoWorkObject(&ClassName::Method);
DoWorkPointer(&ClassName::Method);
}
Run Code Online (Sandbox Code Playgroud)
Kar*_*oor 68
Scott Meyers给出了一个很好的例子:
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter
{
public:
typedef std::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
: healthFunc(hcf)
{ }
int healthValue() const { return healthFunc(*this); }
private:
HealthCalcFunc healthFunc;
};
Run Code Online (Sandbox Code Playgroud)
我认为这个例子就说明了一切.
std::function<> 是编写C++回调的"现代"方式.
Mil*_*kic 14
接受的答案非常有用且非常全面.然而,OP表示
我想看一个编写回调函数的简单示例.
所以在这里,你可以从C++ 11开始,std::function所以不需要函数指针和类似的东西:
#include <functional>
#include <string>
#include <iostream>
void print_hashes(std::function<int (const std::string&)> hash_calculator) {
std::string strings_to_hash[] = {"you", "saved", "my", "day"};
for(auto s : strings_to_hash)
std::cout << s << ":" << hash_calculator(s) << std::endl;
}
int main() {
print_hashes( [](const std::string& str) { /** lambda expression */
int result = 0;
for (int i = 0; i < str.length(); i++)
result += pow(31, i) * str.at(i);
return result;
});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这个例子在某种程度上是真实的,因为你希望print_hashes用不同的哈希函数实现来调用函数,为此我提供了一个简单的函数.它接收一个字符串,返回一个int(提供的字符串的哈希值),并且你需要从语法部分记住的所有内容都std::function<int (const std::string&)>描述了这样的函数作为将调用它的函数的输入参数.
C++中没有明确的回调函数概念.回调机制通常通过函数指针,仿函数对象或回调对象来实现.程序员必须明确地设计和实现回调功能.
根据反馈进行修改:
尽管这个答案得到了负面的反馈,但这并没有错.我会尝试更好地解释我来自哪里.
C和C++拥有实现回调函数所需的一切.实现回调函数的最常见和最简单的方法是将函数指针作为函数参数传递.
但是,回调函数和函数指针不是同义词.函数指针是一种语言机制,而回调函数是一种语义概念.函数指针不是实现回调函数的唯一方法 - 您还可以使用仿函数甚至花园种类的虚函数.使函数调用回调的原因不是用于标识和调用函数的机制,而是调用的上下文和语义.说某事是一个回调函数意味着调用函数和被调用的特定函数之间的分离大于正常,调用者和被调用者之间的概念耦合更松散,调用者可以明确控制被调用的内容.宽松的概念耦合和调用者驱动的函数选择的模糊概念使得某些东西成为回调函数,而不是使用函数指针.
例如,IFormatProvider的.NET文档说"GetFormat是一种回调方法",即使它只是一种普通的接口方法.我认为没有人会认为所有虚方法调用都是回调函数.是什么让GetFormat成为一个回调方法,不是传递或调用它的机制,而是调用者调用哪个对象的GetFormat方法的语义.
某些语言包含具有显式回调语义的功能,通常与事件和事件处理相关.例如,C#具有围绕回调概念明确设计的语法和语义的事件类型.Visual Basic有它的Handles子句,它显式地声明一个方法作为回调函数,同时抽象出委托或函数指针的概念.在这些情况下,回调的语义概念被集成到语言本身中.
另一方面,C和C++ 几乎没有明确地嵌入回调函数的语义概念.机制在那里,集成的语义不是.你可以很好地实现回调函数,但是要获得更复杂的东西,其中包括显式回调语义,你必须在C++提供的基础上构建它,比如Qt对它们的信号和插槽做了什么.
简而言之,C++具有实现回调所需的功能,通常可以非常轻松地使用函数指针.它没有的是关键字和特性,其语义特定于回调,例如raise,emit,Handles,event + =等.如果你来自具有这些类型元素的语言,那么C++中的本机回调支持会感到绝望.
回调函数是C标准的一部分,因此也是C++的一部分.但是如果您正在使用C++,我建议您使用观察者模式:http://en.wikipedia.org/wiki/Observer_pattern
小智 5
请参阅上面的定义,其中指出回调函数被传递给其他函数并在某个时刻被调用。
在 C++ 中,希望回调函数调用类方法。当您执行此操作时,您可以访问会员数据。如果您使用 C 方式定义回调,则必须将其指向静态成员函数。这是不太理想的。
以下是如何在 C++ 中使用回调。假设有 4 个文件。每个类有一对 .CPP/.H 文件。类 C1 是具有我们要回调的方法的类。C2 回调 C1 的方法。在此示例中,回调函数采用 1 个参数,这是我为读者添加的。该示例未显示任何正在实例化和使用的对象。此实现的一个用例是,您有一个类读取数据并将数据存储到临时空间中,而另一个类则对数据进行后处理。使用回调函数,对于读取的每一行数据,回调都可以对其进行处理。该技术减少了所需临时空间的开销。它对于返回大量数据然后必须进行后处理的 SQL 查询特别有用。
/////////////////////////////////////////////////////////////////////
// C1 H file
class C1
{
public:
C1() {};
~C1() {};
void CALLBACK F1(int i);
};
/////////////////////////////////////////////////////////////////////
// C1 CPP file
void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}
/////////////////////////////////////////////////////////////////////
// C2 H File
class C1; // Forward declaration
class C2
{
typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
C2() {};
~C2() {};
void Fn(C1 * pThat,pfnCallBack pFn);
};
/////////////////////////////////////////////////////////////////////
// C2 CPP File
void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
// Call a non-static method in C1
int i = 1;
(pThat->*pFn)(i);
}
Run Code Online (Sandbox Code Playgroud)