125 <chrono> 库 笔记


作者Lou Xiao, Gemini(2.5-Pro)创建时间2025-04-05 01:00:41更新时间2025-04-05 01:00:41
1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 class high_resolution_clock {
2 public:
3 // ... (类似其他时钟) ...
4 // is_steady 的值不确定,取决于实现
5 };

代码: (用法与其他时钟类似)

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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_clocksystem_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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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 对象进行加减运算,或者两个相同 Clocktime_point 对象相减得到一个 duration

3.2.2 time_since_epoch()

名称: time_point::time_since_epoch()
用途: 获取一个 time_point 相对于其关联时钟纪元的时间段 (duration)。
原型: constexpr duration time_since_epoch() const;
代码:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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 和预定义类型的例子)
注释: 获取到的值是基于 durationPeriod 的,直接比较不同 duration 类型的 count() 值是没有意义的,除非你知道它们的单位。

3.3.4 std::chrono::duration_cast()

名称: std::chrono::duration_cast
用途: 显式地将一个 duration 转换为另一个具有不同 Period(单位)或 Rep(内部类型)的 duration转换是截断式的(向零取整)。
原型:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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 ...
用途: 作为 durationPeriod 模板参数,方便定义标准单位。
原型: (见上方别名定义)
代码: (见预定义 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_clocksteady_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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
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_casttime_point_cast。这能提高代码清晰度并避免意外的截断。
使用字面量 (C++14+): 尽量使用 100ms, 5s 等字面量来创建 duration,代码更简洁易读。记得 using namespace std::chrono_literals;
类型别名: 对于特定应用中常用的 durationtime_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_clocksystem_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> 的能力更加完善。

文章目录