时间 Chrono
标准库中时间有关的结构,存储在chrono头文件中。头文件<chrono>
,命名空间:std::chrono
。命名空间中存储两个重要的结构体:duration和time_point
namespace chrono
{
template<typename _Rep, typename _Period = ratio<1>>
struct duration;
template<typename _Clock, typename _Dur = typename _Clock::duration>
struct time_point;
}
duration:表示一段时间
duration结构体如其英文名,表示一段时间。其定义:
template <typename _Rep, typename _Period = ratio<1> >
struct duration;
其中_Rep 表示数目,_Period 表示单位,默认是1,单位是s, 其中ratio其实是分数的表示法,如果想用ms的单位,可以在定义时候用ratio<1,1000>传入模板。
用法
#include <chrono>
#include <iostream>
int main() {
std::chrono::minutes m_min(3); // 3 minutes
std::chrono::seconds m_sec(3); // 3 seconds
std::chrono::microseconds d2 = 1ms;
std::chrono::seconds m_sec2;
m_sec2 = m_min; // m_sec2.count() = 180
std::chrono::duration<float> t2 = 1ms;
std::chrono::duration<float> t2(1.0f);
std::chrono::duration<int, std::ratio<60, 1>> m_minr(3);
std::chrono::duration<int, std::milli> m_mili(3);
std::cout << "m_min:" << m_min.count() << "\nm_sec:" << m_sec.count()
<< "\nm_sec2:" << m_sec2.count();
return 0;
}
-
可以直接用其定义好的时分秒。
如:std::chrono::minutes m_min(3); // 3 minutes std::chrono::seconds m_sec(3); // 3 seconds std::chrono::microseconds d2 = 1ms;
这里的minutes,seconds,microseconds都是直接定义好的,可以直接使用,可以直接使用数字后加单位的表示法如
1ms
,这是用户定义字面量,不过需要C++14的标准,并加上命名空间using namespace std::chrono_literals;
-
可以自己定义radio
如:std::chrono::duration<float> t2(1.0f); std::chrono::duration<int, std::ratio<60, 1>> m_minr(3); std::chrono::duration<int, std::milli> m_mili(3);
自己定义的radio可以调整不光是以时分秒为单位,还可以以2s为一单位,如第二个模板参数传入
std::ratio<2,1>
,那么就是以2s为一单位,这时如果调用count()
获取数值为N,那么这段时间真正的时间为N*2
s -
时间段转换
因为duration重载了运算符,所以不同单位变量是可以互相加减的,如:std::chrono::minutes m_min(3); // 3 minutes std::chrono::seconds m_sec(3); // 3 seconds std::cout << (m_min + m_sec).count() << std::endl; // 结果为183
duration_cast:进行时间单位间的转换。如:
std::chrono::minutes m_min(3); // 3 minutes auto t = std::chrono::duration_cast<std::chrono::seconds>(m_min); std::cout << t.count() << std::endl; // 结果为 180
原理
duration 结构体源码精简后如下:
/// duration
template <typename _Rep, typename _Period>
struct duration {
......
public:
typedef _Rep rep;
typedef _Period period;
......
constexpr rep count() const { return __r; }
// 20.11.5.3 arithmetic
constexpr duration operator+() const { return *this; }
constexpr duration operator-() const { return duration(-__r); }
_GLIBCXX17_CONSTEXPR duration& operator++() {
++__r;
return *this;
}
_GLIBCXX17_CONSTEXPR duration operator++(int) {
return duration(__r++);
}
_GLIBCXX17_CONSTEXPR duration& operator--() {
--__r;
return *this;
}
......
private:
rep __r;
};
可以看到,duration实际存储的是’rep __r;’,而rep是我们模板传入的类型,所以所有实际操作其实是对数值__r进行的操作。当不同单位(不同的ratio)之间进行赋值时,根据不同的ratio进行计算,获取同一的单位。
time_point 记录某一时刻的时间点
类模板 std::chrono::time_point 表示时间中的一个点。它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值。
template< class Clock, class Duration = typename Clock::duration>
class time_point;
其中Clock 表示时钟,可以用system_clock
或steady_clock
,后面会介绍。
用法
来自C++ reference的例子:
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
int main()
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24)); // 变量可以直接和duration的变量做加减
std::cout << "24 hours ago, the time was "
<< std::put_time(std::localtime(&now_c), "%F %T") << '\n';
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::cout << "Printing took "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< "us.\n";
}
可能的结果:
24 hours ago, the time was 2011-10-25 12:00:08
Hello World
Printing took 84us.
需要注意的是time_point 变量可以直接和duration的变量做加减。
原理
其实time_point记录的是一个duration
,其源码如下:
template <typename _Clock, typename _Dur>
struct time_point {
......
// observer
constexpr duration time_since_epoch() const { return __d; }
// arithmetic
_GLIBCXX17_CONSTEXPR time_point& operator+=(const duration& __dur) {
__d += __dur;
return *this;
}
_GLIBCXX17_CONSTEXPR time_point& operator-=(const duration& __dur) {
__d -= __dur;
return *this;
}
......
private:
duration __d;
};
可以看到存储的变量duration __d;
。那么如何表示时间点呢?只要规定时间起点,加上变量 __d 就获得了当前时间。而起始时间点的设定,则需要根据采用哪种时钟了。
时钟 steady_clock和system_clock
-
类 std::chrono::system_clock 表示系统范围的实时壁钟.它可以不单调:大多数系统上,系统时间可以在任何时候被调节。它是唯一有能力映射其时间点到 C 风格时间的 C++ 时钟。
-
类 std::chrono::steady_clock 表示单调时钟。此时钟的时间点无法减少,因为物理时间向前移动。此时钟与壁钟时间无关(例如,它能是上次重启开始的时间),且最适于度量间隔。
两者区别:
关于两者的区别网上有很多介绍,stackoverflow中difference-between-stdsystem-clock-and-stdsteady-clock也有介绍。
按照我的理解:
system_clock是获取系统的时间,比如操作系统内的时间是2020.04.07,那么std::chrono::system_clock::now()
则会返回一个从1970.01.01为时间起点的time_point的时间点。如果你将系统时间进行更改,随之std::chrono::system_clock::now()
的时间点也会更改。所以一般利用system_clock获取墙上时间。
steady_clock是获取单调时钟,std::chrono::steady_clock::now()
可以看做是从你系统开始的那一刻记作0时间点,随着时间推移其记录的时间变长,不会根据你系统时间的改变而改变。可以看作是系统启动的相对时间。一般可以用来记录函数运行的时间。 -
high_resolution_clock
看到源码这样写:using high_resolution_clock = system_clock;
直接等价于system_clock,所以就不多说了。
拓展
ratio 结构体:
template<intmax_t _Num, intmax_t _Den = 1>
struct ratio
{
static_assert(_Den != 0, "denominator cannot be zero");
static_assert(_Num >= -__INTMAX_MAX__ && _Den >= -__INTMAX_MAX__,
"out of range");
// Note: sign(N) * abs(N) == N
static constexpr intmax_t num =
_Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value;
static constexpr intmax_t den =
__static_abs<_Den>::value / __static_gcd<_Num, _Den>::value;
typedef ratio<num, den> type;
};
有两个变量:num
表示分子和den
表示分母,这里只是先将分子分母进行了约分。