C++进阶:chrono之duration

admin 2023年2月26日14:37:15评论39 views字数 8190阅读27分18秒阅读模式

今天我们来学习一下C++的时间库chrono,chrono是一个C++的一个日期和时间库,源于boost,在C++11时加入,现在已经是C++的一个标准库了。

C++11 中提供的日期和时间相关的库 chrono,通过 chrono 库可以很方便地处理日期和时间,为程序的开发提供了便利。要使用chrono库,需要#include,其所有实现均在std::chrono namespace下。chrono是一个模版库,使用简单,功能强大,chrono 库主要包含三种类型的类:时间间隔(duration)、时钟(clocks)、时间点(time point)。今天我们先学习duration。

std::chrono::duration

duration (持续时间) 是定义为时间刻度数的时间间隔,可以指定一个时间刻度是多少秒。因此,时间刻度是衡量时间长短的基础。duration 模板的实例类型的对象定义了 duration。时间刻度所表示的默认时间间隔是 1 秒,但可以将它定义为更多秒或秒几分之一。例如,如果定义时间刻度为 3600 秒,但意味着 10 个 duration 就是 10 个小时;也能够定义时间刻度为一秒的十分之一,在这种情况下,10 个 duration 表示 1 秒。

也就是说,std::chrono::duration 表示的是一段时间,比如五百年,1024天,两个小时,12.88秒,半个时辰,一炷香的时间等等,只要能换算成秒即可。

// 定义于头文件template<class Rep, class Period = std::ratio<1>> class duration;

std::ratio 类表示每个时钟周期的秒数,其中第一个模板参数 Num 代表分子,Denom 代表分母,该分母值默认为 1,因此,ratio 代表的是一个分子除以分母的数值,比如:ratio<2> 代表一个时钟周期是 2 秒,ratio<60 > 代表一分钟,ratio<60*60 > 代表一个小时,ratio<60*60*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一纳秒。

Rep参数代表了可以传入的时间单位的类型,可以为float, int, int64等等,如果为float表示可以传入时间单位的一部分,比如传入1.2表示1.2倍个时间单位Period参数代表了时间单位,可以理解为时间的精度,可以为微秒,毫秒,秒,分钟,小时等(或者其它自定义的单位,类型为上面提到的std::ratio)。如果Period参数不指定,默认为 ratio<1>,也就是以秒为单位。

下面用一个简单例子详细说明一下duration的功能:

#include #include 
int main() { // 表示秒 std::chrono::duration<long long, std::ratio<1, 1>> tick_s{15}; // 15秒 // 表示毫秒 std::chrono::duration<long long, std::ratio<1, 1000>> tick_ms{1500}; // 1500毫秒 // 表示微妙 std::chrono::duration<long long, std::ratio<1, 1000000>> tick_us{150000}; // 150000微妙 // 表示小时 std::chrono::duration<double, std::ratio<60, 1>> tick_hour{60}; // 60小时 //表示1/5秒,也就是0.2秒的单位值 std::chrono::duration<double, std::ratio<1, 5>> tiny{5.5}; // 5.5x0.2=1.1秒}

复制代码

为了方便使用,在标准库中定义了一些常用的时间间隔,比如:时、分、秒、毫秒、微秒、纳秒,它们都位于 chrono 命名空间下,定义如下:

C++进阶:chrono之duration

注意:到 hours 为止的每个预定义时长类型至少涵盖 ±292 年的范围。每个预定义时长类型 daysweeksmonthsyears 至少涵盖 ±40000 年范围。years 等于 365.2425 days (格里高利年的平均长度)。months 等于 30.436875 daysyears 准确的 1/12 )。

count()成员函数

duration 类还提供了获取时间间隔的时钟周期数的方法 count (),函数原型如下,其主要是返回时间间隔的数值:

constexpr rep count() const;

用法如下:

#include #include int main(){    std::chrono::milliseconds ms{3};         // 3 毫秒    std::chrono::microseconds us = 2*ms;     // 6000 微秒    // 时间间隔周期为 1/30 秒    std::chrono::duration<double, std::ratio<1, 30>> hz(3.5);     std::cout <<  "3 ms duration has " << ms.count() << " ticksn"              <<  "6000 us duration has " << us.count() << " ticksn"              <<  "3.5 hz duration has " << hz.count() << " ticksn";       }

输出:

3 ms duration has 3 ticks6000 us duration has 6000 ticks3.5 hz duration has 3.5 ticks

类型转换:std::chrono::duration_cast

duration_cast 是 chrono 库提供的一个模板函数,这个函数不属于 duration 类。但是通过这个函数可以对 duration 类对象内部的时钟周期 Period和周期次数的类型 Rep 进行修改,该函数原型如下:

template <class ToDuration, class Rep, class Period>constexpr ToDuration duration_cast (const duration& dtn);

也就是一个 duration 类型是可以被隐式转换为另一个 duration 类型的,比如从秒转换成毫秒,微妙等等,当源类型的时钟周期是目的类型的时钟周期的整数倍时(例如小时到分钟),浮点时长和整数时长间转型能隐式进行无需使用 duration_cast ,,其他情况下都需要通过函数进行转换。下面是一些示例:

例1:分钟转换为毫秒

#include #include 
int main() { std::chrono::microseconds us{123456}; // 整数时长:要求 duration_cast std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(us); // 小数时长:不要求 duration_cast std::chrono::duration<double, std::ratio<1, 1000>> fp_ms = us; std::cout << "123456 us equals to " << ms.count() << " millisecondsn"; std::cout << "123456 us equals to " << fp_ms.count() << " millisecondsn";
return 0;}

输出:

123456 us equals to 123 milliseconds123456 us equals to 123.456 milliseconds

例2

#include #include  constexpr auto year = 31556952ll; // 格里高利历年的平均秒数 int main(){    using shakes = std::chrono::duration<int, std::ratio<1, 100000000>>;    using jiffies = std::chrono::duration<int, std::centi>;    using microfortnights = std::chrono::duration<float, std::ratio<14*24*60*60, 1000000>>;    using nanocenturies = std::chrono::duration<float, std::ratio<100*year, 1000000000>>;     std::chrono::seconds sec(1);     std::cout << "1 second is:n";     // 无精度损失的整数尺度转换:无转型    std::cout << std::chrono::microseconds(sec).count() << " microsecondsn"              << shakes(sec).count() << " shakesn"              << jiffies(sec).count() << " jiffiesn";     // 有精度损失的整数尺度转换:需要转型    std::cout << std::chrono::duration_cast<std::chrono::minutes>(sec).count()              << " minutesn";     // 浮点尺度转换:无转型    std::cout << microfortnights(sec).count() << " microfortnightsn"              << nanocenturies(sec).count() << " nanocenturiesn";}

输出:

1 second is:1000000 microseconds100000000 shakes100 jiffies0 minutes0.82672 microfortnights0.316887 nanocenturies

例3. 自定义单位转换

#include #include 
typedef std::chrono::duration<long long, std::ratio<5, 1> > second5s;typedef std::chrono::duration<long long, std::ratio<1, 10> > one_tenth_seconds;
int main() { second5s s = std::chrono::duration_cast(one_tenth_seconds(300)); std::cout << "300 [1/10 seconds] equal to " << s.count() << " [5 seconds]n";}

算术运算

由于在 duration 类内部做了操作符重载,因此时间间隔之间可以直接进行算术运算,可以将任何二元算术运算符 +、-、*、/、% 应用到 duration 对象上,会得到一个 duration 对象作为结果。这些都是作为非成员运算符函数实现的。下面是一个示例:

#include #include 
int main() { std::chrono::duration<double, std::ratio<1, 5>> tiny{5.5}; std::chrono::duration<double, std::ratio<1, 5>> small{7.5}; auto total = tiny + small; std::cout << "total = " << total.count() << std::endl;
auto sub = small - tiny; std::cout << "sub = " << sub.count() << std::endl;}

可以将前缀或后缀自增和自减运算符应用到 duration 对象上,并且可以通过调用成员函数 count() 来得到时间刻度数。下面是示例代码:

#include #include 
int main() { std::chrono::duration<double, std::ratio<1, 5>> tiny{5.5}; // Measured in 1/5 second std::chrono::microseconds very_tiny{100}; // Measured in microseconds ++tiny; very_tiny--; std::cout << "tiny = " << tiny.count() << " very_tiny = " << very_tiny.count() << std::endl; // tiny = 6.5 very_tiny = 99}

注意事项:duration 的加减运算有一定的规则,当两个 duration 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有 ratioratio 两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为 ratio。如下示例:

#include #include using namespace std;
int main(){ chrono::duration<double, ratio<9, 7>> d1(3); chrono::duration<double, ratio<6, 5>> d2(1); // d1 和 d2 统一之后的时钟周期 chrono::duration<double, ratio<3, 35>> d3 = d1 - d2;}

对于分子 6,、9 最大公约数为 3,对于分母 7、5 最小公倍数为 35,因此推导出的时钟周期为 ratio<3,35>

比较运算

有一整套完整的运算符可以用来比较两个 duration 对象。它们都是由非成员函数实现的,允许比较不同类型的 duration 对象。这个过程可以确定操作数共同的时钟周期,当表示成相同的时钟周期时,就可以比较 duration 的值。例如:

#include #include #include 
int main (){ std::chrono::duration<int> foo; std::chrono::duration<int> bar (10);
// counts: foo bar // --- --- foo = bar; // 10 10 foo = foo + bar; // 20 10 ++foo; // 21 10 --bar; // 21 9 foo *= 2; // 42 9 foo /= 3; // 14 9 bar += ( foo % bar ); // 14 14
std::cout << std::boolalpha; std::cout << "foo: " << foo.count() << std::endl; std::cout << "bar: " << bar.count() << std::endl; std::cout << "foo==bar: " << (foo==bar) << std::endl; std::cout << "foo > bar: " << (foo > bar) << std::endl; std::cout << "foo < bar: " << (foo < bar) << std::endl;
return 0;}

输出:

foo: 14bar: 14foo==bar: truefoo > bar: falsefoo < bar: false


字面量

可以将 duration 常量指定为整数或浮点值,后缀指定了时钟周期。这里有 8 个可以使用的后缀:

  • y是年,例如 3y 或 3.5y,C++20起

constexpr std::chrono::year operator ""y(unsigned long long y) noexcept{    return std::chrono::year(int(y));}
  • d 是天,例如 3d 或 3.5d,C++20起

    constexpr std::chrono::day operator ""d(unsigned long long d) noexcept{    return std::chrono::day(d);}
  • h 是小时,例如 3h 或 3.5h,C++14起,支持整数和浮点数两种类型:

    constexpr std::chrono::hours operator ""h(unsigned long long h){    return std::chrono::hours(h);}constexpr std::chrono::duration<long double, ratio<3600,1>> operator ""h(long double h){    return std::chrono::duration<long double, std::ratio<3600,1>>(h);}
  • min 是分钟,例如 20min 或 3.5min,C++14起,支持整数和浮点数两种类型

  • s 是秒,例如 10s 或 1.5s,C++14起,支持整数和浮点数两种类型

  • ms 是毫秒,例如 500ms 或 1.5ms,C++14起,支持整数和浮点数两种类型

  • us 是微秒,例如 500us 或 0.5uso,C++14起,支持整数和浮点数两种类型

  • ns 是纳秒,例如 2ns 或 3.5ns,C++14起,支持整数和浮点数两种类型

上面这几个运算符声明于命名空间 std::literals::chrono_literals ,其中 literalschrono_literals 为内联命名空间。能通过 using namespace std::literals 、 using namespace std::chrono_literals 及 using namespace std::literals::chrono_literals 取得对此运算符的访问。

另外,在命名空间 std::chrono 中,标准库提供 using namespace literals::chrono_literals; 指令,故若程序员使用 using namespace std::chrono; 取得对 chrono 库中的类的访问,则对应的字面量运算符亦变为可见。

#include #include 
int main() { using namespace std::chrono_literals;
auto day = 24h; auto halfhour = 0.5h; std::cout << "one day is " << day.count() << " hoursn" << "half an hour is " << halfhour.count() << " hoursn";
auto lesson = 45min; auto halfmin = 0.5min; std::cout << "one lesson is " << lesson.count() << " minutesn" << "half a minute is " << halfmin.count() << " minutesn";
auto snd = 30s; std::cout << "half a minute is " << snd.count() << " secondsn" << "a minute and a half is " << (1min + 30s).count() << " secondsn";
auto ms1 = 250ms; std::chrono::milliseconds ms2 = 1s; std::cout << "250ms = " << ms1.count() << " millisecondsn" << "1s = " << ms2.count() << " millisecondsn";
auto us1 = 250us; std::chrono::microseconds us2 = 1ms; std::cout << "250us = " << us1.count() << " microsecondsn" << "1ms = " << us2.count() << " microsecondsn";
auto ns1 = 250ns; std::chrono::nanoseconds ns2 = 1us; std::cout << "250ns = " << ns1.count() << " nanosecondsn" << "1us = " << ns2.count() << " nanosecondsn";}

std::string 亦定义 operator""s ,表示 std::string 类型字面量对象,但它是字符串字面量:10s 是十秒,而 "10"s 是二字符 string 。

好了,今天的内容到这里了。


作者:Codemaxi
链接:https://juejin.cn/post/7128152013778452487
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


原文始发于微信公众号(汇编语言):C++进阶:chrono之duration

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月26日14:37:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   C++进阶:chrono之durationhttps://cn-sec.com/archives/1575553.html

发表评论

匿名网友 填写信息