STL工具


我们承担ROS,FastDDS等通信中间件,C++,cmake等技术的项目开发和专业指导和培训,有10年+相关工作经验,质量有保证,如有需要请私信联系。

pair

  • 头文件:#include<utility>
  • 操作函数:
    • 构造和赋值:
      • pair<T1, T2> p: default构造函数,建立一个pair,元素类型分别为T1和T2,各自以其default构造函数初始化
      • pair<T1, T2>p(val1, val2):建造一个pair,类型为T1和T2,以val1和val2为初值进行构造或移动构造
      • pair<T1, T2> p(p2):拷贝构造或移动构造
      • p1=p2:拷贝或std::move
    • p.first/p.second:获得pair的第一个或第二个value
    • p1.swap(p2) / swap(p1, p2):互换p1和p2的数据
    • p1==p2:等价于 p1.first==p2.first && p1.second==p2.second
    • make_pair(val1, val2):返回一个pair,带有val1和val2的类型和数值
    • get<0>(p) / get<1>(p):全局函数,等价于p.first/p.second
  • 使用场合:
    • 在很多stl中有用到,如map等
    • 如果需要返回两种类型,可以返回pair

tuple

  • 不是寻常的容器,不可以迭代
  • 元素类型可以是引用
  • 方法/函数:
    • 构造:
      • tuple<T1, T2, …Tn> t: 以n个给定类型建立一个tuple,以各元素类型的default构造函数完成初始化
      • tuple<T1,T2, …> t(v1, v2,…)
      • tuple<T1, T2> t(p):建立一个tuple,以给定的pair p初始化(类型必须吻合)
    • t = t2:赋值操作
    • t = p:将pair p赋值给带两个元素的tuple,pair和tuple的类型必须吻合
    • t1==t2/t1 != t2:如果所有元素相等返回true
    • <, >, <=, >=:使用字典式比较——?
    • t1.swap(t2):互换t1和t2的数据——始自C++11
    • 全局函数swap(t1, t2):同上,是个——始自C++11
    • 全局函数make_tuple:会根据value建立tuple,不需要明确指出元素的类型
    • 全局函数tie(ref1, ref2, …):建立一个由引用构成的tuple
    • 访问tuple的成员:
      • 使用标准库函数模板:get(t)访问tuple对象
  • 其他tuple特性:
    • tuple_size<tupletype>::value:可获得元素个数
    • tuple_elememt<idx, tupletype>::type:可获得idx个元素的类型
    • tuple_cat():可将多个tuple串接成一个tuple

辅助函数

  • 挑选最小值和最大值:#include<algorithm>
  • 两值互换:swap,#include<utility>
    • 只有参数定义了拷贝或移动语义,才可以调用成功
    • 指针数组版本提供重载版本:
template<typename T, size_t N>
void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b));    // vs中的定义和这个不同
  • 增补的比较操作:在std的rel_pos命名空间下,都是利用==<完成,#include<utility>
    • 只需自己定义好"<“和”==“,写上using namespace std::rel_pos,就可以使用”!=“,”>“”<=“”>=”

运行时类型识别

  • typeid:typeid(e)
    • 使用typeid运算符,就有可能以一个引用达到相同的执行期替代路线
    • e可以是任意类型或表达式
    • 操作结果是一个常量对象的引用,该对象类型是type_info或者type_info的公有派生类型
      • 如果参数表达式是一个引用,返回所引用对象的类型
      • 如果参数是数组或函数,并不会执行向指针的标准类型转换,也就是说,对数组a执行typeid(a)所得结果是数组类型而非指针类型
      • 当运算对象。。。
    • typeid也可以适用于内建类型和非多态的使用者自定义类型,这对于异常的支持有必要;与使用多态的差异在于,这时候的type_info对象是静态取得,而非执行期取得
  • type_info类:#include<typeinfo>
    • 一般是作为一个基类出现的,没有默认构造,拷贝,移动构造和赋值运算符都被定义成删除的,因此无法定义或拷贝type_info对象,创建type_info对象的唯一途径是使用typeid
    • 方法/属性:
      • t1==t2/t1 != t2
      • t.name():返回一个C风格字的类型名字符串(经验证,返回的不全是一个类型名称的字符串),不能保证获得的字符串就是这个class的名称,实测windows是类名linux不是
      • hash_code():返回该类型的唯一哈希值
      • t1.before(t2):返回一个bool值,表示t1是否位于t2之前,before所采取的顺序关系是依赖于编译器的;可以运用type_info::before()对type_info对象建立索引
if(typeid(rt)==typeid(fct)){
    fct& rf = static_cast<fct&>(rt);
}
  • decltype:
    • 在编译时进行类型推导,可以将获得的类型定义另外一个变量;在C++11中比较典型的就是decltype于typedef/using的合用
    • std::result_of:基于decltype的模板类,作用是推导函数的返回类型
    • 与auto推导不能带走cv限制符一样,decltype是能够带走表达式的cv限制符的;不过如果对象的定义中有const或volatile限制符,使用decltype推导其成员不会继承const或volatile限制符

随机数工具

