C++11标准库 时间工具<chrono>梳理

cnblogs 2024-07-15 08:09:00 阅读 87

目录

  • <chrono>
    • 时间间隔duration
      • 常用的duration
    • 时间点time_point
    • 时钟system_clock & steady_clock
      • system_clock
        • 代码举例
      • steady_clock
        • 例程:
    • 转换函数
      • 1.duration_cast
        • Description:
        • duration支持隐式转换的规则
      • 2. time_point_cast

<chrono>

C++11中提供了日期和时间相关的库chrono。

chrono库主要包含三种类型的类:<code>时间间隔duration、时钟clocks时间点time point

时间间隔duration

  1. 常用类成员

duration表示一段时间间隔,用来记录时间长度,可以表示几秒、几分钟、几个小时的时间间隔。duration的原型如下:

// 定义于头文件 <chrono>

template<

class Rep, //单位类型 == 单位次数(多少个单位) == 多少个周期数

class Period = std::ratio<1> //单位 Period:周期,默认周期为1s

> class duration;

模板参数:

  • Rep:Representation(表示),这是一个数值类型,用于表示时钟数(周期)的类型(默认为整形)。若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目。

  • Period:表示时钟的周期,它的原型如下:

    // 定义于头文件 <ratio>

    template<

    std::intmax_t Num,

    std::intmax_t Denom = 1

    > class ratio;

    位于命名空间std

    ratio(比率;比例)类表示每个时钟周期的单位,如秒、毫秒、微秒,其中第一个模板参数Num(Numerator)代表分子Denom(denominator)代表分母,该分母值默认为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>代表一纳秒。

    std::ratio - cppreference.com

    image-20240626201201245

常用的duration

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

类型 定义
纳秒:std::chrono::nanoseconds using nanoseconds = duration<long long, nano>;
微秒:std::chrono::microseconds using microseconds = duration<long long, micro>;
毫秒:std::chrono::milliseconds using milliseconds = duration<long long, milli>;
秒 :std::chrono::seconds using seconds = duration ;
分钟:std::chrono::minutes using minutes = duration<int, ratio<60>>;
小时:std::chrono::hours using hours = duration<int, ratio<3600>>;

image-20240625144845831

  1. duration类的构造函数原型如下:

<code>// 1. 拷贝构造函数

duration( const duration& ) = default; //浅拷贝

// 2. 通过指定时钟周期的类型和次数来构造对象(以缺省单位秒直接构造)j

template< class Rep2 >

constexpr explicit duration( const Rep2& r ); //std::chrono::duration<int> sec(1);//1秒

// 3. 通过指定时钟周期类型,和时钟周期长度来构造对象

template< class Rep2, class Period2 >

constexpr duration( const duration<Rep2,Period2>& d );//改变单位

image-20240625152958326

  1. 为了更加方便的进行duration对象之间的操作,类内部进行了操作符重载:

<code>duration& operator= (const duration& rhs) = default;

constexpr duration operator+() const;

constexpr duration operator-() const;

duration& operator++();

duration operator++(int);

duration& operator--();

duration operator--(int);

duration& operator+= (const duration& rhs);

duration& operator-= (const duration& rhs);

duration& operator*= (const rep& r);

duration& operator/= (const rep& r);

duration& operator%= (const rep& r);

duration& operator%= (const duration& rhs);

image-20240625145741710

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

exam:

<code> std::chrono::duration<double, std::ratio<9, 7>> d1(3); //单位为9/7秒

std::chrono::duration<double, std::ratio<6, 5>> d2(1); //单位为6/5秒

/*

9和6的最大公约数是3;

7和5的最小公倍数是35;

*/

// d1 和 d2 统一之后的时钟周期

std::chrono::duration<double, std::ratio<3, 35>> d4 = d1 - d2;

auto d3 = d1 - d2;

std::cout<<d3.count()<<"\n";

image-20240625150330546

image-20240625170847003

  1. duration类还提供了获取时间间隔的时钟周期数的方法count(),函数原型如下:

<code>constexpr rep count() const; //计算有多少个单位

image-20240625143251339

时间点time_point

注:这个类需要有一个时钟才可以使用,一般搭配system_clock、steady_clock使用,这两个类中缺省已将time_point等初始化好,方便用户使用.

用法展示在system_clock、steady_clock小节描述中.以下内容仅补充定义

<code>// 定义于头文件 <chrono>

template<

class Clock,

class Duration = typename Clock::duration //缺省使用时钟内置的duration,一般不需要手动写

