C++时间库 chrono

时间 Chrono

标准库中时间有关的结构,存储在chrono头文件中。头文件<chrono>,命名空间:std::chrono。命名空间中存储两个重要的结构体:durationtime_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*2s

  • 时间段转换
    因为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_clocksteady_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表示分母,这里只是先将分子分母进行了约分。

  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值