本文内容来自:
智谱清言
《深入应用C++11 代码优化与工程级应用》
std::future
std::future作为异步结果的传输通道,可以很方便地获取线程函数的返回值。
std::future_status
Ready (
std::future_status::ready
):
- 当与
std::future
对象关联的异步操作已经完成时,std::future
处于 ready 状态。- 在这个状态下,调用
get()
、wait()
或wait_for()
将立即返回,并且get()
将返回操作的结果(或者抛出异常,如果操作以异常结束)。Timeout (
std::future_status::timeout
):
- 当调用
wait_for()
或wait_until()
并且指定的等待时间已经过去,但异步操作尚未完成时,std::future
处于 timeout 状态。- 这意味着
wait_for()
或wait_until()
调用返回了std::future_status::timeout
,表明等待超时,但std::future
仍然可能在未来某个时间点变为 ready 状态。Deferred (
std::future_status::deferred
):
- 当与
std::future
对象关联的异步操作是延迟执行的(即,它将在调用get()
、wait()
时才执行),std::future
处于 deferred 状态。- 这种情况发生在使用
std::async
时指定了std::launch::deferred
,这意味着操作将在第一次调用get()
、wait()
或wait_for()
时在当前线程上执行。
状态转换图:
std::future
对象在其生命周期内只能从 deferred 状态转移到 ready 状态,或者从 ready 状态转移到 timeout 状态(仅在调用wait_for()
或wait_until()
时)。一旦std::future
对象处于 ready 状态,它将保持这种状态直到其结果被获取或对象被销毁。如果异步操作在调用wait_for()
或wait_until()
之前完成,那么即使等待超时,std::future
对象也会处于 ready 状态。
std::future::get()
std::future::wait
当使用
std::async
、std::packaged_task
或std::promise
创建异步任务时,你可以通过std::future
对象来获取结果。std::future
提供了wait
函数,用于阻塞当前线程,直到与future
对象关联的共享状态变为就绪状态。
std::future::wait
的行为与std::future::get
类似,但wait
不会返回结果,它只是等待操作完成。如果你只是想确保操作完成而不需要立即获取结果,使用wait
是合适的。
需要注意的是,如果你在一个已经就绪的
future
上调用wait
,它将立即返回,而不会阻塞。如果你在一个处于deferred
状态的future
上调用wait
,它将执行关联的任务并等待其完成。
#include <future>
#include <iostream>
int main() {
std::future<int> fut = std::async(std::launch::async, []() {
// 异步执行一些操作
return 42;
});
// 等待 future 完成
fut.wait();
// 获取结果
std::cout << "Result: " << fut.get() << std::endl;
return 0;
}
std::future::wait_for
它允许你等待一个 future 对象指定的时长。如果 future 在这段时间内变为就绪状态,
wait_for
将返回std::future_status
,表明 future 的状态。
测试1:
#include <future>
#include <iostream>
#include <chrono>
int main() {
// 创建一个异步任务
std::future<int> fut = std::async(std::launch::async, []() {
std::this_thread::sleep_for(std::chrono::seconds(5)); // 模拟耗时操作
return 42;
});
// 等待 future 完成,最多等待 3 秒
auto status = fut.wait_for(std::chrono::seconds(3));
if (status == std::future_status::ready) {
std::cout << "Future is ready. Result: " << fut.get() << std::endl;
} else if (status == std::future_status::timeout) {
std::cout << "Future is not ready within the given time." << std::endl;
} else if (status == std::future_status::deferred) {
std::cout << "Future is deferred and will execute in the calling thread." << std::endl;
}
return 0;
}
Future is not ready within the given time.
在这个例子中,我们创建了一个异步任务,它将在 5 秒后返回结果。我们使用
wait_for
来等待 future 完成,但只等待 3 秒。因此,wait_for
将返回std::future_status::timeout
,表明 future 在指定的 3 秒内没有准备好。
如果我们将
wait_for
的等待时间增加到超过 5 秒,那么wait_for
将返回std::future_status::ready
,因为异步任务将在等待时间结束前完成。
测试2:
我自己跑了下,改成5s还是不行:
测试3:
6s可以
如果任务是以延迟方式执行的(即
std::launch::deferred
),那么wait_for
将返回std::future_status::deferred
。
-------------
线程异步操作函数async
std::async可以用来直接创建异步的task,异步任务返回的结果保存在future中。
(1)需要获取异步任务的结果时,调用future.get()方法即可。
(2)不关注异步任务的结果,只是简单地等待任务完成的话,调用future.wait()方法。
std::async的第一个参数: std::launch::async | std::launch::deferred
std::launch::async :在调用async时就开始创建线程
std::launch::deferred :延迟加载方式创建线程。调用async时不创建线程,
直到调用了 future的get或者wait时才创建线程。
std::async的第二个参数:线程函数
std::async的第三个参数:线程函数的参数
示例:
#include <future>
#include <iostream>
int main() {
// 使用 std::async 创建一个异步任务
std::future<int> fut = std::async(std::launch::async, []() {
// 异步任务执行一些计算并返回结果
return 42;
});
// 在这里,主线程可能会执行其他任务...
// 获取异步任务的结果
int result = fut.get(); // 这将阻塞,直到异步任务完成
std::cout << "The result is " << result << std::endl;
return 0;
}
launch:启动(计算机程序) sync:同时,同步;协调,一致
std::promise
std::promise将数据与future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传过来的promise赋值,在线程函数执行完成之后就可以通过promise的future获取该值了。
取值是通过promise内部提供的future来获取的。
promise:承诺
#include <future>
#include <iostream>
#include <chrono>
int main() {
std::promise<int> pr;
std::thread t([](std::promise<int>& p) {
// 执行一些操作...
p.set_value_at_thread_exit(9); // 在线程退出时设置值
}, std::ref(pr));
std::future<int> f = pr.get_future();
// 等待子线程完成
t.join();
// 现在可以安全地获取值
auto r = f.get();
std::cout << "Received: " << r << std::endl;
return 0;
}
Received: 9
set_value_at_thread_exit
它允许你设置一个值,这个值将在当前线程退出时传递给与之关联的
std::future
对象在当前线程退出时,应该将给定的值传递给
std::future
。这可以确保即使线程在设置值后立即退出,相关的值也会被存储,并且可以在其他地方通过std::future
对象获取。
- 它在
std::promise
类中声明。- 它接受一个值,这个值可以是任何可复制的类型。
- 它不会立即设置值,而是在当前线程退出时设置值。
- 调用
set_value_at_thread_exit
后,std::promise
对象处于就绪状态,即future::valid()
为true
。- 一旦
set_value_at_thread_exit
被调用,你就不能再通过这个std::promise
对象设置另一个值。
“任何可复制的类型” 指的是那些可以被复制构造函数或复制赋值运算符安全复制的类型。这意味着这些类型的对象可以被创建为其他同类对象的副本,而不会导致未定义行为。
std::ref
std::ref
用于创建一个包装器,这个包装器能够将引用传递给接受值传递的函数。这在需要将引用传递给像线程函数或函数对象这样的参数时非常有用。
以下是一些使用 std::ref
的例子:
1. 将引用传递给线程函数
通常情况下,当创建一个线程时,你传递给 std::thread
构造函数的参数是通过值传递的。如果你想要传递一个引用,你可以使用 std::ref。
#include <iostream>
#include <thread>
#include <functional>
void func(int& n) {
n++;
}
int main() {
int n = 0;
std::thread t(func, std::ref(n));
t.join();
std::cout << "n = " << n << std::endl; // 输出 n = 1
return 0;
}
在上面的例子中,
std::ref(n)
创建了一个引用包装器,使得func
函数能够通过引用修改n
。
---------
2. 将引用传递给函数对象
std::ref
也可以用于将引用传递给函数对象。
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
struct Increment {
void operator()(int& n) {
n++;
}
};
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), Increment());
std::for_each(vec.begin(), vec.end(), Increment());
for (int n : vec) {
std::cout << n << ' '; // 输出 3 4 5 6 7
}
std::cout << std::endl;
return 0;
}
std::for_each
接受一个函数对象Increment
,并且因为Increment
接受一个引用参数,所以不需要使用std::ref
。但是,如果函数对象是通过值传递参数的,使用std::ref
可以确保传递的是引用。
----------
记住,
std::ref
只在需要引用语义时使用。如果你不需要修改原始对象,或者函数接受的是按值传递的参数,那么使用std::ref
是不必要的。
---------
std::for_each
std::for_each
是 C++ 标准库中的一个算法,它用于对容器中的每个元素执行一个操作。这个算法会遍历指定范围内的所有元素,并对每个元素调用提供的函数或函数对象。
template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );unary:一元的
InputIt first, InputIt last
: 这两个参数定义了要遍历的元素范围。通常,它们是容器的迭代器。UnaryFunction f
: 这是一个函数或函数对象,它将被应用于范围内的每个元素。这个函数或函数对象应该接受一个参数,对应于范围内的每个元素.
由于我们正在修改容器中的元素,迭代器必须是能够修改元素的类型,即随机访问迭代器。在
std::vector
中,begin()
和end()
返回的迭代器就是这种类型。
std::for_each
是 C++ 标准库中的一个算法,它用于对容器中的每个元素执行一个操作。这个算法会遍历指定范围内的所有元素,并对每个元素调用提供的函数或函数对象。
template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );unary:一元的
InputIt first, InputIt last
: 这两个参数定义了要遍历的元素范围。通常,它们是容器的迭代器。UnaryFunction f
: 这是一个函数或函数对象,它将被应用于范围内的每个元素。这个函数或函数对象应该接受一个参数,对应于范围内的每个元素.
由于我们正在修改容器中的元素,迭代器必须是能够修改元素的类型,即随机访问迭代器。在
std::vector
中,begin()
和end()
返回的迭代器就是这种类型。
----------
传递给 std::thread
构造函数的参数默认是通过值传递的
当创建一个
std::thread
对象时,传递给std::thread
构造函数的参数默认是通过值传递的。这意味着,如果传递的是非引用类型,那么线程函数将会接收到传递参数的一个副本。
#include <iostream>
#include <thread>
void threadFunction(int n) {
// 这里我们接收到的是 n 的副本
n += 100;
std::cout << "Inside thread: " << n << std::endl;
}
int main() {
int n = 1;
std::thread t(threadFunction, n); // 传递 n 的副本
t.join();
std::cout << "Outside thread: " << n << std::endl; // n 仍然是 1
return 0;
}
如果我们想要在线程函数中修改原始变量,就需要传递一个引用。但是,由于
std::thread
构造函数默认是按值传递的,因此需要使用std::ref
来传递引用
#include <iostream>
#include <thread>
#include <functional>
void threadFunction(int& n) {
n += 100;
std::cout << "Inside thread: " << n << std::endl;
}
int main() {
int n = 1;
std::thread t(threadFunction, std::ref(n)); // 使用 std::ref 传递引用
t.join();
std::cout << "Outside thread: " << n << std::endl; // n 现在是 101
return 0;
}
这就是
std::ref
的用途所在,它允许在线程或其他按值传递参数的上下文中传递引用。
---------
线程退出
线程的退出时机取决于线程的执行逻辑和线程的创建方式。以下是几种情况下线程可能退出的时机:
-
任务完成退出:线程执行的任务完成后,线程函数(无论是普通函数、成员函数还是 lambda 表达式)会执行返回语句。当线程函数返回时,线程会自然退出。这是线程退出的最常见方式。
-
自毁退出:在 C++ 中,当
std::thread
对象被销毁时,如果关联的线程仍在运行,std::thread
的析构函数会调用std::terminate
来终止程序,除非:- 线程已经完成了它的执行(任务完成)。
- 线程是通过
std::detach
被分离的。 - 线程被
join
过,且std::thread
对象是通过移动构造或移动赋值创建的。
-
分离退出:如果线程被分离(使用
std::thread::detach
),它将在后台运行,与创建它的std::thread
对象无关。线程将在执行完毕后自动退出,资源被系统回收。 -
异常退出:如果线程执行的函数抛出了未捕获的异常,并且没有设置相应的异常处理机制,线程将异常退出。
-
外部干预退出:线程可以被外部干预强制退出,例如:
- 调用
pthread_cancel
(在 POSIX 线程中)来请求取消线程。 - 在 Windows 中,可以通过调用
TerminateThread
或ExitThread
来终止线程,但这通常是不安全的做法,因为它可能导致资源泄露和其他问题。
- 调用
-
程序终止:当主线程退出时,如果其他线程是可连接的(joinable),程序通常会调用
std::terminate
来终止这些线程。如果其他线程是分离的,它们可能在主线程退出后继续运行,直到完成或被系统强制终止。
为了确保资源得到正确释放,线程应当尽可能通过自然的退出路径结束,例如任务完成或通过 join
等待线程结束。避免使用强制退出线程的方法,因为这可能会导致不一致的状态和资源泄露。
---------
-
移动构造和移动赋值:当一个
std::thread
对象是通过移动构造或移动赋值创建的,它会接手源对象所管理的线程,而源对象将不再管理任何线程(即变为非.joinable状态)。 -
被
join
过的std::thread
:如果一个std::thread
对象已经调用了join
,那么它将不再管理任何线程,也就是说它变成了非.joinable状态。在这种情况下,再次调用join
是不允许的,将会导致未定义行为。
---------
可调用对象的包装类std::package_task
package(包装)
std::package_task包装了一个可调用对象的包装类(如function,lambda expression,bind expression和another function object),将函数和future绑定起来,以便异步调用。
它和std::promise在某种程度上有点像,promise保存了一个共享状态的值,而packaged_task保存的是一个函数。
它允许异步执行可调用对象,并能够获取其返回值或异常。
std::packaged_task
的主要用途是在多线程编程中,当你想要在另一个线程中执行某个任务,并随后获取该任务的执行结果时。
std::packaged_task
的一些基本用法:
创建 std::packaged_task
首先,你需要包含 <future>
头文件,然后创建一个 std::packaged_task
对象,并将其绑定到一个可调用对象上。
#include <future>
#include <iostream>
int calculateSomething() {
// 假设这里有一些计算
return 42;
}
int main() {
std::packaged_task<int()> task(calculateSomething);
return 0;
}
获取 std::future
std::packaged_task
对象有一个 get_future()
方法,它返回一个 std::future
对象,该对象可以用来存储异步操作的结果。
std::future<int> result = task.get_future();
执行任务
你可以通过直接调用 std::packaged_task
对象或将其传递给一个线程来执行任务
// 直接调用
task();
// 或者在另一个线程中执行
std::thread thread(std::move(task));
thread.join();
获取结果
在任务执行完毕后,你可以通过 std::future
对象获取结果。
int value = result.get(); // 这将阻塞直到任务完成
std::cout << "The result is " << value << std::endl;
示例:
#include <iostream>
#include <thread>
#include <utility>
#include <future>
#include <thread>
int func (int x) { return x + 2; }
int main()
{
std::packaged_task<int(int)> tsk(func);
std::future<int> fut = tsk.get_future();
std::thread(std::move(tsk),2).detach();
int value = fut.get();
std::cout << "value:" << value << std::endl;
}
value:4
需要注意的是,使用
.detach()
后,线程将在后台运行,主线程不会等待它结束。如果主线程结束,程序会终止,这可能会导致线程在完成其任务之前就被强制结束。
为了避免这种情况,通常建议在程序结束前确保所有线程都已完成其工作或者确保程序不会因为主线程的退出而结束。
在这个例子中,由于
fut.get()
会等待任务完成,所以使用.detach()
是安全的。
测试:
如果主线程结束,程序会终止,这可能会导致线程在完成其任务之前就被强制结束。
#include <iostream>
#include <thread>
#include <QFile>
#include <QDebug>
void threadFunction() {
std::this_thread::sleep_for(std::chrono::seconds(2));
// 创建一个 QFile 对象,并指定文件名
QFile file("example.txt");
// 打开文件用于写入,如果文件不存在则创建它
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
// 使用 QTextStream 来简化向文件写入文本
QTextStream out(&file);
// 写入一些信息
out << "Hello, World!" << endl;
// 关闭文件
file.close();
} else {
// 如果文件打开失败,打印错误信息
qWarning("无法打开文件: %s", qPrintable(file.errorString()));
}
std::cout << "Thread finished execution." << std::endl;
}
int main() {
std::thread t(threadFunction);
t.detach(); // 分离线程
//t.join();
std::cout << "Main thread will now exit." << std::endl;
// 注意:这里没有等待分离的线程结束
return 0;
}
结果:确实没有生成txt文件,也没有打印出"Hello,World!"
---------
detach
是std::thread
类的一个成员函数,用于将当前线程从其std::thread
对象分离。一旦线程被分离,它将独立于其std::thread
对象运行,并且不能再通过该对象与之交互
分离后的线程: 调用
detach
后,线程将在后台运行,这意味着主程序不需要等待该线程结束即可继续执行或退出。分离的线程在结束时将自动释放与其关联的所有资源。无法再次连接: 一旦线程被分离,就不能再通过
std::thread
对象与之连接,也就是说,你不能使用std::thread::join()
方法来等待线程结束。资源管理: 当一个线程对象被销毁时,如果它仍然与一个执行线程相关联(即没有被分离或加入),程序将终止。因此,如果你不打算使用
join
来等待线程结束,你应该使用detach
来避免资源泄漏和潜在的未定义行为。线程结束: 即使创建该线程的
std::thread
对象被销毁,线程也会继续运行直到其任务函数完成。如果线程函数抛出异常,且该异常没有被捕获,程序将调用std::terminate
终止。使用场景:
detach
通常用于那些不需要返回值的后台任务,或者当你知道线程将在程序结束前完成其工作。无法获取返回值:因为线程已经与
std::thread
对象断开连接,所以无法通过std::thread
对象获取线程的返回值(间接)。
完整示例:
#include <future>
#include <iostream>
#include <thread>
int calculateSomething() {
std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
return 42;
}
int main() {
// 创建 packaged_task
std::packaged_task<int()> task(calculateSomething);
// 获取 future 对象
std::future<int> result = task.get_future();
// 在单独的线程中执行任务
std::thread thread(std::move(task));
// 主线程可以继续执行其他任务...
// 获取结果
int value = result.get(); // 阻塞直到任务完成
std::cout << "The result is " << value << std::endl;
// 等待线程结束
thread.join();
return 0;
}
calculateSomething
函数在一个单独的线程中执行,并通过std::future
获取其返回值。注意,当result.get()
被调用时,如果任务尚未完成,它将阻塞调用线程,直到任务完成。
---------
上面使用了std::move
std::thread thread(std::move(task));
std::move
是用来转移task
对象的所有权。这意味着task
变量将不再拥有包装的任务,而新创建的线程将拥有这个任务。在调用std::move
后,task
对象将变为空,不能再次被使用。
---------
还可以使用std::ref
std::thread thread(std::ref(task));
std::ref
是用来传递task
对象的引用给线程。这意味着task
对象本身不会被移动,它在主线程和创建的新线程中都是可用的。在这种情况下,task
对象必须保持有效,直到新线程完成其执行,否则可能会导致未定义行为
---------
以下是对两者的简要比较:
std::move
:适用于当你想要将task
的所有权转移给新线程,并且不再在当前线程中使用task
的情况。std::ref
:适用于当你需要在主线程和新线程中都保持对task
的访问权的情况。
---------
需要注意的是,由于 std::packaged_task
不支持拷贝,所以在创建线程时不能直接传递 task
(除非是传递它的引用)。如果你尝试这样做,编译器会报错,因为 std::thread
的构造函数需要一个可移动的参数。这就是为什么通常使用 std::move
或 std::ref
来传递 std::packaged_task
对象给 std::thread
的原因。
把std::packaged_task对象作为参数传递给std::thread对象时,因为std::packaged_task对象不支持拷贝,无法通过拷贝构造函数生成一个临时对象,所以编译器会报错。
---------
std::thread
的构造函数需要一个可移动的参数。这是因为在 C++11 中,线程的创建通常涉及到资源的转移,例如线程句柄或执行函数的状态。为了支持这种资源转移,std::thread
的构造函数使用了右值引用,这允许构造函数通过移动语义来接管传递给它的资源。
具体来说,std::thread
接受一个可调用对象(如函数、函数对象或 lambda 表达式)作为参数,并且这个可调用对象需要是可移动的。以下是构造函数的声明:
template <class Function, class... Args>
explicit thread(Function&& f, Args&&... args);
在这个构造函数中,Function&&
是一个右值引用,这意味着它可以绑定到一个临时对象或通过 std::move
显式转换成的右值。这允许 std::thread
在构造过程中接管传入的可调用对象的资源,而不需要进行复制。
---------
future与shared_future
future是不可拷贝的。
这意味着一旦 std::future
被创建,它就不能被复制到另一个 std::future
对象中,也不能被添加到需要复制元素的容器(如 std::vector
, std::list
, std::deque
等)中。
std::future
支持移动语义。
这意味着它可以被移动到另一个 std::future
对象中,但是一旦移动,原始的 future
对象将不再有效。这使得 std::future
对象不适合在容器中存储或在多个线程间共享。
虽然可以将 std::future
对象存储在需要移动语义的容器(如 std::vector
)中,但是每次操作(如添加或插入)都会导致原始 future
对象失效。
std::future
提供了一种访问异步操作结果的方式,但是它被设计为只能由一个线程访问其结果。
std::future
提供的 .get()
和 .wait()
方法只能由一个线程调用一次。如果多个线程尝试访问同一个 std::future
对象的结果,则只有第一个调用这些方法的线程能够成功,其他线程将抛出 std::future_error
异常。
一旦 std::future
的结果被获取,它就会变成一个 “ready” 状态,之后就不能再被获取了。这意味着一旦一个线程获取了结果,其他线程就不能再访问该结果。
---------
shared_future是可以拷贝的。
当需要将future放在容器时,使用shared_future。
当需要在多个线程之间共享 future
的结果时,使用 shared_future
。
std::shared_future
允许多个线程共享同一个异步操作的结果。
shared_future
可以被复制,因此可以在多个线程中安全地共享。shared_future
可以多次调用get()
方法来获取异步操作的结果,直到所有shared_future
对象都被销毁。- 当所有共享的
shared_future
对象都析构时,关联的promise
或packaged_task
也会被析构。
以下是如何使用 shared_future
在容器中存储异步操作结果的示例:
#include <iostream>
#include <future>
#include <vector>
int main() {
// 创建一个 promise 和一个 shared_future
std::promise<int> p;
std::shared_future<int> f = p.get_future();
// 在容器中存储 shared_future 的多个副本
std::vector<std::shared_future<int>> futures;
futures.push_back(f); // 复制 shared_future
futures.push_back(f); // 再次复制 shared_future
// 在另一个线程中设置 promise 的值
std::thread t([&p] {
p.set_value(42);
});
// 在容器中的每个 shared_future 上调用 get()
for (auto& fut : futures) {
std::cout << "Future result: " << fut.get() << std::endl;
}
// 等待线程完成
t.join();
return 0;
}
由于我们使用的是 shared_future
,我们可以在容器中的每个 shared_future
对象上调用 get()
方法来获取结果,而不会遇到任何问题。
使用 shared_future
需要注意的是,如果多个线程同时等待同一个 shared_future
的结果,如果结果变得可用,它们将同时被唤醒,这可能导致竞争条件,特别是如果这些线程接下来执行的操作需要互斥访问某个资源时。因此,在使用 shared_future
时,需要确保适当的同步机制以避免潜在的问题。
---------
可调用对象:
指的是可以在调用表达式中使用的对象.C++ 中的可调用对象包括以下几种类型:
-
函数指针:指向函数的指针可以用来调用对应的函数。
-
函数对象(Functors):重载了
operator()
的类对象,使得它们可以被当作函数来调用。 -
Lambda 表达式:C++11 引入的匿名函数,可以作为临时对象使用。
-
成员函数指针:指向类成员函数的指针,需要结合对象来调用。
-
绑定器和函数适配器:如
std::bind
和std::function
,可以用来包装其他可调用对象,提供额外的灵活性。
示例:
函数指针
void func(int x) {
std::cout << x << std::endl;
}
int main() {
void (*funcPtr)(int) = func; // 声明一个函数指针
funcPtr(42); // 通过函数指针调用函数
return 0;
}
函数对象(Functor)
struct Functor {
void operator()(int x) {
std::cout << x << std::endl;
}
};
int main() {
Functor functor;
functor(42); // 通过函数对象调用
return 0;
}
Lambda 表达式
int main() {
auto lambda = [](int x) { std::cout << x << std::endl; };
lambda(42); // 通过 lambda 表达式调用
return 0;
}
成员函数指针
class MyClass {
public:
void memberFunc(int x) {
std::cout << x << std::endl;
}
};
int main() {
MyClass obj;
void (MyClass::*memberFuncPtr)(int) = &MyClass::memberFunc;
(obj.*memberFuncPtr)(42); // 通过成员函数指针调用
return 0;
}
std::function
#include <functional>
#include <iostream>
void func(int x) {
std::cout << x << std::endl;
}
int main() {
std::function<void(int)> funcObj = func;
funcObj(42); // 通过 std::function 调用
auto lambda = [](int x) { std::cout << x << std::endl; };
funcObj = lambda;
funcObj(24); // 通过 std::function 调用 lambda
return 0;
}
在 C++ 中,可调用对象的使用非常灵活,能够实现多种编程模式,如回调函数、策略模式等。标准库中的许多算法也接受可调用对象作为参数,例如
std::sort
、std::for_each
等