使用C++11的function/bind组件封装Thread以及回调函数的使用

本文介绍如何利用C++11的function特性封装线程类,通过回调函数实现线程逻辑的传递,并展示了三种使用方式:普通函数、成员函数及组合线程类。

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

转自:https://www.cnblogs.com/inevermore/p/4038498.html
现在我们采用C++11的function,将函数作为Thread类的成员,用户只需要将function对象传入线程即可,所以Thread的声明中,应该含有一个function成员变量。

类的声明如下:

#ifndef THREAD_H_
#define THREAD_H_

#include <boost/noncopyable.hpp>
#include <functional>
#include <pthread.h>

class Thread : boost::noncopyable
{
public:
    typedef std::function<void ()> ThreadCallback;

    Thread(ThreadCallback callback);
    ~Thread();

    void start();
    void join();

    static void *runInThread(void *);

private:
    pthread_t threadId_;
    bool isRunning_;
    ThreadCallback callback_; //回调函数
};
#endif //THREAD_H_

复制代码
那么如何开启线程?思路与之前一致,写一个static函数,用户pthread_create的第三个参数,this作为最后一个参数即可。

void Thread::start()
{
    pthread_create(&threadId_, NULL, runInThread, this);
    isRunning_ = true;
}

回调函数

注意在这种封装方式中,我们采用了回调函数。回调函数与普通函数的区别就是,普通函数写完由我们自己直接调用,函数调用是一种不断往上堆积的方式,而回调函数通常是我们把某一个函数传入一个“盒子”,由该盒子内的机制来调用它。

在这个例子里面,我们将function传入Thread,当Thread启动的时候,由Thread去执行function对象。

在win32编程中大量用到这种机制,我们为鼠标单击、双击等事件编写相应的函数,然后将其注册给windows系统,然后系统在我们触发各种事件的时候,根据事件的类型,调用相应的构造函数。

关于回调函数,可以参考:http://www.zhihu.com/question/19801131

以后有时间,再专门总结下回调函数。

完整的cpp如下:

复制代码

#include "Thread.h"

Thread::Thread(ThreadCallback callback)
: threadId_(0),
  isRunning_(false),
  callback_(std::move(callback))
{

}

Thread::~Thread()
{
    if(isRunning_)
    {
        //detach
        pthread_detach(threadId_);
    }
}

void Thread::start()
{
    pthread_create(&threadId_, NULL, runInThread, this);
    isRunning_ = true;
}
void Thread::join()
{
    pthread_join(threadId_, NULL);
    isRunning_ = false;
}

void *Thread::runInThread(void *arg)
{
    Thread *pt = static_cast<Thread*>(arg);
    pt->callback_(); //调用回调函数

    return NULL;
}

复制代码

这个线程的使用方式有三种:

一是将普通函数作为回调函数

复制代码

void foo()
{
    while(1)
    {
        printf("foo\n");
        sleep(1);
    }
}

int main(int argc, char const *argv[])
{
    Thread t(&foo);

    t.start();
    t.join();

    return 0;
}

复制代码

二是采用类的成员函数作为回调函数:

复制代码

class Foo
{
public:
    void foo(int i)
    {
        while(1)
        {
            printf("foo %d\n", i++);
            sleep(1);
        }
    }
};


int main(int argc, char const *argv[])
{
    Foo f;
    int i = 34;
    Thread t(bind(&Foo::foo, &f, i));

    t.start();
    t.join();

    return 0;
}

复制代码
最后一种是组合一个新的线程类,注意这里采用的是类的组合:

复制代码

class Foo
{
public:

    Foo()
    : thread_(bind(&Foo::foo, this))
    {
    }

    void start()
    {
        thread_.start();
        thread_.join();
    }

    void foo()
    {
        while(1)
        {
            printf("foo\n");
            sleep(1);
        }
    }
private:
    Thread thread_;
};

int main(int argc, char const *argv[])
{
    Foo f;
    f.start();
    return 0;
}

