

(1) 应用场景的门槛
•
C++做界面开发:MFC、QT、游戏客户端、OpenCV图像处理...
•
如果进不了相关公司,根本没人给你实践的机会
(2) 基础设施的壁垒
•
C/C++基础设施开发:分布式数据库、存储系统、中间件...
•
如果进不了相关公司,根本没人给你实践的机会
•
即使有开源项目,个人也难有合适的环境练习
•
像TiFlash、OceanBase、Ceph这些项目,平时根本用不到
•
没有实际需求,很难静下心来深入研究
(3) 唯一可行的路径
•
只剩下在普通笔记本上,建立工程,用std标准库写demo练习
对比其他同学怎么做的,
通过考研,面试 进入行业公司,然后直接瞬间进入数据库,os ai 行业 然后再开发相关项目。
你看出来了吗?
最关键的 并不是 c++本身,明白了,进入相关行业,
我为什么痴迷于写demo、研究原理?
都是被现实逼的。
哪怕进入公司 讨论 方案设计,bug 定位解决,没有 人关系 c++新特性,
所以,我能做的就是把能做的事情做好:
•
一个笔记本,建立项目工程,写一个 cpp 文件,std 标准库,写 demo,git、cmake
•
把C++11新特性研究透彻
今天 就解决掉最 不关键事情,研究无用事情。
•
unique_ptr 独占所有权 什么场景使用?提示 资源,资源赋值拷贝中传递
本文举例代码均来 3FS ,TiFlash 等事项
目的:资源只有 1 个,传递拷贝只有 1 个
┌─────────────────────────────────────────────────────────────┐
│ ObjectPool<T, NumTLSCachedItem, ...> │
│ (Template Class) │
├─────────────────────────────────────────────────────────────┤
│ ┌─ 类型别名 ─────────────────────────────────────────────┐ │
│ │ using Storage = std::aligned_storage_t<...> │ │
│ │ using Batch = std::vector<std::unique_ptr<Storage>> │ │
│ │ using Ptr = std::unique_ptr<T, Deleter> │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ┌─ 私有成员 ─────────────────────────────────────────────┐ │
│ │ - std::mutex mutex_ │ │
│ │ - std::vector<Batch> global_ │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│
│ 包含
│ (1:N, thread_local)
▼
┌─────────────────────────────────────────────────────────────┐
│ TLS │
│ (Thread Local Storage Class) │
├─────────────────────────────────────────────────────────────┤
│ ┌─ 成员变量 ─────────────────────────────────────────────┐ │
│ │ - ObjectPool& parent_ │ │
│ │ - Batch first_ │ │
│ │ - Batch second_ │ │
│ └─────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ ┌─ 方法 ─────────────────────────────────────────────────┐ │
│ │ + std::unique_ptr<Storage> get() │ │
│ │ + void put(std::unique_ptr<Storage> obj) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
│ 包含 │ 包含
│ (1:1) │ (1:1)
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Batch (first_) │ │ Batch (second_) │
│ ┌────────────────┐ │ │ ┌────────────────┐ │
│ │ vector<unique_ │ │ │ │ vector<unique_ │ │
│ │ ptr<Storage>>│ │ │ │ ptr<Storage>>│ │
│ │ │ │ │ │ │ │
│ │ [最多64个元素] │ │ │ │ [溢出缓存] │ │
│ └────────────────┘ │ │ └────────────────┘ │
└──────────────────────┘ └──────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ ObjectPool 全局结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ static ObjectPool instance (单例,全局唯一) │ │
│ │ └─ mutex_ │ │
│ │ └─ global_: vector<Batch> │ │
│ │ ├─ Batch[0] │ │
│ │ ├─ Batch[1] │ │
│ │ └─ ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 每个线程: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ thread_local TLS tls{instance} (每线程一个) │ │
│ │ └─ parent_ → instance │ │
│ │ └─ first_: Batch │ │
│ │ └─ [64个 unique_ptr<Storage>] │ │
│ │ └─ second_: Batch │ │
│ │ └─ [溢出缓存] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
using Storage = std::aligned_storage_t<sizeof(T), alignof(T)>; 代表:一个连续的内存地址,8 字节,16 字节,32 字节
using Batch = std::vector<std::unique_ptr<Storage>> //存储中不发生拷贝
代表:存储多个连续内存地址(没有调用构造函数呢)
1
只存储指针,vector 默认存储值,内存只有1 份,【减少存储中发生拷贝】
2
移动unique_ptr非常轻量(只移动指针)【why unique_ptr不允许拷贝吗?】
3
明确的资源所有权语义
4
自动内存管理,vector销毁时自动清理
其中一个使用方式: using Ptr = std::unique_ptr<T, Deleter>; // 如何释放资源
// 匿名函数 lamber 表达式
struct Deleter
{
constexpr Deleter() = default;
void operator()(T *item) {
item->~T(); //析构函数 ~foo()
tls().put(std::unique_ptr<Storage>(reinterpret_cast<Storage *>(item)));
}
};
类型转换:
•
static_cast 是一种更安全的转换方式,它在编译时检查转换的合法性, 比如整数类型和浮点类型之间,或者将 void* 转换为具体类型的指针。
•
reinterpret_cast 用于任意指针(或引用)类型之间的转换,但不检查安全性,必须使用 类型 T 不确定
using Ptr = std::unique_ptr<T, Deleter>;
static Ptr get() //返回不继续拷贝
{
return Ptr(new (tls().get().release()) T);
} //返回unique_ptr 这个不是复制 =,移动构造函数
基础回顾:
•
现代编译器会应用返回值优化 (RVO)
•
移动语义允许转移所有权, 移动构造函数 不是赋值操作 a=b vs a(b) vs a(move(b))
// ❌ 错误:尝试拷贝
std::unique_ptr<int> bad_example(std::unique_ptr<int> p) {
return p; // 错误:尝试从左值拷贝
} //a =b
std::unique_ptr<int> good_example1() {
return std::unique_ptr<int>(new int(42)); // 直接构造临时对象
} //a(b)
std::unique_ptr<int> good_example2() {
auto p = std::unique_ptr<int>(new int(100));
return p; // 编译器将其视为 std::move(p)
} //现代编译器会应用**返回值优化 (RVO) /a(std:move(b))
std::unique_ptr<int> good_example3(std::unique_ptr<int> p) {
return std::move(p); // 显式移动
}
// 1. 配合 placement new 使用
void* memory = malloc(sizeof(MyClass));
MyClass* obj = new(memory) MyClass();
obj->~MyClass(); // ✅ 正确
free(memory);
// 2. 在自定义内存池中
obj->~T(); // ✅ 析构对象但不释放池内存
TLS 类图
private:
ObjectPool &parent_; // ObjectPool引用 就是全家变量 vector<Batch> 访问加锁Batch first_; //主缓存批次Batch second_; //溢出缓存批次公共方法详细说明:
•
get(): 获取内存块的完整算法流程
•
put(): 归还内存块的完整算法流程
// put a object into thread local cache or global cache.
// unique_ptr 参数,智能移动构造
void put(std::unique_ptr<Storage> obj) {
// push to the first batch. 不满
if (first_.size() < kThreadLocalMaxNum)
{
first_.push_back(std::move(obj)); //obj 变量与地址,不是右值
return;
}
// push to the second batch.
second_.push_back(std::move(obj));
//full
if (UNLIKELY(second_.size() >= kThreadLocalMaxNum))
{
// push to the global cache.
parent_.putBatch(std::move(second_)); //second 移动被自动走了。
second_.clear(); //为什么敢 clear 不担心 指针指针对象 析构时候释放空间吗?
second_.reserve(kThreadLocalMaxNum);//不是 resize,kThreadLocalMaxNum最大容量 ,1 2 4 8 多申请方式。
}
}
对,重载 2 个
void push_back( const T& value )
void push_back( T&& value )
https://en.cppreference.com/w/cpp/container/vector/push_back
•
不是
void push_back(value_type&& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::move(__x));
++this->_M_impl._M_finish;
}
else
_M_realloc_insert(end(), std::move(__x));//
}
https://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr.html
unique_ptr( unique_ptr&& u ) noexcept
explicit unique_ptr( pointer p ) noexcept;
template<typename T>
class unique_ptr {
T* ptr_; // 底层原始指针
public:
// 移动构造函数
unique_ptr(unique_ptr&& other) noexcept
: ptr_(other.ptr_) { // 1. 接管资源所有权
other.ptr_ = nullptr; // 2. 置空原指针
}
// 禁用拷贝构造
unique_ptr(const unique_ptr&) = delete;
// ... 其他成员函数
};
•
std::move() 将左值,或者右值 转化右值
•
右值 :临时值,无法获取变量地址
•
当函数内部,x 变量有地址,不在右值,需要再次转化
•
需要再次std::move(std::move(std::move(__x)))
Result<std::unique_ptr<TcpSocket>> acquire(Address addr);
std::unique_ptr<Socket> socket_;
•
管理网络连接的所有权转移
•
连接池中连接的获取和归
