目录标题
- 1. 引言
-
- 1.1. std::chrono库的主要功能
- 2. std::chrono库的子类介绍与应用
-
- 2.1. std::chrono::system_clock的用法和示例
-
- 获取当前时间
- 从time_point获取具体时间
- 进行时间运算
- 2.2. std::chrono::steady_clock的用法和示例
-
- 获取当前时间
- 计算经过的时间
- 转换时间单位
- 2.3. std::chrono::high_resolution_clock的用法和示例
-
- 获取当前时间
- 计算经过的时间
- 转换时间单位
- 3. 获取时间戳 (Obtaining Timestamps)
-
- 3.1. 使用std::chrono::system_clock::now获取当前时间戳
-
- 获取当前时间点的详细日期和时间
- 获取时间戳的应用
- 3.2. 时间戳的转换和应用
-
- 时间戳转换为具体日期和时间
- 时间单位的转换
- 时间戳在实际问题中的应用示例
- 3.3. 时间戳在实际问题中的应用示例
-
- 性能测量
- 日志记录
- 时间戳计算
- 4. 计时器的实现
-
- 4.1 使用`std::chrono`库实现基本计时器
-
- 基本计时器实现
- 4.2 高级计时器功能与实现(例如:暂停、重置)
-
- 计时器的暂停与恢复
- 5. 使用std::chrono作为通用的时间参数
-
- 5.1 std::chrono::duration的应用
-
- 5.1.1 创建duration对象
- 5.1.2 duration对象的操作
- 5.2 时间单位转换:如std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds等
-
- 5.2.1 时间单位的转换
- 5.2.2 std::chrono中的时间单位
- 6. 深入探讨std::chrono::system_clock::time_point
-
- 6.1. time_point的定义和主要特性
-
- time_point的使用示例
- 6.2. time_point的常见操作与示例
-
- 加减运算
- 比较操作
- 6.3. time_point在实际问题中的应用示例
-
- 事件调度
- 网络通信的超时控制
- 7. 深入探讨std::chrono::duration
-
- 7.1 duration的定义和主要特性
- 7.2 duration的常见操作与示例
-
- 7.2.1 创建duration
- 7.2.2 执行算术运算
- 7.2.3 比较duration
- 7.2.4 转换为不同的单位
- 7.3 duration在实际问题中的应用示例
-
- 7.3.1 计算代码执行时间
- 7.3.2 实现延迟
- 8. 深入探讨std::chrono::microseconds以及其他常用时间单位
-
- 8.1. `microseconds`及其他时间单位的定义和特性
- 8.2. `microseconds`及其他时间单位的常见操作与示例
- 8.3. `microseconds`及其他时间单位在实际问题中的应用示例
- 第九章: std::chrono在泛型编程和元模板中的应用
-
- 9.1 std::chrono在泛型编程中的使用
- 9.2 std::chrono在元模板编程中的使用
- 9.3 泛型编程和元模板编程中std::chrono的高级应用示例
1. 引言
1.1. std::chrono库的主要功能
std::chrono
是C++标准库中的一个组件,用于表示和处理时间。其功能就像是心理学中的感知系统,它可以为我们捕捉、量化并操作抽象的时间概念。这就如同我们的大脑可以理解和感知周围环境的时间流逝一样,这种感知和理解能力是人类进行日常活动所必需的。
如同马斯洛的需求层次理论中,生理需求位于最底层,时间感知就是计算机程序在处理各种任务时的基础需求,如果没有这个能力,计算机程序将无法进行任何有效的操作。
std::chrono
库主要包含以下功能:
- 时间点:表示特定的时间点,比如当前的时间。这如同人们通过记忆可以回忆起特定的时刻。
- 时间段:表示时间的长度,比如1秒,1分钟,1小时等。这如同人们能够感知时间的流逝,理解”早”,”晚”等概念。
- 时钟:用于获取当前的时间点,有三种类型的时钟:system_clock,steady_clock和high_resolution_clock。这如同人们通过看表来知道现在的具体时间。
为了更好的理解这些功能,让我们看一下下面的表格:
类名 | 描述 | 对应的心理学概念 |
---|---|---|
std::chrono::system_clock | 系统的实际时间,可能会受到系统时间调整的影响 | 外部环境对人的影响 |
std::chrono::steady_clock | 稳定的时钟,时间从不调整 | 稳定、可靠的心理状态 |
std::chrono::high_resolution_clock | 提供最小的可表示的时间间隔 | 细微的心理变化 |
std::chrono::time_point | 表示特定的时间点 | 特定的记忆 |
std::chrono::duration | 表示时间的长度 | 时间的感知 |
接下来,我们将详细探讨std::chrono
库的各种子类如何使用,以及如何在实际编程中应用这些知识。
2. std::chrono库的子类介绍与应用
2.1. std::chrono::system_clock的用法和示例
我们的生活在时间的流逝中不断推进,这个自然的过程在C++中被封装在了std::chrono::system_clock
这个类中。我们如同航海者使用指南针导航一样,可以借助system_clock
在编程的海洋中导航。
std::chrono::system_clock
是一个代表系统广泛使用的实时钟的类。它表示当前的墙上时间,从中可以获得当前时间,也可以在时间点上执行算术运算。
获取当前时间
你可以想象std::chrono::system_clock
就像是一个不断向前的时间车轮。要获取当前时间,你只需要使用now()
成员函数。看下面的代码:
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
这就像是在你的时间旅行中按下暂停按钮,捕捉到了一个时间点。这个点可以被保存,可以用来与其他时间点做比较,或者用作时间运算。
从time_point获取具体时间
system_clock::time_point
表示一个时间点。但是我们通常需要更人性化的表达方式,比如年、月、日、小时、分钟和秒。这时候,我们可以像打开一个礼物盒一样,打开这个time_point
,取出我们需要的信息。请看下面的代码:
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm* ptm = std::localtime(&tt);
std::cout << "Current time is: " << std::put_time(ptm,"%c") << std::endl;
这段代码就像是在一个连续的时间轴上拨动时间,找到那个特殊的瞬间,并用人们熟悉的方式表达出来。
进行时间运算
有时候,我们会需要对时间进行一些计算,比如计算从现在开始的某个时间点是何时。这时候,我们就像是在时间的河流中划动桨板,按照我们的意愿改变时间的前进。请看下面的代码:
std::chrono::system_clock::time_point in_an_hour = std::chrono::system_clock::now() + std::chrono::hours(1);
这就像我们在时间的河流中向前推进了一小时。
功能 | 代码示例 |
---|---|
获取当前时间 | std::chrono::system_clock::now(); |
从time_point 获取具体时间 |
std::chrono::system_clock::to_time_t(now); std::localtime(&tt); std::put_time(ptm,"%c"); |
进行时间运算 | std::chrono::system_clock::now() + std::chrono::hours(1); |
通过上述的学习和比喻,你可能已经对std::chrono::system_clock
有了一些了解。接下来,我们将通过实际的示例,来深入探讨和使用这个强大的类。
2.2. std::chrono::steady_clock的用法和示例
想象一下,你正在烹饪一道需要精确时间的菜肴,你可能需要一个计时器来确保你的食物得到正确的烹饪时间。在这种情况下,你不希望因为 daylight saving time(夏令时)或者系统时钟调整等导致你的时间计算出错。在C++中,这就是std::chrono::steady_clock
的用途。
std::chrono::steady_clock
是一个表示物理时间流逝的时钟,不受任何外部因素(如用户修改系统时间,夏令时等)的影响。就像是你的厨房里的计时器,它按照一致的速度前进,不会突然快了或慢了。
获取当前时间
和std::chrono::system_clock
一样,你可以通过调用now()
函数来获取当前的std::chrono::steady_clock::time_point
。看下面的代码:
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
这就像你在开始烹饪时按下计时器的开始按钮。
计算经过的时间
在你的菜肴烹饪完成后,你可能会想知道实际的烹饪时间。同样,在C++中,你可以通过减去开始时间来得到一个std::chrono::steady_clock::duration
,这个duration
表示了一个时间段。看下面的代码:
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::steady_clock::duration elapsed = end - start;
这就像你在结束烹饪时查看计时器,看你的食物烹饪了多长时间。
转换时间单位
std::chrono::steady_clock::duration
给出的默认时间单位可能并不是你想要的。比如,默认可能给你的是微秒级别的时间,但你可能想要以秒为单位的时间。这时候,你就需要转换时间单位。看下面的代码:
long long elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
这就像你将计时器的显示从“分钟:秒”转换为“总秒数”。
功能 | 代码示例 |
---|---|
获取当前时间 | std::chrono::steady_clock::now(); |
计算经过的时间 | std::chrono::steady_clock::now() - start; |
转换时间单位 | std::chrono::duration_cast<std::chrono::seconds>(elapsed).count(); |
通过对std::chrono::steady_clock
的学习,你可以像烹饪大师一样掌握时间,在编程的厨房里烹饪出美味的代码。
2.3. std::chrono::high_resolution_clock的用法和示例
考虑到现实生活中,我们有时候需要对时间进行极度精确的测量,比如科学实验或者高精度事件的时间戳,std::chrono::high_resolution_clock
就像是我们手中的精密计时器,提供了尽可能高的时间分辨率。
std::chrono::high_resolution_clock
是一个特殊的时钟,它提供了最高的可用时间分辨率。它通常是std::chrono::system_clock
或std::chrono::steady_clock
中的一个类型别名,具体取决于具体平台和库实现。
获取当前时间
使用std::chrono::high_resolution_clock
获取当前时间就像我们按下精密计时器的按钮,记录下现在的时刻。如下所示:
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
计算经过的时间
我们可以通过比较两个时间点来计算经过的时间,就像精密计时器可以为我们提供精确到纳秒级别的时间间隔。请参考以下代码:
std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::duration elapsed = end - start;
转换时间单位
和其他时钟一样,我们可能需要将std::chrono::high_resolution_clock::duration
的时间单位进行转换,使其满足我们的需求。例如,我们可以将时间间隔转换为微秒,如下所示:
long long elapsed_microseconds = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
这就像你的精密计时器可以以各种不同的时间单位来显示测量结果。
功能 | 代码示例 |
---|---|
获取当前时间 | std::chrono::high_resolution_clock::now(); |
计算经过的时间 | std::chrono::high_resolution_clock::now() - start; |
转换时间单位 | std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); |
通过理解和应用std::chrono::high_resolution_clock
,我们可以像使用精密计时器一样,在编程世界里精确地测量和控制时间。
3. 获取时间戳 (Obtaining Timestamps)
3.1. 使用std::chrono::system_clock::now获取当前时间戳
在我们的日常生活中,我们依赖时间去安排我们的日程,设置闹钟,甚至计算烹饪的时间。在编程的世界里,时间同样重要,而std::chrono::system_clock::now
就是我们获取当前时间戳的工具。
auto now = std::chrono::system_clock::now();
这就像你打开手机看看现在几点一样简单。从心理学的角度看,人类对时间的感知并不准确,我们的大脑经常会过度估计或低估实际的时间流逝。但是,计算机是完全精确的,它们对时间的把握可以精确到微秒级。
获取当前时间点的详细日期和时间
有时候,我们需要知道更多的信息,比如当前的具体日期和时间。为此,我们可以使用C++的time_point转换为time_t,然后再使用标准库中的localtime
函数来获取详细信息。
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm* now_tm = std::localtime(&t);
这就像你从简单地知道现在是下午转变为知道现在是下午3点25分。在编程中,这个功能非常有用,比如在记录事件或者在debug中打印出具体的时间点。
获取时间戳的应用
获取时间戳的应用广泛而且重要。例如,在性能调优时,你需要知道代码执行的具体时间,或者在日志记录时,你需要记录事件发生的准确时间。你可以将std::chrono::system_clock::now
想象成一个永不停息的时钟,它可以精确地告诉你现在的时间。当你需要测量一段代码的运行时间时,只需要在代码开始和结束时各取一个时间戳,然后将它们相减,就可以得到运行时间。
人们对于时间的需求,从基本的查看时间,到计算间隔,再到记录精确的时间戳,编程语言都提供了强大的工具来满足。在C++中,std::chrono::system_clock::now
就是这样一个强大的工具。
3.2. 时间戳的转换和应用
在许多场景中,我们需要将时间戳(Timestamp)转换为更容易理解和使用的格式。同时,我们可能需要在不同的时间单位之间进行转换。好在C++中的std::chrono
库提供了丰富的函数和类来满足这些需求。
时间戳转换为具体日期和时间
获取的时间戳通常是从某个参考点(通常是Epoch,1970年1月1日)开始计算的毫秒数。为了将这个时间戳转换为人们习惯的日期和时间格式,我们可以利用std::chrono
库提供的接口将std::chrono::system_clock::time_point
转换为std::time_t
,然后使用C语言的标准库函数将其转换为struct tm
,最后可以使用std::strftime
将struct tm
转换为字符串。
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm* now_tm = std::localtime(&t);
char buffer[80];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", now_tm);
std::cout << "Current time: " << buffer << std::endl;
这就像我们将一个长篇的数字字符串(比如一串电话号码)转换成更容易理解的格式,比如在适当的位置添加了短横线。
时间单位的转换
在std::chrono
库中,我们可以很容易地在不同的时间单位之间转换。这种转换类似于我们在长度、重量等不同的物理单位之间进行转换。比如,我们可以很容易地将std::chrono::seconds
转换为std::chrono::milliseconds
:
std::chrono::seconds sec(1);
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(sec);
通过这样的方式,我们可以将代码中的时间管理变得更加灵活,满足各种精度的需求。
时间戳在实际问题中的应用示例
在实际的编程问题中,时间戳的应用是广泛的。例如,我们可以使用时间戳来度量一段代码的执行时间,也可以在日志记录中添加时间戳,来追踪事件发生的时间。
auto start = std::chrono::high_resolution_clock::now();
// Code to be measured.
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = end - start;
std::cout << "Elapsed time: " << elapsed.count() << "ns\n";
在这个示例中,我们使用std::chrono::high_resolution_clock::now
获取代码开始和结束的时间,然后计算出代码执行所需的时间。这就像我们使用秒表来测量运动员的速度一样,可以帮助我们更好地理解和优化代码的性能。
3.3. 时间戳在实际问题中的应用示例
在软件开发中,时间戳的使用几乎无处不在,例如性能测量、日志记录、时间戳计算等。在这一部分,我们将探讨一些常见的时间戳应用示例。
性能测量
当你需要优化代码以提高执行速度时,时间戳的使用就显得尤为重要。可以在代码块的开始和结束处获取时间戳,然后计算执行时间:
auto start = std::chrono::high_resolution_clock::now();
// ... Your code to measure ...
auto stop = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
std::cout << "Time taken by function: " << duration.count() << " microseconds" << std::endl;
这就像你使用秒表来测量一个运动员完成赛跑所需的时间,从而分析他的运动表现并寻找改进的地方。
日志记录
在日志记录中,时间戳可以帮助我们跟踪事件的发生时间。当我们查看日志以调试或理解系统行为时,知道事件发生的准确时间通常非常有用。
auto now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
std::cout << "Log event happened at " << std::ctime(&now_c);
时间戳计算
有时候,我们需要进行基于时间的计算,比如计算两个日期之间的差值,或者添加特定的时间量。这时,我们可以使用std::chrono::duration
来执行这些操作。
auto t1 = std::chrono::system_clock::now();
// ... some code ...
auto t2 = std::chrono::system_clock::now();
auto duration = t2 - t1;
std::cout << "Duration between t1 and t2: " << duration.count() << " seconds\n";
以上这些只是时间戳在实际问题中的一些应用示例。使用C++的std::chrono
库,你可以进行更复杂和高级的时间处理操作。
4. 计时器的实现
4.1 使用std::chrono
库实现基本计时器
在编程中,我们经常需要测量代码的执行时间,比如,对比两种算法的性能或者查找代码中的性能瓶颈。C++的std::chrono
库为我们提供了这样的工具。
基本计时器实现
我们首先需要理解,计时器 (Timer) 可以被比作是心理学中的“定时器”。当人们需要专注于某项任务时,他们会设定一个定时器来保持专注并度量时间。这与编程中使用计时器的动机非常相似。我们希望对程序的运行时间有个准确的度量,以此来优化我们的代码。
在C++中,一个基本的计时器可以通过std::chrono
库中的high_resolution_clock
来实现。代码示例如下:
#include <iostream>
#include <chrono>
int main() {
auto start = std::chrono::high_resolution_clock::now(); // 开始计时
// 你需要测量的代码块
auto end = std::chrono::high_resolution_clock::now(); // 结束计时
std::chrono::duration<double> diff = end-start; // 计算时间差
std::cout << "Code executed in " << diff.count() << " seconds" << std::endl;
return 0;
}
在上述代码中,start
和end
都是std::chrono::time_point
对象,表示一个时间点。high_resolution_clock::now()
函数返回当前时间点。然后,我们通过计算end
和start
之间的差值,得到代码执行的时间。最后,使用count()
函数以秒为单位打印出运行时间。
这就像是你设定了一个心理学中的定时器,你知道何时开始,何时结束,并且你可以测量这段时间。
在接下来的章节中,我们将详细介绍更高级的计时器功能。
4.2 高级计时器功能与实现(例如:暂停、重置)
计时器的暂停与恢复
在现实生活中,我们的计时器有时需要暂停并稍后恢复。比如说,你正在烹饪并根据食谱倒计时,突然有人敲门,你需要停下来应对。在这种情况下,你会暂停计时器,然后在事情处理完后恢复计时。同样,我们在编程中也会遇到类似的情况。
在C++中,我们可以通过以下方法来实现计时器的暂停和恢复功能:
#include <iostream>
#include <chrono>
class Timer {
private:
bool running;
std::chrono::time_point<std::chrono::high_resolution_clock> start_time, end_time;
public:
Timer() : running(false) {}
void start() {
running = true;
start_time = std::chrono::high_resolution_clock::now();
}
void stop() {
if (running) {
end_time = std::chrono::high_resolution_clock::now();
running = false;
}
}
double elapsed() {
if (running) {
return std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start_time).count();
}
else {
return std::chrono::duration<double>(end_time - start_time).count();
}
}
void reset() {
running = false;
}
};
int main() {
Timer timer;
timer.start();
// Some code
timer.stop();
std::cout << "Elapsed time: " << timer.elapsed() << " seconds." << std::endl;
// Continue with the timer
timer.start();
// Some code
timer.stop();
std::cout << "Total elapsed time: " << timer.elapsed() << " seconds." << std::endl;
return 0;
}
上述代码中定义了一个名为Timer
的类,其中包含了start()
, stop()
, elapsed()
, 和 reset()
这些方法。start()
用于开始或恢复计时,stop()
用于暂停计时,elapsed()
用于获取已过去的时间,而reset()
则用于重置计时器。这就像我们生活中使用的实际计时器一样,能进行开始、暂停、恢复和重置等操作。
通过以上方式,我们不仅增强了计时器的功能,更为我们的编程工作提供了更灵活的时间度量工具。
5. 使用std::chrono作为通用的时间参数
5.1 std::chrono::duration的应用
在我们的日常生活中,时间是一个我们经常要处理的量。我们处理从秒到分钟,到小时,甚至到年。这就像我们的基本需求层次。根据马斯洛的需求层次理论(Maslow’s hierarchy of needs),我们首先满足生理需求,然后是安全需求,然后是爱和归属感,然后是尊重,最后是自我实现。同样地,我们在处理时间时,也需要从满足基本的时间单位开始,比如秒,然后我们可能需要更大的单位,比如分钟或小时,然后我们可能需要处理更复杂的时间单位,比如日、周、月、年。C++的std::chrono::duration
就是设计来帮助我们处理这种层次化需求的。
std::chrono::duration
(时长)是一个模板类,用于表示两个时间点之间的时间跨度。其模板参数是表示此时间跨度的单位。
5.1.1 创建duration对象
我们可以通过以下方式创建duration对象:
std::chrono::duration<int> twenty_seconds(20);
std::chrono::duration<double, std::ratio<60>> half_a_minute(0.5);
std::chrono::duration<long, std::ratio<1,1000>> one_millisecond(1);
在上面的例子中,twenty_seconds
是20秒,half_a_minute
是半分钟,one_millisecond
是1毫秒。
5.1.2 duration对象的操作
我们可以对duration
对象进行各种操作,包括比较、算术运算、赋值等。这就像我们在处理日常生活中的时间需求一样。比如,我们可以比较今天和昨天哪天更长,或者我们可以计算如果我们每天工作8小时,一周工作多少小时。在C++中,我们可以这样做:
std::chrono::seconds work_day(8*60*60); // 8 hours
std::chrono::seconds work_week = 5*work_day;
下面是一个表格,总结了std::chrono::duration
的一些常见操作:
操作 | 描述 |
---|---|
duration1 + duration2 |
两个duration对象相加,返回一个新的duration对象 |
duration1 - duration2 |
两个duration对象相减,返回一个新的duration对象 |
duration1 * n 或 n * duration1 |
duration对象与一个数相乘,返回一个新的duration对象 |
duration1 / n |
duration对象除以一个 |
数,返回一个新的duration对象 |
| duration1 == duration2
| 检查两个duration对象是否相等 |
| duration1 != duration2
| 检查两个duration对象是否不相等 |
| duration1 < duration2
| 检查一个duration对象是否小于另一个 |
| duration1 > duration2
| 检查一个duration对象是否大于另一个 |
| duration1 <= duration2
| 检查一个duration对象是否小于或等于另一个 |
| duration1 >= duration2
| 检查一个duration对象是否大于或等于另一个 |
请注意,所有这些操作都不会改变原始的duration
对象,而是返回一个新的duration
对象。
以此为核心,std::chrono::duration
可以被看作是一个“构建块”,使我们可以创建、操作和比较不同的时间单位。在我们开始处理更复杂的时间需求时,它为我们提供了一个基础。同样,它也可以被用作一个通用的时间参数,在我们的函数或类中。
5.2 时间单位转换:如std::chrono::seconds, std::chrono::milliseconds, std::chrono::microseconds等
在处理时间相关问题时,一个常见的需求就是在不同的时间单位之间进行转换。举一个生活中的例子,我们可能想要知道1000毫秒(milliseconds)是多少秒(seconds)。同样的,在编程中,我们也可能需要做这样的转换。在C++的std::chrono
库中,提供了一种非常方便的方式来进行这种转换。
5.2.1 时间单位的转换
假设我们有一个std::chrono::milliseconds
对象,我们想要将它转换为std::chrono::seconds
,我们可以使用std::chrono::duration_cast
函数,如下所示:
std::chrono::milliseconds ms(1000);
std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(ms);
这里,duration_cast
函数会将ms
对象转换为sec
对象。值得注意的是,这个转换是向下取整的。也就是说,如果ms
是1500毫秒,那么sec
将是1秒。
我们可以将这个过程想象为将一堆石头(milliseconds)转移到不同大小的箱子(seconds)。我们不能将半块石头放进箱子里,所以我们必须将多余的石头扔掉。这就是duration_cast
函数向下取整的原因。
5.2.2 std::chrono中的时间单位
std::chrono
库中定义了许多常用的时间单位,比如:
std::chrono::hours
std::chrono::minutes
std::chrono::seconds
std::chrono::milliseconds
std::chrono::microseconds
std::chrono::nanoseconds
这些单位都是std::chrono::duration
的特化版本。它们的使用方法与std::chrono::duration
完全相同,只是它们的模板参数已经被预设为常用的值。
此外,你也可以使用std::ratio
创建自定义的时间单位,比如:
using half_seconds = std::chrono::duration<double, std::ratio<1, 2>>;
在上面的代码中,half_seconds
代表半秒。
通过使用std::chrono
库中的时间单位,我们可以更方便地处理时间相关的问题,就像我们在生活中使用小时、分钟和秒一样。
6. 深入探讨std::chrono::system_clock::time_point
6.1. time_point的定义和主要特性
std::chrono::system_clock::time_point
(时间点)可以被视为一个特殊的”时间戳”,它表示自纪元以来的时间量。纪元是指定的起点时间,对于std::chrono::system_clock
来说,纪元通常是1970年1月1日午夜。
在这个定义中,我们可以看出time_point
的核心概念:它是一个表示时间的数值,而不是一个具体的“现在”,“过去”或“将来”。正如著名心理学家阿布拉罕·马斯洛(Abraham Maslow)在描述人类需求层次时指出,满足低层需求是实现高层需求的基础。类似地,理解time_point
的定义和性质是我们理解更高级和更复杂的时间处理技术的基础。
std::chrono::system_clock::time_point
是一个模板类型,可以表示不同精度的时间。例如,我们可以用std::chrono::system_clock::time_point
表示到纳秒级别的精确时间。
下表总结了一些time_point
的主要方法:
方法名称 | 描述 | 返回类型 |
---|---|---|
min() | 获取可能的最小时间点 | system_clock::time_point |
max() | 获取可能的最大时间点 | system_clock::time_point |
now() | 获取从纪元开始到现在的时间点 | system_clock::time_point |
为了更好地理解time_point
,我们可以将其比喻为一个足球场上的地标。纪元(epoch)就像球场的一端,而time_point
就像球场上的一个具体位置,通过度量从球场一端到这个位置的距离(时间),我们可以确定这个位置的确切位置。
time_point的使用示例
#include <iostream>
#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::cout << "Current time: " << std::ctime(&now_c) << std::endl;
return 0;
}
在上述示例中,我们获取了当前的时间点,并将其转换为C时间,然后打印。这只是time_point
的基本使用,随着我们深入研究std::chrono
库,我们将发现更多关于time_point
的有趣和强大的应用。
6.2. time_point的常见操作与示例
在了解了std::chrono::system_clock::time_point
的定义和基本特性之后,我们可以探索一些对time_point
进行操作的方法。这些操作包括了加减运算、比较、以及转换为其他时间单位等。
这些操作提供了我们对时间进行更深层次的控制,类似于心理学中的”自我效能”理论,人们对自己能否成功完成任务的信心,可以决定他们是否会尝试这个任务,以及他们在面对困难时是否会坚持。对time_point
的操作提供了这样的“自我效能”,使我们在面对复杂的时间问题时,能有信心进行处理。
加减运算
我们可以对time_point
进行加减运算来得到新的time_point
:
#include <iostream>
#include <chrono>
int main() {
// 获取当前时间点
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
// 创建一个1小时的duration对象
std::chrono::hours one_hour(1);
// 通过加法运算得到1小时后的时间点
std::chrono::system_clock::time_point one_hour_later = now + one_hour;
return 0;
}
在上述代码中,我们首先获取了当前的时间点now
,然后创建了一个表示1小时的std::chrono::hours
对象one_hour
。通过将now
和one_hour
进行加法运算,我们得到了1小时后的时间点one_hour_later
。
比较操作
我们可以使用比较操作符比较两个time_point
对象:
#include <iostream>
#include <chrono>
int main() {
// 获取当前时间点
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
// 创建一个1秒后的时间点
std::chrono::system_clock::time_point one_sec_later = now + std::chrono::seconds(1);
// 比较两个时间点
if (one_sec_later > now) {
std::cout << "one_sec_later is later than now.\n";
} else {
std::cout << "one_sec_later is not later than now.\n";
}
return 0;
}
在上述代码中,我们创建了一个1秒后的时间点one_sec_later
,然后使用大于操作符>
比较了one_sec_later
和now
,并打印了比较结果。
通过上述示例,我们可以看到,std::chrono::system_clock::time_point
提供了一种直观、灵活的方式来操作和比较时间。这就像我们通过心理学的理论和技术来理解和控制自己的行为和情绪一样,通过理解和掌握time_point
,我们可以更好地控制和操作时间。
6.3. time_point在实际问题中的应用示例
std::chrono::system_clock::time_point
在实际问题中的应用往往涉及到复杂的时间处理,例如事件的调度,网络通信的超时控制等。在这些问题中,我们需要对time_point
进行精细的操作和处理。
事件调度
在许多情况下,我们需要在某个具体的时间点执行某个任务或事件。这就需要我们能够准确地表示和计算时间点。以下是一个简单的示例:
#include <iostream>
#include <chrono>
#include <thread>
void schedule_event(std::chrono::system_clock::time_point event_time) {
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
if (event_time > now) {
// 计算需要等待的时间
std::chrono::duration<double> wait_time = event_time - now;
// 等待相应的时间
std::this_thread::sleep_for(wait_time);
}
// 执行事件
std::cout << "Event executed at " << std::chrono::system_clock::to_time_t(now) << std::endl;
}
int main() {
// 计划在5秒后执行事件
std::chrono::system_clock::time_point event_time = std::chrono::system_clock::now() + std::chrono::seconds(5);
schedule_event(event_time);
return 0;
}
在上述代码中,我们首先计算了事件的执行时间event_time
,然后调用schedule_event
函数来执行事件。在schedule_event
函数中,我们先获取当前的时间,然后计算出需要等待的时间,并使用std::this_thread::sleep_for
函数来等待相应的时间,最后执行事件。
就像心理学家给出的建议,我们在面对复杂任务时,需要将其分解成小的、可管理的部分。这样,我们可以更好地控制任务的执行,并实现我们的目标。在这个示例中,我们首先计算了事件的执行时间,然后等待相应的时间,最后执行事件。这就是一个典型的任务分解的例子。
网络通信的超时控制
在网络通信中,超时控制是非常重要的一部分。例如,在建立连接、发送数据时,如果在一定时间内没有得到回应,我们需要进行超时处理。这就需要我们能够精确地表示和计算时间。以下是一个简单的示例:
#include <iostream>
#include <chrono>
#include <thread>
bool try_connect(std::chrono::system_clock::time_point deadline) {
while (std::chrono::system_clock::now() < deadline) {
// 尝试连接
bool success = false; // 这里只是示例,实际应用
中需要调用具体的连接函数
if (success) {
return true;
}
// 等待一段时间后再尝试
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 超时,连接失败
return false;
}
int main() {
// 尝试在10秒内建立连接
std::chrono::system_clock::time_point deadline = std::chrono::system_clock::now() + std::chrono::seconds(10);
bool connected = try_connect(deadline);
if (connected) {
std::cout << "Connected.\n";
} else {
std::cout << "Failed to connect within 10 seconds.\n";
}
return 0;
}
在上述代码中,我们设置了一个超时时间deadline
,然后在这个时间内不断尝试建立连接。如果在超时时间内成功建立了连接,则返回true
;否则,返回false
。这就像我们在面对困难时,需要有持久性和决心。如果我们坚持尝试,而不是在失败后立即放弃,我们往往可以实现我们的目标。
通过上述示例,我们可以看到,std::chrono::system_clock::time_point
提供了一种强大、灵活的方式来表示和操作时间,使我们可以有效地解决实际问题。
7. 深入探讨std::chrono::duration
7.1 duration的定义和主要特性
std::chrono::duration
(时长)是C++中表示时间段的类型。它是一个模板类,可以表示秒、毫秒、微秒等不同的时间单位。
为了更好地理解duration
,让我们从心理学的角度看待时间。时间是我们生活中的一部分,它能够影响我们的感觉和行为。心理学家Zimbardo曾经指出,每个人对时间的感知都是不同的,有些人更关注过去,有些人更关注现在,而有些人则更关注未来。编程中的时间处理也是如此。在处理时间问题时,我们可能关注过去(即计算已经过去多少时间)、关注现来(即计算现在的时间)、或关注未来(即预计还需多少时间)。
std::chrono::duration
正是帮助我们处理这些时间问题的工具。它不仅可以表示时间长度,也可以表示特定时间点到另一个时间点的间隔。与人的感知类似,duration
可以表示”过去多久”(例如5秒前)、“现在”(例如现在到程序开始的时间)、或”未来多久”(例如5秒后)。
std::chrono::duration
的定义是非常灵活的,可以表示各种时间单位。例如,以下是一些常见的duration
类型:
std::chrono::seconds
:以秒为单位的时间长度std::chrono::milliseconds
:以毫秒为单位的时间长度std::chrono::microseconds
:以微秒为单位的时间长度std::chrono::nanoseconds
:以纳秒为单位的时间长度
以下是std::chrono::duration
主要特性的总结:
特性 | 描述 |
---|---|
类型安全 | std::chrono::duration 的不同单位不能直接混合使用,这避免了由于单位不匹配导致的错误 |
自动类型转换 | 当进行时间单位转换时,std::chrono::duration 可以自动进行,例如从毫秒转换到秒 |
支持算术运算 | 可以对std::chrono::duration 进行加、减、乘、除等算术运算 |
高精度 | 可以表示非常小的时间单位,例如纳秒 |
正如心理学家之所以研究时间感知,因为了解和理解我们如何看待和使用时间,可以帮助我们更好地理解自己并改进我们的生活。同样,熟练掌握std::chrono::duration
的定义和主要特性,能够帮助我们更有效地解决编程中的时间问题。
7.2 duration的常见操作与示例
std::chrono::duration
为我们提供了一系列操作,以便我们能够轻松地处理时间问题。这些操作包括创建时长、执行算术运算、比较时长、将时长转换为不同的单位等。下面,我们将通过一些示例来详细说明这些操作。
7.2.1 创建duration
创建duration的方式非常简单,只需要指定所需的时间长度即可。例如,创建一个表示5秒的duration:
std::chrono::seconds sec(5);
7.2.2 执行算术运算
std::chrono::duration
支持基本的算术运算,包括加法、减法、乘法和除法。例如:
std::chrono::seconds sec1(5);
std::chrono::seconds sec2(3);
auto sec3 = sec1 + sec2; // sec3 is 8 seconds
auto sec4 = sec1 - sec2; // sec4 is 2 seconds
7.2.3 比较duration
std::chrono::duration
还支持比较操作,包括等于、不等于、小于、大于、小于等于和大于等于。例如:
std::chrono::seconds sec1(5);
std::chrono::seconds sec2(3);
if (sec1 > sec2) {
// do something
}
7.2.4 转换为不同的单位
通过使用duration_cast,我们可以将一个duration转换为不同的单位。例如:
std::chrono::minutes min(1);
auto sec = std::chrono::duration_cast<std::chrono::seconds>(min); // sec is 60 seconds
了解和掌握这些操作,就像心理学家熟悉人的行为和反应一样。我们能够预测和控制人的行为,因为我们理解人的需求和动机。同样,我们能够有效地处理时间问题,因为我们理解std::chrono::duration
的操作和行为。
7.3 duration在实际问题中的应用示例
在我们的编程实践中,std::chrono::duration
提供了处理时间问题的强大工具。让我们通过一些具体的例子来看看如何使用它。
7.3.1 计算代码执行时间
我们经常需要测量代码的执行时间,以评估性能并找到优化的可能性。这是一个基本的使用std::chrono::duration
的例子:
auto start = std::chrono::high_resolution_clock::now();
// Code to measure...
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = end-start;
std::cout << "Code executed in " << diff.count() << " seconds\n";
在这个例子中,我们使用std::chrono::high_resolution_clock
来获取代码执行前后的时间点,然后计算二者的差值,得到代码的执行时间。
7.3.2 实现延迟
有时,我们可能需要在程序中创建一个延迟,这可以通过结合std::chrono::duration
和std::this_thread::sleep_for
来实现:
std::chrono::seconds delay(5); // a delay of 5 seconds
std::this_thread::sleep_for(delay);
这个例子中,我们创建了一个5秒的duration
,然后使用std::this_thread::sleep_for
函数来使当前线程暂停执行5秒。
就像心理学家用实验证据来验证理论和模型一样,这些示例演示了std::chrono::duration
如何在实际编程中解决时间相关的问题。这些应用不仅能帮助我们更好地理解duration
,而且也可以为我们的编程实践提供有用的工具。
8. 深入探讨std::chrono::microseconds以及其他常用时间单位
8.1. microseconds
及其他时间单位的定义和特性
在 std::chrono
中,我们可以通过各种预定义的 duration
类型来表示时间。这些类型包括 std::chrono::hours
, std::chrono::minutes
, std::chrono::seconds
, std::chrono::milliseconds
, std::chrono::microseconds
, 和 std::chrono::nanoseconds
等。这些类型是模板类 std::chrono::duration
的特化版本,它们都接受一个表示时长的整数作为构造参数。
让我们将这些 duration
类型想象成心理学中的”激励”(“motivations”)。正如人们在生活中有各种各样的需求和动机,从基本的生理需求到高级的自我实现需求,编程中的时间需求也同样多样化。例如,有时候我们只需要精确到秒,就像我们只需要满足基本的饮食需求;有时候我们需要更精细的毫秒级或微秒级的精确度,这就像我们追求更高层次的精神满足和自我实现。这些 duration
类型就像是我们的”动机工具箱”,让我们可以根据需要选择合适的工具。
对于std::chrono::microseconds
(微秒),它能提供到百万分之一秒的精确度。下面是一个创建microseconds
实例的例子:
std::chrono::microseconds microSec(1000); // 创建一个表示1000微秒的duration对象
类似地,我们可以创建其他时间单位的实例,例如:
std::chrono::seconds sec(60); // 创建一个表示60秒的duration对象
std::chrono::hours hr(1); // 创建一个表示1小时的duration对象
各种duration类型的创建和使用比较如下:
时间单位 | 创建实例 | 功能 |
---|---|---|
std::chrono::hours | std::chrono::hours hr(1); | 创建一个表示1小时的duration对象 |
std::chrono::minutes | std::chrono::minutes min(30); | 创建一个表示30分钟的duration对象 |
std::chrono::seconds | std::chrono::seconds sec(60); | 创建一个表示60秒的duration对象 |
std::chrono::milliseconds | std::chrono::milliseconds milliSec(1000); | 创建一个表示1000毫秒的duration对象 |
std::chrono::microseconds | std::chrono::microseconds microSec(1000); | 创建一个表示1000微秒的duration对象 |
std::chrono::nanoseconds | std::chrono::nanoseconds nanoSec(1000); | 创建一个表示1000纳秒的duration对象 |
这些 duration
类型为我们提供了一种简洁、准确而直观的方式来表示和操作时间,从而满足我们编程中的各种需求。
8.2. microseconds
及其他时间单位的常见操作与示例
std::chrono::duration
提供了一系列常用的操作,例如加法、减法、乘法、除法以及比较操作。这些操作让我们能够以非常直观的方式处理时间。
让我们再次借用心理学的概念,将这些操作想象为处理激励和需求的策略。例如,我们可以增加或减少激励(对应于加法和减法操作),或者将激励与某个因素(例如资源或时间)相结合(对应于乘法和除法操作)。
下面是一些使用 std::chrono::microseconds
和其他 duration
类型的常见操作的示例:
std::chrono::microseconds usec1(1000);
std::chrono::microseconds usec2(2000);
// 加法
auto usec3 = usec1 + usec2; // usec3现在是3000微秒
// 减法
auto usec4 = usec2 - usec1; // usec4现在是1000微秒
// 乘法
auto usec5 = 2 * usec1; // usec5现在是2000微秒
// 除法
auto half = usec1 / 2; // half现在是500微秒
// 比较
if (usec1 < usec2) {
// 这个条件是真的,因为1000微秒小于2000微秒
}
在 std::chrono
中,所有的 duration
类型都支持这些操作。这些操作可以让我们以非常直观和灵活的方式处理时间相关的问题。
8.3. microseconds
及其他时间单位在实际问题中的应用示例
我们来看一个实际的使用 std::chrono::microseconds
和其他时间单位的例子,假设我们正在开发一个音视频同步的应用程序。在这个应用程序中,我们需要确保音频和视频的播放是同步的,也就是说,我们需要确保音频和视频的播放延迟是一致的。
为了实现这个目标,我们可能需要测量处理每一帧音频或视频数据所需的时间,然后根据需要调整播放速度。我们可以使用 std::chrono::microseconds
来进行这种测量:
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
// 处理音频或视频帧...
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
std::chrono::microseconds processing_time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
这里,我们使用 std::chrono::steady_clock::now()
来获取处理开始和结束的时间点,然后通过减法得到处理所需的 duration
,最后我们使用 std::chrono::duration_cast
将这个 duration
转换为微秒。通过这种方式,我们可以精确地测量处理每一帧数据所需的时间。
如果我们发现处理时间超过了我们的预期(例如,如果我们希望每一帧的处理时间不超过16毫秒,以达到60帧/秒的播放速度),我们就可以采取措施,例如降低处理负载、优化代码,或者采用更高效的算法,以减少处理时间并实现音视频的同步播放。
总的来说,std::chrono
库和它的 duration
类型提供了一种强大而灵活的方式来处理时间相关的问题。无论你是在处理基本的时间测量,还是在处理复杂的音视频同步问题,你都可以找到合适的工具来满足你的需求。
第九章: std::chrono在泛型编程和元模板中的应用
9.1 std::chrono在泛型编程中的使用
C++的泛型编程(Generic Programming)是一种在类型级别进行抽象的方法,通过模板(template)实现。这种思想源于心理学的“抽象思维”理论。就如心理学家Dedre Gentner所说的,我们经常通过抽象思考来理解和处理复杂性,人类的思维能力在很大程度上依赖于我们对抽象的理解。
让我们借用std::chrono
的duration
(持续时间)来进行一个泛型编程的例子。std::chrono::duration
是一个模板类,用于表示一段时间,单位可以是任何可以表示时间的类型,如秒(seconds)、毫秒(milliseconds)、微秒(microseconds)等。
这种设计就像是人类思维中的元认知(Metacognition),可以针对特定问题选择最合适的思维策略。就如心理学家John Flavell所说,人们能对自己的思维过程进行反思和调整,这是人类独有的元认知能力。
template <typename T>
void print_duration(T d) {
auto value = std::chrono::duration_cast<std::chrono::microseconds>(d).count();
std::cout << "Duration: " << value << " microseconds\n";
}
在上述代码中,print_duration
是一个模板函数,接受任何类型的std::chrono::duration
,并将其转换为微秒输出。这就像我们在面对问题时,会自动将问题抽象化,然后应用最适合解决问题的策略。我们不需要对每种类型的duration
写一个特定的函数,而是写一个泛型函数,能处理所有类型的duration
。
同样,std::chrono
也能够在复杂度更高的问题中应用这种“元认知”。例如,我们可以写一个模板类,它的内部逻辑会根据传入的duration
类型做出不同的行为。这就像我们在面对更复杂的问题时,会用更高级的元认知策略去处理。
template <typename T>
class Timer {
public:
void start() {
start_time = std::chrono::steady_clock::now();
}
T elapsed() {
auto end_time = std::chrono::steady_clock::now();
return std::chrono::duration_cast<T>(end_time - start_time);
}
private:
std::chrono::steady_clock::time_point start_time;
};
在上述代码中,Timer
类是一个泛型类,可以接受任何类型的std::chrono::duration
。当你调用elapsed
函数时,它会返回一个你指定类型的duration
。这样的设计就像是在处理复杂问题时,我们会用到更复杂的元认知策略,如“深度处理”和“关联”。
心理学理论给我们提供了一种方法来理解和利用泛型编程的能力。而泛型编程又给我们提供了一种强大的工具来在编程中使用这种方法。它们的结合使得我们能更好地理解和使用std::chrono
库。
我们将在下一章节中,深入讨论std::chrono
在元模板编程中的应用。
9.2 std::chrono在元模板编程中的使用
在C++中,元模板(Metatemplates)是一种在编译期执行计算的技巧,它们大大增强了C++的表达能力,类似于心理学中的“前瞻性记忆”(Prospective Memory),这是人类大脑的一种预先规划和执行的功能,让我们能在适当的时间执行预定的行动。
考虑一个简单的例子,假设我们想要编写一个函数,计算一个事件在两个不同时间单位下的持续时间。这需要将一个时间单位转换为另一个时间单位。使用元模板编程,我们可以在编译期完成这种转换,而无需在运行期执行。这正如我们的大脑会提前计划要在特定时刻执行的任务,使得我们的思维更加有效率。
template <typename FromDuration, typename ToDuration>
struct duration_converter {
static constexpr double ratio =
double(ToDuration::period::num) / FromDuration::period::num *
double(FromDuration::period::den) / ToDuration::period::den;
static ToDuration convert(FromDuration d) {
return ToDuration(static_cast<long long>(d.count() * ratio));
}
};
在上述代码中,duration_converter
是一个模板结构体,它可以在编译期计算出从一个std::chrono::duration
到另一个std::chrono::duration
的转换比率。使用它的convert
函数,我们可以在运行期间将一个duration
从一个单位转换为另一个单位。
这种方法有两个主要优点。首先,我们避免了在运行期间进行单位转换,这会带来一定的性能提升。其次,通过将单位转换放在编译期,我们可以在编译期间捕获潜在的错误,例如,如果两个单位不兼容,编译器就会报错。
这种编译期计算的方式是元模板编程的一种应用,通过预先计算并缓存结果,可以在运行期节省计算资源,提高程序效率。这就像人类的前瞻性记忆在我们日常生活中的作用,使我们能提前规划和准备,使我们的生活更加高效。
总的来说,std::chrono
库可以很好地与泛型编程和元模板编程相结合,为我们提供了一种高效、灵活的方式来处理时间相关的问题。
9.3 泛型编程和元模板编程中std::chrono的高级应用示例
在C++的编程实践中,std::chrono
库的灵活性允许我们创建高级的时间相关功能。这可以帮助我们创建更具可读性和可维护性的代码。这种方式类似于心理学中的“分布式实践”原则,这是一种通过将任务分解并在不同时间段进行学习的有效方法。
让我们考虑一个例子,假设我们正在编写一个图形渲染引擎,需要根据时间调整渲染的效果。我们可能需要根据时间的不同,对图像应用不同的特效。这就像人们在不同时间段完成不同的任务以提高效率。
在这个例子中,我们可以定义一个EffectController
类,它可以根据当前的std::chrono::duration
应用不同的特效:
template <typename T>
class EffectController {
public:
void applyEffect(T duration) {
if (duration < std::chrono::seconds(10)) {
applyMorningEffect();
} else if (duration < std::chrono::seconds(20)) {
applyNoonEffect();
} else {
applyNightEffect();
}
}
private:
void applyMorningEffect() {
// Apply morning effect...
}
void applyNoonEffect() {
// Apply noon effect...
}
void applyNightEffect() {
// Apply night effect...
}
};
在上述代码中,EffectController
类接受一个std::chrono::duration
类型的参数,并根据其值决定应用哪种特效。这是一种泛型编程的应用,它使得我们可以以一种类型安全和灵活的方式处理时间。
此外,我们还可以利用元模板编程的技术,在编译期计算出不同特效之间的切换点,从而进一步提高程序的性能。例如,我们可以使用std::ratio
来表示特效切换的具体时间点,并在编译期计算出这些时间点:
template <typename T, typename Ratio>
class EffectController {
public:
void applyEffect(T duration) {
constexpr auto switch_point = std::chrono::duration<int, Ratio>(1);
if (duration < switch_point) {
applyMorningEffect();
} else if (duration < 2 * switch_point) {
applyNoonEffect();
} else {
applyNightEffect();
}
}
// ...
};
在上述代码中,我们使用了std::ratio
来表示每个特效持续的时间长度,并在编译期计算出切换点。这是元模板编程的一种应用,它使我们能在编译期完成更多的计算,从而提高运行期的性能。
这两个例子都显示了std::chrono
库在泛型编程和元模板编程中的高级应用,它们提供了一种强大而灵活的方式来处理时间相关的问题。
版权声明:本文为博主作者:泡沫o0原创文章,版权归属原作者,如果侵权,请联系我们删除!
原文链接:https://blog.csdn.net/qq_21438461/article/details/131198438