宁波建设网站的公司,口碑营销方案,高级营销网站建设只需1200元,wordpress更改文件上传目录文章目录 5 智能指针与内存管理5.1 RAII与引用计数5.2 std::shared_ptr5.3 std::unique_ptr5.4 std::weak_ptr 6 正则表达式7 并行与并发7.1 并行基础7.2 互斥量与临界区7.3 期物7.4 条件变量7.5 原子操作与内存模型 5 智能指针与内存管理
5.1 RAII与引用计数
在传统 C 中 中『记得』手动释放资源总不是最佳实践。因为我们很有可能就忘记了去释放资源而导致泄露。 所以通常的做法是对于一个对象而言我们在构造函数的时候申请空间而在析构函数在离开作用域时调用的时候释放空间 也就是我们常说的 RAII 资源获取即初始化技术。
凡事都有例外我们总会有需要将对象在自由存储上分配的需求在传统 C 里我们只好使用 new 和 delete 去 『记得』对资源进行释放。而 C11 引入了智能指针的概念使用了引用计数的想法让程序员不再需要关心手动释放内存。 这些智能指针包括 std::shared_ptr/std::unique_ptr/std::weak_ptr使用它们需要包含头文件 memory。
5.2 std::shared_ptr
std::shared_ptr 是一种智能指针它能够记录多少个 shared_ptr 共同指向一个对象从而消除显式的调用 delete当引用计数变为零的时候就会将对象自动删除。
但还不够因为使用 std::shared_ptr 仍然需要使用 new 来调用这使得代码出现了某种程度上的不对称。
std::make_shared 就能够用来消除显式的使用 new所以std::make_shared 会分配创建传入参数中的对象 并返回这个对象类型的std::shared_ptr指针。
5.3 std::unique_ptr
std::unique_ptr 是一种独占的智能指针它禁止其他智能指针与其共享同一个对象从而保证代码的安全。
既然是独占换句话说就是不可复制。但是我们可以利用 std::move 将其转移给其他的 unique_ptr。
5.4 std::weak_ptr
std::weak_ptr是一种弱引用相比较而言 std::shared_ptr 就是一种强引用。弱引用不会引起引用计数增加。
std::weak_ptr 没有 * 运算符和 - 运算符所以不能够对资源进行操作它可以用于检查 std::shared_ptr 是否存在其 expired() 方法能在资源未被释放时会返回 false否则返回 true除此之外它也可以用于获取指向原始对象的 std::shared_ptr 指针其 lock() 方法在原始对象未被释放时返回一个指向原始对象的 std::shared_ptr 指针进而访问原始对象的资源否则返回nullptr。
6 正则表达式
这一章没什么好介绍的跳过。
7 并行与并发
7.1 并行基础
std::thread 用于创建一个执行的线程实例所以它是一切并发编程的基础使用时需要包含 头文件 它提供了很多基本的线程操作例如 get_id() 来获取所创建线程的线程 ID使用 join() 来等待一个线程结束与该线程汇合等等。
7.2 互斥量与临界区
我们在操作系统、亦或是数据库的相关知识中已经了解过了有关并发技术的基本知识mutex 就是其中的核心之一。 C11 引入了 mutex 相关的类其所有相关的函数都放在 mutex 头文件中。
std::mutex 是 C11 中最基本的 mutex 类通过实例化 std::mutex 可以创建互斥量 而通过其成员函数 lock() 可以进行上锁unlock() 可以进行解锁。 但是在实际编写代码的过程中最好不去直接调用成员函数 因为调用成员函数就需要在每个临界区的出口处调用 unlock()当然还包括异常。 这时候 C11 还为互斥量提供了一个 RAII 语法的模板类 std::lock_guard。 RAII 在不失代码简洁性的同时很好的保证了代码的异常安全性。
由于 C 保证了所有栈对象在生命周期结束时会被销毁所以这样的代码也是异常安全的。 无论 critical_section() 正常返回、还是在中途抛出异常都会引发堆栈回退也就自动调用了 unlock()。
而 std::unique_lock 则是相对于 std::lock_guard 出现的std::unique_lock 更加灵活 std::unique_lock 的对象会以独占所有权没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权 的方式管理 mutex 对象上的上锁和解锁的操作。所以在并发编程中推荐使用 std::unique_lock。
std::lock_guard 不能显式的调用 lock 和 unlock 而 std::unique_lock 可以在声明后的任意位置调用 可以缩小锁的作用范围提供更高的并发度。
如果你用到了条件变量 std::condition_variable::wait 则必须使用 std::unique_lock 作为参数。
7.3 期物
期物Future表现为 std::future它提供了一个访问异步操作结果的途径这句话很不好理解。 为了理解这个特性我们需要先理解一下在 C11 之前的多线程行为。
试想如果我们的主线程 A 希望新开辟一个线程 B 去执行某个我们预期的任务并返回我一个结果。 而这时候线程 A 可能正在忙其他的事情无暇顾及 B 的结果 所以我们会很自然的希望能够在某个特定的时间获得线程 B 的结果。
在 C11 的 std::future 被引入之前通常的做法是 创建一个线程 A在线程 A 里启动任务 B当准备完毕后发送一个事件并将结果保存在全局变量中。 而主函数线程 A 里正在做其他的事情当需要结果的时候调用一个线程等待函数来获得执行的结果。
而 C11 提供的 std::future 简化了这个流程可以用来获取异步任务的结果。 自然地我们很容易能够想象到把它作为一种简单的线程同步手段即屏障barrier。
7.4 条件变量
条件变量 std::condition_variable 是为了解决死锁而生当互斥操作不够用而引入的。 比如线程可能需要等待某个条件为真才能继续执行 而一个忙等待循环中可能会导致所有其他线程都无法进入临界区使得条件为真时就会发生死锁。 所以condition_variable 实例被创建出现主要就是用于唤醒等待线程从而避免死锁。 std::condition_variable的 notify_one() 用于唤醒一个线程 notify_all() 则是通知所有线程。
值得一提的是在生产者中我们虽然可以使用 notify_one()但实际上并不建议在此处使用 因为在多消费者的情况下我们的消费者实现中简单放弃了锁的持有这使得可能让其他消费者 争夺此锁从而更好的利用多个消费者之间的并发。话虽如此但实际上因为 std::mutex 的排他性 我们根本无法期待多个消费者能真正意义上的并行消费队列的中生产的内容我们仍需要粒度更细的手段。
7.5 原子操作与内存模型