复制代码
有些复杂的类,还需要将三种方式加以整合,例如后面要谈到的TimerThread,里面含有一个Thread和Timer,用户将逻辑注册给Timer,然后Timer的start函数注册给Thread。

这种方式的Thread,使用灵活性相对于面向对象的风格,提高了很多。

基于对象和面向对象

这里总结几点:

面向对象依靠的是虚函数+继承,用户通过重写基类的虚函数,实现自己的逻辑。

基于对象,依赖类的组合,使用function和bind实现委托机制,更加依赖于回调函数传入逻辑。

<think>首先,用户的问题是:“我想理解C++回调函数使用方法 请问C++ 回调函数如何使用 示例教程”。根据系统级指令,我需要: - 使用中文回答。 - 结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 - 所有行内数学表达式用$...$格式。 - 独立公式用$$...$$格式并单独成段。 - LaTeX语法正确。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 参考的引用是关于C++回调函数的,包括: - 引用[1]:解释了回调函数的概念,包括函数指针、std::function和lambda表达式。 - 引用[2]:示例使用回调函数实现后端功能。 - 引用[3]:函数指针和std::bind/std::function实现回调。 - 引用[4]:C语言和C++回调函数的定义和使用。 用户的问题是基于这些引用,所以我应该整合这些信息来回答。 回答结构: 1. **介绍回调函数的概念**:简要解释什么是回调函数,基于引用。 2. **实现方法**:详细说明如何在C++使用回调函数,包括: - 基于函数指针(引用[1]和[3]) - 使用std::function和std::bind(引用[3]) - 使用lambda表达式(引用[1]) 3. **示例教程**:提供代码示例,展示实际使用。 4. **总结**:简要回顾关键点。 5. **生成相关问题**:在最后添加相关问题部分。 确保回答清晰、逐步引导用户。 添加引用标识:在引用概念或示例时,自然地添加[^1]、[^2]等。 生成相关问题:在最后,添加“相关问题”部分,列出3-5个相关问题。 现在,规划内容: - **引言**:定义回调函数,引用[1]和[4]。 - **方法1:基于函数指针**:解释并给出代码示例。 - **方法2:使用std::function和lambda**:解释并给出代码示例。 - **实际应用示例**:可能基于引用[2],展示一个简单后端功能。 - **总结**:好处和常见场景。 - **相关问题**:列出3个问题。 代码示例:使用Markdown代码块,确保语法正确。 最后,输出格式:使用中文,结构清晰。</think>### C++ 回调函数使用教程 回调函数C++中一种强大的机制,允许你将一个函数作为参数传递给另一个函数,从而实现事件驱动、异步操作或自定义行为[^1]。简单来说,回调函数不是由调用方直接执行,而是在特定条件(如事件触发)时由接收方调用[^4]。本教程将逐步介绍C++回调函数使用方法,包括三种常见实现方式:基于函数指针、使用std::function和lambda表达式,并提供代码示例。所有示例均基于标准C++11及以上版本。 #### 1. 回调函数的基本概念 回调函数的核心思想是“函数作为参数”。在数学中,这类似于高阶函数的概念,例如在函数式编程中,函数$f(g(x))$表示$g(x)$作为$f$的参数[^1]。在C++中,回调函数常用于: - 事件处理(如按钮点击)。 - 异步操作(如网络请求完成时通知)。 - 算法自定义(如排序比较函数)[^2]。 #### 2. 实现方法及示例教程 下面分步说明三种主流实现方式。每个示例都包含完整代码和解释。 ##### 方法1: 基于函数指针 函数指针是C++中最基础的回调实现方式,直接存储函数的地址。它适用于简单场景,但类型安全性较低。 ```cpp #include <iostream> // 定义回调函数类型:接受int参数,返回void typedef void (*CallbackFunc)(int); // 业务函数:接收回调函数作为参数 void processData(int data, CallbackFunc callback) { std::cout << "处理数据: " << data << std::endl; callback(data); // 在特定条件触发回调 } // 实际回调函数 void myCallback(int result) { std::cout << "回调执行,结果: " << result << std::endl; } int main() { int input = 42; processData(input, myCallback); // 传递函数指针 return 0; } ``` **解释**: - `typedef void (*CallbackFunc)(int)` 定义回调类型。 - `processData` 函数在完成数据处理后调用回调。 - 输出:`处理数据: 42` 后跟 `回调执行,结果: 42`。 - 优点:简单高效;缺点:不支持带状态的函数(如类成员函数)[^3]。 ##### 方法2: 使用std::function和std::bind `std::function` 提供类型安全的封装,`std::bind` 用于绑定参数,适用于类成员函数或复杂回调。 ```cpp #include <iostream> #include <functional> // 使用std::function定义回调类型 using CallbackFunc = std::function<void(int)>; // 业务函数 void processData(int data, CallbackFunc callback) { std::cout << "处理数据: " << data << std::endl; callback(data); } // 示例类:包含成员函数作为回调 class MyClass { public: void memberCallback(int result) { std::cout << "类成员回调,结果: " << result << std::endl; } }; int main() { int input = 100; MyClass obj; // 绑定类成员函数:std::bind(&类名::函数名, 对象地址, std::placeholders::_1) CallbackFunc callback = std::bind(&MyClass::memberCallback, &obj, std::placeholders::_1); processData(input, callback); // 传递std::function对象 return 0; } ``` **解释**: - `using CallbackFunc = std::function<void(int)>` 定义通用回调类型。 - `std::bind` 绑定成员函数和对象实例。 - 输出:`处理数据: 100` 后跟 `类成员回调,结果: 100`。 - 优点:支持类成员和参数绑定;缺点:语法稍复杂[^3]。 ##### 方法3: 使用lambda表达式 Lambda表达式是C++11引入的简洁方式,尤其适合一次性回调或内联定义。 ```cpp #include <iostream> #include <functional> using CallbackFunc = std::function<void(int)>; void processData(int data, CallbackFunc callback) { std::cout << "处理数据: " << data << std::endl; callback(data); } int main() { int input = 200; int extra = 50; // 外部变量 // 定义lambda作为回调:捕获外部变量extra processData(input, [extra](int result) { std::cout << "Lambda回调,结果: " << result + extra << std::endl; }); return 0; } ``` **解释**: - Lambda `[extra](int result) {...}` 捕获外部变量`extra`。 - 输出:`处理数据: 200` 后跟 `Lambda回调,结果: 250`。 - 优点:简洁灵活,支持闭包;缺点:过度使用可能导致代码可读性下降[^1]。 #### 3. 实际应用场景示例 基于引用[2],以下是一个简化后端功能:模拟网络请求完成后通过回调通知。 ```cpp #include <iostream> #include <functional> #include <thread> #include <chrono> using CallbackFunc = std::function<void(const std::string&)>; // 模拟异步网络请求 void fetchData(const std::string& url, CallbackFunc callback) { std::thread([url, callback]() { std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟延迟 std::string response = "数据来自 " + url; callback(response); // 请求完成时调用回调 }).detach(); } int main() { std::string apiUrl = "https://api.example.com"; fetchData(apiUrl, [](const std::string& result) { std::cout << "收到响应: " << result << std::endl; }); std::cout << "请求已发送,等待响应..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待回调完成 return 0; } ``` **解释**: - `fetchData` 启动新线程模拟异步操作。 - 回调在数据准备好时触发,实现非阻塞通知。 - 输出:`请求已发送,等待响应...` 后跟 `收到响应: 数据来自 https://api.example.com`。 - 此模式广泛应用于GUI框架或服务器后端[^2]。 #### 4. 总结 回调函数C++中通过函数指针、`std::function`或lambda实现,提供了灵活的事件处理能力。关键点包括: - **函数指针**:高效但局限性强。 - **std::function**:类型安全,支持成员函数。 - **Lambda**:简洁,适合临时逻辑。 - **性能考虑**:回调可能引入间接调用开销,但在事件驱动系统中通常可忽略[^1]。 回调函数的核心优势在于解耦调用方和实现方,提升代码模块化,这在异步编程中尤为重要[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值