0%

C++_标准库

简介

  • 所有标准库类和函数都在std名称空间或其子名称空间中声明
  • C++标准库的核心是泛型容器和泛型算法。该库中的这个子集通常程序标准模板库(Standard Template Library, STL),因为它最初基于第三方库“标准模板库”,该库大量使用了模板。

<string> – 字符串 [ok]

  • C++20包括了一个强大的类型安全的字符串格式化库,可以通过定义在<format>头文件中的std::format()进行访问

<regex> – 正则表达式[ok]

<fstream> – I/O流[ok]

  • 大多数的I/O功能在如下几个头文件中定义:
    • <fstream>
    • <iomanip>
    • <ios>
    • <iosfwd>
    • <iostream>
    • <istream>
    • <ostream>
    • <sstream>
    • <streambuf>
    • <strstream>

<iomanip>

resetiosflags()

  • 简述:清除指定的 ios_base 标志

setiosflags()

  • 简述:设置指定的 ios_base 标志

setbase()

  • 简述:更改用于整数 I/O 的基数

setfill()

  • 简述:更改填充字符

setprecision()

  • 简述:更改浮点精度

setw()

  • 简述:更改下个输入/输出域的宽度

get_money()

  • 简述:剖析货币值

put_money()

  • 简述:格式化并输出货币值

get_time()

  • 简述:剖析指定格式的日期/时间值

put_time()

  • 简述:按照指定格式格式化并输出日期/时间值
  • 声明:template<class CharT> put_time(const std::tm* tmb, const CharT* fmt);
  • 参数:
    • tmb – 指向从 std::localtime 或 std::gmtime 获得的日历时间结构体的指针
    • fmt – 指向指定转换格式的空终止 CharT 串的指针,具体参考:https://zh.cppreference.com/w/cpp/io/manip/put_time(默认使用%c %Z

quoted()

  • 简述:插入和读取带有内嵌空格的被引号括起来的字符串

<memory> – 智能指针[ok]

  • 编写健壮程序时,需要面对的一个问题就是要知道何时删除对象。
  • 有几种可能发生的故障:
    • 根本没有删除对象(没有释放内存),这称为内存泄漏(memory leaks)。
    • 一段代码删除了内存,而另一段代码仍然引用了这个内存,导致直线那个内存的指针不再可用或已重新分配作他用,这称为悬空指针(dangling pointer)
    • 一段代码释放了内存,而另一端代码试图释放同一块内存,这称为双重释放(double deletion)

<exception> – 异常[ok]

  • 大多数异常支持在下面几个头文件中定义:
    • <exception>
    • <stdexcept>
    • <system_error>

<cmath> – 数学工具

  • 数学工具类和函数

<chrono> – 时间和日期工具[ok]

  • 简述:在C++11的日期时间库中,共有3个概念,分别是持续时间(Duration)、时间点(timepoint)、时钟(clock)。

