文章目录
1.std::function 开启函数式编程之路
std::function 是 C++ 标准库中的一个模板类,用于封装可调用对象(函数、函数对象、成员函数指针等),并提供一种统一的调用接口。它的主要作用是将可调用对象抽象为一个对象,使得可以像普通对象一样进行传递、存储和调用,从而提高了 C++ 中函数的灵活性和可重用性。
代码:
#include <iostream>
#include <functional>
// 函数对象示例
struct Functor {
int operator()(int a, int b) const {
return a + b;
}
};
// 普通函数示例
int add(int a, int b) {
return a + b;
}
// 成员函数示例
class MyClass {
public:
int addMember(int &a, int b) const {
return a + b;
}
};
int main() {
// 绑定普通函数
std::function<int(int, int)> func1 = add;
std::cout << "func1 result: " << func1(1, 2) << std::endl;
// 绑定函数对象
Functor functor;
std::function<int(int, int)> func2 = functor;
std::cout << "func2 result: " << func2(3, 4) << std::endl;
// 绑定 lambda 表达式
std::function<int(int, int)> func3 = [](int a, int b) -> int { return a + b; };
std::cout << "func3 result: " << func3(5, 6) << std::endl;
// 绑定成员函数
MyClass obj;int a = 100;
std::function<int(const MyClass&, int&, int)> func4 = &MyClass::addMember;
a = 1000;
std::cout << "func4 result: " << func4(obj, std::ref(a), 8) << std::endl;
// 使用 std::placeholders 绑定成员函数
std::function<int(const MyClass&, int&, int)> func5 = std::bind(&MyClass::addMember, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
std::cout << "func5 result: " << func5(obj, a, 10) << std::endl;
// 使用 std::ref 传递引用
int result = 0;
std::function<void(int, int)> func6 = [&result](int a, int b) { result = a + b; };
func6(11, 12);
std::cout << "func6 result: " << result << std::endl;
return 0;
}
2.std::bind 灵活处理函数调用和传参
std::bind 是 C++ 标准库中的一个函数模板,用于将函数与其参数绑定起来,生成一个可调用对象。它的主要作用是延迟函数调用,并且可以在调用时提供部分或全部的参数。std::bind 可以用于创建函数对象、绑定成员函数、创建函数适配器等。
代码:
#include <iostream>
#include <functional>
// 函数对象示例
int add(int a, int b) {
return a + b;
}
// 成员函数示例
class MyClass {
public:
int addMember(int a, int b) {
return a + b;
}
};
int main() {
int a = 1, b = 2 ;
// 绑定普通函数
auto func1 = std::bind(add, std::ref(a), std::ref(b)); // std::ref 见 std::ref 对应节
b = a = 111;
std::cout << "func1 result: " << func1() << std::endl;
// 绑定成员函数
MyClass obj;
auto func2 = std::bind(&MyClass::addMember, &obj, std::ref(a), std::ref(b));
a=b=1;
std::cout << "func2 result: " << func2() << std::endl;
// 绑定成员函数并传递额外参数
auto func3 = std::bind(&MyClass::addMember, &obj, std::placeholders::_1, std::placeholders::_2);
std::cout << "func3 result: " << func3(a, b) << std::endl;
// 绑定成员函数的成员
auto func4 = std::bind(&MyClass::addMember, &obj, std::placeholders::_1, std::ref(b));
b = 2;
std::cout << "func4 result: " << func4(a) << std::endl;
return 0;
}
3. std::ref 传递引用的必要包装
std::ref 是 C++ 标准库中的一个函数模板,用于将一个对象或者引用包装为一个 std::reference_wrapper 对象,从而使得对象可以像引用一样被传递。std::reference_wrapper 对象可以在函数传参时传递引用,而不是对象的副本,这在某些情况下可以提高程序的效率。诸如:std::bind、std::function
还是不理解的话,举例如下:std::thread 在传入参数时默认为值传递,此时,对要传递的左值使用std::ref(variable) 则可以传递引用进去。示例可见 std::thread。
代码
#include <iostream>
#include <functional>
void modify(int& num) {
num += 10;
}
int main() {
int num = 5;
// 通过 std::ref 将引用包装成 std::reference_wrapper
auto refNum = std::ref(num);
// 通过 std::reference_wrapper 传递引用
modify(refNum);
// 输出修改后的值
std::cout << "Modified num: " << num << std::endl;
return 0;
}
4.std::thread 标准库的多线程
std::thread 是 C++ 标准库中用于创建线程的类,它提供了一种方便的方式来创建并发执行的线程。
code ①:常规各类用法示例
#include <iostream>
#include <thread>
#include <vector>
// 线程函数
void threadFunction(int& num) {
++num;
std::cout << "Hello from thread! Num: " << num << std::endl;
}
class Worker {
public:
void operator()() const {
std::cout << "Hello from worker thread!" << std::endl;
}
};
int main() {
int Num = 1;
// 创建一个线程并启动
//std::thread t1(threadFunction, Num ); 编译报错
//t1.join();
std::thread t1_ref(threadFunction, std::ref(Num) );
t1_ref.join();
std::cout << "Hello from Main thread! Num: " << Num << std::endl;
// 创建一个线程并启动,使用 Lambda 表达式
int val = 2;
std::thread t2([val](){
std::cout << "Hello from thread! Value: " << val << std::endl;
});
// 创建一个线程并启动,使用函数对象
Worker worker;
std::thread t3(worker);
// 等待所有线程结束
t2.join();
t3.join();
// 创建多个线程并启动
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back([i]() {
std::cout << "Hello from thread " << i << std::endl;
});
}
// 等待所有线程结束
for (auto& thread : threads) {
thread.join();
}
// 获取硬件线程数
unsigned int numThreads = std::thread::hardware_concurrency();
std::cout << "Number of hardware threads: " << numThreads << std::endl;
return 0;
}
code ②:类成员重载时需要转换指针类型,否则报错(std::bind也有同类问题)
#include <iostream>
#include <thread>
#include <string>
class A {
public:
void setName(const std::string& name) {
this->name = name;
}
void funcDefault(int a) {
std::cout << "func4(int) called with value: " << a << " and name: " << name << std::endl;
}
// Overload for int argument
void func(int a) {
std::cout << "func4(int) called with value: " << a << " and name: " << name << std::endl;
}
// Overload for string argument
void func(std::string str) {
std::cout << "func4(string) called with value: " << str << " and name: " << name << std::endl;
}
private:
std::string name;
};
int main() {
// default
A* aPtr = new A();
aPtr->setName("int Default Name");
std::thread defThread(&A::funcDefault, aPtr, 100); // func4 expecting int
defThread.join();
delete aPtr;
// Create an object of class A and call func4 with an int
A* aPtr2 = new A();
aPtr2->setName("int Name");
std::thread iThread( (void(A::*)(int))&A::func, aPtr2, 100); // func4 expecting int
iThread.join();
delete aPtr2;
// Create another object of class A and call func4 with a string
A* sPtr = new A();
sPtr->setName("string Name");
std::thread sThread( static_cast<void(A::*)(std::string)>(&A::func), sPtr, "100 string"); // func4 expecting string
sThread.join();
delete sPtr;
// Example of calling a free function (not a member function)
auto free_func = [](int x) {
std::cout << "free_func called with value: " << x << std::endl;
};
// Starting thread with a free function
std::thread t_free(free_func, 150);
t_free.join();
return 0;
}
5.std::packaged_task 封装函数为可异步调用的对象
std::packaged_task 是 C++ 标准库中的一个类模板,用于将函数封装为一个可调用的对象,并可以异步执行该函数。它通常与 std::future 结合使用,用于获取函数执行结果。
代码:
#include <iostream>
#include <future>
#include <thread>
#include <functional>
// 任务函数
int taskFunction(int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
return a + b;
}
int main() {
// 创建一个 packaged_task,绑定任务函数
std::packaged_task<int(int, int)> task(taskFunction);
// 获取与 packaged_task 关联的 future
std::future<int> future = task.get_future();
// 启动一个新线程执行任务,并传递参数
std::thread t(std::move(task), 1, 2);
// 等待任务执行完成并获取结果
int result = future.get();
// 输出结果
std::cout << "Result: " << result << std::endl;
// 等待线程结束
t.join();
// 重新设置 packaged_task 关联的任务函数
std::packaged_task<int(int, int)> task2;
task2.reset(); // 重置为默认构造函数创建的状态
return 0;
}
6. placement new
“placement new” 是一种特殊形式的 new
,它允许在指定的内存地址上构造对象,而不是在堆上分配新的内存。这种形式的 new
主要用于特殊的内存管理需求,例如在已经分配的内存块中创建对象,或在特定的内存区域(如共享内存或硬件寄存器)中构造对象。
为什么写这个呢?不怎么用的东西,然而可能在一些代码里看到,不懂这个就会懵掉。
a. placement new 的函数声明:
#include <new> // 需要包含头文件
void* operator new(size_t size, void* ptr) noexcept;
void operator delete(void* ptr, void* place) noexcept;
b. 代码:
- 解释:
- 使用
placement new
在指定的内存地址上构造对象。注意:不是去申请堆内存,而是用户指定的内存地址上构造类。 - 使用
operator delete(ptr, place)
释放对象,其中ptr
是对象的指针,place
是对象所在的内存地址。
#include <iostream>
#include <new> // 包含 placement new
class MyClass {
public:
MyClass(int value) : m_value(value) {
std::cout << "Constructor called with value: " << m_value << std::endl;
}
~MyClass() {
std::cout << "Destructor called for value: " << m_value << std::endl;
}
void printValue() {
std::cout << "Value: " << m_value << std::endl;
}
private:
int m_value;
};
int main() {
char buffer[sizeof(MyClass)]; // 预分配一块内存,大小足够容纳 MyClass 对象
MyClass* myObject = new (buffer) MyClass(42); // 在预分配的内存块中使用 placement new 构造 MyClass 对象
myObject->printValue(); // 调用对象的成员函数
myObject->~MyClass(); // 需要显式调用对象的析构函数(不会自动调用),注意不要使用 delete
operator delete(myObject, buffer); // 不能调用默认 delete,用户指定内存不是一定是堆内存程序可能会寄掉。另外 本行代码的 delete 一般也是不需要调用的,析构函数中处理好即可。
return 0;
}
c. 场景
- 定位构造和销毁对象:当需要在指定的内存地址上构造和销毁对象时,可以使用
placement new
和operator delete(ptr, place)
。 - 定制内存管理:在一些特殊的内存管理场景中,可能需要精确地控制对象的内存分配和释放,这时可以使用该方法。
- 对象池:在实现对象池时,可以使用
placement new
和operator delete(ptr, place)
来管理对象的分配和释放。 - 硬件寄存器映射:在嵌入式系统等场景中,可以使用该方法将对象映射到特定的硬件寄存器地址上。
[持续。。。]
最近更新:2024-05-09
the end~