std::function<void(Socket*)> cb = std::bind(&Server::newConnection, this, std::placeholders::_1);
std::bind
std::bind
是一个用于绑定函数、成员函数或可调用对象的工具,它允许你将某些参数预先绑定到一个函数上,从而生成一个新的可调用对象
std::placeholders::_1:
占位符的作用正是为绑定的函数预留参数位置,以便在调用时动态传递参数。它允许你在使用 std::bind
时,预先绑定部分参数,同时为其他参数保留位置,这些参数可以在调用时动态提供。
占位符的编号规则
占位符的编号从 _1
开始,表示调用时传递的第一个参数,_2
表示第二个参数,依此类推。这些占位符的顺序和数量决定了绑定函数的参数顺序和数量。
std::function<void(Socket*)> newConnectionCallback;
std::bind
返回的对象是一个可调用对象,但不是普通函数指针。因此,不能直接将 std::bind
的返回值赋给 void(Socket*)
类型的变量。
std::function
是一个通用的函数封装器,它可以存储任何可调用对象,只要其调用签名匹配即可。
std::move()
std::function<void()> sub_loop = std::bind(&EventLoop::Loop, sub_reactors_[i]);
thread_pool_->Add(std::move(sub_loop));
将 sub_loop
变成一个右值,以便进行移动语义。
std::move()
不会移动数据,它只是将一个变量的类型从左值变成右值,让它支持移动语义(Move Semantics)。
- 左值(Lvalue):可以取地址,有名字,生命周期长。
- 右值(Rvalue):通常是临时对象,没有名字,生命周期短。
示例:
std::string str = "Hello"; std::string new_str = std::move(str);
std::move(str)
不会真的移动str
的内容,但它告诉编译器str
已经不需要了,可以被移动。new_str
可能会接管str
的数据,而str
变成空字符串(避免额外的内存分配)。
std::make_shared和std::shared_ptr<T>
auto task =
std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
-
std::make_shared 避免了手动 new 和 delete,减少内存泄漏的风险。 std::shared_ptr 自动管理 std::packaged_task 的生命周期,在没有引用时释放资源。 比 new 和 std::shared_ptr 分开使用更高效,因为它减少了额外的内存分配。
std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
std::shared_ptr<int> ptr2 = ptr1; // 共享所有权
特点:
- 多个
shared_ptr
可以共享同一个资源。 - 采用引用计数(Reference Counting),当计数变为
0
时自动释放资源。 - 可以复制(
copy
),不会导致delete
被多次调用。
适用场景:
- 需要 多个对象共享同一个资源。
- 适用于 动态分配的对象,如共享缓存、树结构、图结构等
-
智能指针 特点 使用场景 std::unique_ptr<T>
独占所有权,不能复制 适用于 独占资源管理,如文件句柄、数据库连接 std::shared_ptr<T>
引用计数,可多个 shared_ptr
共享适用于 多个对象共享同一资源,如缓存、图结构 std::make_shared<T>()
高效创建 shared_ptr
(单次内存分配)推荐使用,避免 new
std::unique_ptr<T>
C++11 之前:
- 需要手动管理 动态分配的对象(
new
和delete
),容易导致内存泄漏。
C++11 引入的改进:
std::unique_ptr<T>
是 独占所有权 的智能指针,保证资源不会被泄露。- 当
unique_ptr
作用域结束时,自动释放资源。
特点:
-
只有一个指针拥有资源,不能被复制(copy)。 但可以通过 std::move() 转移所有权。 当 unique_ptr 离开作用域时,自动释放资源(调用 delete)。
适用场景:
- 需要唯一所有权,不需要多个对象共享同一个资源。
- 适用于 RAII(资源获取即初始化) 管理,如文件句柄、数据库连接等
用法:
std::unique_ptr<Socket> socket_; std::unique_ptr<Channel> channel_;
优势:
🚀 std::unique_ptr<T>
适合独占资源,std::shared_ptr<T>
适合多线程共享!
- 避免
new
和delete
造成的 内存泄漏。 - 不能被复制,只能通过
std::move()
转移所有权,防止 意外的多重释放。 std::unique_ptr<T>
不能在多个线程共享,但可以用std::move()
转移所有权。std::unique_ptr<T>
不是线程安全的,如果多个线程访问它,需要用std::mutex
保护。std::shared_ptr<T>
支持多线程共享,内部采用原子操作管理引用计数,适用于多线程环境。- 优先使用
std::unique_ptr<T>
,只有当确实需要多个线程共享资源时,才使用std::shared_ptr<T>
。
问题 | std::unique_ptr<T> |
std::shared_ptr<T> |
---|---|---|
能否多个线程共享? | ❌ 不能,每次只能一个线程拥有 | ✅ 可以,多个线程共享 |
能否在多个线程间转移? | ✅ 可以,但必须用 std::move() |
✅ 共享资源,自动管理引用计数 |
线程安全? | ❌ 不是,需要 std::mutex 保护 |
✅ 线程安全(内部有原子操作) |
适用场景 | 资源必须唯一所有权,适用于文件句柄、数据库连接等 | 多线程共享资源,适用于缓存、任务队列等 |
std::make_unique<T>()
std::make_unique<T>()
是一个工厂函数,用于创建 unique_ptr
,它比直接使用 new
创建对象并传递给 unique_ptr
更安全和简洁。它在 C++14 中被引入,主要解决了几个问题:
- 避免裸指针泄漏:使用
std::make_unique
可以避免裸指针泄漏问题。因为它确保了创建对象和包装在unique_ptr
之间的一致性,避免了在对象创建失败时忘记释放内存的情况。 - 避免重复的
new
操作:std::make_unique
自动将new
操作封装在内部,不需要显式调用new
,从而减少代码冗余。
创建 std::unique_ptr<T>
std::unique_ptr<Socket> socket_ = std::make_unique<Socket>();
- 通过
std::make_unique
创建Socket
对象,并返回一个指向该对象的unique_ptr
。 std::make_unique
无法传递nullptr
,它总是返回有效的指针。-
特性 std::unique