> class time_point;

它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值,通过这个类最终可以得到时间中的某一个时间点。

  • Clock:此时间点在此时钟上计量
  • Duration:用于计量从纪元起时间的 std::chrono::duration 类型

// 1. 构造一个以新纪元(epoch,即:1970.1.1)作为值的对象,需要和时钟类一起使用,不能单独使用该无参构造函数

time_point();

// 2. 构造一个对象,表示一个时间点,其中d的持续时间从epoch开始,需要和时钟类一起使用,不能单独使用该构造函数

explicit time_point( const duration& d );

// 3. 拷贝构造函数,构造与t相同时间点的对象,使用的时候需要指定模板参数

template< class Duration2 >

time_point( const time_point<Clock,Duration2>& t );

operator重载和duration类似

在这个类中除了构造函数还提供了另外一个time_since_epoch()函数,用来获得1970年1月1日到time_point对象中记录的时间经过的时间间隔(duration),函数原型如下:

duration time_since_epoch() const;

实际应用中将单位转换成秒后就是常说的时间戳.

在线时间戳转换工具(Unix timestamp) - 在线工具 (tools.fun)

时钟system_clock & steady_clock

system_clock

struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime

using rep = long long;

using period = ratio<1, 10'000'000>; // 100 nanoseconds

using duration = chrono::duration<rep, period>;

using time_point = chrono::time_point<system_clock>;

static constexpr bool is_steady = false; //不是单调时钟

/*3个静态函数*/

// get current time

// 返回当前计算机系统时间的时间点。

_NODISCARD static time_point now() noexcept

{

return time_point(duration(_Xtime_get_ticks()));

}

// convert to __time64_t

// 将 time_point 时间点类型转换为 std::time_t 类型

_NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept

{

return duration_cast<seconds>(_Time.time_since_epoch()).count();

}

// convert from __time64_t

// 将 std::time_t 类型转换为 time_point 时间点类型

_NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept

{

return time_point{seconds{_Tm}};

}

};

system_clock中的time_point类型通过系统时钟做了初始化chrono::time_point<system_clock>,里面记录了新纪元时间点

system_clock还提供了3个静态函数:

static std::chrono::time_point<std::chrono::system_clock> now() noexcept;

// 将 time_point 时间点类型转换为 std::time_t 类型

static std::time_t to_time_t( const time_point& t ) noexcept;

// 将 std::time_t 类型转换为 time_point 时间点类型

static std::chrono::system_clock::time_point from_time_t( std::time_t t ) noexcept;

代码举例
  1. 计算一段时间

int main(){

//新纪元起始时间点:

std::chrono::system_clock::time_point epoch;//系统时间的时间点(缺省为新纪元)

std::cout<<epoch.time_since_epoch().count()<<"\n"; //

//一日时间段

std::chrono::duration<long long> day(std::chrono::hours(24));

//std::chrono::hours day(24); //相同

//新纪元后的一天的时间点:

std::chrono::system_clock::time_point epoch1 = epoch+day;

//std::chrono::system_clock::time_point epoch1(epoch+day); //相同

std::cout<<epoch1.time_since_epoch().count()<<"\n";

return 0;

}

image-20240625232221795

证明获取到的是新纪元起始一天之后的时间:

将周期单位从100纳秒换成s,即:

864000000000 (100ns) = 864000000000 00(1ns) * 10^(-9) = 86400 (s)

再将时间戳转换,得证

image-20240625232614183

新纪元的时间戳是0,得证

image-20240625232620806

  1. 获取当前计算机系统时间

<code>int main(){

std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now();

//法一:

time_t time = std::chrono::system_clock::to_time_t(now_time);

std::cout<<ctime(&time)<<"\n";

//法二:获取后还需要单位转换+时间戳工具

std::cout<<now_time.time_since_epoch().count()<<"\n";

}

运行结果:

image-20240625235819974

steady_clock

别名:

<code>using high_resolution_clock = steady_clock;

如果我们通过时钟不是为了获取当前的系统时间,而是进行程序耗时的时长,此时使用syetem_clock就不合适了,因为这个时间可以跟随系统的设置发生变化。在C++11中提供的时钟类steady_clock相当于秒表,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。

定义:

std::chrono::steady_clock 表示单调时钟。此时钟的时间点无法随物理时间向前推进而减少。此时钟与壁钟时间无关(例如,它能是上次重启开始的时间),且最适于度量间隔。

