125 <chrono> 库 笔记
1
Gemini(2.5-Pro) Prompts:
2
请生成系统性、结构化的学习笔记,要求涵盖各个知识点。关于:c++的库。
3
**内容要求**如下:
4
1. 先写介绍,再写正文,最后做总结。
5
2. 合理使用emoji;
6
3. 不能遗漏任何知识点;
7
4. 对每个API(函数、变量)给出名称、用途、原型、代码、注释;
8
5. 在专业的角度指明最佳实践、常见错误用法、陷阱等;
9
**格式要求**如下:
10
1. **多级标题**:用 `## 1 一级标题` → `### 1.1 二级标题` → `### 1.1.1 三级标题` 的层级编号;
11
2. **单级条目**:所有 `1.` `2.` `3.` 这样的单级列表,改用带圈数字 `①` `②` `③` 表示;
12
3. **禁止使用**:Markdown 的 `-` `*` 无序列表或 `1.` `2.` 有序列表语法;
13
4. 请自动生成层级编号(如 1.1、1.2),无需我手动输入数字。不要大标题,正文从## 1. 开始;
14
5. 在表格或代码块的前后添加空行;
1 介绍 (Introduction) 📜
<chrono>
是 C++ 标准库中用于处理时间的头文件,自 C++11 起引入。它提供了一套精确且类型安全的方式来表示和操作时间段 (Durations)、时间点 (Time Points) 以及时钟 (Clocks)。相比于 C 风格的 <ctime>
库,<chrono>
提供了更高的精度、更好的类型安全(避免隐式单位转换错误)以及更强的灵活性。
主要目标:
① 提供独立于特定操作系统的时钟抽象。
② 提供表示时间间隔(时间段)的统一方式。
③ 提供表示特定时间点的方式。
④ 实现不同时间单位之间的类型安全转换。
2 核心概念 (Core Concepts) 💡
<chrono>
库主要围绕以下几个核心概念构建:
2.1 时钟 (Clocks) ⏰
时钟代表了时间流逝的参照系,并提供了获取当前时间点的方法。
① 定义:一个时钟包含一个起始时间点(纪元,epoch)和一个指示时间流逝速度(频率,tick rate)的机制。
② 关键信息:
* now()
: 返回表示当前时间的 time_point
。
* is_steady
: 表明时钟是否是单调递增的(不受系统时间调整影响)。
* period
: 时钟的滴答周期(精度)。
2.2 时间点 (Time Points) 📍
时间点表示一个具体的时间瞬间。
① 定义:它由一个时钟和一个相对于该时钟纪元的时间段组成。
② 关键信息:
* time_since_epoch()
: 返回从关联时钟的纪元开始到该时间点所经过的时间段 (duration
)。
2.3 时间段 (Durations) ⏳
时间段表示一个时间间隔。
① 定义:它由一个计数值 (count) 和一个周期 (period) 组成。周期定义了计数值的单位(如秒、毫秒、纳秒)。
② 关键信息:
* count()
: 返回时间段内部的计数值。
* 周期 (Period):通过模板参数 Period
(一个 std::ratio
类型) 指定单位。
2.4 时间单位/周期 (Time Units / Periods) ⚙️
周期使用 std::ratio
类模板表示,它定义了一个编译期的有理数(分子/分母),代表秒的某个分数。
① 定义:std::ratio<Num, Den>
表示一个分数 Num / Den
秒。
② 用途:作为 duration
的模板参数,定义其时间单位。
3 主要 API 详解 (Detailed API Explanation) 🛠️
3.1 时钟 (Clocks)
3.1.1 std::chrono::system_clock
① 名称: std::chrono::system_clock
② 用途: 代表系统的壁钟时间 (wall clock time)。它可能不单调(例如,可以被用户或系统调整)。通常用于与外部世界的时间(日历时间)进行交互。
③ 原型:
1
class system_clock {
2
public:
3
using period = /* implementation-defined ratio */; // 时钟周期 (精度)
4
using rep = /* implementation-defined arithmetic type */; // 内部计数类型
5
using duration = std::chrono::duration<rep, period>; // 关联的时间段类型
6
using time_point = std::chrono::time_point<system_clock>; // 关联的时间点类型
7
static constexpr bool is_steady = false; // 非单调
8
9
static time_point now() noexcept; // 获取当前时间点
10
11
// C 风格时间转换 (C++11)
12
static std::time_t to_time_t(const time_point& t) noexcept;
13
static time_point from_time_t(std::time_t t) noexcept;
14
// C++20 提供了更多日历和时区支持
15
};
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
#include <ctime> // for std::time_t, std::gmtime, std::strftime
4
5
int main() {
6
// 获取当前系统时间点
7
std::chrono::system_clock::time_point now_tp = std::chrono::system_clock::now();
8
9
// 转换为 C 风格的 time_t
10
std::time_t now_c = std::chrono::system_clock::to_time_t(now_tp);
11
12
// 格式化输出 (注意线程安全问题,使用 gmtime_s 或 localtime_s 更安全)
13
char buf[100] = {0};
14
// std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", std::gmtime(&now_c)); // C++ 标准库推荐使用更安全的版本
15
// 为了跨平台和简洁性,这里使用标准函数,但在生产代码中要注意
16
#ifdef _WIN32
17
struct tm timeinfo;
18
gmtime_s(&timeinfo, &now_c);
19
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", &timeinfo);
20
#else
21
std::tm* timeinfo_ptr = std::gmtime(&now_c); // 注意:非线程安全
22
if (timeinfo_ptr) {
23
std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S UTC", timeinfo_ptr);
24
}
25
#endif
26
27
std::cout << "Current system time: " << buf << std::endl;
28
29
// 从 time_t 转换回 time_point
30
std::time_t t = 1678886400; // Example timestamp (approx. 2023-03-15 12:00:00 UTC)
31
std::chrono::system_clock::time_point tp_from_t = std::chrono::system_clock::from_time_t(t);
32
33
// (可选) 计算时间差,展示 time_point 的基本操作
34
auto duration_since_epoch = tp_from_t.time_since_epoch();
35
long long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration_since_epoch).count();
36
std::cout << "Time_t " << t << " corresponds to " << milliseconds << "ms since epoch." << std::endl;
37
38
return 0;
39
}
⑤ 注释: system_clock
用于获取当前日历时间,并可以方便地与 C 风格的 time_t
进行转换。但由于它可能被调整,不适合用于精确测量时间间隔。
3.1.2 std::chrono::steady_clock
① 名称: std::chrono::steady_clock
② 用途: 代表一个单调递增的时钟。其时间点永远不会减少,即使系统物理时间被调整。最适合用于测量时间间隔(例如,代码执行时间)。
③ 原型:
1
class steady_clock {
2
public:
3
using period = /* implementation-defined ratio */;
4
using rep = /* implementation-defined arithmetic type */;
5
using duration = std::chrono::duration<rep, period>;
6
using time_point = std::chrono::time_point<steady_clock>;
7
static constexpr bool is_steady = true; // 单调
8
9
static time_point now() noexcept; // 获取当前时间点
10
};
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
#include <thread> // for std::this_thread::sleep_for
4
5
int main() {
6
// 记录开始时间
7
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
8
9
// 执行一些操作 (例如,暂停)
10
std::this_thread::sleep_for(std::chrono::milliseconds(150)); // 暂停 150 毫秒
11
12
// 记录结束时间
13
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
14
15
// 计算时间差
16
std::chrono::steady_clock::duration elapsed = end - start;
17
18
// 将时间差转换为更易读的单位 (例如毫秒)
19
long long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
20
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(elapsed).count(); // 转为 double 秒
21
22
std::cout << "Elapsed time: " << milliseconds << " ms" << std::endl;
23
std::cout << "Elapsed time: " << seconds << " s" << std::endl;
24
25
return 0;
26
}
⑤ 注释: steady_clock
是测量耗时的首选时钟,因为它保证了单调性,不受系统时间跳变的影响。
3.1.3 std::chrono::high_resolution_clock
① 名称: std::chrono::high_resolution_clock
② 用途: 旨在提供具有可用最短滴答周期(最高分辨率)的时钟。
③ 原型:
1
class high_resolution_clock {
2
public:
3
// ... (类似其他时钟) ...
4
// is_steady 的值不确定,取决于实现
5
};
④ 代码: (用法与其他时钟类似)
1
#include <iostream>
2
#include <chrono>
3
4
int main() {
5
auto start = std::chrono::high_resolution_clock::now();
6
// ... do work ...
7
auto end = std::chrono::high_resolution_clock::now();
8
9
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
10
std::cout << "High resolution elapsed time: " << duration << " ns" << std::endl;
11
12
// 检查它是否是 steady_clock 的别名 (常见情况)
13
if constexpr (std::is_same_v<std::chrono::high_resolution_clock, std::chrono::steady_clock>) {
14
std::cout << "high_resolution_clock is an alias for steady_clock on this system." << std::endl;
15
} else {
16
std::cout << "high_resolution_clock is NOT an alias for steady_clock on this system." << std::endl;
17
std::cout << "high_resolution_clock::is_steady = " << std::boolalpha << std::chrono::high_resolution_clock::is_steady << std::endl;
18
}
19
20
21
return 0;
22
}
⑤ 注释: high_resolution_clock
在很多标准库实现中通常是 steady_clock
或 system_clock
的别名。因此,它的 is_steady
特性不确定。最佳实践是直接使用 steady_clock
进行耗时测量,除非你明确需要 system_clock
的特性并且其分辨率足够。
3.2 时间点 (Time Points)
3.2.1 std::chrono::time_point
类模板
① 名称: std::chrono::time_point<Clock, Duration>
② 用途: 表示一个时间点,关联到一个特定的时钟 (Clock
) 和一个表示自该时钟纪元以来的时间段 (Duration
)。
③ 原型:
1
template <class Clock, class Duration = typename Clock::duration>
2
class time_point {
3
public:
4
using clock = Clock;
5
using duration = Duration;
6
using rep = typename duration::rep;
7
using period = typename duration::period;
8
9
// 构造函数 (通常通过 Clock::now() 或时间点运算获得)
10
constexpr time_point(); // 纪元时间点
11
explicit constexpr time_point(const duration& d); // 从时间段构造 (相对于纪元)
12
13
// 核心方法
14
constexpr duration time_since_epoch() const; // 获取自纪元以来的时间段
15
16
// 算术运算 (+=, -=, +, -)
17
time_point& operator+=(const duration& d);
18
time_point& operator-=(const duration& d);
19
// ... 其他操作符 ...
20
};
21
22
// 非成员函数 +/- (time_point + duration, duration + time_point, time_point - duration)
23
// 非成员函数 - (time_point - time_point -> duration)
④ 代码: (参见 system_clock::now()
和 steady_clock::now()
的例子,它们返回 time_point
对象)
⑤ 注释: time_point
对象本身不能直接相加,但可以与 duration
对象进行加减运算,或者两个相同 Clock
的 time_point
对象相减得到一个 duration
。
3.2.2 time_since_epoch()
① 名称: time_point::time_since_epoch()
② 用途: 获取一个 time_point
相对于其关联时钟纪元的时间段 (duration
)。
③ 原型: constexpr duration time_since_epoch() const;
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
4
int main() {
5
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
6
std::chrono::system_clock::duration d = tp.time_since_epoch();
7
8
// 将时间段转换为毫秒
9
long long ms = std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
10
std::cout << "Milliseconds since system_clock epoch: " << ms << std::endl;
11
12
return 0;
13
}
⑤ 注释: 这是获取 time_point
内部表示的主要方式,通常用于后续转换或计算。
3.2.3 std::chrono::time_point_cast()
① 名称: std::chrono::time_point_cast
② 用途: 将一个 time_point
转换为具有不同 Duration
类型(即不同精度)的 time_point
。它只改变时间段的精度,不改变关联的时钟。
③ 原型:
1
template <class ToDuration, class Clock, class FromDuration>
2
constexpr std::chrono::time_point<Clock, ToDuration>
3
time_point_cast(const std::chrono::time_point<Clock, FromDuration>& tp);
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
4
int main() {
5
// 获取当前时间点 (精度可能很高)
6
std::chrono::system_clock::time_point now_precise = std::chrono::system_clock::now();
7
8
// 转换为秒精度的时间点 (会发生截断)
9
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> now_seconds =
10
std::chrono::time_point_cast<std::chrono::seconds>(now_precise);
11
12
// 转换为毫秒精度的时间点
13
auto now_milliseconds =
14
std::chrono::time_point_cast<std::chrono::milliseconds>(now_precise);
15
16
17
// 输出自纪元以来的秒数和毫秒数
18
std::cout << "Seconds since epoch: "
19
<< now_seconds.time_since_epoch().count() << std::endl;
20
std::cout << "Milliseconds since epoch: "
21
<< now_milliseconds.time_since_epoch().count() << std::endl;
22
23
return 0;
24
}
⑤ 注释: time_point_cast
对于需要在不同精度级别上处理时间点的场景非常有用。转换是截断式的(向零取整)。
3.3 时间段 (Durations)
3.3.1 std::chrono::duration
类模板
① 名称: std::chrono::duration<Rep, Period>
② 用途: 表示一个时间间隔,由一个计数值 (Rep
) 和一个表示单位的周期 (Period
) 组成。
③ 原型:
1
template <class Rep, class Period = std::ratio<1>> // Period 默认为秒
2
class duration {
3
public:
4
using rep = Rep;
5
using period = Period; // 是一个 std::ratio 类型
6
7
// 构造函数
8
constexpr duration() = default;
9
template <class Rep2>
10
explicit constexpr duration(const Rep2& r); // 从计数值构造
11
12
// 核心方法
13
constexpr rep count() const; // 获取计数值
14
15
// 算术运算 (+, -, *, /, %, +=, -=, *=, /=, %=)
16
// 比较运算 (==, !=, <, <=, >, >=)
17
// ... 其他操作 ...
18
19
// 特殊值
20
static constexpr duration zero();
21
static constexpr duration min();
22
static constexpr duration max();
23
};
④ 代码: (参见 steady_clock
例子中的 elapsed
计算和转换)
⑤ 注释: duration
是 <chrono>
库的核心。不同 duration
类型之间的转换(如果安全)可以隐式进行(例如,秒可以隐式转为毫秒),但从高精度到低精度的转换(可能丢失信息)需要显式使用 duration_cast
。
3.3.2 预定义 duration
类型
<chrono>
提供了一些常用的预定义 duration
类型别名:
① 名称:
* std::chrono::nanoseconds
(duration<long long, std::nano>
)
* std::chrono::microseconds
(duration<long long, std::micro>
)
* std::chrono::milliseconds
(duration<long long, std::milli>
)
* std::chrono::seconds
(duration<long long>
)
* std::chrono::minutes
(duration<int, std::ratio<60>>
)
* std::chrono::hours
(duration<int, std::ratio<3600>>
)
* (C++20) std::chrono::days
, weeks
, months
, years
(这些与日历相关)
② 用途: 方便地表示特定单位的时间段。
③ 原型: (见上方别名定义)
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
4
int main() {
5
std::chrono::seconds s(5); // 5 秒
6
std::chrono::milliseconds ms = s; // 隐式转换: 5000 毫秒
7
std::chrono::microseconds us = ms; // 隐式转换: 5000000 微秒
8
9
std::cout << s.count() << " seconds" << std::endl;
10
std::cout << ms.count() << " milliseconds" << std::endl;
11
std::cout << us.count() << " microseconds" << std::endl;
12
13
// 从毫秒转秒需要显式转换 (可能截断)
14
std::chrono::milliseconds precise_ms(5500);
15
std::chrono::seconds truncated_s = std::chrono::duration_cast<std::chrono::seconds>(precise_ms);
16
std::cout << precise_ms.count() << "ms is truncated to " << truncated_s.count() << "s" << std::endl;
17
18
// 使用浮点数表示的 duration
19
std::chrono::duration<double, std::ratio<1>> seconds_fp = precise_ms; // 隐式转换到 double 秒
20
std::cout << precise_ms.count() << "ms is " << seconds_fp.count() << " seconds (double)" << std::endl;
21
22
23
return 0;
24
}
⑤ 注释: 使用这些预定义类型可以使代码更清晰易懂。注意隐式和显式转换规则。
3.3.3 count()
① 名称: duration::count()
② 用途: 返回 duration
对象内部存储的计数值(tick count)。
③ 原型: constexpr rep count() const;
④ 代码: (见上面 duration
和预定义类型的例子)
⑤ 注释: 获取到的值是基于 duration
的 Period
的,直接比较不同 duration
类型的 count()
值是没有意义的,除非你知道它们的单位。
3.3.4 std::chrono::duration_cast()
① 名称: std::chrono::duration_cast
② 用途: 显式地将一个 duration
转换为另一个具有不同 Period
(单位)或 Rep
(内部类型)的 duration
。转换是截断式的(向零取整)。
③ 原型:
1
template <class ToDuration, class Rep, class Period>
2
constexpr ToDuration duration_cast(const std::chrono::duration<Rep, Period>& d);
④ 代码: (见 steady_clock
和预定义 duration
类型的例子)
⑤ 注释: 当从高精度向低精度转换,或者在不允许隐式转换的类型之间转换时,必须使用 duration_cast
。这是类型安全的重要体现。
3.3.5 字面量后缀 (Literals) (C++14 及以后)
C++14 引入了用户定义的字面量,使得创建 duration
对象更加简洁。需要 using namespace std::chrono_literals;
① 名称: h
, min
, s
, ms
, us
, ns
② 用途: 直接在数字后面添加后缀来创建对应的 duration
对象。
③ 原型: (用户定义字面量)
1
// 示例
2
constexpr std::chrono::hours operator""h(unsigned long long h);
3
constexpr std::chrono::minutes operator""min(unsigned long long m);
4
constexpr std::chrono::seconds operator""s(unsigned long long s);
5
// ... 等等,也包括浮点版本
6
constexpr std::chrono::duration<long double, std::milli> operator""ms(long double ms);
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
#include <thread>
4
5
// 必须包含或 using namespace
6
using namespace std::chrono_literals;
7
8
int main() {
9
auto duration1 = 2h; // 2 小时
10
auto duration2 = 30min; // 30 分钟
11
auto duration3 = 5s; // 5 秒
12
auto duration4 = 150ms; // 150 毫秒
13
auto duration5 = 500us; // 500 微秒
14
auto duration6 = 1000ns; // 1000 纳秒
15
auto duration7 = 1.5s; // 1.5 秒 (返回 duration<double, ratio<1>>)
16
17
std::cout << "Duration 1 (hours): " << duration1.count() << std::endl;
18
std::cout << "Duration 4 (milliseconds): " << duration4.count() << std::endl;
19
std::cout << "Duration 7 (seconds, double): " << duration7.count() << std::endl;
20
21
std::this_thread::sleep_for(100ms); // 使用字面量更方便
22
23
return 0;
24
}
⑤ 注释: 字面量极大提高了代码的可读性和简洁性,是现代 C++ 中处理 duration
的推荐方式。
3.4 时间单位/周期 (Periods)
3.4.1 std::ratio
类模板
① 名称: std::ratio<Num, Den>
② 用途: 在编译期表示一个有理数 Num / Den
。主要用于定义 duration
的周期(单位相对于秒)。
③ 原型:
1
template <std::intmax_t Num, std::intmax_t Den = 1>
2
struct ratio {
3
static constexpr std::intmax_t num = /* 计算后的分子 */;
4
static constexpr std::intmax_t den = /* 计算后的分母 */;
5
using type = ratio<num, den>;
6
// C++11 中 num, den 是静态成员常量
7
// C++17 中 num, den 也是静态数据成员
8
};
9
// 相关类型操作: ratio_add, ratio_subtract, ratio_multiply, ratio_divide
10
// 相关比较: ratio_equal, ratio_not_equal, ratio_less, etc.
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
#include <ratio> // ratio 头文件
4
5
int main() {
6
// 定义一个表示 1/1000 秒的周期 (毫秒)
7
using milliseconds_period = std::ratio<1, 1000>;
8
std::cout << "Milliseconds period: " << milliseconds_period::num << "/"
9
<< milliseconds_period::den << " seconds" << std::endl;
10
11
// 定义一个表示 60/1 秒的周期 (分钟)
12
using minutes_period = std::ratio<60, 1>;
13
std::cout << "Minutes period: " << minutes_period::num << "/"
14
<< minutes_period::den << " seconds" << std::endl;
15
16
// 定义一个自定义周期,例如 1/30 秒 (如游戏帧率)
17
using frame_period = std::ratio<1, 30>;
18
std::chrono::duration<double, frame_period> frame_duration(1.0); // 1 帧的时间
19
std::cout << "1 frame duration = "
20
<< std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(frame_duration).count()
21
<< " ms" << std::endl;
22
23
return 0;
24
}
⑤ 注释: ratio
是编译期的工具,确保了单位转换的数学基础。我们通常不直接操作 ratio
对象,而是通过预定义的别名或作为模板参数使用它。
3.4.2 预定义 ratio
类型
标准库提供了一些常用的 ratio
别名:
① 名称:
* std::nano
(ratio<1, 1000000000>
)
* std::micro
(ratio<1, 1000000>
)
* std::milli
(ratio<1, 1000>
)
* std::centi
(ratio<1, 100>
)
* std::deci
(ratio<1, 10>
)
* std::ratio<1>
(秒,默认)
* std::deca
(ratio<10, 1>
)
* std::hecto
(ratio<100, 1>
)
* std::kilo
(ratio<1000, 1>
)
* std::mega
(ratio<1000000, 1>
)
* std::giga
(ratio<1000000000, 1>
)
* std::tera
, peta
, exa
...
② 用途: 作为 duration
的 Period
模板参数,方便定义标准单位。
③ 原型: (见上方别名定义)
④ 代码: (见预定义 duration
类型的例子,它们内部使用了这些 ratio
别名)
⑤ 注释: 这些预定义 ratio
覆盖了绝大多数常见的时间单位。
3.5 时钟与时间点/时间段交互 (Clock/Time Point/Duration Interaction)
3.5.1 Clock::now()
① 名称: Clock::now()
(静态成员函数)
② 用途: 获取与 Clock
相关联的当前时间点 (time_point
)。
③ 原型: static time_point now() noexcept;
(对于 system_clock
, steady_clock
, high_resolution_clock
)
④ 代码: (见 system_clock
和 steady_clock
的例子)
⑤ 注释: 这是获取当前时间的主要入口点。
3.5.2 时间点算术运算
① 名称: operator+
, operator-
② 用途:
* time_point + duration
-> time_point
(时间点向前推移)
* duration + time_point
-> time_point
(同上)
* time_point - duration
-> time_point
(时间点向后回溯)
* time_point - time_point
-> duration
(计算两个时间点之间的时间段)
③ 原型: (作为非成员函数或成员函数重载)
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
4
using namespace std::chrono_literals;
5
6
int main() {
7
auto now = std::chrono::system_clock::now();
8
auto future_time = now + 1h + 30min; // 当前时间 1.5 小时后
9
auto past_time = now - 5s; // 当前时间 5 秒前
10
11
auto duration_between = future_time - past_time; // 计算时间差
12
13
std::cout << "Time difference: "
14
<< std::chrono::duration_cast<std::chrono::seconds>(duration_between).count()
15
<< " seconds" << std::endl;
16
17
return 0;
18
}
⑤ 注释: 时间点运算非常直观,但必须确保参与运算的 time_point
具有相同的 Clock
类型。duration
可以是任意类型,库会自动处理单位转换。
3.5.3 时间段算术运算
① 名称: operator+
, operator-
, operator*
, operator/
, operator%
② 用途: 对 duration
对象进行加、减、乘(与数值)、除(与数值或另一个 duration)、取模运算。
③ 原型: (作为非成员函数或成员函数重载)
④ 代码:
1
#include <iostream>
2
#include <chrono>
3
4
using namespace std::chrono_literals;
5
6
int main() {
7
auto d1 = 10s;
8
auto d2 = 500ms;
9
10
auto sum = d1 + d2; // 10s + 500ms = 10500ms
11
auto diff = d1 - d2; // 10s - 500ms = 9500ms
12
auto product = d1 * 3; // 10s * 3 = 30s
13
auto quotient = d1 / 2; // 10s / 2 = 5s
14
auto ratio = d1 / d2; // 10000ms / 500ms = 20 (返回 Rep 类型)
15
auto remainder = d1 % std::chrono::seconds(3); // 10s % 3s = 1s
16
17
std::cout << "Sum: " << sum.count() << "ms" << std::endl; // count() 返回的是结果 duration 的 Rep 值
18
std::cout << "Diff: " << std::chrono::duration_cast<std::chrono::milliseconds>(diff).count() << "ms" << std::endl;
19
std::cout << "Product: " << product.count() << "s" << std::endl;
20
std::cout << "Quotient: " << quotient.count() << "s" << std::endl;
21
std::cout << "Ratio (d1/d2): " << ratio << std::endl;
22
std::cout << "Remainder: " << remainder.count() << "s" << std::endl;
23
24
return 0;
25
}
⑤ 注释: duration
之间的运算会自动处理单位转换,结果的 duration
类型通常是足以精确表示结果的类型(例如,秒加毫秒,结果可能是毫秒或更精细的单位)。duration
除以 duration
返回一个数值(标量)。
3.6 与 C 风格时间库的转换 (Conversion with C-style Time Library)
3.6.1 system_clock::to_time_t()
① 名称: std::chrono::system_clock::to_time_t
(静态成员函数)
② 用途: 将 system_clock::time_point
转换为 C 风格的 std::time_t
。
③ 原型: static std::time_t to_time_t(const time_point& t) noexcept;
④ 代码: (见 system_clock
的例子)
⑤ 注释: 转换过程中可能会丢失精度,因为 time_t
通常只有秒级精度。
3.6.2 system_clock::from_time_t()
① 名称: std::chrono::system_clock::from_time_t
(静态成员函数)
② 用途: 将 C 风格的 std::time_t
转换为 system_clock::time_point
。
③ 原型: static time_point from_time_t(std::time_t t) noexcept;
④ 代码: (见 system_clock
的例子)
⑤ 注释: 这是与旧代码或需要 time_t
的 API 交互的桥梁。
4 最佳实践与常见陷阱 (Best Practices & Common Pitfalls) ✅❌
4.1 最佳实践 (Best Practices)
① 测量时间间隔: 始终使用 std::chrono::steady_clock
。因为它保证单调递增,不受系统时间调整影响,最适合测量代码执行时间、超时等。
② 获取当前壁钟时间: 使用 std::chrono::system_clock
。当需要与日历时间、文件时间戳或跨进程/系统的时间进行交互时使用。
③ 显式转换: 在可能丢失精度或改变单位含义时,总是使用 duration_cast
和 time_point_cast
。这能提高代码清晰度并避免意外的截断。
④ 使用字面量 (C++14+): 尽量使用 100ms
, 5s
等字面量来创建 duration
,代码更简洁易读。记得 using namespace std::chrono_literals;
。
⑤ 类型别名: 对于特定应用中常用的 duration
或 time_point
类型,可以创建类型别名以提高可读性,例如 using PreciseTimestamp = std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds>;
。
⑥ 优先使用 <chrono>
: 尽量避免使用 C 风格的 <ctime>
函数,除非需要与旧 API 兼容。<chrono>
提供类型安全和更高的精度。
⑦ 注意浮点数 duration
: duration
可以使用浮点数作为 Rep
类型 (std::chrono::duration<double, std::milli>
)。这在需要表示非整数单位时很有用,但要注意浮点数精度问题。
⑧ 了解 C++20 扩展: 如果使用 C++20 或更高版本,可以利用 <chrono>
对日历、时区(std::chrono::zoned_time
)和更多时间单位(天、周、月、年)的支持,处理日期和时间会更方便。
4.2 常见错误与陷阱 (Common Errors & Pitfalls)
① 混淆时钟: 错误地使用 system_clock
测量时间间隔。如果系统时间在测量期间被调整,结果将是错误的。比较或计算不同时钟的 time_point
是未定义行为(编译通常会报错)。
② 隐式截断: 依赖从高精度 duration
到低精度 duration
的隐式转换(如果允许的话,例如通过构造函数),这可能导致非预期的精度损失。例如 std::chrono::seconds sec = std::chrono::milliseconds(1500);
可能会截断为 1 秒(取决于构造函数是否 explicit
以及上下文)。总是推荐 duration_cast
。
③ count()
的误用: 直接比较不同 duration
类型的 .count()
值。例如,1s.count()
(值为 1) 和 1000ms.count()
(值为 1000) 比较是没有意义的。必须先将它们转换为相同的 duration
类型再比较。
④ 整数溢出: 当使用非常长的时间段和非常高的精度时,duration
的内部计数值 (Rep
) 可能会溢出。特别是当进行乘法运算时。选择合适的 Rep
类型(如 long long
或更大的自定义类型)很重要。
⑤ high_resolution_clock
的误解: 认为 high_resolution_clock
总是具有最高的物理分辨率并且是单调的。实际上它常常是 steady_clock
或 system_clock
的别名,其特性需要检查。直接用 steady_clock
更可靠。
⑥ system_clock
的非单调性: 忘记 system_clock
可以向后跳跃,导致基于它的时间差计算出现负值或不正确的值。
⑦ 与 C 库转换的精度损失: to_time_t
会将 time_point
的精度截断到秒,丢失亚秒级信息。
5 总结 (Conclusion) ✨
C++ <chrono>
库提供了一个强大、类型安全且灵活的时间处理框架。通过理解其核心概念——时钟、时间点和时间段——以及掌握关键 API(如 now()
, duration_cast
, time_point_cast
)和最佳实践,开发者可以编写出健壮、精确且易于维护的时间相关代码。相比于旧的 C 风格时间库,<chrono>
在现代 C++ 开发中是处理时间的首选工具。随着 C++20 对日历和时区的进一步支持,<chrono>
的能力更加完善。