C++ 类的成员函数指针 ( function bind )

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 func = std::bind(&MyClass::add, &myObj, std::placeholders::_1, std::placeholders::_2);

std::cout << func(2, 3) << std::endl; // 输出 5

return 0;

}

```

在这个示例中,我们首先包含了头文件 ``,然后定义了一个名为 `MyClass` 的类,其中我们定义了一个名为 `add` 的成员函数用于求两个数的和。在 `main` 函数中,我们创建了一个 `MyClass` 的对象 `myObj`,并使用 `std::bind` 函数将 `MyClass::add` 函数和 `myObj` 对象绑定到了一个函数对象 `func` 上。然后通过 `func(2, 3)` 调用成员函数 `MyClass::add`,并输出其结果 `5`。

4. std::bind 的使用

`std::bind` 函数可以用于绑定函数或成员函数的参数,从而产生新的函数对象。其函数原型如下:

```c++

template

std::function::type()>

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 func1 = std::bind(add, 2, 3);

std::cout << func1() << std::endl; // 输出 5

MyClass myObj;

std::function func2 = std::bind(&MyClass::add, &myObj, 2, 3);

std::cout << func2() << std::endl; // 输出 5

std::function func3 = std::bind(add, std::placeholders::_1, 3);

std::cout << func3(2) << std::endl; // 输出 5

std::function func4 = std::bind(&MyClass::add, &myObj, std::placeholders::_1, 3);

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 Callback;

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 Callback;

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 Task;

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 lock(m_mutex);

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 lock(m_mutex);

m_queue.push(task);

m_cond.notify_one();

}

private:

void workerThread() {

while (true) {

Task task;

{

std::unique_lock lock(m_mutex);

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 m_threads;

std::queue m_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/

点赞(55) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部