今天我们来学习一下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的功能:
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 命名空间下,定义如下:
注意:到 hours 为止的每个预定义时长类型至少涵盖 ±292 年的范围。每个预定义时长类型
days
、weeks
、months
和years
至少涵盖 ±40000 年范围。years
等于 365.2425days
(格里高利年的平均长度)。months
等于 30.436875days
(years
准确的 1/12 )。count()成员函数
duration 类还提供了获取时间间隔的时钟周期数的方法 count (),函数原型如下,其主要是返回时间间隔的数值:
constexpr rep count() const;
用法如下:
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 ticks
6000 us duration has 6000 ticks
3.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:分钟转换为毫秒
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 milliseconds
123456 us equals to 123.456 milliseconds
例2
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 microseconds
100000000 shakes
100 jiffies
0 minutes
microfortnights
nanocenturies
例3. 自定义单位转换
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 对象作为结果。这些都是作为非成员运算符函数实现的。下面是一个示例:
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() 来得到时间刻度数。下面是示例代码:
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 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有
ratio
和ratio
两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为ratio
。如下示例: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 的值。例如:
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: 14
bar: 14
foo==bar: true
foo > bar: false
foo < 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
,其中 literals
与 chrono_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 库中的类的访问,则对应的字面量运算符亦变为可见。
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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论