C++ Callback

本文详细介绍了如何在C++中实现灵活的回调机制,并通过具体示例展示了如何使用自定义的Callback类来设置和执行回调函数。此外,还讨论了回调函数在不同情况下的应用,如重新定义和回调数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++ Callback

2006/3/26 15:17:02
 

This fully functional example shows how in C++ callbacks can be done in an absolutely flexible way!

This demo is written in Visual Studio 6 but the Callback class is platform independent and also runs under Linux, Mac, and so forth....

Callbacks in C++ are not as simple as in C. Pure C functions are from the type __cdecl. C++ functions are from the type __thiscall. (They differ in the way how they pass arguments on the stack.)

In C++, you have classes and, additionally, instances of classes. Every instance uses its own memory area for storing class variables. The pointer to this area of variables is the "this" pointer. It represents the instance. Every time you call any C++ function, the "this" pointer is passed to the function as an invisible parameter! (M$ Visual Studio 6 uses the processor register ECX to pass the "this" pointer.)

So, in C++ it is not enough to store the address of the function, which you want to call back. You also have to store the "this" pointer!

Using the Callback Class

You can include "Callback.h" into your project. The usage it is very simple because the cCallback class has only two member functions: SetCallback() and Execute(). You can understand the following examples without knowing what is happening inside cCallback.

cMyProject.h:

#include "callback.h"

private:
    // the functions of your project
    void CallbackFox   (int Param);
    void CallbackRabbit(int Param);
    void TestTheCallback(cCallback *pCallbackFunction, int Param);

    // Some instances of the Callback class
    TCallback<cMyProject> i_Callback_1;
    TCallback<cMyProject> i_Callback_2;

cMyProject.cpp:

void cMyProject::CallbackRabbit(int Param)
{
    char Buf[50];
    sprintf(Buf, "Now I'm in Rabbit with Param %d !/n", Param);
    printf(Buf);
}

void cMyProject::CallbackFox(int Param)
{
    char Buf[50];
    sprintf(Buf, "Now I'm in Fox with Param %d !/n", Param);
    printf(Buf);
}

void TestTheCallback(cCallback *pCallbackFunction, int Param)
{
    pCallbackFunction->Execute(Param * Param);
}

void cMyProject::CallbackDemo()
{
    // defining where the callback should jump to
    i_Callback_1.SetCallback(this, &cMyProject::CallbackRabbit);
    i_Callback_2.SetCallback(this, &cMyProject::CallbackFox);

    // now you can pass i_Callback like a pointer to a function
    TestTheCallback(&i_Callback_1, 4);
    TestTheCallback(&i_Callback_2, 5);
}

If you call cMyProject::CallbackDemo(), the output will be:

Now I'm in Rabbit with Param 16 !
Now I'm in Fox    with Param 25 !

Callback Re-Definitions

It is also possible to re-define the callback with SetCallback() as often as you like:

void cMyProject::CallbackDemo()
{
    i_Callback_1.SetCallback(this, &cMyProject::CallbackRabbit);
    TestTheCallback(&i_Callback_1, 4);

    i_Callback_1.SetCallback(this, &cMyProject::CallbackFox);
    TestTheCallback(&i_Callback_1, 5);
}

The output would be the same, but i_Callback_2 is not needed anymore.

Callback Arrays

It is also possible to use arrays of callbacks:

cMyProject.h:

private:
    TCallback<cMyProject> i_Callback[10];

cMyProject.cpp:

void TestTheCallback(int Index, int Param)
{
    i_Callback[Index].Execute(Param * Param);
}

void cMyProject::CallbackDemo()
{
    i_Callback[0].SetCallback(this, &cMyProject::CallbackRabbit);
    i_Callback[1].SetCallback(this, &cMyProject::CallbackFox);
    i_Callback[2].SetCallback(.....);

    TestTheCallback(0, 4);
    TestTheCallback(1, 5);
}

Callback Arrays, Part 2

In the above example, all callbacks are from cMyProject. In i_Callback you can ONLY store callbacks to the cMyProject class because it is defined as TCallback<cMyProject>.

If you want to store callbacks to different classes in a callback array, you have to create the array from cCallback instead of TCallback:

cMyProject.h:

private:
    cCallback *p_Callback[10];

cMyProject.cpp:

void cMyProject::StoreCallback(cCallback *p_CallbackFunction,
                               int Index)
{
    p_Callback[Index] = p_CallbackFunction;
}

StoreCallback() then can be called by ANY class to set a callback to itself. For example:

cDemo.h:

private:
    TCallback<cDemo> i_MyCallback;

cDemo.cpp:

#include "cMyProject.h"
extern cMyProject i_MyProject;
......
    i_MyCallback.SetCallback(this, &cDemo::MyCallbackFunction);

    i_MyProject.StoreCallback(&i_MyCallback, Index);
......