C++随机数库为std中的#include<random>。包含三大组件:随机数引擎,引擎适配器,分布。
随机数引擎负责生成实际的随机数,并存储生成后续随机数的状态;分布决定了生成的随机数范围以及他们在该范围内的数学分布方式;

C风格随机数生成器

C++11之前生成随机数的唯一方法是C风格的srand()和rand()函数。

  • srand:需要在应用程序中调用一次,这个函数初始化随机数生成器,即设置种子。通常使用当前系统时间作为种子,因为如果每次都用同一个种子初始化随机数生成器,那么每次生成的随机数序列是一样的。所以通常采用当前系统时间作为种子。
  • rand:完成初始化随机数生成器之后,通过rand生成随机函数。rand函数生成的随机数在0到RAND_MAX之间,根据标准,RNAD_MAX至少为32767
srand(static_cast<unsigned int>(time(nullptr)));
std::cout << rand() << '\n';

可以通过以下函数生成特定范围内的随机数

int getRandom(int min, int max) {
  return static_cast<int>(rand() % (max + 1UL - min)) + min;
}

随机数引擎

随机数引擎负责生成实际的随机数。可以使用的随机数引擎有以下:

  • random_device
  • linear_congruential_engine
  • mersenne_twister_engine
  • subtract_with_carry_engine
    是一个带有状态的随机性的源头,其状态决定了它将生成哪一个随机值序列(注意这并非随机数—有什么区别?)。是一个函数对象,能产生随机的无正负值,每次以operator()调用之,就可以产出一个随机的无正负号值,并且内部状态会改变,使得可以此后再产出一个新随机值。均匀分布在预定义的最大和最小值之间:default_random_engine

随机数引擎适配器

* 引擎的位置可能导致产生的随机数不变

分布

分布是一个描述数字在特定范围内分布的数学公式,分布可与伪随机数引擎结合使用,从而定义生成的随机数的分布。

  • 分布:是把引擎产生的随机值转换为真实而有用的随机数。这些随机数由一个由使用者给定的参数所决定的区间内(包含两个区间值)。目前了解均匀分布:uniform_int_distribution和uniform_real_distribution就足够了,其他分布需要时再学习。
    • uniform_int_distribution:产生整数,类型为(short, int, long, long long及其相应的unsigned),默认int,构造第一实参为最小值,第二实参为最大值,随机数包括这两个数。产生一个随机数的做法是,对分布调用operator(), 将引擎作为实参传入
    • uniform_real_distribution:默认double,类型可以为float,double,long double
      注意:任何引擎的初始状态都有明确定义,并非随机,所以示例产生的随机数是一样的:
std::uniform_int_distribution<int> d;
std::default_random_engine dre1;
cout << d(dre1) << endl;
std::default_random_engine dre2;
cout << d(dre1) << endl;          
// 要避免这种情况,需要将引擎和分布定义成static的,暂时不知道原理
  • 算法shuffle():利用一个均匀随机数生成器如std::default_random_engine重排元素
    • 可以但不应该传入一个临时引擎,原因是每次初始化一个引擎,其初始状态是相同的

时间和日期工具

C++标准库中的chrono库提供了处理时间和日期相关的功能,使用时包含#include<chrono>头文件;这个库包含以下组件:

  • 持续时间 duration
  • 时钟 Clock
  • 时间点 Time point
  • 日期 Date(C++20)
  • 时区 Time zone(C++ 20) <ctime>头文件提供了C风格的时间和日期工具。

编译期有理数

