背景

最近准备写 cpp 的一些项目, 学习下不太了解的领域.

基础

std::mutex 是基础的锁, 但是使用 std::mutex 很容易经常忘记解锁, 而且缺乏一些常用的特性: 延迟加锁、尝试加锁. 为此, cpp提供了 std::unique_lock 和 std::lock_gurad , 都是简化锁的使用, std::local_guard 是通过栈上变量的 构造和析构 解决加锁和解锁逻辑, std::unique_lock 则提供了更多的特性: 延迟锁定、递归锁定、有时限尝试、所有权转移等.

std::scoped_lock: raii风格的互斥包装器, 创建对象的时候 会获取 互斥的所有权, 离开的时候 逆序释放. 相当于两步操作的封装 (std::lock(a, b), std::lock_quard g1(a1, std::adopt_lock), std::lock_gurad g2(a2, std::adopt_lock))

https://zh.cppreference.com/w/cpp/thread/lock https://zh.cppreference.com/w/cpp/thread/mutex https://zh.cppreference.com/w/cpp/thread/lock_guard https://zh.cppreference.com/w/cpp/thread/unique_lock http://www.cplusplus.com/reference/mutex/unique_lock/ https://zh.cppreference.com/w/cpp/thread/scoped_lock

补充, std::lock 是一个函数, 用来对多个互斥量上锁, 还有尝试加锁的函数: std::try_lock 更多线程相关的信息: https://zh.cppreference.com/w/cpp/thread

智能指针

为了避免在处理指针导致指针对象泄露, cpp提供了多种智能指针的处理方式. 除了 auto, 更常见的是 std::shared_pter 和 std::unique_ptr.

std::shared_ptr: 通过指针保持对象共享所有权的智能指针, 多个 shared_ptr 对象可占有同一对象, 当最后的shared_ptr 被销毁或者被赋值为其他指针就会释放、销毁 对象 并释放内存. 但是用share_ptr处理双向链表会导致泄露 (pre和next相互引用, 造成循环), 需要借助 weak_ptr

std::weak_ptr: 对shared_pter的弱引用, 在访问所需引用的对象的时候需要转换为 std::shared_ptr, 用来表达临时所有权的概念. 常见的例子是 双向列表

std::unique_ptr: 通过指针占有并管理另一个对象, 并在 unique_ptr离开作用域的时候销毁对象. 可以通过 std::move 转移 控制权.

std::move:

https://zh.cppreference.com/w/cpp/memory/shared_ptr https://zh.cppreference.com/w/cpp/memory/unique_ptr https://zh.cppreference.com/w/cpp/memory/weak_ptr

常用容器

boost.circular_buffer: 用数组维护了一段数据, 超过容量后覆盖开头的数据写入

其他概念

pimpl

pimpl: 指向实现的指针, 将类的实现细节从对象表示中移除, 放到另一个分离的类, 并以一个不透明的指针进行访问, 一开始看的略微懂了, 但是实践不是很懂, 看到这篇文章: https://www.cnblogs.com/senior-engineer/p/9811937.html, 理解了出发点: 基础的头文件修改会导致 依赖的 所有源文件都得重新编译.

https://zh.cppreference.com/w/cpp/language/pimpl

raii

resource acquisition is initializaiton: 资源即获取, 使用局部变量来管理资源. 因为对象的构造和析构是系统调用的, 可以在构造函数中初始化资源, 在析构函数中释放资源, 比如 std::lock_gurard. 也可以自己设计.

函数绑定

这个是最近学习到的, 可以动态申明绑定一个函数. 需要注意的是, std::ref 是引用传递, 外部修改和内部修改 都会奏效.

std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));

参照:

左值、右值

虽然有文章: https://zh.cppreference.com/w/cpp/language/value_category, 但是一次还是没读懂

赋值、构造

移动构造函数: 构造函数很特别, 还要区分 析构函数阻止隐式移动构造函数 和 强制生成移动构造函数, 不是特别理解, 使用如下

A(A&& o) noexcept :
       s(std::move(o.s)),       // 类类型成员的显式移动
       k(std::exchange(o.k, 0)) // 非类类型成员的显式移动
{ }

https://zh.cppreference.com/w/cpp/language/move_constructor https://zh.cppreference.com/w/cpp/named_req/MoveConstructible

移动赋值运算符: 不是很懂. 定义如下:

 V& operator=(V&& other) {}

https://zh.cppreference.com/w/cpp/language/move_assignment https://zh.cppreference.com/w/cpp/named_req/MoveAssignable

仿函数

函数指针 使函数作为参数 传递给处理逻辑, 解耦了 可插拔逻辑, 但是函数指针并不能维护 一些本地信息: 比如局部变量, 这个时候可以使用 Functor/仿函数,

https://cloud.tencent.com/developer/article/1347883

总结

目前参考 https://zh.cppreference.comhttps://zh.cppreference.com 就足够了. 更多的需要example 辅助学习.