You can even later modify i_MyCallback with SetCallback() without having to call StoreCallback() again!!

In the source code (see the download link at the end of this article) you find a different example, and additionally a demonstration of a global callback, which you need, if you want to be called back by the operating system. (Windows API callbacks always go into the global namespace.)

The Callback Class

Finally, here comes the great cCallback class itself. It consits of only a header file without a corresponding cpp file.

Callback.h:

class cCallback
{
    public:
        virtual void Execute(int Param) const =0;
};


template <class cInstance>
class TCallback : public cCallback
{
    public:
        TCallback()    // constructor
        {
            pFunction = 0;
        }

        typedef void (cInstance::*tFunction)(int Param);

        virtual void Execute(int Param) const
        {
            if (pFunction) (cInst->*pFunction)(Param);
            else printf("ERROR : the callback function has not
                                 been defined !!!!");
        }

        void SetCallback (cInstance  *cInstancePointer,
                          tFunction   pFunctionPointer)
        {
            cInst     = cInstancePointer;
            pFunction = pFunctionPointer;
        }

    private:
        cInstance  *cInst;
        tFunction  pFunction;
};

This class defines an Execute() function that takes one integer parameter and returns no parameter (void). You can simply adapt it to your needs; for example, a callback that takes five paramaters and returns a bool. (Then, you have to modify three lines: the two lines beginning with "virtual void Execute" and the typedef.)

To completely understand this class, you need advanced C++ knowledge. I will not explain all the details here because this would be too much.

Instead I recommend the very good book:
Author: André Willms
Title: C++ Programming (German title: "C++ Programmierung")
Publisher: Addison Wesley
ISBN 3-8273-1495-X

And from my homepage, you can download free C++ books in the compiled HTML format.

### C++ 中的回调函数 (Callback) 使用方法 在 C++ 中,回调函数是一种允许函数作为参数传递给另一个函数的技术。这种方式可以实现事件驱动编程或异步操作。以下是回调函数的基本用法和示例。 #### 1. 简单回调函数 回调函数可以通过函数指针实现。以下是一个简单的示例[^2]: ```cpp #include <iostream> using namespace std; // 定义回调函数 void callbackFunc(int a, int b) { cout << "a + b = " << a + b << endl; } // 接受回调函数的函数 void doSomething(int a, int b, void (*callback)(int, int)) { // 执行某些操作 // 调用回调函数 callback(a, b); } int main() { // 调用doSomething函数,并传递回调函数 doSomething(10, 20, callbackFunc); return 0; } ``` 上述代码中,`doSomething` 函数接受一个函数指针作为参数,并在内部调用该函数指针指向的函数。 #### 2. 类的静态成员函数作为回调函数 如果需要将类的静态成员函数作为回调函数,可以参考以下示例[^1]: ```cpp #include <iostream> using namespace std; class A { public: // 静态成员函数作为回调函数 static void callback() { cout << "回调函数开始执行了!" << endl; } // 调用回调函数的方法 void f(void (*p)()) { p(); // 调用回调函数 } }; int main() { A obj; obj.f(&A::callback); // 将静态成员函数作为回调函数传递 return 0; } ``` 在这个例子中,`A::callback` 是一个静态成员函数,可以直接通过函数指针传递。 #### 3. 非静态成员函数作为回调函数 非静态成员函数不能直接作为函数指针使用,因为它们需要绑定到具体的对象实例。可以通过 `std::function` 和 `std::bind` 来实现[^3]: ```cpp #include <iostream> #include <functional> using namespace std; class B { public: void callback() { cout << "非静态成员函数回调执行了!" << endl; } }; void invokeCallback(function<void()> callback) { callback(); } int main() { B obj; // 使用 std::bind 绑定对象和成员函数 invokeCallback(bind(&B::callback, &obj)); return 0; } ``` #### 4. 回调函数与异步编程结合 在现代 C++ 中,回调函数也可以与协程结合,用于异步编程[^4]。以下是一个简单的协程示例: ```cpp #include <iostream> #include <coroutine> #include <thread> #include <chrono> struct Task { struct promise_type { Task get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; }; Task asyncOperation() { cout << "Start async operation..." << endl; this_thread::sleep_for(chrono::seconds(2)); // 模拟耗时操作 cout << "Async operation completed." << endl; co_return; } int main() { asyncOperation(); this_thread::sleep_for(chrono::seconds(3)); // 等待异步操作完成 return 0; } ``` 此示例展示了如何通过协程实现异步操作,并可以在操作完成后触发回调逻辑。 ### 注意事项 - 回调函数广泛应用于事件处理、系统级接口(如 Windows 编程中的 Hook 回调)等场景[^5]。 - 在使用非静态成员函数作为回调时,必须确保正确绑定对象实例。 - 现代 C++ 提供了更灵活的工具(如 `std::function` 和 `std::bind`),可以简化回调函数的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值