Clocks

  • 定义中的时钟主要有3个,分别是:

    • system_clock,顾名思义,就是系统时钟,他一般是unix时间,即从1970年1月1日到现在的间隔。如果系统时间发生调整,那么调用该方法获取的时间值也会跟着变化。
    • steady_clock,是单调时间,即每后一次调用都一定会比前一次调用的时间要晚,它并不反映真实的世界时间。在内部的实现中,有可能是系统开启以来的时间。通过该时钟很适合用来获取一段时间内的间隔,它不随系统时间改变而改变
    • high_resolution_clock,高精度时间,用来测量细微间隔的一段时间,它的内部实现中可能是system_clock或是steady_clock,也可能是其他独立的时间。
  • 一般来说,我们使用的都是system_clock

  • system_clock的主要方法有3个,分别是

    • now,用来获取当前时间。
    • to_time_t,用来将系统时间转变为std::time_t类型。
    • from_time_t,用来将std::time_t类型转换为系统时间点
  • 使用system_clock获取当前系统时间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include <iostream>
    #include <chrono>
    int main() {
    auto time = std::chrono::system_clock::now();
    auto tt = std::chrono::system_clock::to_time_t(time);
    std::cout << std::put_time(std::localtime(&tt), "%Y-%m-%d %H:%M:%S") << std::endl;

    return 0;
    }
  • 统计函数的执行耗时,这种情况更适合使用steady_clock。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
        #include <iostream>
    #include <chrono>

    long fibonacci(unsigned n)
    {
    if (n < 2) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
    }

    int main()
    {
    auto start = std::chrono::steady_clock::now();
    std::cout << "f(42) = " << fibonacci(42) << '\n';
    auto end = std::chrono::steady_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
    }
    ```

    ### Duration

    + 持续时间表示一个时间段,比如1s、1min、1day等等,这些时间段是有单位的,因此持续时间也是一个模板变量,用来指定不同的单位
    + 它的定义如下:
    ```cpp
    // Rep表示单位的数量单位
    // Period表示单位
    template<class Rep, class Period = std::ratio<1>> class duration;
  • Period用来表示时间的单位,比如秒、天、小时等等,而Rep则表示容纳某个数量时间的单位。比较绕口,举个例子,比如说10天,那么Period是单位day,那么其数量是10,而10的类型可以是int型,也可以short型,Rep就用来表示这到底是什么类型存储这个量,如果类型过小,如果时间太长的话将会导致溢出。

  • 再来看下Period的定义,

    1
    2
    3
    // Num,表示分子
    // Denom,表示分母
    template<std::intmax_t Num, std::intmax_t Denom = 1> class ratio;
  • 举个简单点的例子,Num为1,Denom也为1时,表示是1s,如果Num为1,Denom为1000,那么相当于是1/1000s,也即毫秒。如果Num为60,Denom为1,那么表示 60 / 1s,也即相当于1min。

  • 下面是chrono头文件中的一些常见持续时间定义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Type	Definition
    std::chrono::nanoseconds duration</*signed integer type of at least 64 bits*/, std::nano>
    std::chrono::microseconds duration</*signed integer type of at least 55 bits*/, std::micro>
    std::chrono::milliseconds duration</*signed integer type of at least 45 bits*/, std::milli>
    std::chrono::seconds duration</*signed integer type of at least 35 bits*/>
    std::chrono::minutes duration</*signed integer type of at least 29 bits*/, std::ratio<60>>
    std::chrono::hours duration</*signed integer type of at least 23 bits*/, std::ratio<3600>>
    std::chrono::days (since C++20) duration</*signed integer type of at least 25 bits*/, std::ratio<86400>>
    std::chrono::weeks (since C++20) duration</*signed integer type of at least 22 bits*/, std::ratio<604800>>
    std::chrono::months (since C++20) duration</*signed integer type of at least 20 bits*/, std::ratio<2629746>>
    std::chrono::years (since C++20) duration</*signed integer type of at least 17 bits*/, std::ratio<31556952>>
  • 针对Duraion来说,一般有两种操作,

    • 时间的增减。
    • 不同持续时间单位之间的转换。
  • Duration重载了+/-运算符,可以直接进行+/-操作,类型之间的转换可以使用duration_cast方法。

  • 下面是一些使用实例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <iostream>
    #include <chrono>

    int main() {
    std::chrono::milliseconds ms(3);
    printf("%lld\n", ms.count());

    using second = std::chrono::duration<double>;
    auto s = std::chrono::duration_cast<second>(ms);
    printf("%llf\n", s.count());

    std::chrono::seconds sec(10);
    auto du = sec + ms;
    printf("%lld\n", du.count());

    return 0;
    }

TimePoint

  • 从时钟拿到的时间是时间点,它代表了时间段上的一个点,比如2019年10月21日10:20:10。

  • 时间点的定义如下。

    • template<class Clock, class Duration = typename Clock::duration> class time_point;
  • 通过time_point的模板参数,我们可以看到,一个是时钟,另外一个是持续时间,实际上timepoint就是一个时钟的基准时间加上一个时间段

  • 对于时间点来说,下面这几个方法比较重要。

    • time_since_epoch,获取以模板时钟的起始时间到现在的这个持续时间
    • time_point_cast,用来将时间点转换为基于同一个时钟,但不同类型持续时间的时间点。
    • 时间点也重载了+/-运算符,用来进行两个时间点之间的相加减
  • 下面是一些使用示例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include <iostream>
    #include <chrono>

    int main() {
    std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
    std::chrono::minutes m(20);
    std::chrono::time_point<std::chrono::system_clock> then = now + m;
    std::time_t tt = std::chrono::system_clock::to_time_t(then);
    std::cout << "timestamp is : " << now.time_since_epoch().count() << std::endl;
    std::cout << std::put_time(std::localtime(&tt), "%F %T") << std::endl;

    // 降低时间精度,默认system_clock的duration精度是 1/10 micro sec
    auto then_min = std::chrono::time_point_cast<std::chrono::minutes>(then);
    tt = std::chrono::system_clock::to_time_t(then_min);
    std::cout << std::put_time(std::localtime(&tt), "%F %T") << std::endl;

    return 0;
    }

