• 【C++实战 】标准库


    C++实战其他两篇文章:
    C++实战 概述 |代码风格
    C++ 实战 语言特性

    文本处理

    认识字符串

    using string = std::basic_string<char>; // string其实是一个类型别名
    //是模板类 basic_string 的特化形式,是一个 typedef:
    
    • 1
    • 2
    • 字符串,就是字符的序列。字符是人类语言、文字的计算机表示。
    • Unicode它的目标是用一种编码方式统一处理人类语言 文字,使用 32 位(4 个字节)来保证能够容纳过去或者将来所有的文字。
    //char 类型,无法支持 Unicode
    using wstring = std::basic_string<wchar_t>; 
    using u16string = std::basic_string<char16_t>;  //适配 UTF-16
    using u32string = std::basic_string<char32_t>;  //适配 UTF-32
    
    • 1
    • 2
    • 3
    • 4

    +Unicode 还有一个 UTF-8 编码方式,与单字节的 char 完全兼容

    sring使用

    基本使用:
    在这里插入图片描述

    • 字符串和容器完全是两个不同的概念。把每个字符串都看作是一个不可变的实体,你才能在 C++ 里真正地用好字符串
    • 我们也确实需要存储字符的容器,比如字节序列、数据缓冲区,这该怎么办 呢?
      这个时候,我建议你最好改用vector<char>

    1. 字面量后缀

    • 新增了一个字面量的后缀“s”,明确地表示它是 string 字符 串类型,而不是 C 字符串
    • 为了避免与用户自定义字面量的冲突,后缀“s”不能直接使用,必须 用 using 打开名字空间才行
      在这里插入图片描述

    2. 原始字符串

    • C++11 还为字面量增加了一个“原始字符串”(Raw string literal)的新表示形式。这样就没必要使用\ 对不可打印字符进行转义
      在这里插入图片描述
    // 如果想输出()
    auto str5 = R"==(R"(xxx)")==";// 原样输出:R"(xxx)"
    
    • 1
    • 2

    3. 字符串转换函数

    • stoi()、stol()、stoll() 等把字符串转换成整数;
    • stof()、stod() 等把字符串转换成浮点数;
    • to_string() 把整数、浮点数转换成字符串。
      在这里插入图片描述

    4. 字符串视图类

    • 大字符串的拷贝、修改代价很高,所 以我们通常都尽量用 const string&
    • C++17 : string_view , 一个字符串的视图,成本很低,内部只保存一个指针和长度,无论是拷贝,还是修改,都非 常廉价。
      在这里插入图片描述

    正则表达式

    • C++ 正则表达式主要有两个类。
      regex:表示一个正则表达式,是 basic_regex 的特化形式;
      smatch:表示正则表达式的匹配结果,是 match_results 的特化形式。

    • C++ 正则匹配有三个算法,注意它们都是“只读”的,不会变动原字符串。
      regex_match():完全匹配一个字符串;
      regex_search():在字符串里查找一个正则匹配;
      regex_replace():正则查找再做替换。
      在这里插入图片描述

    • 你只要用 regex 定义好一个表达式,然后再调用匹配算法,就可以立刻得到结果, 用起来和其他语言差不多。不过,在写正则的时候,记得最好要用“原始字符串”,不然转义符会特别的麻烦。

    • regex_match() 检查字符串,函数会返回 bool 值表示是否完全匹配 正则。如果匹配成功,结果存储在 what 里,可以像容器那样去访问,第 0 号元素是整个 匹配串,其他的是子表达式匹配串:
      在这里插入图片描述在这里插入图片描述

    • 在这里插入图片描述

    • 尽量不要反复创建正则对象,能重用就重用。 在使用循环的时候更要特别注意,一定要把正则提到循环体外。

    • regex_replace() 不需要匹配结果,而是要提供一个替换字符串,因为算法是“只读”的, 所以它会返回修改后的新字符串。

    • https://github.com/chronolaw/cpp_study/blob/master/section3/string.cpp

    容器

    容器,就是能够“容纳”“存放”元素的一些数据结构。 容器 == 数据结构
    容器,它也是 C++ 泛型编程范式的基础。

    • 连续存储的数组:array、vector 和 deque。 指针结构的链表:list 和 forward_list。
    • 指针结构的链表:list 和 forward_list。

    容器的通用特效

    • 容器里存储的是元素的拷贝、副本,而不是引用。
    • array 和 vector 直接对应 C 的内置数组,内存布局与 C 完全兼容,所以是开销最低、速 度最快的容器。它们两个的区别在于容量能否动态增长。array 是静态数组,大小在初始化的时候就固定 了,不能再容纳更多的元素。而 vector 是动态数组,虽然初始化的时候设定了大小,但可 以在后面随需增长,容纳任意数量的元素。

    在这里插入图片描述
    在这里插入图片描述

    有序容器

    • 顺序容器的特点是,元素的次序是由它插入的次序而决定的,访问元素也就按照最初插入的 顺序。而有序容器则不同,它的元素在插入容器后就被按照某种规则自动排序,所以是“有序”的。
    • 为了解决容器判断俩个元素的“先后顺序” , 需要在定义容器的时候必须要指定 key 的 比较函数。

    在这里插入图片描述

    • 方法一: 重载“<”,另一个是自定义模板参数。在这里插入图片描述
    • 方法二:编写专门的函数对象或者 lambda 表达式,然后在容器的模板参数里指定。
      在这里插入图片描述
    • 有序容器: 集合关系就用 set,关联数组就用 map

    无序容器

    • 无序容器也有四种,名字里也有 set 和 map,只是加上了 unordered(无序)前缀,分别 是 unordered_set/unordered_multiset、unordered_map/unordered_multimap。 散列表(也叫哈希表,hash table)。
      +

    在这里插入图片描述

    • key 具备两个条件: 一是可以计算 hash 值,二是能够执行相等比较操作。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 如果只想要单纯的集合、字典,没有排序需求,就应该用无序容 器,没有比较排序的成本,它的速度就会非常快。
      +
      就是多利用类型别名 using xx,而不要“写死”容器定义。因 为容器的大部分接口是相同的,所以只要变动别名定义,就能够随意改换不同的容器,对于 开发、测试都非常方便。
    • vector 是动态的, 不要使用new / delete在这里插入图片描述

    算法

    算法是 STL(标准库前身)的三大要件之一(容器、算法、迭代器),也是 C++ 标准 库里一个非常重要的部分,但它却没有像容器那样被大众广泛接受。

    认识算法

    • C++ 里的算法,指的是工作在容器上的一些泛型函数,会对容器内的元素实施的各种操作。
    • 算法其实并不神秘,因为所有的算法本质上都是 for 或者 while,通 过循环遍历来逐个处理容器里的元素,比如说 count 算法,它的功能非常简单,就是统计某个元素的出现次数,完全可以用 range-for 来实现同样的功能。
      +

    在这里插入图片描述

    • 追求更高层次上的抽象和封装,也是函数式编程的基本理念。
    • 算法只能通过迭代器去“间接”访问容器以及元素,算法的能力是由迭代器决定的.
    • 好处:分离了数据和操作。弊端:算法是通用的,但对有的数据结构虽然可行 但效率比较低

    迭代器

    • C++ 里的迭代器也有很多种,比如输入迭代器、输出迭代器、双向迭代器、随机访问迭代 ,可以把它简单地理解为另一种形式的“智能指针”,只是它强调的是对数据的访问,而不是生命周期管理。
    • 容器一般都会提供 begin()、end() 成员函数,调用它们就可以得到表示两个端点的迭代 器,具体类型最好用 auto 自动推导
      在这里插入图片描述
    • 类似 ++ – 操作的操作符 distance(),计算两个迭代器之间的距离; advance(),前进或者后退 N 步; next()/prev(),计算迭代器前后的某个位置。
      在这里插入图片描述

    for_each 代替 for

    在这里插入图片描述

    • for_each 算法的价值就体现在这里,它把要做的事情分成了两部分,也就是两个函数:一个遍历容器元素,另一个操纵容器元素,而且名字的含义更明确,代码也有更好的封装。它能够促使我们更多地以“函数式编程”来思考,使用 lambda 来封装逻辑,得到更干净、更安全的代码。

    排序算法

    • sort() 快排算法 , 不稳定,而且是全排所有元素。
      在这里插入图片描述
    • stable_sort,它是稳定的;
    • 选出前几名(TopN),应该用 partial_sort;
    • 选出前几名,但不要求再排出名次(BestN),应该用 nth_element;
    • 中位数(Median)、百分位数(Percentile),还是用 nth_element;
    • 按照某种规则把元素划分成两组,用 partition;
    • 第一名和最后一名,用 minmax_element。
      在这里插入图片描述
    • 在使用这些排序算法时,还要注意一点,它们对迭代器要求比较高,通常都是随机访问迭代 器(minmax_element 除外),所以最好在顺序容器 array/vector 上调用。

    查找算法

    需要排序后使用查找算法

    • binary_search () 查找一个数 如果在返回true
      在这里插入图片描述
    • lower_bound查找左边界 二分的 search_left
    • upper_bound 查右边界 二分的 search_right
      在这里插入图片描述

    无需排序的查找算法

    • 标准库里还有一些查找算法可以 用于未排序的容器,虽然肯定没有排序后的二分查找速度快,但也正因为不需要排序,所以 适应范围更广。
    • find 和 search 命名,不过可能是当时制定标准时的疏忽,名称有点混乱,其 中用于查找区间的 find_first_of/find_end,或许更应该叫作 search_first/search_last
      在这里插入图片描述
    • C++ 里的算法像是一个大宝库,非常值得你去发掘。比如类似 memcpy 的 copy/move 算法(搭配插入迭代器)、检查元素的 all_of/any_of 算法,用好了都可以替 代很多手写 for 循环
      在这里插入图片描述
      在这里插入图片描述

    “并发”(Concurrency)、“多线程”(multithreading)

    认识线程和多线程

    • 在 C++ 语言里,线程就是一个能够独立运行的函数。
      在这里插入图片描述
    • 任何程序一开始就有一个主线程,它从 main() 开始运行。主线程可以调用接口函数,创建 出子线程。子线程会立即脱离主线程的控制流程,单独运行,但共享主线程的数据。程序创 建出多个子线程,执行多个不同的函数,也就成了多线程。
    • 多线程的好处你肯定能列出好几条,比如任务并行、避免 I/O 阻塞、充分利用 CPU、提高 用户界面响应速度,等等。
    • 缺点:比如同步、死锁、数据竞争、系统调度开 销等……

    “读而不写”就不会有数据竞争。

    • C++ 多线程编程里读取 const 变量总是安全的,对类调用 const 成员函数、对 容器调用只读算法也总是线程安全的。
    • 多用 const 关键字,尽可能让操作都 是只读的,为多线程打造一个坚实的基础。

    多线程开发实践

    仅调用一次

    • 程序免不了要初始化数据,这在多线程里却是一个不大不小的麻烦。因为线程并发,如果没 有某种同步手段来控制,会导致初始化函数多次运行.
    • 声明一个 once_flag 类型的变量,最好是静态、全局的.
    static std::once_flag flag; // 全局的初始化标志
    然后调用专门的 call_once() 函数 
    
    
    auto f = []() 
    {
    std::call_once(flag, [](){
    // 仅一次调用,注意要传flag // 匿名lambda,初始化函数,只会执行一次 
    // 在线程里运行的lambda表达式 
    cout << "only once" << endl; } ); 
    };
    thread t1(f);
    thread t2(f);
    // 启动两个线程,运行函数f
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    线程局部存储

    • 读写全局(或者局部静态)变量是另一个比较常见的数据竞争场景,因为共享数据,多线程 操作时就有可能导致状态不一致。
    • 全局变量并不一定是必须共享的,可能仅仅是 为了方便线程传入传出数据,或者是本地 cache,而不是为了共享所有权。
    • 线程独占所有权,不应该在多线程之间共同拥有,术语叫**“线程局部存储”**(thread local storage)。
      在这里插入图片描述
      两个线程分别输出了 1020,互不干扰 。
      如果使用 static 就会输出 30

    原子变量

    • 要想保证多线程读写共享数据的一致性,关键是要解决同步问题,不能让两个线程同时写, 也就是“互斥”。
    • 所谓原子(atomic),在多线程领域里的意思就是不可分的。操作要么完成,要么未完成,不能被任何外部操作打断,总是有一个确定的、完整的状态。所以也就不会存在竞争读 写的问题,不需要使用互斥量来同步,成本也就更低。 【避免同时执行一个操作】
      在这里插入图片描述
    • 除了模拟整数运算,原子变量还有一些特殊的原子操作,比如 store、load、fetch_add、 fetch_sub、exchange、compare_exchange_weak/compare_exchange_strong,最后 一组就是著名的 CAS(Compare And Swap)操作。
    • 而另一个同样著名的 **TAS(Test And Set)**操作,则需要用到一个特殊的原子类型 atomic_flag。
    • 它不是简单的 bool 特化(atomic),没有 store、load 的操作,只用来实现 TAS,保证 绝对无锁.
    • 原子变量禁用了拷贝构造函数,所以在初始化的时候不能用“=”的赋值形式,只能用圆括号或者花括号:
      在这里插入图片描述
    • 除了模拟整数运算,原子变量还有一些特殊的原子操作,比如 store、load、fetch_add、 fetch_sub、exchange、compare_exchange_weak/compare_exchange_strong,最后一组就是著名的 CAS(Compare And Swap)操作。
    • 而另一个同样著名的 TAS(Test And Set)操作,则需要用到一个特殊的原子类型 atomic_flag。
    • 它不是简单的 bool 特化(atomic),没有 store、load 的操作,只用来实现 TAS,保证绝对无锁.
    • 原子操作 的用法是把原子变量当作线程安全的全局计数器或者标志位,这也算是“初心”吧。 但它还有一个更重要的应用领域,就是实现高效的无锁数据结构(lock-free)

    线程

    • call_once、thread_local 和 atomic 这三个用于 C++ 多线程变成的工具,尽量消除显式地使用线程。

    • 线程 : C++ 标准库里有专门的线程类thread,使用它就可以简单地创建线程,在名字空间 std::this_thread 里,还有 yield()、get_id()、sleep_for()、sleep_until() 等几个方便的管理函数。
      原子操作atomic 结合 thread:
      在这里插入图片描述

    • async(),它的含义是“异步运行”一个任务。
      在这里插入图片描述

    • async() 会返回一个 future 变量,可以认为是代表了执行结果的“期货”,如果任务有返回值,就可以用成员函数 get() 获取。不过要特别注意,get() 只能调一次,再次获取结果会发生错误,抛出异常 std::future_error。(至于为什么这么设计我也不太清楚,没找到官方的解释)
      另外,这里还有一个很隐蔽的“坑”,如果你不显式获取 async() 的返回值(即 future 对象),它就会同步阻塞直至任务完成(由于临时对象的析构函数),于是“async”就变成 了“sync”。所以,即使我们不关心返回值,也总要用 auto 来配合 async(),避免同步阻塞,就像下面 的示例代码那样
      在这里插入图片描述

    小结

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Mybatis的关系关联配置
    RocketMQ 系列(五)高可用与负载均衡
    企业聊天应用程序使用 Kubernetes
    户外骑行运动耳机哪个好,几款适合在骑行佩戴的耳机推荐
    源码部署ELK日志分析管理系统
    Git 的基本概念和使用方式
    《安富莱嵌入式周报》第275期:2022.07.18--2022.07.24
    Jquery切换样式并执行原生CSS实现过度动画
    推荐3款小众软件,可以满足一些奇怪的需求
    C++ 环境变量 二
  • 原文地址:https://blog.csdn.net/weixin_49486457/article/details/126598450