
C++编程专栏
介绍现代C++语法,数据结构,算法,STL用法
黑不溜秋的
GPU全栈博主
展开
-
一文带你掌握C++编程:从入门到精通
本系列文章持续更新中...原创 2024-08-10 13:57:19 · 1607 阅读 · 0 评论 -
C++ STL 随机数用法介绍
目录一:C语言中的随机数二:C++中的随机数 1. 生成随机数的例子 2. 随机数引擎 3. 随机数引擎适配器 4. C++中预定义的随机数引擎,引擎适配器 5. 随机数分布二:C++中的随机数 1. 生成随机数的例子 2. 随机数引擎 引擎 说明 random_device 真随机数(非基于软件生成器),通常用来产生随机种子 linear_congruential_en原创 2024-07-07 14:32:24 · 348 阅读 · 0 评论 -
C++ STL 多线程库用法介绍
【代码】C++ STL 多线程库用法介绍。原创 2024-07-07 11:35:45 · 602 阅读 · 0 评论 -
C++ STL 时间日期用法介绍
C++ 标准库提供了Time Library,它由时间点,时间段,时钟三部分组成,此外,该库还提供了日期功能,日历支持,时区支持,以及输入和输出。原创 2024-07-06 19:12:49 · 276 阅读 · 0 评论 -
C++ STL 正则表达式用法介绍
【代码】C++ 正则表达式(std::regex_search使用)原创 2024-06-18 00:10:01 · 871 阅读 · 0 评论 -
C++ 编程指南33 - 使用模板来表达适用于多种参数类型的算法
在 C++ 中,模板(Templates)提供了一种强大的泛型编程方式,使代码可以适用于不同的数据类型,而无需重复编写类似的逻辑。模板的主要目标是:泛化能力(Generality):能够适用于不同的数据类型,提高代码的通用性。减少源代码量(Minimizing the amount of source code):避免为不同数据类型重复编写代码,提高代码复用性。互操作性(Interoperability):使代码能够在不同类型的容器或数据结构之间共享。原创 2025-04-02 22:44:24 · 260 阅读 · 0 评论 -
C++编程指南32 - 模板编程时要避免过度约束以提高通用性
由于使用模板会提高代码的抽象级别,所以在编写模板代码时,要尽量让代码更灵活、通用,而不是只针对某些特定的操作或类型。目的是为了提高代码的重用性和效率。不要过度限制模板的类型要求,让代码更通用。尽量避免只要求单一操作,要考虑更通用的操作集合。用概念来约束类型,而不是直接依赖操作符,这样代码能处理更多类型,增加灵活性和复用性。原创 2025-04-01 21:15:41 · 453 阅读 · 0 评论 -
C++编程指南31 - 除非绝对必要,否则不要使用无锁编程
无锁编程容易出错,并且需要专家级的知识,包括:语言特性(如 C++ 的计算机架构(如 CPU 缓存一致性协议)数据结构(如无锁队列、无锁栈)高层次的并发机制(如线程和互斥锁)本质上是基于无锁编程实现的,但它们隐藏了底层的复杂性,使开发者更容易正确使用。如果必须使用无锁数据结构,优先使用现成的库(Facebook 开源库)实现了无锁链表libcds提供成熟的无锁数据结构(队列、哈希表等)原创 2025-03-31 20:57:30 · 264 阅读 · 0 评论 -
C++编程指南30 - 不要为初始化编写自己的双重检查锁定
自 C++11 起,静态局部变量的初始化已经是线程安全的。结合 RAII(资源获取即初始化)模式,静态局部变量可以完全取代双重检查锁定的需求。此外,也可以实现相同的目的。因此,应该使用 C++11 的静态局部变量或,而不是自己编写双重检查锁定。不要 手动实现双重检查锁定(DCLP, Double-Checked Locking Pattern)。进行一次性初始化,适用于非局部的初始化情况。C++11 的线程安全静态局部变量,适用于局部作用域的初始化。原创 2025-03-25 23:21:07 · 420 阅读 · 0 评论 -
C++编程指南29 - 仅在与非 C++ 代码交互时使用 volatile
volatile关键字用于声明与**“非 C++”代码或不遵循 C++ 内存模型的硬件交互的对象。例如,它常用于表示硬件寄存器**,因为这些寄存器的值可能在程序未直接操作的情况下发生变化。原创 2025-03-25 22:54:13 · 406 阅读 · 0 评论 -
C++编程指南28 - 使用 std::async() 启动并发任务
与(之前介绍的)避免使用裸指针管理资源类似,我们应该避免直接使用std::thread和std::promise,而是使用std::async 这样的工厂函数来启动并发任务。std::async能够自动决定是创建新线程,还是重用已有的线程,从而避免直接管理std::thread带来的复杂性和潜在错误。原创 2025-03-13 22:35:17 · 177 阅读 · 0 评论 -
C++ 编程指南27 - 始终将 mutex 与它所保护的数据一起定义,并尽可能使用 synchronized_value<T>
在多线程编程中,互斥锁(std::mutex)的作用是保护共享数据的访问。但如果mutex锁的使用不明显:程序员可能会忘记获取mutex就访问数据,导致数据竞争(race condition)。锁管理混乱:代码阅读者难以明确哪个mutex保护哪个数据,可能会误用错误的mutex,导致死锁或数据不一致。封装性不足:数据和mutex分离,使得访问数据变得不安全。原创 2025-03-11 23:57:23 · 298 阅读 · 0 评论 -
C++ 编程指南26 - 尽量缩短在临界区(critical section)内的执行时间
在临界区中持有互斥锁(mutex)的时间越短,线程之间的等待时间就越少,从而减少线程被挂起和恢复的开销,提高程序的并发性能。一般来说,无法自动检测是否持有mutex过长。但可以标记裸露的lock()和unlock()调用,鼓励使用 RAII 进行资源管理。或者进行代码审查(Code Review) 确保临界区最小化。原创 2025-03-10 23:58:38 · 257 阅读 · 0 评论 -
C++编程指南25 - 不要在没有条件的情况下等待
如果没有条件就进行等待(wait()),可能会导致以下问题:一是某个线程可能会错过其他线程的通知,从而一直处于等待状态。二是线程可能被唤醒后发现根本没有可执行的任务,导致不必要的上下文切换和资源消耗。永远不要 在没有条件的情况下调用wait(),否则可能会导致线程永远等待或不必要的唤醒。始终 使用条件谓词(如!q.empty())来控制wait(),确保线程只有在满足特定条件时才会被唤醒。检查代码,确保所有wait()调用都使用了谓词表达式,否则应该标记为潜在错误。原创 2025-03-10 23:53:45 · 368 阅读 · 0 评论 -
C++编程指南24 - 避免线程频繁的创建和销毁
线程的创建和销毁是昂贵的操作,尤其在多线程程序中频繁创建和销毁线程时,可能会导致性能问题。特别是当消息处理需要创建多个线程时,系统资源会被过度消耗。在高并发处理的场景中,线程池是非常常见的解决方案。原创 2025-03-08 21:49:58 · 271 阅读 · 0 评论 -
C++编程指南23 - 在无关线程之间共享资源时应使用shared_ptr
当多个线程需要共享一个堆内存(即动态分配的内存)时,如果这些线程之间没有直接关系,比如它们的生命周期不重叠,那么使用shared_ptr(智能指针)是最安全的做法。因为如果有多个线程要共享一块内存,而且这块内存需要在某个时候被删除,直接用普通指针可能会导致一些问题,比如内存泄漏或者在某个线程结束时,其他线程仍然在访问这块已经被删除的内存。shared_ptr是一种智能指针,它会自动管理内存的释放。只有当所有的线程都不再使用这块内存时,shared_ptr才会销毁它,避免了内存泄漏和重复释放的问题。原创 2025-03-06 23:02:13 · 299 阅读 · 0 评论 -
C++编程指南22 - 在线程之间传递少量数据时,使用值传递,而不是引用或指针传递
传递少量数据时,复制比通过某些锁机制共享数据更便宜。复制数据自然会导致唯一所有权(简化代码),并消除了数据竞争的可能性。注意:“少量数据”的定义是无法精确界定的。这个规则的核心思想是在线程间传递少量数据时,尽量使用值传递,而非引用或指针传递。避免数据竞争通过值传递,数据拥有独立的副本,不需要担心多线程间对同一内存位置的访问冲突。这避免了数据竞争(data race),简化了代码。相比之下,如果使用引用或指针传递,多个线程可能会同时访问和修改同一数据,导致难以调试的并发问题。复制的开销较低。原创 2025-03-05 23:09:54 · 426 阅读 · 0 评论 -
C++编程指南21 - 线程detach后其注意变量的生命周期
如果一个线程被detach()了,那么它的生命周期将独立于创建它的作用域。全局变量(global/static objects)堆上分配的对象(free-store allocated objects,即new创建的对象) 其他作用域的对象可能会在线程访问它们之前被销毁,导致 悬空指针(dangling pointer) 和 未定义行为。原创 2025-03-02 22:07:25 · 275 阅读 · 0 评论 -
C++编程指南20 - 使用 joining_thread以确保线程不会在变量生命周期之外运行
在多线程编程中,如果一个线程需要访问外部作用域的变量,就必须保证这个变量在 线程结束之前依然有效,否则可能会出现 悬空指针(dangling pointer) 的问题,导致 未定义行为。原创 2025-03-02 21:50:36 · 231 阅读 · 0 评论 -
C++编程指南19 - 在持有锁的情况下,绝不要调用未知代码(例如回调函数)
在持有互斥锁的情况下调用 未知代码(例如回调函数或虚函数) 可能会导致 死锁(deadlock) 或 长时间阻塞(blocking),从而影响程序的性能和正确性。原创 2025-03-02 20:40:24 · 276 阅读 · 0 评论 -
C++编程指南18 - 使用 std::lock() 或 std::scoped_lock 来同时获取多个互斥锁
当一个线程需要同时获取多个std::mutex互斥锁时,如果不注意锁的获取顺序,可能会导致 死锁(deadlock)。为了解决这个问题,C++ 提供了和,它们可以确保多个互斥锁的获取是 无死锁 的。原创 2025-03-02 20:25:13 · 194 阅读 · 0 评论 -
C++编程指南17 - 使用 RAII(资源获取即初始化),避免直接调用 lock()/unlock()
本规则核心思想是避免手动管理锁的获取和释放,而是利用 C++ 的 RAII(Resource Acquisition Is Initialization) 机制,通过或来自动管理锁的生命周期。如果手动调用lock()和unlock()代码可能在unlock()之前 提前返回,导致锁未释放。代码可能在unlock()之前 抛出异常,导致锁未释放。维护代码的人可能会忘记调用unlock(),导致锁未释放。原创 2025-02-25 21:29:57 · 287 阅读 · 0 评论 -
C++ 设计模式 - 并发模式概述
在并发领域,有许多成熟的设计模式。它们不仅用于处理共享和修改时的同步挑战,还涉及并发架构。本文将从总体上介绍这些模式。在并发领域,一个至关重要的术语是数据竞争,什么是数据竞争?数据竞争指的是至少有两个线程同时访问一个共享变量,并且至少有一个线程试图修改该变量。如果程序存在数据竞争,它将导致未定义行为。这意味着任何结果都有可能发生,因此程序的行为将变得无法预测和推理。数据竞争的一个必要条件是可变的共享状态。如果你能够妥善处理共享或修改,就不会发生数据竞争。这正是同步模式所关注的重点。原创 2025-02-24 21:14:22 · 1048 阅读 · 0 评论 -
C++编程指南16 - 尽可能使用工具验证并发代码
经验表明,C++并发代码极难编写正确;相比于顺序代码,编译时检查、运行时检查在发现并发错误方面的效果往往较差。而微妙的并发错误可能会引发严重问题,如内存损坏、死锁或安全漏洞。线程安全是一个复杂的问题,即使是经验丰富的程序员也容易犯错。因此,利用工具是降低风险的重要策略。市面上有许多商用或开源的工具,既有学术研究用途的,也有工业生产环境中的。然而,不同项目的需求和约束千差万别,因此我们不推荐具体工具,而是介绍一些通用方法。静态分析工具(Static enforcement tools)原创 2025-02-24 13:08:34 · 193 阅读 · 0 评论 -
C++编程指南15 - 不要使用 volatile 进行同步
在 C++ 中,volatilevolatile变量的读写不是原子操作,可能被中断。volatile不能保证多线程访问时的正确性。防止指令重排序(Memory Ordering):编译器和 CPU 仍然可能对volatile变量的操作进行优化或重排序。C++ 的volatile仅用于防止编译器优化访问,但不会提供线程安全性。原创 2025-02-21 13:00:12 · 352 阅读 · 0 评论 -
C++ 设计模式 - 策略模式
策略模式是一种行为设计模式,来源于《设计模式:可复用面向对象软件的基础》一书。它定义了一组算法,并将它们封装成独立的对象。策略模式在标准模板库(STL)中被广泛使用。原创 2025-02-21 12:48:09 · 600 阅读 · 0 评论 -
C++设计模式 - 模板模式
模板方法(Template Method)是一种行为型设计模式。它定义了一个算法的基本框架,并且可能是《设计模式:可复用面向对象软件的基础》一书中最常用的设计模式之一。模板方法的核心思想很容易理解。我们需要定义一个包含多个固定步骤的算法框架。具体的实现类只能重写这些步骤,但不能改变整体框架。这些步骤通常被称为钩子方法(hook methods)。原创 2025-02-10 23:57:27 · 364 阅读 · 0 评论 -
C++ 设计模式 - 访问者模式
访问者模式将作用于对象层次结构的操作封装为一个对象,并使其能够在不修改对象层次结构的情况下定义新的操作。《设计模式:可复用面向对象软件的基础》一书中的访问者模式因两个原因而具有传奇色彩:一是因为它的复杂性,二是因为它使用了一种名为“双重分派”的技术。双重分派指的是根据对象和函数参数选择成员函数的过程。当然,访问者模式的复杂性主要在于 C++ 本身不支持双重分派。在讨论单分派和双重分派之前,让我先谈谈访问者模式。什么是“双重分派”?原创 2025-02-09 15:55:18 · 917 阅读 · 0 评论 -
C++编程指南14 - 从任务的角度思考问题,而不是直接操作线程
从 任务 的角度思考问题,而不是直接操作 线程,可以让程序设计更加高效和安全。通过使用更高层次的抽象工具(如std::async),这样可以减少对底层细节的关注,专注于业务逻辑,同时减少出错的可能性。线程是一种实现概念,是一种从机器层面思考问题的方式。任务则是一种应用概念,表示某个你希望完成的操作,最好是可以与其他任务并发执行的操作。应用层的概念更容易推理和理解。原创 2025-02-08 23:35:25 · 282 阅读 · 0 评论 -
C++编程指南13 - 尽量减少可写数据的共享
数据竞争是多线程编程中常见的一个问题,当多个线程同时访问一个共享的可写数据且至少有一个线程对其进行修改时,就会发送数据竞争。这个规则的核心就是通过减少对可写数据的共享来避免数据竞争。具体来说,如果数据是只读的(用const修饰),那么无论有多少线程访问它,都不会发送数据竞争。常量数据可以在多个线程之间安全地共享,无需额外的同步机制。不可变的数据可以被安全、高效地共享。不可变数据不需要加锁:常量数据不会产生数据竞争。原创 2025-01-13 23:17:48 · 369 阅读 · 0 评论 -
C++编程指南12 - 避免数据竞争
如果不避免数据竞争,程序的行为将无法得到任何保证,潜在的错误可能持续存在。简单来说,如果两个线程可以同时访问同一个对象(没有同步机制),并且至少有一个线程是写操作(执行非const操作),那么就会发生数据竞争。原创 2025-01-12 14:39:54 · 439 阅读 · 0 评论 -
C语言 - 理解函数栈帧
函数栈帧是函数调用过程中为管理和存储函数相关信息(如局部变量、返回地址等)而在栈上分配的一块内存区域。它是实现函数调用、递归和返回的关键机制。原创 2025-01-04 22:47:22 · 404 阅读 · 0 评论 -
C++ 并发专题 - std::promise 和 std::future 介绍
std::promise 和 std::future 是C++标准库的两种工具,主要用于实现线程之间的异步通信。它们属于C++并发库的一部分,提供了一种安全,优雅的方式来在线程之间传递结果或状态。std::promise 用于在线程之间传递值或状态,是一种能够在一个线程中存储值(或异常)的对象。常用方法有以下几种:set_value(value) 设置异步操作的结果,通知关联的std::future.set_exception(ex) 设置一个异常,通知关联的 std::future 发生错误。原创 2025-01-03 23:36:23 · 519 阅读 · 0 评论 -
C++ 并发专题 - 实现一个线程安全的队列
利用 C++ 标准库中的多线程、条件变量、互斥锁等工具来实现一个线程安全的队列,并且使用多个线程来向队列中添加和获取数据。原创 2024-12-28 23:37:56 · 247 阅读 · 0 评论 -
C++编程指南11 - 假设你的代码将作为多线程程序的一部分运行
在编写代码时,应该预先假设代码会被运行在一个多线程环境中,而不是单线程环境。在多线程程序中,多个线程可能同时访问和修改共享资源,因此需要确保代码在这种情况下能够正确运行。在多线程程序中,多个线程可能同时访问和修改共享资源,因此需要确保代码在这种情况下能够正确运行。即使当前程序是单线程的,未来可能会被扩展到多线程场景。假设代码会在多线程中运行,可以提前避免后续改动时引入的问题。这意味着,在设计类时,确保静态成员和单例模式是线程安全的。在编写全局变量或共享资源时,确保访问方式是原子化的或受到适当保护。原创 2024-12-26 23:35:00 · 405 阅读 · 0 评论 -
C++ 并发专题 - C++线程同步的几种方法
线程同步是多线程编程中的一个重要概念,它用于控制多个线程之间对共享资源的访问,避免竞态条件(race condition)和数据不一致的问题。线程同步确保在多线程环境中,多个线程访问共享数据时能够按照某种预定的顺序或规则进行,以保证程序的正确性和稳定性。原创 2024-12-14 22:36:43 · 396 阅读 · 0 评论 -
C++ 并发专题 - std::latch 介绍
是 C++20 引入的一个同步原语,属于线程同步工具的一部分。它可以用于控制多个线程等待,直到某些事件发生为止,适用于需要等待一组线程在继续之前完成某些操作的场景。可以理解为一种“门闩”机制,它使得多个线程等待,直到达到预定的触发条件。其工作方式类似于一个计数器,线程可以通过。比较适合用于那种固定数量的线程同步的场景(如 N 个线程任务执行完之后才继续进行其他操作)。它通常应用于需要等待多个线程完成某项任务后才能继续执行后续操作的场景。操作使计数器递减,直到计数器达到零时,所有等待的线程才会继续执行。原创 2024-12-06 00:58:25 · 225 阅读 · 0 评论 -
C++ 并发专题 - 使用 std::lock 避免多线程死锁
std::lock是 C++11 引入的一个函数,它可以原子性地锁定多个互斥量(mutex),确保在多个线程竞争锁时避免死锁。它的主要用途是在多个互斥量之间避免因不同线程的不同锁定顺序而导致的死锁问题。原创 2024-12-04 00:01:35 · 475 阅读 · 0 评论 -
C++ 编程指南10 - 如果函数不抛出异常,则应声明为 noexcept
对于那些保证不会抛出异常的函数,应该显式地使用noexcept说明符进行标记。在 C++ 中,异常处理是一种用于处理错误的机制。抛出异常会影响程序的性能,因为它需要进行异常的捕获、传播等操作,尤其是在复杂的异常传播路径中,程序的执行效率会受到较大影响。如果编译器知道某个函数不会抛出异常,它可以做出一些优化决策,避免不必要的异常处理开销。例如,编译器可以更有效地生成代码,避免为了异常处理而插入额外的检查逻辑。通过显式标记函数为noexcept,可以确保在调用这些函数时,程序不会在执行过程中意外抛出异常。原创 2024-11-24 23:09:23 · 498 阅读 · 0 评论 -
C++ 编程指南09 - 如需ABI兼容,则应该使用 C 风格的子集
如果你希望在不同的编译器之间(例如,GCC 和 Clang 或者不同平台之间)共享二进制接口(ABI),那么应该使用 C 风格的子集来编写代码(extern "C")。这是因为 C 语言和 C++ 在 ABI 方面有许多不同,C 语言的ABI通常具有更好的跨编译器兼容性。是应用程序与操作系统或不同编译器之间交互时所遵循的一组规则。它定义了函数调用、参数传递、返回值处理、内存布局等二进制级别的接口。ABI 确保了程序能够在不同的编译器和平台上运行,避免了运行时的不兼容问题。原创 2024-11-24 22:41:30 · 410 阅读 · 0 评论