struct steady_clock { // wraps QueryPerformanceCounter 包装查询性能计数器-- VS

using rep = long long;

using period = nano;

using duration = nanoseconds; //单位是1ns,精度比system高了100倍

using time_point = chrono::time_point<steady_clock>; //

static constexpr bool is_steady = true; //稳定时钟标志,始终为 true

/* 1个静态方法 */

// get current time

// 获取一个稳定增加的时间点

_NODISCARD static time_point now() noexcept

{

//VS版实现,需要可以查VS中的定义

}

};

这个类只提供了一个now方法,就用于统计时长.

例程:

int main() {

// 获取开始时间点

std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();

// 执行业务流程

std::cout << "print 1000 stars ...." << "\n";

for (int i = 0; i < 1000; ++i)

{

std::cout << "*";

}

std::cout << "\n";

// 获取结束时间点

std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();

// 计算差值

//std::chrono::duration<long long,std::nano> dt = end - start; //相同

//std::chrono::nanoseconds dt = end - start; //相同

auto dt = end - start; //相同

std::cout << "总共耗时: " << dt.count() << "纳秒" << "\n";

}

结果:

image-20240626004254286

转换函数

1.duration_cast

duration_cast是chrono库提供的一个模板函数,这个函数不属于duration类,属于chrono命名空间.

通过这个函数可以对duration类对象内部的时钟周期Period,和周期次数的类型Rep进行修改,该函数原型如下:

<code>template <class ToDuration, class Rep, class Period>

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

参数:

ToDuration:为转换目的对象的类型.

std::chrono::hours h = std::chrono::duration_cast<std::chrono::hours>(std::chrono::minutes(60));

Rep和Period都是duration模板参数,已经存在,不需要提供.

Description:

这个函数用于duration对象不能隐式转换的时候,即提供给用户用于强制转换.

duration_cast提供给用户使用,即数据安全交由程序员负责,底层不再负责.

duration支持隐式转换的规则
  1. 如果是对时钟周期进行转换:原时钟周期必须能够整除目的时钟周期(比如:小时到分钟)。

  2. 如果是对时钟周期次数的类型进行转换:低等类型默认可以向高等类型进行转换(比如:int 转 double)

    (1和2点反过来都会损失精度,是不安全的,因此默认不支持.)

  3. 如果时钟周期和时钟周期次数类型都变了,只看第二点(也就是只看时间周期次数类型)。

  4. 以上条件都不满足,那么就需要使用 duration_cast 进行显示转换。

Exam:

  1. 周期: 分钟 -> 小时

int main() {

//分钟 -> 小时

std::chrono::hours h = std::chrono::minutes(60);

return 0;

}

image-20240626005644964

默认不支持小周期向大周期转换.需要使用duration_cast.

正确格式:

<code> std::chrono::hours h= std::chrono::duration_cast<std::chrono::hours>(std::chrono::minutes(60));

  1. 类型 浮点 -> 整型

    报错:

    image-20240626010620202

    正确:牺牲精度完成转换

    image-20240626010743694

  2. 类型+周期

    • 类型不满足,周期大小满足

    <code> std::chrono::duration<double,std::ratio<1,1000>> t1(2.2);

    std::chrono::duration<int,std::ratio<1,100>> t2 = t1;

    image-20240626011437244

    根据规则2,只看类型,类型不满足,因此需要转换.

    • 类型满足,周期大小不满足

      <code>std::chrono::duration<int,std::ratio<1,100>> t3(1);

      std::chrono::duration<double,std::ratio<1,1000>> t2 = t3;

      image-20240626011855165

      没有警告,说明可以隐式转换,即只看类型.

2. time_point_cast

time_point_cast 和 duration_cast类似.也是chrono库提供的一个模板函数,属于chrono命名空间,不属于time_point类.

转换规则也和duration_cast一样.

<code>template <class ToDuration, class Clock, class Duration>

time_point<Clock, ToDuration> time_point_cast(const time_point<Clock, Duration> &t);

Exam:

std::chrono::time_point<std::chrono::system_clock,std::chrono::milliseconds> millis;

std::chrono::time_point<std::chrono::system_clock,std::chrono::seconds> s = millis;

image-20240626013327137

修改:

image-20240626013602699

反之可以支持隐式转换

image-20240626013427080

引用 :

https://subingwen.cn/tags/C-11/

https://zh.cppreference.com/w/cpp

https://zh.cppreference.com/w/cpp/chrono



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。