C++ 時(shí)間庫(kù)duration 類(lèi)的原理與復(fù)現(xiàn)(最新整理)
C++ 時(shí)間庫(kù)實(shí)現(xiàn):duration 類(lèi)的原理與復(fù)現(xiàn)解析
在現(xiàn)代編程中,時(shí)間處理是一個(gè)常見(jiàn)且重要的需求。C++11 標(biāo)準(zhǔn)庫(kù)引入了 <chrono> 頭文件,提供了一套處理時(shí)間的工具,其中 duration 類(lèi)是時(shí)間表示的核心組件之一。在上一篇文章中,分析和復(fù)現(xiàn)了 <ratio>, 本文將基于 ratio 深入解析 duration 類(lèi)的實(shí)現(xiàn)原理,并詳細(xì)介紹筆者自己復(fù)現(xiàn)這一功能強(qiáng)大的時(shí)間間隔表示類(lèi)的完整過(guò)程。
duration 類(lèi)的基本概念
duration 類(lèi)模板用于表示時(shí)間間隔,它以模板參數(shù)的形式定義了兩個(gè)關(guān)鍵要素:
Rep_:表示時(shí)間間隔的數(shù)值類(lèi)型(如int64_t)Period_:表示時(shí)間間隔的單位(通過(guò)ratio模板定義)
這種設(shè)計(jì)使得 duration 能夠靈活處理不同精度和單位的時(shí)間間隔,從納秒到天數(shù)都能統(tǒng)一表示和操作。標(biāo)準(zhǔn)庫(kù)中通過(guò)特化 ratio 模板定義了常見(jiàn)的時(shí)間單位:
- 納秒:
nano = ratio<1, 1000000000> - 微秒:
micro = ratio<1, 1000000> - 毫秒:
milli = ratio<1, 1000> - 秒:
ratio<1> - 分鐘:
ratio<60> - 小時(shí):
ratio<3600> - 天:
ratio<86400>
duration 類(lèi)的核心實(shí)現(xiàn)原理
1. 模板參數(shù)設(shè)計(jì)
duration 類(lèi)的模板定義如下:
template<typename Rep_, typename Period_ = ratio<1>> class duration;
Rep_:存儲(chǔ)時(shí)間間隔的具體數(shù)值,通常為整數(shù)類(lèi)型(如int64_t)Period_:時(shí)間單位,基于ratio模板實(shí)現(xiàn),默認(rèn)單位為秒
采用參數(shù)化設(shè)計(jì)使得 duration 具有高度的靈活性和類(lèi)型安全性,不同單位的 duration 是不同的類(lèi)型,避免了隱式類(lèi)型轉(zhuǎn)換帶來(lái)的錯(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>;
/// 類(lèi)型轉(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_);
}- 通過(guò)
ratio_divide計(jì)算兩個(gè)時(shí)間單位的轉(zhuǎn)換因子(時(shí)間轉(zhuǎn)換因子用于在不同時(shí)間單位或者系統(tǒng)之間進(jìn)行轉(zhuǎn)換,計(jì)算的原理和場(chǎng)景是強(qiáng)相關(guān)的) - 使用編譯期計(jì)算的
CF::num和CF::den完成單位轉(zhuǎn)換,分別代表源單位與目標(biāo)單位的比例關(guān)系中的分子部分與分母部分 - 利用
static_cast進(jìn)行安全的類(lèi)型轉(zhuǎn)換
通過(guò)這種分子/分母的組合實(shí)現(xiàn)方式確保了單位轉(zhuǎn)換在編譯期完成,既保證了運(yùn)行時(shí)的效率,同時(shí)又避免了浮點(diǎn)數(shù)的誤差,實(shí)現(xiàn)了類(lèi)型安全并且高精度的時(shí)間單位轉(zhuǎn)換。
3. 數(shù)值表示與存儲(chǔ)
duration 類(lèi)內(nèi)部使用 Rep_ 類(lèi)型存儲(chǔ)時(shí)間間隔的數(shù)值:
private:
Rep_ rep_;通過(guò)公開(kāi)的 count() 方法獲取存儲(chǔ)的數(shù)值:
constexpr Rep_ count() const {
return rep_;
}4. 構(gòu)造函數(shù)設(shè)計(jì)
duration 提供了多種構(gòu)造方式以滿(mǎn)足不同需求:
/// 默認(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 類(lèi)實(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)算符類(lèi)似,不重復(fù)貼出實(shí)現(xiàn)源碼3. 復(fù)合賦值運(yùn)算符
實(shí)現(xiàn)復(fù)合運(yùn)算符進(jìn)行的方式都是類(lèi)似的,這里給出+=的實(shí)現(xiàn)方式,其他運(yùn)算符類(lèi)似。
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ù)值類(lèi)型
using CommonRep = typename std::common_type<Rep1_, Rep2_>::type;
using CommonDuration = mychrono::duration<CommonRep, CommonPeriod>;
/// 轉(zhuǎn)換到統(tǒng)一類(lèi)型后計(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ù)值類(lèi)型(使用
std::common_type) - 將兩個(gè)操作數(shù)轉(zhuǎn)換為統(tǒng)一類(lèi)型
- 執(zhí)行算術(shù)運(yùn)算并返回結(jié)果
精度選擇與類(lèi)型推導(dǎo)
1. 更高精度類(lèi)型的選擇
higher_precision_duration 結(jié)構(gòu)體用于在兩個(gè) duration 類(lèi)型中選擇精度更高的類(lèi)型:
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;
};- 通過(guò)
ratio_less比較兩個(gè)時(shí)間單位的周期 - 選擇周期更?。ň雀撸┑?
duration類(lèi)型作為結(jié)果
2. 數(shù)值類(lèi)型的安全處理
在進(jìn)行算術(shù)運(yùn)算時(shí),使用 std::common_type 確定安全的數(shù)值類(lèi)型,定義與頭文件如下:
#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è)類(lèi)型 T1、T2…,Tn,std::common_type會(huì)推導(dǎo)出一個(gè)公共類(lèi)型,使得所有的Ti類(lèi)都可以隱式轉(zhuǎn)換到公共類(lèi)型,同時(shí)是可以同時(shí)滿(mǎn)足所有類(lèi)型的最小公共類(lèi)型。
所以,在復(fù)現(xiàn)過(guò)程中使用如下方式,找到公共類(lèi)型:
using CommonRep = typename std::common_type<Rep1_, Rep2_>::type;
這種方式確保了不同數(shù)值類(lèi)型(如 int 和 long long)之間的運(yùn)算不會(huì)發(fā)生精度丟失。
預(yù)定義的時(shí)間單位
為了方便使用,代碼中預(yù)定義了常見(jiàn)的時(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>>;
這些定義使得我們可以用有意義的類(lèi)型名表示不同精度的時(shí)間間隔,例如:
milliseconds ms(1000); /// 1000毫秒 seconds s(1); /// 1秒 hours h(24); /// 24小時(shí)
使用示例
下面給出關(guān)于 duration 類(lèi)的使用方法:
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)類(lèi)型)
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)化
通過(guò)使用 ratio 模板中的(ratio_divide、ratio_less 等方法),許多計(jì)算可以在編譯期完成,在編譯期計(jì)算可以:
- 提高運(yùn)行時(shí)效率,避免重復(fù)計(jì)算
- 提前發(fā)現(xiàn)潛在的單位轉(zhuǎn)換錯(cuò)誤
- 支持編譯期常量表達(dá)式(
constexpr)
2. 類(lèi)型安全設(shè)計(jì)
- 不同單位的
duration是不同的類(lèi)型,避免了隱式轉(zhuǎn)換錯(cuò)誤 - 通過(guò)
explicit構(gòu)造函數(shù)防止意外類(lèi)型轉(zhuǎn)換 - 精確的模板類(lèi)型推導(dǎo)確保了運(yùn)算的安全性
3. 擴(kuò)展性設(shè)計(jì)
- 基于模板的設(shè)計(jì)使得可以輕松支持自定義時(shí)間單位
- 統(tǒng)一的接口設(shè)計(jì)使得新的時(shí)間單位可以無(wú)縫融入現(xiàn)有框架
- 通過(guò)
duration_cast實(shí)現(xiàn)任意單位之間的轉(zhuǎn)換
與標(biāo)準(zhǔn)庫(kù) <chrono> 的對(duì)比
本文復(fù)現(xiàn)的 duration 類(lèi)實(shí)現(xiàn)了標(biāo)準(zhǔn)庫(kù) <chrono> 中 duration 類(lèi)的核心功能,但也存在一些差異:
- 命名空間:復(fù)現(xiàn)版本使用
mychrono命名空間,這里是我基于std::chrono實(shí)現(xiàn)的我自己的mychrono類(lèi) - 完整度:標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)更為完整,包含更多的特化和輔助功能,我以學(xué)習(xí)和理解原理為主,故只完成部分內(nèi)容
- 錯(cuò)誤處理:標(biāo)準(zhǔn)庫(kù)包含更完善的錯(cuò)誤處理機(jī)制
- 浮點(diǎn)支持:復(fù)現(xiàn)版本主要使用整數(shù)類(lèi)型,標(biāo)準(zhǔn)庫(kù)同時(shí)支持浮點(diǎn)類(lèi)型
總結(jié)
duration 類(lèi)是 C++ 時(shí)間處理的基礎(chǔ)組件,其設(shè)計(jì)思想體現(xiàn)了現(xiàn)代 C++ 模板編程的強(qiáng)大實(shí)力。通過(guò)將時(shí)間單位和數(shù)值表示分離,duration 實(shí)現(xiàn)了靈活而類(lèi)型安全的時(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í)間庫(kù)實(shí)現(xiàn):duration 類(lèi)的原理與復(fù)現(xiàn)解析的文章就介紹到這了,更多相關(guān)C++ 時(shí)間庫(kù)duration 類(lèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
常用Hash算法(C語(yǔ)言的簡(jiǎn)單實(shí)現(xiàn))
下面小編就為大家?guī)?lái)一篇常用Hash算法(C語(yǔ)言的簡(jiǎn)單實(shí)現(xiàn))。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09
c語(yǔ)言中的二級(jí)指針做函數(shù)參數(shù)說(shuō)明
這篇文章主要介紹了c語(yǔ)言中的二級(jí)指針做函數(shù)參數(shù)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Qt利用QSortFilterProxyModel代理實(shí)現(xiàn)自定義排序與聯(lián)合過(guò)濾
QsortFilterProxyModel類(lèi)用來(lái)為model和view之間提供強(qiáng)大的排序和過(guò)濾支持。這篇文章將利用QSortFilterProxyModel代理實(shí)現(xiàn)自定義排序與聯(lián)合過(guò)濾,需要的可以參考一下2022-11-11
淺談VS中添加頭文件時(shí)顯示無(wú)法找到文件的問(wèn)題
下面小編就為大家?guī)?lái)一篇淺談VS中添加頭文件時(shí)顯示無(wú)法找到文件的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解
大家好,本篇文章主要講的是C++中成員函數(shù)和友元函數(shù)的使用及區(qū)別詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01
C++中為什么要使用動(dòng)態(tài)內(nèi)存
大家好,本篇文章主要講的是C++中為什么要使用動(dòng)態(tài)內(nèi)存,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-02-02
C語(yǔ)言編程數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)詳解小白篇
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ),非常適合初學(xué)數(shù)據(jù)結(jié)構(gòu)的小白,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-09-09
C語(yǔ)言實(shí)現(xiàn)繪制LoveBeat愛(ài)心曲線的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何溧陽(yáng)C語(yǔ)言實(shí)現(xiàn)繪制LoveBeat愛(ài)心曲線,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03

