C++ 時(shí)間庫duration 類的原理與復(fù)現(xiàn)(最新整理)
C++ 時(shí)間庫實(shí)現(xiàn):duration 類的原理與復(fù)現(xiàn)解析
在現(xiàn)代編程中,時(shí)間處理是一個(gè)常見且重要的需求。C++11 標(biāo)準(zhǔn)庫引入了 <chrono> 頭文件,提供了一套處理時(shí)間的工具,其中 duration 類是時(shí)間表示的核心組件之一。在上一篇文章中,分析和復(fù)現(xiàn)了 <ratio>, 本文將基于 ratio 深入解析 duration 類的實(shí)現(xiàn)原理,并詳細(xì)介紹筆者自己復(fù)現(xiàn)這一功能強(qiáng)大的時(shí)間間隔表示類的完整過程。
duration 類的基本概念
duration 類模板用于表示時(shí)間間隔,它以模板參數(shù)的形式定義了兩個(gè)關(guān)鍵要素:
Rep_:表示時(shí)間間隔的數(shù)值類型(如int64_t)Period_:表示時(shí)間間隔的單位(通過ratio模板定義)
這種設(shè)計(jì)使得 duration 能夠靈活處理不同精度和單位的時(shí)間間隔,從納秒到天數(shù)都能統(tǒng)一表示和操作。標(biāo)準(zhǔn)庫中通過特化 ratio 模板定義了常見的時(shí)間單位:
- 納秒:
nano = ratio<1, 1000000000> - 微秒:
micro = ratio<1, 1000000> - 毫秒:
milli = ratio<1, 1000> - 秒:
ratio<1> - 分鐘:
ratio<60> - 小時(shí):
ratio<3600> - 天:
ratio<86400>
duration 類的核心實(shí)現(xiàn)原理
1. 模板參數(shù)設(shè)計(jì)
duration 類的模板定義如下:
template<typename Rep_, typename Period_ = ratio<1>> class duration;
Rep_:存儲(chǔ)時(shí)間間隔的具體數(shù)值,通常為整數(shù)類型(如int64_t)Period_:時(shí)間單位,基于ratio模板實(shí)現(xiàn),默認(rèn)單位為秒
采用參數(shù)化設(shè)計(jì)使得 duration 具有高度的靈活性和類型安全性,不同單位的 duration 是不同的類型,避免了隱式類型轉(zhuǎn)換帶來的錯(cuò)誤。
2. 單位轉(zhuǎn)換的核心:duration_cast
duration_cast 函數(shù)是實(shí)現(xiàn)不同時(shí)間單位轉(zhuǎn)換的關(guān)鍵,其核心邏輯如下:
/// 用于將一個(gè)時(shí)間段從一個(gè)周期單位轉(zhuǎn)換到另一個(gè)周期單位
template<typename ToDuration_, typename Rep_, typename Period_>
constexpr ToDuration_ duration_cast(const duration<Rep_, Period_> &d__) {
using CF = ratio_divide<Period_, typename ToDuration_::period>;
/// 類型轉(zhuǎn)換(整型除法,可能發(fā)生截?cái)啵?
auto r_ = static_cast<typename ToDuration_::rep>(
static_cast<long long>(d__.count()) * CF::num / CF::den
);
return ToDuration_(r_);
}- 通過
ratio_divide計(jì)算兩個(gè)時(shí)間單位的轉(zhuǎn)換因子(時(shí)間轉(zhuǎn)換因子用于在不同時(shí)間單位或者系統(tǒng)之間進(jìn)行轉(zhuǎn)換,計(jì)算的原理和場景是強(qiáng)相關(guān)的) - 使用編譯期計(jì)算的
CF::num和CF::den完成單位轉(zhuǎn)換,分別代表源單位與目標(biāo)單位的比例關(guān)系中的分子部分與分母部分 - 利用
static_cast進(jìn)行安全的類型轉(zhuǎn)換
通過這種分子/分母的組合實(shí)現(xiàn)方式確保了單位轉(zhuǎn)換在編譯期完成,既保證了運(yùn)行時(shí)的效率,同時(shí)又避免了浮點(diǎn)數(shù)的誤差,實(shí)現(xiàn)了類型安全并且高精度的時(shí)間單位轉(zhuǎn)換。
3. 數(shù)值表示與存儲(chǔ)
duration 類內(nèi)部使用 Rep_ 類型存儲(chǔ)時(shí)間間隔的數(shù)值:
private:
Rep_ rep_;通過公開的 count() 方法獲取存儲(chǔ)的數(shù)值:
constexpr Rep_ count() const {
return rep_;
}4. 構(gòu)造函數(shù)設(shè)計(jì)
duration 提供了多種構(gòu)造方式以滿足不同需求:
/// 默認(rèn)構(gòu)造,數(shù)值為0
constexpr duration() : rep_() {}
/// 從數(shù)值構(gòu)造
template<typename Rep2_>
explicit constexpr duration(const Rep2_ &r_) : rep_(r_) {}
/// 從其他duration構(gòu)造
template<typename Rep2_, typename Period2_>
constexpr duration(const duration<Rep2_, Period2_> &d_)
: rep_(duration_cast<duration>(d_).count()) {} 運(yùn)算符重載與算術(shù)操作
duration 類實(shí)現(xiàn)了完整的算術(shù)運(yùn)算符重載,使得時(shí)間間隔的計(jì)算變得直觀自然。核心的思想都是將其轉(zhuǎn)換為相同單位后再進(jìn)行運(yùn)算。
1. 一元運(yùn)算符
/// 正號(hào)
constexpr duration operator+() const { return *this; }
/// 負(fù)號(hào)
constexpr duration operator-() const { return duration(-count()); } 2. 遞增遞減運(yùn)算符
/// 前置遞增
duration &operator++() {
++rep_;
return *this;
}
/// 后置遞增
duration operator++(int) {
duration temp_(*this);
++*this;
return temp_;
}
/// 遞減運(yùn)算符類似,不重復(fù)貼出實(shí)現(xiàn)源碼3. 復(fù)合賦值運(yùn)算符
實(shí)現(xiàn)復(fù)合運(yùn)算符進(jìn)行的方式都是類似的,這里給出+=的實(shí)現(xiàn)方式,其他運(yùn)算符類似。
duration &operator+=(const duration &d_) {
rep_ += d_.count();
return *this;
}4. 二元運(yùn)算符實(shí)現(xiàn)
二元運(yùn)算符的實(shí)現(xiàn)采用了"統(tǒng)一單位后計(jì)算"的策略:
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
constexpr auto operator+(const mychrono::duration<Rep1_, Period1_> &lhs,
const mychrono::duration<Rep2_, Period2_> &rhs) {
/// 找出更高精度的時(shí)間單位
using CommonPeriod = typename mychrono::higher_precision_duration<Rep1_, Period1_, Rep2_, Period2_>::type::period;
/// 確定統(tǒng)一的數(shù)值類型
using CommonRep = typename std::common_type<Rep1_, Rep2_>::type;
using CommonDuration = mychrono::duration<CommonRep, CommonPeriod>;
/// 轉(zhuǎn)換到統(tǒng)一類型后計(jì)算
CommonRep lhs_val = duration_cast<CommonDuration>(lhs).count();
CommonRep rhs_val = duration_cast<CommonDuration>(rhs).count();
return CommonDuration(lhs_val + rhs_val);
}關(guān)鍵步驟包括:
- 確定更高精度的時(shí)間單位(周期更小的單位)
- 確定統(tǒng)一的數(shù)值類型(使用
std::common_type) - 將兩個(gè)操作數(shù)轉(zhuǎn)換為統(tǒng)一類型
- 執(zhí)行算術(shù)運(yùn)算并返回結(jié)果
精度選擇與類型推導(dǎo)
1. 更高精度類型的選擇
higher_precision_duration 結(jié)構(gòu)體用于在兩個(gè) duration 類型中選擇精度更高的類型:
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
struct higher_precision_duration {
using type = typename std::conditional<
ratio_less<Period1_, Period2_>::value,
duration<Rep1_, Period1_>,
duration<Rep2_, Period2_>>::type;
};- 通過
ratio_less比較兩個(gè)時(shí)間單位的周期 - 選擇周期更?。ň雀撸┑?
duration類型作為結(jié)果
2. 數(shù)值類型的安全處理
在進(jìn)行算術(shù)運(yùn)算時(shí),使用 std::common_type 確定安全的數(shù)值類型,定義與頭文件如下:
#include <type_traits> template< class... T > struct common_type; template< class... T > using common_type_t = typename common_type<T...>::type; // C++14起的別名模板
實(shí)現(xiàn)的功能是:同時(shí)給出多個(gè)類型 T1、T2…,Tn,std::common_type會(huì)推導(dǎo)出一個(gè)公共類型,使得所有的Ti類都可以隱式轉(zhuǎn)換到公共類型,同時(shí)是可以同時(shí)滿足所有類型的最小公共類型。
所以,在復(fù)現(xiàn)過程中使用如下方式,找到公共類型:
using CommonRep = typename std::common_type<Rep1_, Rep2_>::type;
這種方式確保了不同數(shù)值類型(如 int 和 long long)之間的運(yùn)算不會(huì)發(fā)生精度丟失。
預(yù)定義的時(shí)間單位
為了方便使用,代碼中預(yù)定義了常見的時(shí)間單位特化:
using nano = ratio<1, 1000000000>; using micro = ratio<1, 1000000>; using milli = ratio<1, 1000>; using nanoseconds = duration<int64_t, nano>; using microseconds = duration<int64_t, micro>; using milliseconds = duration<int64_t, milli>; using seconds = duration<int64_t, ratio<1>>; using minutes = duration<int64_t, ratio<60>>; using hours = duration<int64_t, ratio<3600>>; using days = duration<int64_t, ratio<86400>>;
這些定義使得我們可以用有意義的類型名表示不同精度的時(shí)間間隔,例如:
milliseconds ms(1000); /// 1000毫秒 seconds s(1); /// 1秒 hours h(24); /// 24小時(shí)
使用示例
下面給出關(guān)于 duration 類的使用方法:
1. 基本時(shí)間間隔創(chuàng)建
/// 創(chuàng)建不同單位的時(shí)間間隔 milliseconds ms(500); /// 500毫秒 seconds s(1); /// 1秒 minutes m(10); /// 10分鐘 /// 從其他duration構(gòu)造 seconds s2(ms); /// 500毫秒轉(zhuǎn)換為0秒(發(fā)生截?cái)啵? milliseconds ms2(s); /// 1秒轉(zhuǎn)換為1000毫秒
2. 時(shí)間間隔計(jì)算
seconds s1(5); seconds s2(3); /// 加法運(yùn)算 seconds s_sum = s1 + s2; /// 8秒 /// 減法運(yùn)算 seconds s_diff = s1 - s2; /// 2秒 /// 復(fù)合賦值 s1 += s2; /// s1現(xiàn)在為8秒 /// 不同單位的運(yùn)算 milliseconds ms(1500); seconds s_total = s1 + ms; /// 自動(dòng)轉(zhuǎn)換為秒后相加,結(jié)果為9.5秒(假設(shè)使用浮點(diǎn)類型)
3. 單位轉(zhuǎn)換
seconds s(1); /// 轉(zhuǎn)換為毫秒 milliseconds ms = duration_cast<milliseconds>(s); /// 1000毫秒 /// 轉(zhuǎn)換為納秒 nanoseconds ns = duration_cast<nanoseconds>(s); /// 1000000000納秒 /// 精度損失示例 milliseconds ms(500); seconds s = duration_cast<seconds>(ms); /// 0秒
設(shè)計(jì)亮點(diǎn)與技術(shù)總結(jié)
1. 編譯期計(jì)算優(yōu)化
通過使用 ratio 模板中的(ratio_divide、ratio_less 等方法),許多計(jì)算可以在編譯期完成,在編譯期計(jì)算可以:
- 提高運(yùn)行時(shí)效率,避免重復(fù)計(jì)算
- 提前發(fā)現(xiàn)潛在的單位轉(zhuǎn)換錯(cuò)誤
- 支持編譯期常量表達(dá)式(
constexpr)
2. 類型安全設(shè)計(jì)
- 不同單位的
duration是不同的類型,避免了隱式轉(zhuǎn)換錯(cuò)誤 - 通過
explicit構(gòu)造函數(shù)防止意外類型轉(zhuǎn)換 - 精確的模板類型推導(dǎo)確保了運(yùn)算的安全性
3. 擴(kuò)展性設(shè)計(jì)
- 基于模板的設(shè)計(jì)使得可以輕松支持自定義時(shí)間單位
- 統(tǒng)一的接口設(shè)計(jì)使得新的時(shí)間單位可以無縫融入現(xiàn)有框架
- 通過
duration_cast實(shí)現(xiàn)任意單位之間的轉(zhuǎn)換
與標(biāo)準(zhǔn)庫 <chrono> 的對(duì)比
本文復(fù)現(xiàn)的 duration 類實(shí)現(xiàn)了標(biāo)準(zhǔn)庫 <chrono> 中 duration 類的核心功能,但也存在一些差異:
- 命名空間:復(fù)現(xiàn)版本使用
mychrono命名空間,這里是我基于std::chrono實(shí)現(xiàn)的我自己的mychrono類 - 完整度:標(biāo)準(zhǔn)庫實(shí)現(xiàn)更為完整,包含更多的特化和輔助功能,我以學(xué)習(xí)和理解原理為主,故只完成部分內(nèi)容
- 錯(cuò)誤處理:標(biāo)準(zhǔn)庫包含更完善的錯(cuò)誤處理機(jī)制
- 浮點(diǎn)支持:復(fù)現(xiàn)版本主要使用整數(shù)類型,標(biāo)準(zhǔn)庫同時(shí)支持浮點(diǎn)類型
總結(jié)
duration 類是 C++ 時(shí)間處理的基礎(chǔ)組件,其設(shè)計(jì)思想體現(xiàn)了現(xiàn)代 C++ 模板編程的強(qiáng)大實(shí)力。通過將時(shí)間單位和數(shù)值表示分離,duration 實(shí)現(xiàn)了靈活而類型安全的時(shí)間間隔表示和計(jì)算。完成對(duì)duration的理解學(xué)習(xí)之后,對(duì)模板元編程方法理解更深刻。
最后,本文僅僅是我在學(xué)習(xí)復(fù)習(xí)相關(guān)知識(shí)點(diǎn)的時(shí)候進(jìn)行的自我總結(jié)和整理,存在很多不好的地方,如果錯(cuò)誤請(qǐng)指出,接收一切批評(píng)并加以改正,認(rèn)真學(xué)技術(shù),加油。如有侵權(quán),請(qǐng)聯(lián)系我刪除~
到此這篇關(guān)于C++ 時(shí)間庫實(shí)現(xiàn):duration 類的原理與復(fù)現(xiàn)解析的文章就介紹到這了,更多相關(guān)C++ 時(shí)間庫duration 類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析C++中構(gòu)造函數(shù)的默認(rèn)參數(shù)和構(gòu)造函數(shù)的重載
這篇文章主要介紹了解析C++中構(gòu)造函數(shù)的默認(rèn)參數(shù)和構(gòu)造函數(shù)的重載,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09
C++ 虛函數(shù)與純虛函數(shù)的使用與區(qū)別
本文主要介紹了C++ 虛函數(shù)與純虛函數(shù)的使用與區(qū)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
VC中控制臺(tái)程序創(chuàng)建窗口的實(shí)例方法
在本篇文章里小編給大家分享的是關(guān)于VC中控制臺(tái)程序創(chuàng)建窗口的實(shí)例方法及相關(guān)代碼內(nèi)容,有需要的朋友學(xué)習(xí)下吧。2021-12-12
opencv實(shí)現(xiàn)機(jī)器視覺檢測和計(jì)數(shù)的方法
在機(jī)器視覺中,有時(shí)需要對(duì)產(chǎn)品進(jìn)行檢測和計(jì)數(shù)。其難點(diǎn)無非是對(duì)于產(chǎn)品的圖像分割。本文就來介紹一下機(jī)器視覺檢測和計(jì)數(shù)的實(shí)現(xiàn),感興趣的可以參考一下2021-05-05
詳解C語言中的wait()函數(shù)和waitpid()函數(shù)
這篇文章主要介紹了C語言中的wait()函數(shù)和waitpid()函數(shù),注意其在中斷進(jìn)程方面用法的不同,需要的朋友可以參考下2015-08-08
C++設(shè)計(jì)模式之建造者模式(Builder)
這篇文章主要介紹了C++設(shè)計(jì)模式之建造者模式Builder的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03

