C++常用的新特性-->day06
时间间隔duration
duration表示一段时间间隔,用来记录时间长度,可以表示几秒、几分钟、几个小时的时间间隔。duration的原型如下
// 定义于头文件 <chrono>
template<
class Rep,
class Period = std::ratio<1>
> class duration;
Rep:这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目。
Period:表示时钟的周期,它的原型如下:
// 定义于头文件 <ratio>
template<
std::intmax_t Num,
std::intmax_t Denom = 1
> class ratio;
ratio类表示每个时钟周期的秒数,其中第一个模板参数Num代表分子,Denom代表分母,该分母值默认为1,因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2>代表一个时钟周期是2秒,ratio<60>代表一分钟,ratio<6060>代表一个小时,ratio<6060*24>代表一天。而ratio<1,1000>代表的是1/1000秒,也就是1毫秒,ratio<1,1000000>代表一微秒,ratio<1,1000000000>代表一纳秒。
duration类的构造函数
// 1. 拷贝构造函数
duration( const duration& ) = default;
// 2. 通过指定时钟周期的类型来构造对象
template< class Rep2 >
constexpr explicit duration( const Rep2& r );
// 3. 通过指定时钟周期类型,和时钟周期长度来构造对象
template< class Rep2, class Period2 >
constexpr duration( const duration<Rep2,Period2>& d );
chrono库中根据duration类封装了不同长度的时钟周期(也可以自定义),基于这个时钟周期再进行周期次数的设置就可以得到总的时间间隔了(时钟周期 * 周期次数 = 总的时间间隔)。
综合案例
#include <chrono>
#include <iostream>
using namespace std;
int main()
{
chrono::hours h(1); // 一小时
chrono::milliseconds ms{ 3 }; // 3 毫秒
chrono::duration<int, ratio<1000>> ks(3); // 3000 秒
// chrono::duration<int, ratio<1000>> d3(3.5); // error
chrono::duration<double> dd(6.6); // 6.6 秒
// 使用小数表示时钟周期的次数
chrono::duration<double, ratio<1, 30>> hz(3.5);
//count()统计的是时间周期
cout << "count:" << h.count() << endl;
cout << "count:" << ms.count() << endl;
cout << "count:" << ks.count() << endl;
cout << "count:" << dd.count() << endl;
cout << "count:" << hz.count() << endl;
cout << "************************************************************" << endl;
chrono::milliseconds mss{ 3 }; // 3 毫秒
chrono::microseconds uss = 2 * mss; // 6000 微秒
// 时间间隔周期为 1/30 秒 --> 一共有3.5个时间周期,每个周期为1/30秒
chrono::duration<double, ratio<1, 30>> hzz(3.5);
//count()统计的是时间周期
cout << "3 ms duration has " << mss.count() << " ticks\n"
<< "6000 us duration has " << uss.count() << " ticks\n"
<< "3.5 hz duration has " << hzz.count() << " ticks\n";
cout << "************************************************************" << endl;
chrono::minutes t1(10);
chrono::seconds t2(60);
chrono::seconds t3 = t1 - t2;
cout << t3.count() << " second" << endl;
cout << "************************************************************" << endl;
//统一时钟周期
//分子的最大公约数是1 分母的最小公倍数是1 ---> 统一为ration<1,1>
chrono::duration<int, ratio<60, 1>> jb(10);
chrono::duration<int, ratio<1,1>> jk(60);
auto jkb = jb - jk;
cout << "count:" << jkb.count() << endl;
cout << "************************************************************" << endl;
//分子的最大公约数是3 分母的最小公倍数是35 ---> 统一为ration<3,35>
chrono::duration<int, ratio<9, 7>> ak(3);
chrono::duration<int, ratio<6, 5>> m4(1);
auto hql = ak - m4;//auto --> chrono::duration<int,ration<3,35>>
cout << "count:" << hql.count() << endl;
}
时间点和时钟
时间点结构体
hrono库中提供了一个表示时间点的类time_point,该类的定义如下:
// 定义于头文件 <chrono>
template<
class Clock,
class Duration = typename Clock::duration
> class time_point;
它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值,通过这个类最终可以得到时间中的某一个时间点。
Clock:此时间点在此时钟上计量
Duration:用于计量从纪元起时间的 std::chrono::duration 类型
时间点time_point构造函数
// 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 );
时钟clocks
system_clock:系统的时钟,系统的时钟可以修改,甚至可以网络对时,因此使用系统时间计算时间差可能不准。
steady_clock:是固定的时钟,相当于秒表。开始计时后,时间只会增长并且不能修改,适合用于记录程序耗时
high_resolution_clock:和时钟类 steady_clock 是等价的(是它的别名)。
在使用chrono提供的时钟类的时候,不需要创建类对象,直接调用类的静态方法就可以得到想要的时间了。
成员类型 | 描述 |
---|---|
rep | 表示时钟周期次数的有符号算术类型 |
period | 表示时钟计次周期的 std::ratio 类型 |
duration | 时间间隔,可以表示负时长 |
time_point | 表示在当前时钟里边记录的时间点 |
system_clock
时钟类system_clock是一个系统范围的实时时钟。system_clock提供了对当前时间点time_point的访问,将得到时间点转换为time_t类型的时间对象,就可以基于这个时间对象获取到当前的时间信息了。
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;
_NODISCARD static time_point now() noexcept
{ // get current time
return time_point(duration(_Xtime_get_ticks()));
}
_NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept
{ // convert to __time64_t
return duration_cast<seconds>(_Time.time_since_epoch()).count();
}
_NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept
{ // convert from __time64_t
return time_point{seconds{_Tm}};
}
};
静态方法
// 返回表示当前时间的时间点。
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;
system_clock案例
#define _CRT_SECURE_NO_WARNINGS
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
int main()
{
// 新纪元1970.1.1时间
system_clock::time_point epoch;
chrono::hours h(10 * 24);
system_clock::time_point epoch1 = epoch + h;
system_clock::time_point epoch2(epoch + h);
//当前的时间
system_clock::time_point nowTime = system_clock::now();
//时间点 -> 时间段(单位秒)
time_t allSec = system_clock::to_time_t(nowTime);
//时间格式化 -> 通过ctime转化为字符串
cout << "当前的时间:" << ctime(&allSec) << endl;
//时间段转化为时间点
system_clock::time_point tp = system_clock::from_time_t(allSec);
}
steady_clock
在C++11中提供的时钟类steady_clock相当于秒表,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。
底层源码
struct steady_clock { // wraps QueryPerformanceCounter
using rep = long long;
using period = nano;
using duration = nanoseconds;
using time_point = chrono::time_point<steady_clock>;
static constexpr bool is_steady = true;
// get current time
_NODISCARD static time_point now() noexcept
{
// doesn't change after system boot
const long long _Freq = _Query_perf_frequency();
const long long _Ctr = _Query_perf_counter();
static_assert(period::num == 1, "This assumes period::num == 1.");
const long long _Whole = (_Ctr / _Freq) * period::den;
const long long _Part = (_Ctr % _Freq) * period::den / _Freq;
return time_point(duration(_Whole + _Part));
}
};
静态方法
static std::chrono::time_point<std::chrono::steady_clock> now() noexcept;
案例
#define _CRT_SECURE_NO_WARNINGS
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
void steadyClockTest()
{
auto p1 = steady_clock::now();
for (int i = 0; i < 100; i++)
{
cout << "*";
}
cout << endl;
auto p2 = steady_clock::now();
auto d1 = p2 - p1;
cout << "count:" << d1.count() << endl;
}
int main()
{
steadyClockTest();
return 0;
}
得到的值是纳秒,去掉9个0换算成秒
high_resolution_clock
同system_clock
转换函数
如果是对时钟周期进行转换:源时钟周期必须能够整除目的时钟周期(比如:小时到分钟)。
如果是对时钟周期次数的类型进行转换:低等类型默认可以向高等类型进行转换(比如:int 转 double)。
如果时钟周期和时钟周期次数类型都变了,根据第二点进行推导(也就是看时间周期次数类型)。
以上条件都不满足,那么就需要使用 duration_cast 进行显示转换。
duration_cast
通过这个函数可以对duration类对象内部的时钟周期Period,和周期次数的类型Rep进行修改
#define _CRT_SECURE_NO_WARNINGS
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
void durationCastTest()
{
//分钟 -> 小时
hours h = duration_cast<hours>(minutes(60));
cout << "60 minutes is " << h.count() << " hours\n";
//小时 -> 分钟
minutes m1 = hours(1);
cout << "1 hour is " << m1.count() << " minutes\n";
//浮点 -> 整形 --> 不建议会损失精度
//seconds ss = duration<double> s(2.5);//err
seconds ss1 = duration_cast<seconds>(duration<double> (2.5));
using mydouble = duration<double>;
seconds ss2 = duration_cast<seconds>(mydouble(2.5));
cout << "ss1.count():" << ss1.count() << endl;
cout << "ss2.count():" << ss2.count() << endl;
//整形 -> 浮点
mydouble dd = seconds(9);
cout << "dd.count():" << dd.count() << endl;
//时钟周期数据类型和时钟周期都变了
duration<int, ratio<1, 100>>t1(100);
duration<double, ratio<1, 100>>t2(12.56);
//duration<int, ratio<1, 100>>t3 = t2;//err double转int有问题
duration<int, ratio<1, 100>>t3 = duration_cast<duration<int, ratio<1, 100>>>(t2);
cout << "t3.count():" << t3.count() << endl;
duration<double, ratio<1, 100>>t4 = t1;// >>>> 时钟周期和时钟周期的数据类型都不相同,看类型转化,由于是int转double是没问题的-->会进行隐式类型转化
cout << "t4.count():" << t4.count() << endl;
}
int main()
{
durationCastTest();
return 0;
}
time_point_cast
函数的作用是对时间点进行转换,因为不同的时间点对象内部的时钟周期Period,和周期次数的类型Rep可能也是不同的
#define _CRT_SECURE_NO_WARNINGS
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
void durationCastTest()
{
//分钟 -> 小时
hours h = duration_cast<hours>(minutes(60));
cout << "60 minutes is " << h.count() << " hours\n";
//小时 -> 分钟
minutes m1 = hours(1);
cout << "1 hour is " << m1.count() << " minutes\n";
//浮点 -> 整形 --> 不建议会损失精度
//seconds ss = duration<double> (2.5);//err
/*
template< class Rep2 >
constexpr explicit duration( const Rep2& r );
*/
seconds ss1 = duration_cast<seconds>(duration<double> (2.5));
using mydouble = duration<double>;
seconds ss2 = duration_cast<seconds>(mydouble(2.5));
duration<double>a(2.5);
seconds ss3 = duration_cast<seconds>(a);
cout << "ss1.count():" << ss1.count() << endl;
cout << "ss2.count():" << ss2.count() << endl;
cout << "ss3.count():" << ss3.count() << endl;
//整形 -> 浮点
mydouble dd = seconds(9);
cout << "dd.count():" << dd.count() << endl;
//时钟周期数据类型和时钟周期都变了
duration<int, ratio<1, 100>>t1(100);
duration<double, ratio<1, 100>>t2(12.56);
//duration<int, ratio<1, 100>>t3 = t2;//err double转int有问题
duration<int, ratio<1, 100>>t3 = duration_cast<duration<int, ratio<1, 100>>>(t2);
cout << "t3.count():" << t3.count() << endl;
duration<double, ratio<1, 100>>t4 = t1;// >>>> 时钟周期和时钟周期的数据类型都不相同,看类型转化,由于是int转double是没问题的-->会进行隐式类型转化
cout << "t4.count():" << t4.count() << endl;
}
//时间段传入
template<typename Duration>
using MyTimePoint = time_point<system_clock, Duration>;
int main()
{
durationCastTest();
cout << "********************************" << endl;
MyTimePoint<seconds> mPoint(seconds(100));
MyTimePoint<milliseconds> millPoint(milliseconds(1000));
//s->ms
MyTimePoint<milliseconds> ms = mPoint;
time_t tm1 = system_clock::to_time_t(ms);//时间点转化
cout << " " << ctime(&tm1);
//ms->s
//MyTimePoint<seconds> mss = millPoint;//err >>>>
MyTimePoint<seconds> mss = time_point_cast<seconds>(millPoint);
time_t tm2 = system_clock::to_time_t(mss);
cout << " " << ctime(&tm2);
return 0;
}
作者: 苏丙榅
链接: https://subingwen.cn/cpp/chrono/#1-2-%E7%B1%BB%E7%9A%84%E4%BD%BF%E7%94%A8