ratio库可以精确表示任何在编译期使用的有限有理数(#include<ratio>)。有理数的分子和分母通过类型为std::intmax_t的编译期常量表示,这是一种有符号的整数类型,最大宽度由编译器指定。
ratio对象的定义方式和普通对象的定义方式不同,而且不能调用ratio对象的方法,需要使用类型别名,如定义一个表示1/60的有理数编译期常量:using r1 = ratio<1, 60>

using r1 = std::ratio<1, 60>;
// r1有理数的分子(num)和分母(den)是编译期常量,可通过以下方式访问:
std::intmax_t num { r1::num };
std::intmax_t den { r1::den };

ratio是一个编译期常量,分子和分母需要在编译期确定

std::intmax_t n {1};
std::intmax_t d {60};
using r2 = std::ratio<n, d>;  // 编译失败,n和d是变量,需要定义为常量
constexpr std::intmax_t n {1};
constexpr std::intmax_t d {60};
using r2 = std::ratio<n, d>;

ratio库支持有理数的加减乘除,由于所有这些操作都是在编译期进行的,所有不能使用标准算术运算符,可使用的算术ratio模板包含ratio_add,ratio_subtract, ratio_multiply,ratio_divide执行加减乘除。

持续时间 duration

表示两个时间点之间的间隔时间,通过模板化的std::chrono::duration类来表示。duration类保存了滴答数和滴答周期(tick period)(表示两个滴答数之间的秒数,是一个编译期ratio常量)
duration模板接收两个模板参数 template<class Rep, class Period=ratio<1>> class duration { ... }; 第一个参数Rep为保存滴答数的变量类型,是一种算术类型,如long,double等。第二个模板参数为滴答周期,如果不指定滴答周期,会使用默认值ratio<1>, 也就是1s。
支持以下方法或操作:

  • 支持算术运算:+,-,*,/, ==, <==>
  • Req count() const:以滴答数返回duration值
  • static duraton zero() 返回持续时间值等于0的duration
  • static duration min() / static duration max() : 返回duration模板指定的类型参数表示的最小值/最大值持续时间的duration值
    示例:
std::chrono::duration<long, ratio<60>> d3{10};
std::chrono::duration<long, ratio<1>> d4{14};
if (d3 > d4) {
  std::cout << "d3 > d4" << endl;
}
++d4;
d4 *= 2;
std::chrono::duration<double, ratio<60>> d5 {d3 + d4};
std::chrono::duration<long> d7 { 30 };
std::chrono::duration<double, ratio<60>> d8 { d7 };

将30s的duration转换为0.5分钟,使用这个方向的转换可能会得到非整数值,因此要求使用浮点数类型表示的duration,否则可能得不到正确的结果。

时钟

时钟由time_point和duration组成,即定义时间点的起点和一个tick周期(就是计时单位,如秒),不同的clock有不同的起点。一般当处理两个时间点的差距时,必须提供相同的起点/clock。
标准库提供了三个clock:

  • system_clock:所提供的timepoint将关联至现行系统的即时时钟,这个clock提供便捷函数to_time_t()和from_time_t(),允许转换成C的系统时间类型time_t;精度为100ns
  • steady_clock:它保证不会被调整;精度为ms
  • high_resolution_clock:表现的是系统中带有最短tick周期的clock,可能就是steady_clock或者system_clock,取决于编译器;精度为100ns

clock提供的类型定义和static成员:

  • clock::duration:获得clock的duration成员
  • clock::rep:获得tick类型
  • clock::period:获得单位类型的类型,等价于clock::duration::period
  • clock::time_point:获得clock的timepoint类型
  • clock::is_steady:如果clock是steady则为true
  • clock::now:获得一个用来表现目前时间的time_point
  • Timepoint:表现出某个特定时间点,关联至某个clock的某个正值或负值duration
    C和Posix中的时间相关函数#include<ctime>:注意,time_t通常只是始自unix起始点(1970.1.1)的秒数,但不能保证如此
    system_clock定义的方法:
  • to_time_t():将给定的time_point转换为time_t
  • from_time_t():将time_t转换为time_point

时间点

time_point类表示某个时间点,存储为相对于纪元(epoch)的duration,表示开始时间。time_point总是和特定的clock关联,纪元就是所关联的clock的原点。例如 UNIX/Linux的时间纪元是1970年1月1日,duration用秒度量。
time_point类包含time_since_epoch 函数,它返回的duration表示所关联clock的纪元和保存的时间点之间的时间。
C++支持合理的time_point和duration算术运算
time_point有三个构造函数:

  • time_point():构造一个time_point,通过duration::zero进行初始化,得到的time_point表示所关联clock的纪元
  • time_point(const duration&d):构造一个time_point,通过给定的duration进行初始化,得到的time_point表示纪元+d

std::bitset

template< std::size_t N >
class bitset;
  • std::bitset是一个类模板,头文件包含:#include<bitset>, 它提供了固定大小的位集合的表示,通过模板参数指定可容纳bit的数量。
  • 使用std::bitset,可以创建一定大小(n位)的位集合,并可以对它进行位级别的操作,如位移、取反、按位与、按位或等操作。
  • 传统方式是通过整型来作为bit数组,再借由&,|,~等操作符操作各个bit。std::bitset的优点在于可容纳任意个数的bit(但不能动态改变个数),并提供各项操作;
  • 不可以改变bitset的bit个数,如果需要一个可变长度的bit容器,可以考虑使用vector<bool>
  • std::bitset的构造函数允许你指定位集合的大小,以及一个可选的无符号长整数来初始化位集合。也可以使用一个字符串来初始化std::bitset。
std::bitset<8> b1;             // b1包含8位,全部初始化为0
std::bitset<16> b2(0xffff);    // b2包含16位,初始化为16个1
std::bitset<32> b3("1100");    // b3包含32位,前28位为0,后4位为1100

方法:

  • set()
  • reset()
  • flip()
  • count()
  • size()

数值的极值

模板numeric_limits,头文件 #include<limits>,成员有以下:

  • is_specialized:类型是否有极值
  • is_signed:类型是否带有正负号
  • is_integer:整数类型
  • min():最小的极值
  • max():最大的极值
  • 。。。

其他

  1. std::addressof,#include<memory>,取地址,和取地址一元运算符&有什么区别?如果重载&,一元运算符会调用到重载函数中,取不到this的地址,这个模板函数可以拿到真实地址,示例参考C++标准库文档
  2. 标准库函数:begin和end,C++11引入,为了让指针的使用更简单,更安全
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int* beg = begin(a);     // 指向ia首元素的地址
int* last = end(a);         // 指向ia尾元素下一位的指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值