轉載請注明出處:http://blog.csdn.net/luotuo44/article/details/46854229
C++ 11添加了三個與時間相關的類型:時間段、時鍾、時間點。
以史為鑒
現有的系統API中,時間太過於碎片化了。有time_t(秒)、struct timeval(微秒)、struct timespec(納秒)這幾個時間單位,他們的接口非常不統一,點擊這里能夠體會一下。主要原因:是由於新業務的需求,要求提供不同精度的時間。
於是每次出現新需求就定義一個新類型。
為此,C++11提出一個”一統江湖”的做法,避免這類現象的發生。
時間精度事實上也就是時間分辨率。
拋開時間量綱單論分辨率。事實上就是一個比率。如:
這些比率加上距離量綱就變成距離分辨率,加上時間量綱就變成時間分辨率了。為此,C++11定義了一個新的模板類ratio。用於表示比率。定義例如以下:
//#include<ration>
template<std::intmax_t Num, std::intmax_t Denom = 1> //前者是分子,后者是分母
class ratio;
為了方便,C++標准委員會還提前定義了以下這些分辨率,供用戶使用。
//#include<ratio>
typedef ratio<1, 1000000000000000000> atto;
typedef ratio<1, 1000000000000000> femto;
typedef ratio<1, 1000000000000> pico;
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
typedef ratio<1, 100> centi;
typedef ratio<1, 10> deci;
typedef ratio< 10, 1> deca;
typedef ratio< 100, 1> hecto;
typedef ratio< 1000, 1> kilo;
typedef ratio< 1000000, 1> mega;
typedef ratio< 1000000000, 1> giga;
typedef ratio< 1000000000000, 1> tera;
typedef ratio< 1000000000000000, 1> peta;
typedef ratio< 1000000000000000000, 1> exa;
時間段
30分鍾。0.5秒都表示一段時間。
由此能夠得到:一段時間是由”數值+時間單位”組成的。反映在編程上就是要存儲兩個變量。顯然,數值有整數和小數兩種,到底是使用整數(int)還是小數(double)應該由用戶指定。無疑。用模板表示之是非常合適的。結合前面提到的時間精度,不難得出一段時間應該定義為例如以下形式:
//#include<chrono>
template<class Rep, class Period = std::ratio<1> >
class duration;
於是,30秒就有以下幾種表示方式:
//Author: luotuo44 http://blog.csdn.net/luotuo44
std::chrono::duration<int, std::ratio<1,1>> a(30);//30秒
std::chrono::duration<int> b(30);//30秒
std::chrono::duration<int, std::ratio<1, 1000>> c(30000);//30 000毫秒
std::chrono::duration<double, std::ratio<60,1>> d(0.5);//半分鍾
同樣,C++標准委員會提前定義了以下這些類型。供我們直接使用。
std::chrono::nanoseconds duration</*signed integer type of at least 64 bits*/, std::nano>
std::chrono::microseconds duration</*signed integer type of at least 55 bits*/, std::micro>
std::chrono::milliseconds duration</*signed integer type of at least 45 bits*/, std::milli>
std::chrono::seconds duration</*signed integer type of at least 35 bits*/>
std::chrono::minutes duration</*signed integer type of at least 29 bits*/, std::ratio<60>>
std::chrono::hours duration</*signed integer type of at least 23 bits*/, std::ratio<3600>>
又是討厭的implementation-defined,gcc4.9.0的一個實現為:
typedef duration<int64_t, nano> nanoseconds; typedef duration<int64_t, micro> microseconds; typedef duration<int64_t, milli> milliseconds; typedef duration<int64_t> seconds; typedef duration<int64_t, ratio< 60>> minutes; typedef duration<int64_t, ratio<3600>> hours;
當定義了一個時間段后,怎樣從中得知該時間段類型的rep和period模板參數的類型呢?事實上,當我們用詳細的類型實例化duration模板類后,實例化的類就會有rep和period這兩個成員類型。
於是,能夠借助C++11中的關鍵字decltype以及直接從定義中獲取。
//Author: luotuo44 http://blog.csdn.net/luotuo44
typedef std::chrono::duration<int, std::ratio<60>> Minus;
Minus::rep a = 30; //a是int類型
Minus::period per;//per的類型是 std::ration<60, 1>。
即per.num 為60, per.den 為1 Minus twenty_seconds(20); decltype(twenty_seconds.count()) b = 30;//后面會介紹count()的返回值類型, b的類型為int decltype(twenty_seconds)::rep e = 40;//e的類型為int
duration模板類提供了一個成員函數count()用戶獲取該時間段的值,這個值也稱為滴答數。它的返回值類型就是實例化后成員類型rep。
//Author: luotuo44 http://blog.csdn.net/luotuo44
std::chrono::seconds twenty_sec(20);
int c = twenty_sec.count();//c等於20
std::chrono::duration<double> half_sec(0.5);
double d = half_sec.count();//d等於0.5
時鍾
時鍾有電子表、懷表、秒表、原子鍾等。這些時鍾的時間精度有所不同。此外,對於電子表的時間是無法取小數的,而懷表則能夠人為地取小數。這說明不同的時鍾也會有前面時間段類型duration的rep和period這兩個屬性。而且也是以成員類型的形式出現。但時鍾類本身不是模板類。
C++11為我們提供了三種時鍾類型:system_clock、steady_clock、high_resolution_clock。 這三個時間類都提供了rep、period、duration成員類型。
由於各個系統能提供的時間精度可能不同。所以period的真正類型是implementation-defined的。這三個時鍾類都提供了一個靜態成員函數now()用於獲取當前時間,該函數的返回值是一個time_point類型。后面會介紹。
注意:。盡管這三個時鍾都非常多同樣的成員類型和成員函數。但它們是沒有親緣關系的。
這三個時鍾類型都是類。並不是模板類。
這三個時鍾有什么差別呢?system_clock就相似Windows系統右下角那個時鍾。是系統時間。
明顯那個時鍾是能夠亂設置的。
明明是早上10點,卻能夠設置成下午3點。steady_clock則針對system_clock能夠任意設置這個缺陷而提出來的,他表示時鍾是不能設置的。
想了解很多其它,能夠點擊這里。high_resolution_clock則是一個高分辨率時鍾。
system_clock除了now()函數外,還提供了to_time_t()靜態成員函數。用於將系統時間轉換成熟悉的std::time_t類型。得到了std::time_t類型的值。就能夠非常方便地打印當前時間了。
//Author: luotuo44 http://blog.csdn.net/luotuo44
auto tp = std::chrono::system_clock::now();
std::time_t cur_time = std::chrono::system_clock::to_time_t(tp);
std::string str_time = std::ctime(&cur_time);
std::cout<<str_time<<std::endl;
注意:不要將steady_clock、high_resolution_clock時鍾的now()返回值作為to_time_t的參數,這會導致編譯通只是。由於類型不匹配。后面會詳細解釋。
事實上。假設讀者有把剛才那個鏈接點開,並去了解的steady_clock的話,不用了解now()返回值類型就能知道這樣做肯定是不行的了。
steady_clock的實現是使用monotonic時間,而monotonic時間通常是從boot啟動后開始計數的。
明顯這不能獲取日歷時間(年月日時分秒)。
那么steady_clock有什么用途呢?時間比較!而且是不受用戶調整系統時鍾影響的時間比較。
簡單的樣例例如以下:
//Author: luotuo44 http://blog.csdn.net/luotuo44
auto begin = std::chrono::steady_clock::now();
for(int i = 0; i < 10000000; ++i)
{
//復雜的數學運算
}
auto end = std::chrono::steady_clock::now();
auto diff = (end - begin).count();//end-begin得到一個duration類型
std::cout<<diff<<std::endl;
即使在做數學運算的時候,有人改動了系統時間。也能准確計算出for循環消耗的時間。
時間點
前面已經指出:時鍾類的now()函數的返回值是一個time_point類(型)。不言而喻。時間點(time_point)必定是時鍾相關的。從懷表讀取到的時間(點)唯獨小時、分鍾、秒,手機則能提供日期+時分秒。
此外。不同一時候鍾提供的時間精度也有所不同。所以,時間點(time_point)類至少得有時鍾類型、時間精度這兩個屬性。
同前面介紹的時間段模板類一樣。這兩個屬性也是以模板參數的形式出現。
time_point的定義例如以下:
//#include<chrono>
template< class Clock, class Duration = typename Clock::duration >
class time_point;
時間精度是duration類,默認的時鍾模板參數提供的duration。時鍾模板參數除了前面介紹的那三個時鍾(這是C++默認提供的)外,還能夠是碼農自定義的時鍾類。
同前面的時間段類和時鍾類一樣,時間點類也提供了rep、period、duration、clock這幾個成員類型。
前面已經指出,各個時鍾能提供的時間精度是implementation-defined的。
所以應該如以下代碼那樣獲取時鍾的now()函數返回值。
auto tp = std::chrono::system_clock::now();
std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
以下代碼是不對的,不具有可移植性。樣例來自stackoverflow。
std::chrono::time_point<std::chrono::system_clock,std::chrono::nanoseconds> time_point;
time_point = std::chrono::system_clock::now();
除了從時鍾類的now函數中獲取一個time_point,還能夠直接構造一個time_point對象。
time_point模板類的構造函數有無參的、用duration作為參數這兩個構造函數。對此,我感到非常奇怪:直接構造出來的time_point有什么含義呢?
time_point有一個time_since_epoch()成員函數。返回從epoch時間到此刻的時間段。
epoch時間是時鍾的開啟時間。
對於system_clock和high_resolution_clock來說,epoch是1970-01-01T00:00:00。熟悉C語言time()函數的讀者對此應該不陌生。
steady_clock時鍾則是boot啟動時間,所以不應該對steady_clock::now()返回的time_point調用time_since_epoch()。以下是霸王強上弓使用,打印出來的時間是1970年的。
//Author: luotuo44 http://blog.csdn.net/luotuo44
auto tp = std::chrono::steady_clock::now();
std::time_t cur_time = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch()).count();
std::string str_time = std::ctime(&cur_time);
cout<<str_time<<endl;
假設time_point不是從時鍾類的now()返回值得來的,而是直接構造出來的話,那么它調用time_since_epoch()的返回值就等同於構造函數中的duration參數。
運算
一段時間的加減乘除無疑是合理的,當然關系比較也是合理的。C++標准委員會還把取模運算也變成合法的了,不只模一個數值合法,模duration類也是合法的。於是有以下這些運算:
詳細的使用樣例就不寫了。讀者能夠參考en.cppreference.com
上面的運算都是二元運算,假設參與運算的兩個duration具有同樣的模板參數。那直接執行就可以。假設不同,那么將發生隱式轉換:int->double; 低分辨率->高分辨率。比方:
//Author: luotuo44 http://blog.csdn.net/luotuo44
std::chrono::duration<int> two(2);
std::chrono::duration<double, std::ratio<60>> thirty(0.5);
auto ad = two + thirty;
cout<<sizeof(decltype(ad)::rep)<<endl;//8
cout<<ad.count()<<"\t"<<decltype(ad)::period::num<<"/"<<decltype(ad)::period::den<<endl;//32 1/1
假設參與運算的兩個duration具有不同的模板參數。那么能不能進行+=和-=這里運算呢?a+=b等同於a=a+b。a+b肯定是能夠的,如今問題轉換為a=c是否可行?從低精度(包括Rep和Period)到高精度的隱式轉換是可行的,反之則是不能夠的。由於這將導致截斷。丟失部分信息。
//Author: luotuo44 http://blog.csdn.net/luotuo44
std::chrono::minutes a(30);
std::chrono::seconds b = a;//OK
std::chrono::minutes c = b;//compile error
std::chrono::duration<double, std::ratio<60>> d(3);
std::chrono::seconds e = d;//compile error
std::chrono::duration<double> f = d;//OK
假設確實須要進行轉換的話,那么須要使用duration_cast進行顯式轉換。
//Author: luotuo44 http://blog.csdn.net/luotuo44
std::chrono::seconds a(30);
std::chrono::minutes b = std::chrono::duration_cast<std::chrono::minutes>(a);
cout<<b.count()<<endl;//結果是0
//以下是system_clock時鍾類to_time_t函數的gcc實現
static std::time_t to_time_t(const time_point& __t) noexcept
{
return std::time_t(duration_cast<chrono::seconds> (__t.time_since_epoch()).count());
}
time_point類也有一些運算,例如以下圖:
gcc4.9.0實現time_point模板類時,time_point內部有一個duration類型的成員變量。所以對於tp+=d和dp-=d的實現,都是直接對內部的成員變量直接調用+=,-=。所以調用者必須保證精度的正確性,否則編譯出錯。