1. 概述
C++ 中的函数指针非常重要,它不仅可以传递函数作为参数,还可以作为返回值。与此类似,C++ 中也可以使用类成员函数指针,它指向某个特定对象下的特定成员函数。 在 C++11 中,可以使用 `std::function` 和 `std::bind` 来操作成员函数指针,从而使代码更加简洁易读。
本文将详细介绍 C++ 类成员函数指针的使用方法及其常见的应用场景,包括 `std::function` 和 `std::bind` 的使用,同时提供大量示例代码来帮助读者理解。
2. 成员函数指针的定义和使用
在 C++ 中,定义类成员函数指针的语法如下:
```c++
返回类型 (类名::*指针名)(参数列表) = &类名::成员函数名;
```
其中,`返回类型` 表示成员函数的返回值类型,`类名` 表示该成员函数所属的类名,`指针名` 表示定义的成员函数指针名称,`参数列表` 表示成员函数的参数列表。在赋值时需要使用 `&类名::成员函数名` 来获取该成员函数的地址。
下面是一个简单的示例代码:
```c++
#include class MyClass { public: int add(int a, int b) { return a + b; } }; int main() { MyClass myObj; int (MyClass::*p)(int, int) = &MyClass::add; std::cout << (myObj.*p)(2, 3) << std::endl; // 输出 5 return 0; } ``` 在这个示例中,我们定义了一个名为 `MyClass` 的类,其中包含一个名为 `add` 的成员函数,该函数接受两个整数作为参数并返回它们的和。在 `main` 函数中,我们首先创建了一个 `MyClass` 的对象 `myObj`,然后定义了一个名为 `p` 的成员函数指针,它指向 `MyClass::add` 函数。最后通过 `(myObj.*p)(2, 3)` 调用了 `MyClass::add` 函数,并输出了结果 `5`。 3. std::function 的使用 `std::function` 是 C++11 中的一个模板类,它可以包装任何可调用对象,包括函数指针、成员函数指针、函数对象等,并提供一种统一的方式来操作它们。 使用 `std::function` 要包含头文件 ` ```c++ std::function<返回类型(参数类型)> func = std::bind(&类名::成员函数名, 对象指针, std::placeholders::_1, ...); ``` 其中,`返回类型` 表示该成员函数的返回值类型,`参数类型` 表示该成员函数的参数类型;`func` 是 `std::function` 的对象名,需要与成员函数的返回值和参数类型对应;`std::bind` 函数用于绑定成员函数指针和对象指针,并返回一个函数对象;`对象指针` 是指向对象的指针,用于绑定成员函数的调用者;`std::placeholders` 用于占位符,对应于成员函数的参数列表,可以使用 `std::placeholders::_1`、`std::placeholders::_2` 等来表示第一个、第二个参数等等。 下面是使用 `std::function` 的示例代码: ```c++ #include #include class MyClass { public: int add(int a, int b) { return a + b; } }; int main() { MyClass myObj; std::function std::cout << func(2, 3) << std::endl; // 输出 5 return 0; } ``` 在这个示例中,我们首先包含了头文件 ` 4. std::bind 的使用 `std::bind` 函数可以用于绑定函数或成员函数的参数,从而产生新的函数对象。其函数原型如下: ```c++ template std::function bind(F&& f, Args&&... args); ``` 其中,`F` 表示待绑定的函数或成员函数的名称,`Args` 表示函数的参数列表;`std::result_of` 是 C++11 中的一个模板类,可以自动根据函数的返回类型推断出其类型。 下面是 `std::bind` 的使用示例代码: ```c++ #include #include int add(int a, int b) { return a + b; } class MyClass { public: int add(int a, int b) { return a + b; } }; int main() { std::function std::cout << func1() << std::endl; // 输出 5 MyClass myObj; std::function std::cout << func2() << std::endl; // 输出 5 std::function std::cout << func3(2) << std::endl; // 输出 5 std::function std::cout << func4(2) << std::endl; // 输出 5 return 0; } ``` 在这个示例中,我们首先定义了一个名为 `add` 的自由函数和一个名为 `MyClass` 的类,其中包含一个名为 `add` 的成员函数。在 `main` 函数中,我们使用 `std::bind` 函数分别绑定了 `add` 和 `MyClass::add` 函数的参数,并将结果放入了函数对象 `func1` 和 `func2` 中,最后通过调用这两个函数对象,分别输出了 `5`。同时我们还使用了 `std::placeholders` 来占位符,对应于绑定的参数。 5. 常见的应用场景 成员函数指针的使用非常灵活,它可以应用于很多场景中,下面我们介绍几个常见的使用场景: 5.1 回调函数 回调函数是一种高效的通信方式,它允许主程序向外部程序注册自己的一个函数指针,当某个事件发生时,外部程序就可以调用主程序中注册的函数指针,以实现数据的传输和处理。 在 C++ 中,可以使用成员函数指针来实现回调函数。 下面是一个回调函数的使用示例代码: ```c++ #include #include class MyClass { public: typedef std::function void setCallback(const Callback &cb) { m_cb = cb; } void doSomething(int data) { // do something if (m_cb) { m_cb(data); } } private: Callback m_cb; }; class Client { public: Client(MyClass *pObj) : m_pObj(pObj) {} void onCallback(int data) { std::cout << "Client: received data = " << data << std::endl; } void doSomethingAndRegisterCallback(int data) { m_pObj->setCallback(std::bind(&Client::onCallback, this, std::placeholders::_1)); m_pObj->doSomething(data); } private: MyClass *m_pObj; }; int main() { MyClass obj; Client client(&obj); client.doSomethingAndRegisterCallback(123); return 0; } ``` 在这个示例中,我们首先定义了一个名为 `MyClass` 的类,其中包含了一个名为 `Callback` 的类型别名,用于定义回调函数的格式。该类还包含了一个名为 `setCallback` 的成员函数,用于将回调函数与数据绑定,并在需要时调用回调函数。在 `Client` 类中,我们定义了一个名为 `onCallback` 的成员函数,该函数就是我们需要注册给 `MyClass` 类的回调函数。在 `Client` 类的成员函数 `doSomethingAndRegisterCallback` 中,我们使用 `std::bind` 函数,将 `onCallback` 成员函数和当前对象 `this` 绑定,将绑定的结果作为回调函数注册给了 `MyClass` 类,并调用了 `doSomething` 函数。在 `MyClass` 类中,当 `doSomething` 函数执行到一定位置时,就会调用回调函数。 5.2 GUI 编程 在 GUI 编程中,经常需要定义一些响应事件的动作,这些动作可以是函数或成员函数,也可以是 Lambda 表达式或函数对象。在 C++11 中,可以使用 `std::function` 和 `std::bind` 来实现动作回调。下面是一个简单的示例代码: ```c++ #include #include class Button { public: typedef std::function void setOnClick(const Callback &cb) { m_cb = cb; } void click() { if (m_cb) { m_cb(); } } private: Callback m_cb; }; class Window { public: void init() { m_button.setOnClick(std::bind(&Window::onClick, this)); } void onClick() { std::cout << "Click!" << std::endl; } private: Button m_button; }; int main() { Window wnd; wnd.init(); wnd.onClick(); return 0; } ``` 在这个示例中,我们定义了一个名为 `Button` 的类,其中包含了一个名为 `Callback` 的类型别名,用于定义动作的回调函数格式。该类包含了一个名为 `setOnClick` 的成员函数,用于将回调函数绑定到 Button 上,以响应 Click 事件。在 `Window` 类中,我们定义了一个名为 `onClick` 的成员函数,用于处理 Click 事件。在 `init` 函数中,我们使用 `std::bind` 函数,将 `onClick` 成员函数与 `this` 对象绑定,然后将绑定结果作为回调函数绑定到了 `Button` 组件上。 5.3 线程池 在多线程编程中,线程池是一种高效的调度方式。在实现线程池时,我们通常会使用队列来存储任务,然后启动多个线程来处理队列中的任务。 在这个过程中,我们需要使用成员函数指针来将任务和线程池的对象绑定在一起。 下面是一个简单的线程池示例代码: ```c++ #include #include #include #include #include #include #include class ThreadPool { public: typedef std::function ThreadPool() : m_exit(false) { m_threads.reserve(4); for (int i = 0; i < 4; ++i) { m_threads.emplace_back(std::bind(&ThreadPool::workerThread, this)); } } ~ThreadPool() { { std::unique_lock m_exit = true; } m_cond.notify_all(); for (auto &thread : m_threads) { if (thread.joinable()) { thread.join(); } } } void addTask(const Task &task) { std::unique_lock m_queue.push(task); m_cond.notify_one(); } private: void workerThread() { while (true) { Task task; { std::unique_lock m_cond.wait(lock, [&]() { return m_exit || !m_queue.empty(); }); if (m_exit && m_queue.empty()) { return; } task = m_queue.front(); m_queue.pop(); } task(); } } private: std::vector std::queue std::mutex m_mutex; std::condition_variable m_cond; bool m_exit; }; class MyTask { public: void operator()() { std::cout << std::this_thread::get_id() << ": do some work" << std::endl; } }; int main() { ThreadPool pool; for (int i = 0; i < 10; ++i) { pool.addTask(MyTask()); } std::this_thread::sleep_for(std::chrono::seconds(1)); return 0; } ``` 在这个示例中,我们首先定义了一个名为 `ThreadPool` 的线程池类,其中包含了一个名为 `Task` 的类型别名,用于定义任务的类型。该类包含了多个私有变量,包括工作线程向任务队列中添加任务的队列 `m_queue`,线程池退出标志变量 `m_exit`,互斥量 `m_mutex` 和条件变量 `m_cond`。该类包含了多个公有函数,包括添加任务的函数 `addTask`,以及多个线程池工作线程共享的函数 `workerThread`。在 `ThreadPool` 类的构造函数中,我们启动了 4 个工作线程,每个工作线程都在执行 `workerThread` 函数。在 `ThreadPool` 类的析构函数中,我们设置了线程池退出标志为 `true`,并通过互斥量和条件变量通知所有工作线程退出。在 `addTask` 函数中,我们使用互斥量保护了任务队列,并通过条件变量唤醒正在等待的工作线程。最后,我们定义了一个名为 `MyTask` 的可调用对象,在 `main` 函数中,我们创建了一个 `ThreadPool` 类对象 `pool`,并添加了 10 个 `MyTask` 任务到任务队列中,然后让主线程休眠 1 秒钟。在工作线程中,每个工作线程都会不断地从任务队列中取出任务,并执行。 6. 小结 本文介绍了 C++ 中的成员函数指针的使用方法,以及使用 `std::function` 和 `std::bind` 函数来封装和操作成员函数指针的方法。同时,本文还介绍了成员函数指针在回调函数、GUI 编程和线程池等场景中的应用。 使用成员函数指针的好处是增加了代码的灵活性和可维护性,避免了代码冗余,同时也使得代码更加简洁易懂。 如果你喜欢我们三七知识分享网站的文章,
欢迎您分享或收藏知识分享网站文章
欢迎您到我们的网站逛逛喔!https://www.37seo.cn/
发表评论 取消回复