一些应用

  • 获取时间戳。

    1
    2
    3
    4
    5
    6
    7
    8
    std::time_t getTimeStamp()
    {
    std::chrono::time_point<std::chrono::system_clock,std::chrono::milliseconds> tp = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    auto tmp=std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
    std::time_t timestamp = tmp.count();
    //std::time_t timestamp = std::chrono::system_clock::to_time_t(tp);
    return timestamp;
    }
  • 将时间戳转为日期。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    std::tm* gettm(int64 timestamp)
    {
    int64 milli = timestamp+ (int64)8*60*60*1000;//此处转化为东八区北京时间,如果是其它时区需要按需求修改
    auto mTime = std::chrono::milliseconds(milli);
    auto tp=std::chrono::time_point<std::chrono::system_clock,std::chrono::milliseconds>(mTime);
    auto tt = std::chrono::system_clock::to_time_t(tp);
    std::tm* now = std::gmtime(&tt);
    printf("%4d年%02d月%02d日 %02d:%02d:%02d\n",now->tm_year+1900,now->tm_mon+1,now->tm_mday,now->tm_hour,now->tm_min,now->tm_sec);
    return now;
    }
  • 在上面的程序中,时间精度只到秒,这是time_t结构的限制,如果希望获取到ms精度,那么直接使用timestamp % 1000的方式就行。

<random> – 随机数[ok]

  • 随机数库,带有随机数引擎,随机数引擎适配器以及随机数分布。
  • 通过这些组件可以生成更适合特定问题的随机数,例如正态分布,负指数分布等

<initializer_list> – 初始化列表

  • 它们便于编写参数数目可变的函数

<utility> – pair

  • 它定义了pair类模板,用于存储两种不同类型的元素。这称为存储异构元素
  • pair允许在一个对象中保存类型毫不相关的元素

<tuple> – tuple

  • 它定义的tuple是pair的一种泛化,它是固定大小的序列,元组的元素可以是异构的,tuple实例化的元素数目和类型在编译器是固定不变的。

词汇类型(vocabulary type)

  • <optional>

    • 要么存储指定类型的值,要么什么都不存储
  • <variant>

    • 可存储单个值(属于一组给定类型中的一种类型)
  • <any>

    • 可包含单个值,值可以是任何类型

<functional> – 函数对象

  • <functional>中定义的std::function函数模板是一个多态函数包装器,可以用来创建指向任何可调用对象的类型,例如函数,函数对象或者lambda表达式

  • std::function的示例可以用作函数指针,也可以用作函数实现回调的参数,可以存储,赋值,移动,也可以执行

  • 函数模板的模板参数看起来和大多数模板参数有些不同,语法如下:

    • std::function<R(ArgTypes...)>
  • 详解:

    • R : 是函数的返回类型
    • ArgTypes : 是函数参数类型的逗号分隔列表
  • 实现函数调用运算符的类称为函数对象(function object)

  • 函数对象可用作某些标准库算法的谓词

<filesystem> – 文件系统

<thread> – 多线程[ok]

<type_traits> – 类型萃取

<cstdint> – 标准整数类型

  • 它定义了大量标准整数类型,例如int8_t 和 int64_t 等,还包含了多个宏(指定这些类型的最小值和最大值)

<version> – 相关实现信息[ok]

  • 它可用于查询正在使用的C++标准库的相关实现信息
    • 版本号
    • 发布日期
    • 版权声明

<source_location> – 源位置

  • C++20添加了std::source_location类,用来查询有关源代码的信息,例如文件名,函数名,行号和列号,并可用于替换旧的C风格宏__FILE____LINE__

容器

<vector>[ok]

<list>

<forward_list>

<deque>

<array>

<span>

<queue>

<stack>

<set>

<map>

<alogrithm> – 算法[ok]

<ranges> – 范围库

  • C++20引入了范围库,这使得处理元素序列变得更加简单和优雅
感谢老板支持!敬礼(^^ゞ