C++11 标准库源代码剖析:连载之五

std::chrono

说起来有点令人难以置信,直到C++ 11之前,标准库中唯一可以处理时间的就是<ctime>提供的有限的几个函数,而即使这有限的几个函数还是C函数,这使得在C++中处理时间成为一件头疼的事情。不过现在情况有了很大改观,因为有了std::chrono

chrono简介

chrono是一个基于模板的,面向对象的,设计优雅且功能强大的time librarychrono内部定义了三种和时间相关的类型:

  • duration:一个duration就代表了一个时间段,比如2分钟,4小时等等。

  • clock: clock的作用就相当于我们日常使用的手表:显示时间。chrono内部定义了三种clocksystem clocksteady clockhigh-resolution-clock

  • time pointtime point表示某个特定的时间点。

std::duration

前面说过,duration代表了一段时间,比如2分钟,4小时等。注意我不仅指出了数量(“2”、“4”),还指出了单位(“分钟”,“小时”),也就是说一个duration的定义应该包括两个部分:数量和单位。我们来看chrono是怎样做到这一点的。

// file: chrono

namespace chrono {

    template<class _Rep, class _Period = ratio<1> >
    class duration {
    public:
        typedef _Rep     rep;
        typedef _Period  period;
    private:
        rep __rep_;
    
    // ...
    };
}

duration的声明包含两个模板参数,第一个模板参数是C++的原生数值类型,如long, long long等,代表了duration的数值部分。第二个模板参数_Period又是一个模板类std::ratio,它的定义如下:

// file: ratio

namespace chrono {

    // ratio以模板的方式定义了有理数,比如ratio<1,60>就表示有理数 ‘1/60’
    // _Num代表 'numerator'(分子)
    // _Den代表 'denominator'(分母)
    template<intmax_t _Num, intmax_t _Den = 1>
    class ration {
    
        // 求__Num的绝对值
        static constexpr const intmax_t __na = __static_abs<_Num>::value;
    
        // 求_Den的绝对值
        static constexpr const intmax_t __da = __static_abs<_Den>::value;
    
        // __static_sign的作用是求符号运算
        static constexpr const intmax_t __s = __static_sign<_Num>::value * __static_sign<_Den>::value;
    
        // 求分子、分母的最大公约数
        static constexpr const intmax_t __gcd = __static_gcd<__na, __da>::value;
    
    public:
    
        // num是化简后的_Num
        static constexpr const intmax_t num = __s * __na / __gcd;
    
        // den是化简后的_Den
        static constexpr const intmax_t den = __da / __gcd;
    };
}

ratio用两个整数型模板参数来表示一个有理数的分子和分母部分,比如ratio<1, 1000>就表示有理数0.001。理解了这一点,我们再来看duration的定义:

template<class _Rep, class _Period = ratio<1> > class duration 

ratio在这里的确切含义为:以秒为单位的放大倍率,比如ratio<60, 1>表示一个1秒的60倍,也就是1分钟,而ratio<1, 1000>表示1秒的千分之一倍,也就是1毫秒。所以duration<long, ratio<60, 1>>就定义了一个类型为longduration,而这个duration的单位为“分钟”。很巧妙的定义,是不是?

为了方便我们使用,对常用的时间单位,标准库都已经替我们定义好了:

namespace chrono {

    // 1nano = 1/1,000,000,000 秒
    typedef ratio<1LL, 1000000000LL> nano;

    // 1micro = 1/1,000,000秒
    typedef ratio<1LL, 1000000LL> micro;

    // 1milli = 1/1,000秒
    typedef ratio<1LL, 1000LL> milli;

    // 1centi = 1/100秒
    typedef ratio<1LL, 100LL> centi;

    // 1kilo = 1,000秒
    typedef ratio<1000LL, 1LL> kilo;

    // 1mega = 1,000,000秒
    typedef ratio<1000000LL, 1LL> mega;
    
    // ...
    
    typedef duration<long long,         nano> nanoseconds;
    typedef duration<long long,        micro> microseconds;
    typedef duration<long long,        milli> milliseconds;
    typedef duration<long long              > seconds;
    typedef duration<     long, ratio<  60> > minutes;
    typedef duration<     long, ratio<3600> > hours;
    
    // ...
}

duration的用法

标准委员会重写了标准库中全部和时间有关的函数,使之可以和duration协同工作,这使得duration的使用非常简单直观,举个例子:

#include <iostream>
#include <chrono>
#include <thread>
 
int main()
{
    // 定义一个"chrono::seconds"
    std::chrono::seconds two_seconds{2}; 
    std::cout << "Start waiting..." << std::endl;
    auto start = std::chrono::high_resolution_clock::now();
    
    // sleep_for函数现在接受一个`duration`类型的参数
    std::this_thread::sleep_for(two_seconds);
    
    auto end = std::chrono::high_resolution_clock::now();
    
    // duration支持算术运算
    std::chrono::duration<double, std::milli> elapsed = end-start;
    std::cout << "Waited " << elapsed.count() << " ms\n";
}

// output
Start waiting...
Waited 2002.58 ms

为什么你应该使用duration

  1. duration是轻量级的,在前面的源代码中我们已经看到了,duration内部仅有一个数值类型的变量,很是轻量级。至于ratio,因为其成员变量都是静态的,所以你不用担心系统内会有很多的ratio存在。

  2. duration是类型安全的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容