平安的飞轮动了

如果生命只剩下24小时,你会做什么?

 

平安的飞轮动了

 

开国伟人生命的最后24小时里花了2小时50分钟来读书。

 

野蛮其体魄,文明其精神,学到老,活到老。

 

终身学习的概念大家已经听过很多。

 

今天咱们聊聊,抛开无法掌控的投胎技术,人与人之间的差距是怎么拉开的?

 

明明小时候的考题跟人生诸多难题相比都不值一提,为什么当年第一名的背影显得这么遥不可及?

 

答案取决于两个事物发展的规律:万事开头难和量变产生质变。

 

打基础的时候,过程枯燥无味需要接受新事物,并且改变自己的认知模式,这一过程阻挡了大部分人;量变累积产生质变是哲学理论,同样适用于个人成长,当知识累积达到一定程度,认知产生了质的飞跃,完成了自我蜕变,这个时候人与人之间的差距迅速拉开。

 

学习是这样,企业发展也是这样,所以那些大型公司的成长速度往往会更快。

 

保险行业里也有一个坐在第一排的学生。

 

如今,它在医疗健康上的布局已经走到了人们的想象之外。

 

平安的飞轮动了

 

从中国平安的一季度财报来看,表现一如既往的稳健。

 

平安的飞轮动了

2021年第一季度,公司归属于母公司股东的净利润272.23亿元,同比增长4.5%。

 

其中保险主业持续向好,寿险及健康险新业务价值同比增长15.4%,产险营运利润同比增长15.2%。

 

保险主业的增长原因有很多:

 

一是代理人团队素质的提升,在2020年度业绩发布会上,管理层表示,计划在未来三年内代理人总数零增长,通过数字化管理提升整体代理人的素质。

 

二是通过产品结构的调整,加大了年金产品和寿险的推广力度等,成功抓住了老龄化趋势和新中产崛起机遇。

 

这些几乎所有保险企业都在做,平安作为行业龙头,做得更好一些。2019年初中国平安董事长马明哲就提出要全面进行数据化经营,即提高数据共享的时效性和准确性,实现经营管理的“先知、先觉、先行”,这两年数据化运营实施后带来的提速增效成果开始在业绩中显现。

但是千万不要以为这些就是平安全部的着力点,围绕着数据化经营,平安有着更为长远的考虑。

 

平安不一样的地方,同时也是我们最应该关注的地方,是其在医疗健康生态圈上的爆发。

 

这个是平安重点打造的业务板块,同时也是“有温度的金融”最直接的体现,为了构建这个生态圈,平安付出了近十年的努力。

 

平安的飞轮动了

 

平安医疗健康生态圈,个人能够直接感受到的是看病全流程革命式的创新。

 

这个流程的第一步是平安好医生App(现更名为平安健康App)提供的在线看病服务。

 

我们可以把平安好医生看作是一家开在互联网上的医院。

 

实际上,平安好医生不仅有涵盖3000种疾病知识图谱的AI医生,还有超过1800多位驻司医生,先后成立了全国第一家互联网眼科专科中心、互联网肿瘤专科等专科中心。

 

经过医生线上诊断后,平安好医生可以为病人提供药物治疗方案,而且可以通过平安好医生线上医保买药,一小时到家,比外卖平台上的药店靠谱很多。

 

在疫情期间,平安好医生AI系统已经为11.1亿人次提供医疗咨询服务。

 

对于无法单纯依靠药物治疗或者需要进一步检查的病症,平安好医生会引导病人进行线下就医。

 

线下医院也是平安医疗健康生态圈的重头戏。

 

在这一环节,平安智慧医疗可以用人工智能技术辅助医生进行检查治疗。

 

平安的医生AI助手“AskBob医生站”斩获人工智能领域的多个世界级奖项,在心血管诊疗方面的专业度可以媲美三级医院住院医生水平。

 

平安的OCT智能眼部筛查系统,可以在3分钟内完成检查,并获得智能筛查报告。

 

“AskBob医生站”联合中山大学附属肿瘤医院开发的鼻咽癌AI风险预测系统还可以通过人工智能,预测患者5年内的鼻咽癌病情恶化风险。

 

通过与全球领先的医学健康企业、知名三甲医院等机构展开合作,平安智慧医疗还开发出了覆盖3000多种疾病的诊断治疗模型及30多种全科常见病的用药推荐模型。

 

截至2021年3月末,平安智慧医疗已经累计覆盖161个城市,赋能超3.7万家医疗机构,惠及约75万名医生。

 

在诊断之后,治疗之前,病人还要解决的是医药费支付问题。

 

这里平安医保科技提供的智慧医保解决方案,实现医保和商业保险协同支付。

 

让病人的看病成本最小化,让医院的管理成本最小化。

 

以上是病人最能直接体验的平安医疗健康生态圈。在辅助医生之外,平安智慧医疗还承担着帮助政府部门实现公共卫生应急管理、医保支付制度智能化改革等重要任务,为此平安与各地医保局开展紧密合作。2021年第一季度,平安新中标了7个省级医保平台建设工程项目。

 

平安的飞轮动了

 

看到这里大家可能不太理解,在很多人的印象中,平安是全牌照金融服务集团,为什么会把医疗健康生态作为核心战略来搞。

 

平安的飞轮动了

 

平安有一个口号,叫“有温度的金融”,我的理解是平安的金融服务以后会更加侧重于用户需求服务,群众需要什么,平安就提供什么金融服务。

 

基于这个概念,我们再来看平安的医疗健康生态就很清晰了。

 

亚马逊贝佐斯有一个飞轮理论,即以提升用户体验为出发点,实现各个业务环节的互相推动。

 

平安的飞轮动了

 

这个飞轮有个特点,就是刚开始转时特别费力,但是当飞轮转动起来的时候,会越来越省力,同时增长也就越来越快。

 

对于平安来说,医疗健康生态圈可以让平安更清晰地了解大众和个人的健康趋势,同时带来保险等金融服务流量和质量的全面提升,金融服务的增长可以为医疗健康生态圈提供更多研发资金,给医院和病人带来更好的使用体验,更好的使用体验可以为医疗健康生态圈提供流量,由此形成一个巨大的飞轮。

 

通过这个飞轮,平安可以为用户提供一站式医疗服务;通过布局健康管理、慢病管理、重疾管理、养老管理等四大服务,全力以赴构建“有温度的保险”。以”平安臻享RUN”健康服务计划为例,平安人寿以“一名私人医生,健康、医疗、慢病、重疾四大服务场景”为基础,可以为客户提供提供私家医生、私人教练、门诊预约及陪诊、术后护理、重疾专案管理等12项核心亮点服务。

 

当然转动飞轮的过程并不容易,初期往往是转动飞轮最费力的时候,尤其是对于平安这样的庞然大物来说。

 

在互联网医疗领域中,平安一开始就采取了最重的运营模式,即以互联网和人工智能为连接手段,自行主导对“医院-医生-药店-支付”全产业链的覆盖和整合。

 

这也就决定了,平安医疗健康生态圈不仅要改变平安自身,更要为整个医疗行业带来永久性的革新,为了让这个飞轮动起来,平安每年将营业收入的1%用于创新科技的研发,其中很大一部分投入到了医疗生态圈的建设。

高额的成本和多年的研发形成了平安的护城河,也带来了初步成效,2020年疫情高峰期间,平安好医生累计访问量突破10亿。最正确的事往往也最困难,但只要持续地往前迈进,飞轮就会转得越来越快,未来十年,平安准备再投入一千亿进行医疗生态圈的建设。

 

当这个飞轮转动起来的时候,客户体验和流量结构都将产生质的飞跃,这个护城河也是平安未来最大的增长极。

 

凭借这个飞轮,平安终将成为一众险企心目中最美好的样子

 来源:老斯基财经(ID:laosijicj)

转自:https://mp.weixin.qq.com/s/j6fNfs4HFR523877RdkeKw

做一晚黄牛能坑多少昧心钱?

先声明一下,我不是黄牛,因为要帮外地朋友挂号,因而跟黄牛有过几次交锋,得知了他们合伙“作案”的一些套路。写下来不是让大家照葫芦画瓢去赚昧心钱,而是让大家多个心眼,别让黄牛给坑惨了。
交代一下背景:我在北京打工谋生,让我帮忙挂号的这位朋友在南方某省会城市安居乐业。她孩子两三年前就被当地医院诊断为多动症,开着药在吃。我这朋友呢,不太确定当地医院的诊断是否靠谱,这药到底是要一直吃还是咋整。于是,她就想着来北京的一家精神专科医院,也是全国最有名的精神科医院找专家给再细瞧瞧。
说实话,我觉得当地医院也不错,既然看了医生就好好听医生的该咋地就咋地,真没必要来北京折腾。但她总放心不下,觉得可能不太需要吃药,偶尔还给孩子停药。我一想,这不来一趟,她心里总不会踏实,这药吃吃停停的对孩子也不好。前年当她又一次说起这事儿的时候,我就决定留意一下,想着给她孩子挂个号,不过当时我就是在网上倒腾,在114上一直没给挂上号。
2020年疫情来了,跨省就医太不方便,这一拖就拖到了今年春节后。她孩子九岁多了,读三年级不太跟得上趟了,火烧眉毛的她就下决心一定要来北京给瞧瞧。我心想在114上就没挂到过号,每次都是秒光。于是我就决定去医院走一趟,看看有没有什么办法可想。好在医院离我上班的地方不远,我去医院门诊大厅问了问,原来医院在支付宝和114上同时放号,不过医院挂号窗口和咨询前台说支付宝更容易挂到,而且没有现场挂号,“都在网上挂”。
打探到消息后,我便发动朋友一起帮忙在支付宝上挂号。由于儿童门诊的专家比较少,且每周只有三天坐诊,接下来一个半月我们都没有挂到专家号,号源总是秒光。而且不光专家号挂不到,儿童门诊的普通号也挂不到。时间就这么来到了清明假期,突然有一天医院在支付宝上的生活号弹出消息,说因为系统维护,清明节后的第一个工作日(4月6号)的号源全部在当天由挂号窗口现场放出。
由于此前网上都是提前两天放号,我跟朋友说的是,我先挂到号了她再来北京,但一直没有在网上抢到号。不过,这次偶然现场放号,而且刚好有两个专家一个上午一个下午坐诊,这意味着只要排队排得足够早,就一定能挂到号。但是前提条件是,她必须提早带着孩子来北京候着,当天挂号当天瞧。于是,朋友提前一天来到北京,住在医院附近的宾馆。由于她要照顾孩子,且人生地不熟,排队的责任就落在了我身上。我心想,她们来这么一趟不容易,我一定要帮她们挂到号。于是,在放号的前一天,我又一次来到医院打探情况。
前面说了这么多,而且都是念念碎,只是想说明,每一个来医院看病的病人和家属都不容易。但是,有一些渣渣就盯上了这群原本就不幸的人。他们就是黄牛。
我是放号前一天下午到医院的,在大门口我跟保安打听第二天现场排号的事情,保安有一搭没一搭地敷衍我,不想说啥。我问在哪里排队,保安就随手一指,我问多早来能挂上号,保安只说七点开医院大门,七点半开挂号大厅的门,准时放号,接着就不吭声了。这时候,一直站在保安室外面抽烟的一位黑胖男子搭腔了:你现在站着门口开始排,肯定能挂上号。
我一看这家伙的神情,心想他应该是黄牛,可能有门路。其实,来医院探路之前,我和朋友也有心理准备,如果实在挂不到号,也只能找黄牛。说话间,我看保安这边没有太多信息提供,便跟那个黑胖男子对了个眼。他看我看过去,便忙不迭地说,“来来来,你先把车挪一下,咱慢慢谈。你停车的那里有摄像头,拍了就是3分200块。”我心想,好家伙,黄牛还仗义了?
我挪完车后刚把朋友孩子的情况跟他说完,他说这儿童门诊的号不好挂,想要号的话,就今天可以提前找我预定。嘿,还能预定。我说那得多少钱?儿童门诊专家杨**800块,是服务费,挂号的钱您再额外出。我心想,这也太贵了。能便宜点吗?黄牛抽了一口烟,慢悠悠地说,儿童专家号不好挂,都是这个价。说完他就眼望天。太贵了,我说考虑考虑。黄牛从兜里拿出一张“名片”递给我,说要号的话就打电话,最晚明天早晨,不然就没了。我接过名片,就开动车了。

做一晚黄牛能坑多少昧心钱?

做一晚黄牛能坑多少昧心钱?

黄牛给的名片
在车上,我和朋友商量,这个服务费也太贵了,心理预期最多500块,没想到居然要800。合计了一下,我跟她说,我明天三点来排,如果前面人多感觉挂不到号的话,就找黄牛,如果人不多,那就自己挂号。商量妥了之后,我就早早吃完饭躺下睡觉,为早起排队蓄力。
一夜梦不断,魑魅魍魉不断出现。两点一刻的闹钟还没响,我就醒了,简单抹了一把脸,我就出门。到医院附近的时候我看了下手表,刚好三点钟。医院的大门关着,门外有一小溜人排队。我小跑着来到队伍最后,数了数前面,一共有6个人。我心下稍安,就给朋友发信息,说只有六七个人,应该能挂到专家号,让她在好好睡一会,天亮了再过来。朋友估计也是一夜没睡,我马上就收到信息,她说希望可以上午和下午的专家都挂上,上午做检查,如果检查结果出来的慢,下午的专家也能瞧。
4月初的北京白天还好,凌晨真的是挺冷。我坐在从家里带来的小板凳上,开始打盹,前面的六个人也都静默不语。迷迷糊糊坐在小凳子上也不知道是过了多久,我看了看手表,才过了二十分钟。太难熬了。我开始观察前面的几个人。最头上的两个人把自己用帽子围巾过得严严实实,头碰头在窃窃私语。第三个人在刷手机,第四个人在打游戏,是个小伙子,第五个人是个老爹爹,坐在纸板上,第六个是个中年男子,在刷短视频,声音老大。

做一晚黄牛能坑多少昧心钱?

决定攀谈打发一下时间。我就问前面的大叔是给谁挂号。没想到这一问就打开了大叔的话匣子。大叔是浙江台州人,他24岁的女儿厌弃工作,从杭州开始走遍全国,大叔一路追到江西,追到甘肃,追到陕西,直到北京密云才寻到女儿。听他的描述,女儿应该是有抑郁症,于是就准备把她带到医院来看看。
大叔两点就到了,他说他前面的这位老爹爹是帮人挂号,我当时心想这钱也挣得不容易,后来才知道到我还是太年轻。随后,我后面又开始陆陆续续有人来排队了。五点左右,排队人数已超过二十人。我跟大叔说了一句,用小凳子占位,然后去旁边麦当劳买了份早餐,回来的时候发现前面的大叔不见了,换成了一位大婶。我说大婶你别插队啊,没想到大婶说我丈夫刚刚在这里。原来是夫妻,大婶倾诉欲比较强,把她丈夫跟我说过的事情,又跟我讲了一遍。只不过这一次,她夹杂着对丈夫的恨,孩子小时候还是好好的,就是因为丈夫脾气太差,经常发脾气打人,孩子才不愿意归家,变得话越来越少,人越来越消沉。她说她们前一阵也来排过队,不过那时候现场挂不到号,碰到黄牛,说300块服务费可以帮他们挂号。我跟她描述了一下我昨天碰到的人,她说是同一个人。聊着聊着,大叔又回来了,原来是去上厕所,大叔出现在视线之前,大婶早已闭上了嘴。在我和大叔夫妇聊天这一段时间,前面排队的人仿佛石化了一般,也不动也不说话。
终于熬到了六点,鼻涕都冻出来了,排队的人也有二三十米了。这时候,有骑着电动车自行车的人开始来到大门口喊保安开门,伸缩大门吱呀打开,骑车的人陆陆续续进去,人群骚动起来,后面的人不停的张望,门口也有点堵。保安说这是医院的内勤,人进去后,大门又吱吱呀呀地关上了。
人群恢复了平静,但我一看我前面,情况不对了。我记得前面是六个人,现在咋乌压压一片人了呢。我数了数,总共有十一个人了。除了大叔旁边多了他妻子之外,第五个老爹爹的旁边多了一个中年男子,之前一直睡在地上,我还以为是流浪汉,没想到也是排队的,看来也是200块代排位。第四个穿黄色衣服的小伙子旁边多了一男一女,也是中年人,之前没见过。第三个人旁边也多了一个生面孔,是个光头白净胖子。第一个人第二个人还是原来的人。
我一下懵了,这是怎么回事。正疑惑时,昨天看到的那个黑脸胖子又出现了。他就站在大门另一头冷眼看着队伍。我火不打一处来,心想,这前面多出来的这些人,除了台州这对夫妻之外,其他人肯定有毛病。于是,我大声对着前面一群人喊,谁他妈插队的滚后面去吗,这里都是排队一晚上的啊。周围的人都愣愣地看着我,不知道发生了什么。我又提高声调说,我三点就来了,前面只有六个人,现在多了五六个人,除了这位大婶,其他都是怎么回事啊。别他妈不要脸啊,排队了!
这时候,那个白皮光头新面孔回过头说,怎么啦,家里还不能有两人生病啊,我正想这是什么意思时,黄衣服小伙子旁新出现的中年妇女说,就是啊,咱们三人是一家人,没谁插队。周围的人都很疑惑地看着我们,这时黑脸胖子走过来,对前面那几个人说,都好好排队啊,别插队啊。我看那神情,绝对是一伙的,随后他又跟白脸光头交头接耳了几秒。
大概是认出我来了,黑脸胖子对我说,你要不服气就报警,你报警吧。我火顿时窜了起来,指着他骂,你一大老爷们当黄牛,挣这个昧心钱有意思吗。那黑脸胖子不屑地看着我,就一直重复地说,你要不服气就报警。这时候,后面一位穿冲锋衣的年轻人走上来说,怎么回事有人插队吗?我们这在后面都排了几个钟头了,谁他妈插队?我就指着前面的队伍说,之前6个人,现在11个人。黑脸胖子不知道啥时候走到队尾去了,台州大叔也用眼睛示意我,别说了,要不然可能会吃亏。我心想,这前面也没多几个人,应该能挂上号。
接下来队伍一直风平浪静,七点大门打开后,队伍依次再流调登记,然后就是等着七点半,安检之后进挂号大厅。终于七点半到了,带包的需要将包过安检机,我前面的大叔大婶带了包等机器吐包的同时,我就走到了他们前面。安检处离挂号窗口有二三十米左右的距离,前面的那九个人竟然超挂号窗口飞奔而去。
总共有三个挂号窗口开着,等我来到窗口时,左边一个排了三个人,右边一个排了四个人,中间一个排了两个人,我选了中间那一个窗口。没想到啊没想到,前面这两个人真是慢,先是向窗口内的护士咨询挂什么号,一分钟,接下来确定挂号了就开始付钱,由于窗口挂号只收现金,两人从兜里掏出来的是一大摞一元纸币,专家挂号费是60元,两人就慢慢地数了六十张,递进窗口,然后收费的护士又是一张张地数。
我听见旁边两个窗口哗啦啦地打印挂号条的声音,心里那个鸡。两分半钟后,终于轮到我,我以最快的速度说出了两个专家的名字,同时递进去早已准备好的120块挂号费,挂号条终于出来,一位专家是上午第九号,一位专家是下午第十三号。谢天谢地,完成了任务,一夜队没白排。

做一晚黄牛能坑多少昧心钱?

我挂到的号
朋友也到了,我把挂号单给她后就准备去上班。在医院大门口,黑脸胖子还在那抽着烟,看到我走出来,讪笑着说,挂到号了吗,没挂到我这儿有号。我看了他一眼,没回应他就走了。下午四点半我约朋友吃饭,没想到她说医生按照号单顺序看病,她还没看到医生。她是倒数第二个,马上就到她了。
听到这话,我倒吸一口凉气,这才意识到我差一点没有排到号。回想起早晨插队的人,挂号窗口在我前面慢慢数钱的人,以及旁边窗口飞快吐出的挂号单,我明白这就是一个团伙。他们先组织几个人排队,然后占领挂号窗口,昨天提前预定了号的客户他们今天一早帮忙挂,此外还可以把紧缺的专家号挂完,随后奇货可居地等着挂不到号的人来找他们需求帮助。由于这家医院挂号只需要提供患者的身份证号即可,黄牛拿着囤积的号去挂号窗口退号的同时,直接给目标客户挂上号即可。这是现场挂号的操作,至于黄牛在网上挂号的时候如何操作,我就不得而知了。
我简单算了一下,当天两个儿童专家坐诊,每个人是14个号,我挂的两个号一个是9号,一个是13号,这意味着我前面那九个人至少抢走了8+12=20个专家号。这 “服务费”一共就是20*800=16000。这还只是儿童门诊专家的服务费,还不包括成人门诊的专家号。一个晚上,十个人,就能昧到黑心钱一万六,分账肯定有不少,但黑脸胖子绝对占大头。此外,在没有儿童专家出诊的时候,虽然成人门诊服务费低一些(300块),但是也够黄牛倒腾了。
有人可能会问了,他们这号一定能卖出去吗?当我走出医院的时候,我看到队伍已经从挂号大厅排到街上,长度超过一百米,他们神色各异,面容忧愁,大部分人笼着手、缩着脖子在寒风中等着初升的太阳光照到身上。他们中肯定有人挂不到号而求助于黄牛,黄牛也绝不会亏本——如果没人找他们要号,他们退掉号即可,完全是无本买卖。
权威数据显示,近年来,中国精神障碍患病率总体呈上升趋势,精神障碍患病总人数过亿。
目前,国内尚缺乏全国性儿童和少年精神障碍患病率的调查数据,但区域性调查结果显示,近年来,我国儿童青少年精神障碍患病率正呈逐年增高趋势。数据显示,上世纪80年代初期焦虑抑郁问题的青少年占比千分之六,现在增加了十倍甚至十几倍。而又一项针对北京地区6183名中小学生的抽样调查也显示,其心理障碍患病率达15.43%,基本与发达国家心理障碍患病率持平。中国青少年研究中心和共青团中央国际联络部此前发布的《中国青年发展报告》也显示,中国17岁以下儿童青少年中,约3000万人受到各种情绪障碍和行为问题困扰。而全国各地专门从事儿童精神科工作的医生不足500人。于是,全国最好的精神科医院永远是一号难求。
我不敢说医院的保安和挂号员跟黄牛是否认识,也不敢推断他们是否熟知彼此,更不敢妄测他们是否有勾连。以我三次来医院的经历,三次与黑脸胖子的交锋来看,这群黄牛肯定不是独舞。
文章来自读者投稿
 

-END-

转自:https://mp.weixin.qq.com/s/j8RIrZi47UbyDZucVkGvkg

2021年,我的顶级开发工具都在这了

来自:程序员书库(ID:CodingBook) 

链接:https://medium.com/swlh/my-top-dev-tools-in-2020-ec1fc8571dd3

开发人员应始终了解他们使用的工具,工具用对了,你就可以更加轻松高效地完成工作,今天就和大家分享一下2021年我在日常工作中最常使用的工具。
1、Ubuntu Linux

2021年,我的顶级开发工具都在这了

在过去的四年中,我一直在使用Linux作为主要操作系统。我使用的第一个发行版是2012年的Ubuntu 12.04 LTS。我尝试了许多Linux版本。Kali,Debian,Pirot,Mint等。我发现Ubuntu是一个开发人员友好的发行版,其中包含许多开发人员工具。在Dell Vostro 5590中,我一直使用Ubuntu 20.04.1 LTS focus作为主要操作系统。
2. Vim

2021年,我的顶级开发工具都在这了

我使用Vim作为主要的文本编辑器已有大约两年了。掌握Vim会使生活变得容易得多。当你了解它的功能特性之后,你的工作就会变得更快。
3. VSCode

2021年,我的顶级开发工具都在这了

我更喜欢的另一个文本编辑器是VSCode。它是轻量级的,VSCode的一大特点就是你可以通过多种方式来调整设置,而且Vim可以和VScode集成在一起,它还带有一个集成终端,可用的扩展太多,使开发人员的工作变得更加轻松。我最喜欢的一些扩展是AWS Toolkit,ESLint,Live Server,Live Share,Prettier —代码格式化程序,YAML,Live Sass编译器,Docker等。
4. Docker

2021年,我的顶级开发工具都在这了

Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows机器上,也可以实现虚拟化。Docker在短时间内获得了如此多的人气,已经改变了我们打包软件的方式,现在一切都在向自动化方向发展,我几乎每天都在使用Docker。
5. Git

2021年,我的顶级开发工具都在这了

Git是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容。我远程代码存储操作都是选择Github和GitLab。
6. Postman

2021年,我的顶级开发工具都在这了

Postman简化了构建API的每个步骤并简化了协作,有了它你可以更快地创建更好的API,我使用Postman进行API开发、请求、调试,除了我还使用Insomnia和Insomnia Designer作为API文档。
7. Swagger Editor

2021年,我的顶级开发工具都在这了

Swagger Editor是一款非常不错的API开发工具,专门用于生成一个具有互动性的API控制台,开发者可以用来快速学习和尝试API。还可以生成客户端SDK代码用于各种不同的平台上的实现。Swagger Editor是使用OpenAPI规范(以前称为Swagger)的简单方法,它支持Swagger 2.0和OpenAPI 3.0。
我在docker容器上的本地计算机中使用了swagger编辑器。(下载地址:https://swagger.io/tools/swagger-editor/download/
8. Lucid Chart

2021年,我的顶级开发工具都在这了

作为软件工程师或开发人员,我们喜欢将要构建的复杂系统用可视化图表表示除了,Lucidchart是我用来绘制UML图表和AWS架构的工具。
9. Zoom

2021年,我的顶级开发工具都在这了

Zoom是现代企业视频通讯领域的领导者,为跨移动设备、台式机和会议室系统的视频/音频会议、协作、聊天和网络研讨会提供简单可靠的平台。是一个很常用的工具。
10. Figma

2021年,我的顶级开发工具都在这了

Figma是一个矢量图形编辑器和原型工具,主要是基于浏览器了,支持macOS和Windows桌面应用程序的离线功能,面向Android和iOS用户的Figma Mirror允许你在移动设备上查看Figma原型。
11. htop

2021年,我的顶级开发工具都在这了

htop是一个交互式系统监视、进程查看和进程管理器。它被设计为Unix下的任务管理器top的替代品,它显示了在计算机上运行的进程列表,通常按CPU使用量排序,她还可以垂直和水平滚动列表以查看所有进程和完整的命令行。
12. Chrome DevTools

2021年,我的顶级开发工具都在这了

Chrome DevTools是一套直接内置在谷歌Chrome浏览器中的Web开发工具。DevTools可以帮助你实时编辑页面,快速诊断问题,最终帮助你更快更好地构建网站。

— EOF —

送你一个小红人

认识老李,还是龙湖CEO邵明晓三年前组的一个局。

老邵是中国地产界唱歌最好的CEO,这是惠新钱柜KTV的保安都知道的。从龙湖公司下来,拐个弯,就能到钱柜。老邵一出现在大厅,保安会迅速刷开3楼的电梯。龙湖曲折的发展过程中,很多决定都是在三楼VIP包厢里诞生的。龙湖能把自己熬成市值最高的地产公司,这个包厢功不可没。

这些年,北京的钱柜陆续关了,只剩惠新那家。我们常说,要不是老邵支持,这硕果仅存的钱柜早关门了。

老李和老魏朋友。空降到饭局那天,老邵和老魏都介绍说,送你一个小红人,老李有很多网红和美女资源。

我不禁对老邵肃然起敬。这些年每次跟他唱歌,都是和一帮油腻的中老年男孩。这次好像待遇不一样了。

我加了老李的微信。他的电子名片是:天下秀董事长李檬。这种姓“天”的公司名字一听,就十分敞亮。我很感动,不断敬老邵酒。

可是当晚老邵连钱柜都没有去。后来,我看到老李在朋友圈分享最多的,不是会所,而是他自己写的公众号,关于创业、新消费乃至广告的思考。

再后来,看到了老李公司上市的消息。我当时仔细研究了下,非常失望:

原来天下秀不是一家演艺公司。

1

1999年有点意思。

20岁的龙岩人王兴在清华读大三,刚建了人生中第一个网站。他喜欢跳一种叫“黄土黄”的传统舞蹈,跳时赤裸上身,胸前绑鼓。

精通计算机和BBS的李想,出于对互联网和IT的热爱,决定放弃参加高考,选择创业,成立泡泡网。25岁的刘强东在中关村卖刻录机,面对电视台的镜头,他一脸羞涩地说:

一个月能赚个几万块。

那一年,即将大学毕业的李檬,是这些人里起点最高的,他在北京郊区昌平县有个美食城。

大学时,他给父亲写了一封很长的信,要了3万块钱。父亲跟他说3万块钱可以买一辆自己喜欢的车。但李檬拿着那3万块钱去创业了。

他在学校周边找到一个刚建成的大楼,用那3万块钱给大厦老板交了定金,承包了2层小吃城。他又去学校周围找推着小推车的商贩们,预收了十几万定金。

他用这些定金把小吃城装修好,租给了这群小商贩。开业后他雇了很多人发传单,小吃城很快就火了。他一年把定金和成本挣了回来。

兽爷摊煎饼最享受的,是每次微信到账的声响,叮铃一声,您有五块钱到账了。但李檬非常凡尔赛,他说第一次创业过程最享受的,不是挣了钱,而是自己的逻辑判断得到了验证。

第一次创业成功的李檬,很快进入贤者时刻。

还好,他读到了一本名字叫《未来时速—数字神经系统与商务新思维》的书。书的作者,是当时的世界首富比尔盖茨。这本书很朴实,作为过去30年占据世界首富宝座最久的富豪,盖茨在书里历数了微软犯下的错误。

不过书里最吸引李檬的,是盖茨的15个预言。43岁的盖茨,在1999年就描述了十年后世界会变成什么样子。

他预言,由于数字信息速度的增加,智能手机、即时支付、社交媒体、在线招聘、在线课程、基于用户兴趣的网站等,将彻底重塑未来的商业和生活。盖茨说:

你越年轻,计算机对你而言就越重要。

作家柳青说过,人生道路虽然漫长,但紧要处常常只有几步。

为了跟盖茨一样酷,不差钱的李檬毕业前去微软ATC教育部门实习了几个月,在高校发展和推广微软的软件。

之后,这个山东祖籍内蒙出生河北长大北京上学的小伙子决定卖掉美食城,投身互联网。

煎饼生意从此少了一个竞对,互联网生意又多了个抓手。

2

现在年轻网民们最熟悉的数字,不是“91”,可能就是“1024”。但时间倒流20多年,老一代的中国网虫最熟悉的数字,是“163”和“263”。

163是丁磊的,263是首都在线的。那会儿两家打得很凶,都有游戏,也都有热门产品——邮箱。

首都在线(263.net)是当时最骚的网站之一,除了邮箱业务之外,全国网民都用电话拨号上网的方式,用极慢的网速在263上面打牌下棋,访问量稳居国内前三。

丁磊是很反对学生创业的。他讲过,世界上只有一个比尔·盖茨。曾经的美食城老板李檬看到这句话,转身就去帮助首都在线做商业化探索了。

探索都是痛苦的。比如上世纪90年代,天王刘德华开始探索自己写歌,老前辈黄霑很快关注到了。霑伯也真不客气,在他自己的专栏里骂了刘德华三年,而且每次都能骂得花样翻新。

刘德华实在受不了了,他找到黄霑说,霑伯,拜托不要骂得那么狠,那么用力。黄霑拍了拍刘天王:

不要放弃,人是会进步的。

当年互联网广告的遭遇还不如刘德华,没有多少广告主啊愿意被贴上粗制滥造、绑架用户的标签。2001年,中国电视的广告额是互联网广告额的50倍。

黄霑有资本骂刘德华,作为广告人,他写出过人头马最经典的广告词:“人头马一开,好事自然来。”作为词作者,他写出过:“沧海一声笑,滔滔两岸潮。”

那是广告人最幸福的时代,孙大伟可以凭“马的内在”系列选举广告帮助马英九击败陈水扁,这种案例,做邮箱和棋牌游戏的互联网人当时想也不敢想。

土生土长的互联网创业者李檬,和当时卖中国黄页的马云一样,做的是教育市场的工作。稍不留心,就会被大公司当成骗子轰出来。

但这个处女座小伙一直没有放弃。这个人走的慢,但从来不后退。

2003年,并称为“中国互联网2.0时代”的博客异军突起。李檬的创业公司也很快被方兴东的博客网盯上、并购。他也加入博客网,担任营销总经理,负责博客网商业产品运营。

卖掉第二个创业公司,李檬拿到一笔现金。他去望京附近看房,当时看的小区一套小户型总价才二十万,首付只要两成。

售楼小姐跟他说,你把这一栋十几户都买了,总共首付也就二三十万。

李檬说你绝对是个骗子,哪有这么卖楼的。销售一听就急了,说哥我给你签担保协议,一年后这房子涨不到20%,这钱我补给你。

李檬心想这更像个骗子了。他最后只花了二十万的全款,买了自己人生的第一套房。

后来有朋友跟李檬说,如果他那时听了女销售的话,买了望京那一栋楼,那早就成为包租公,财务自由了。

之后,也就不会有天下秀敲钟、进胡润榜的事了。

3

许知远并不是第一个觉得时代不精致的人。

2003年10月底,南方周末跑到香港去采访黄霑,香港湾仔福临门酒楼人声鼎沸,霑伯用能压住所有声音的大嗓门跟记者喷了两个人:

金庸和周杰伦。

末了,记者问他如果周杰伦请你写歌,你怎么写。霑伯说没想过,不知道。流行毕竟是年轻人的,你说恋爱,我的初恋离开我都50多年了,不记得了,我老了。

一周后的一个早晨,方兴东起床后登录博客网,发现访问不了。刚开始以为被黑了,后来才发现,是因为访问量增加了十倍。

再一看,他发现三大门户都在头条区推荐了关于木子美的文章。当时木子美在中国博客网上写私生活,但媒体张冠李戴,写成了方兴东的博客网。

于是全中国的网民,都涌进博客网,争相学习如何用身体写作。

霑伯这种老派文人当年也想写色情文学,劝退他的是周星驰:

你必须好好往下写啊,我妈妈特别喜欢你的小说!

这是划时代的事件,中文语境竟然对情色有了一定的包容。

后来木子美和李檬一样,也来到博客网工作。博客广为普及,博客网也很快成为全球中文第一博客网站,流量跻身到Alexa全球网站流量前100位。

过去,不管是门户网站还是传统媒体,都是采编人员做内容,再植入广告。在北京奥运会之后,媒体这种影响力和商业价值,到了巅峰。

但在互联网2.0时代,媒体去中心化了。不管是木子美、芙蓉姐姐还是罗永浩,他们都变成了内容创作者。这些初代网红都聚集了一大批粉丝。

这也是最早的自媒体形态。虽然那时还没有智能手机,博客产品也很简单。但李檬想,如果能将这些有粉丝的内容创作者推向广告市场,中小企业会有更多的营销选择。他隐约感到,颠覆性的市场就要出现了。

2006年,还在华清嘉园搞校内网的龙岩人找上博客网。他要谈一个大合作——借博客网的服务器用用。

当时校内网只发布几个月,用户量就暴增,很快成为最大的中文社交网络。王兴没有钱加服务器和带宽,只好过来博客网借服务器了。

李檬他们说,借服务器可以呀,得两万块钱。龙岩人想了想,咬牙来了一句:

一万八行不行?

博客网当时也不容易。他们坐拥着全网最顶级的流量和“初代网红”——博客主。但当时的环境还是很难变现。

现如今网红们玩的各种手段,李檬当年都干过。比如他跟TCL空调合作进行“初代网红”带货,只要在博客主底下留言,要买TCL空调,家住哪,国美线下就可以提供送货。

几千个人留言要空调。但电话打过去,对方都回复说:

我不买空调,就是为了试一试。

但校内网的大热,让李檬坚定了自己的预感。校内网之后,微博和微信先后诞生了。移动端流量要爆发了,人们接受信息的方式即将被彻底改变。

后来,李檬从博客网离职。嗅到了社会化营销趋势的他,又一次创业,成立了天下秀。

4

2009年,传统广告行业没有经历回光返照。4A公司集体裁员,老大哥奥美打破了祖训,零月费服务伊利。

他们的眼泪并没有浇灌刚成立的天下秀。

那时候大家根本不知道什么是社会化营销,麦当劳在校内网做的小互动案例,还只是学生们上课讨论的范畴,智能手机还没普及,很多老板们根本不知道什么是社交媒体。

石头太多了,李檬一块都没少摸。李檬自己跑销售、盯客户,开发的时候和CTO大半夜在沟通。有一次在介绍自家公司的业务时,被一个企业主当面否定:

你们这个东西不入流,我们是绝对不会做的。

这一次创业,李檬又差点成“先烈”。创业初,天下秀曾几个月发不出工资。A轮时,李檬见了新浪董事长曹国伟,讲了商业计划书。曹国伟对李檬的商业计划书并不是清晰的看好,但觉得李檬这个人和团队还挺靠谱,不久后天下秀收到了一笔四百万美元的救命钱。

当时,新浪微博还在内测中,“网红”这个词还不存在。微博上线后,曹国伟指明方向力排众议,坚定支持天下秀,开发了国内第一个自媒体投放平台——微任务。

到B轮时,李檬找了100多家投资商,还是没人看得懂它的商业模式。一个投资经理说:

网红不就在直播间里给咱哥们唱唱歌,还能做广告发财?

转折点是2010年世博会。他们在微博发起了一个“美好生活@中粮”主题活动,鼓励粉丝随时随地发现中粮产品,引导网友上传分享与世博相关照片。

这个在微博上的品牌活动大获成功。还是陈奕迅的《十年》唱得好:

成千上万个路口,总要有一个人先走。

校内网、开心网、博客网先后都走了,但是自媒体人沉淀下来了,帮助自媒体赚钱的天下秀也成了。骂了比尔·盖茨十几年的李檬,终于站到了风口。

其实,很多复杂或者深奥的道理,最后都可以简化成两个词:准备,时机。

5

2011年前的天下秀,其实与一个传统代理公司无异。

接品牌方的单,找网红、媒体投放,中间挣个差价。资本很快也进来了。天下秀之后又拿到了6亿人民币C轮融资。

沿着这个轨迹,站在风口上的李檬躺着赚钱都行。但他更多是危机感,社交媒体时代,代理商的本质是他们越挣钱,品牌方与媒体方、网红就越亏钱。

我们的价值增量在哪?

创立天下秀第三年,他有了想法,打造一个交易平台——类似于红人新经济领域的阿里巴巴或者Airbnb。

简单说,天下秀不会是一家MCN公司、广告公司或者传媒公司,更不是老魏说的有很多网红和美女资源的“演艺公司”,而是一个红人经济平台。

像住房市场的贝壳连接房源和房客,打车市场的滴滴连接了司机和乘客,外卖市场的美团连接了餐厅和食客,天下秀是连接了红人和商家。

数百个大V、自媒体成了WEIQ平台的第一波玩家。对于那些投不起百度竞价排名、淘宝排名的中小企业来说,WEIQ成为他们打公司品牌提供了更好的选择。

天下秀很快颠覆了中国广告业的原有模式。去年4月,天下秀成功借壳上市;8月25日,这家红人新经济上市公司在上交所举办了敲锣仪式。仪式上,李檬也讲了自己跟曹国伟的会面。

这曾是决定天下秀生死的会面。

一度濒临死亡的小团队,就这样成为敲钟上市的资本市场炸子鸡。天下秀去年市值一度超过了400亿,比陌陌、斗鱼等老牌互联网公司还要高。

李檬押中的赛道还在爆发期。红人经济方兴未艾,李佳琦、薇娅成为新经济的“模范”,一场直播动辄上亿的销售额,堪比一家实体商场一个月的营收。

整个2020年,中国光直播电商业全年带货金额就将近一万亿。他们成为企业主 2019 年以来市场营销的潮流与趋势,被赋予了“新消费风口”地位。

前几天,李檬发布微博视频,他边骑自行车边解读天下秀2020年财报:到去年,WEIQ平台覆盖了图文类视频类直播类等29万职业红人、7500家MCN机构,营收在去年超过了30亿元。视频中,他还对一个卖菠萝的大爷说:

红人的影响力越来越大,你可以试试,不要被时代抛弃。

看完这个视频,我望着煎饼摊愣了很久。原来时代又一次抛弃了我。

今年,盖茨在时隔20多年后又出版了一本名为《气候经济与人类未来》的新书。新书里他说:我是一个乐观主义者,我知道技术的力量,我知道人类的力量。

这些年,盖茨犹如一个章鱼帝一般,1999年书里的十五个预言,如今几乎全部实现了。而当年受他影响而迈入互联网行业的年轻人,如今也偷得了师傅一点真传。

2015年,李檬第一个预判“网红要职业化”,2018年他高喊“红人将会干掉广告公司”,2020年,他说“红人会成为连接一切商业的节点”。如今,国家发布了新的职业。广告公司都在寻求转型,新国货消费品牌都在这样的势头下起来了。

下一步,我就准备让李檬预言下,煎饼摊当然也能上市。

《喜剧之王》有个场景,张柏芝说,看,前面漆黑一片,什么也看不到。周星驰说:

也不是,天亮后便会很美的。

人生这场戏,其实就是一个个选择和坚持叠加起来的总和。选择好,坚持下来,天终会亮。

看看1999年的李想、王兴或者李檬们,你会相信这一点的。


转自:https://mp.weixin.qq.com/s/_717kUhZRcvn3PrLayON0A

两万字总结《C++ Primer》要点

对于想要入门C++的同学来说,《C++ Primer》是一本不能错过的入门书籍,它用平易近人的实例化教学激发学生的学习兴趣,帮助学生一步步走进C++的大门。在本文中,作者Jacen用两万多字总结了《C++ Primer 中文版(第五版)》1-16章的阅读要点,可以作为该书的阅读参考。注:原书更为详细,本文仅作学习交流使用。

第一章 开始

1.1 编写一个简单的C++程序

int main()
{
return 0;
}

每个C++程序都包含一个或多个函数,其中一个必须命名为main.

1.2 初识输入输出

对象用途
cin标准输入
cout标准输出
cerr标准错误
clog输出运行时的一般性消息

1.3 注释简介

两种:单行注释://界定符:/* 和 */

1.4 控制流

while;for;if;
第二章 变量和基本类型
P30-P71数据类型是程序的基础。C++语言支持广泛的数据类型。

基本内置类型

1.算术类型

类型最小尺寸
bool未定义
char8位
w_char_t16位
char16_t16位
char32_t32位
short16位
int16位
long32位
long long64位
float6位有效数字
double10位有效数字
long double10位有效数字

2.类型转换

不要混用符号类型和无符号类型。

变量

1.变量定义

(1)基本形式:类型说明符,随后紧跟着一个或者多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。(2)初始值在C++中,初始化和赋值是2个完全不同的操作。初始化的含义是创建变量的时候赋予一个初始值,而赋值的含义是把对象的当前值擦除,用一个新值来替代。两者区别很小。(3)列表初始化用花括号来初始化变量的方式,称为列表初始化。(4)默认初始化如果定义变量没有指定初始值,则变量被默认初始化。::: tip例外情况:定义在函数体内部的内置类型变量将不被初始化,其值未定义。建议初始化每个内置类型的变量。:::

2.变量声明和定义的关系

变量声明:规定了变量的类型和名字。变量定义:除声明之外,还需要申请存储空间。如果想声明一个变量,而非定义它,需要使用extern关键词。

extern int i;    // 声明i而非定义i
int j;           // 声明并定义j

::: tip变量只能被定义一次,但可以被多次声明。:::

3.名字的作用域

作用域:C++中大多数作用域都用花括号分隔。作用域中一旦声明了某个名字,它所嵌套的所有作用域都能访问该名字。同时,允许在内层作用域中重新定义外层作用域中有的名字。::: warning如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量。:::

复合类型

定义:复合类型是基于其他类型定义的类型。

1.引用

引用:为对象起另外一个名字。::: warning引用必须被初始化。引用本身不是对象,所以不能定义引用的引用。引用要和绑定的对象严格匹配。引用类型的初始值,必须是一个对象。:::

2.指针

指针:本身就是一个对象。允许对指针赋值和拷贝。指针无须在定义的时候赋值。(1)利用指针访问对象如果指针指向了一个对象,则允许使用解引用符(*)来访问该对象。(2)void* 指针

3.理解复合类型的声明

(1)指向指针的指针** 表示指向指针的指针*** 表示指向指针的指针的指针(2)指向指针的引用不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。

const限定符

定义:const用于定义一个变量,它的值不能被改变。const对象必须初始化。::: tip默认状态下,const对象仅在文件内有效。当多个文件出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量。如果想让const变量在文件间共享,则使用extern修饰。:::(1)const的引用允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式。一般,引用的类型必须与其所引用对象的类型一致,特殊情况是表达式。(2)指针和const弄清楚类型,可以从右边往左边阅读。(3)顶层consttop-level const 表示指针本身是个常量low-level const表示指针所指的对象是一个常量。(4)constexpr和常量表达式C++新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。

处理类型

类型别名

两种方法用于定义类型别名:(1)使用关键词typedef

typedef double wages; //wages是double的同义词
typedef wages *p; // p是double*的同义词

(2)别名声明

using SI = Sales_item;  // SI是Sales_item的同义词

auto类型说明符:让编译器通过初始值来推算变量的类型。decltype类型指示符:选择并返回操作符的数据类型。只得到类型,不实际计算表达式的值。

自定义数据结构

(1)类数据结构是把一组相关的数据元素组织起来,然后使用它们的策略和方法。类一般不定义在函数体内,为了确保各个文件中类的定义一致,类通常被定义在头文件中,而且类所在头文件的名字应该与类的名字一样。头文件通常包含那些被定义一次的实体。(2)预处理器

#ifndef SALES_DATA_H
#define SALES_DATA_H
#endif

一般把预处理变量的名字全部大写。

术语

空指针 :值为0的指针,空指针合法但是不指向任何对象。nullPtr是表示空指针的字面值常量。void*:可以指向任意非常量的指针类型,不能执行解引用操作。
第三章 字符串、向量和数组
P74-P118string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列。

命名空间的 using 声明

using namespace:name;

头文件不应包含using声明。

标准库类型 string

#include <string>
using namespace std;

(1)定义和初始化

string s1;
sting s2(s1);
string s3("value");
string s3 = "value";
string s4(n, 'c');

(2)string对象的操作

s.empty();      // 判空
s.size(); // 字符个数
s[n]; // s中第n个字符的引用
s1+s2; // s1和s2连接
<,<=,>,>= // 比较

::: warning标准局允许把字面值和字符串字面值转换成string对象。字面值和string是不同的类型。:::(3)处理string对象中的字符::: tipC++程序的头文件应该使用cname,而不应该使用name.h的形式:::遍历给定序列中的每个值执行某种操作

for (declaration : expression)
statement

标准库类型 vector

标准库vector表示对象的集合,其中所有对象的类型都相同。vector是一个类模板,而不是类型。(1)定义和初始化vector对象

vector<T> v1;
vector<T> v2(v1);
vector<T> v2 = v1;
vector<T> v3(n, val);
vector<T> v4(n);
vector<T> v5{a,b,c...}
vecrot<T> v5={a,b,c...}

如果用圆括号,那么提供的值是用来构造vector对象的。如果用花括号,则是使用列表初始化该vector对象。(2)向vector对象添加元素先定义一个空的vector对象,在运行的时候使用push_back向其中添加具体指。(3)其他vector操作

v.empty();
v.size();
v.push_back(t);
v[n];

::: warning只能对确认已存在的元素执行下标操作。:::

迭代器介绍

迭代器运算符

*iter            // 解引用,返回引用
iter->mem // 等价于 (*iter).mem
++iter
--iter
iter1 == iter2
iter1 != iter2
iter + n
iter - n
iter += n
iter -= n
iter1 - iter2 // 两个迭代器相减的结果是它们之间的距离
>, >=, <, <= // 位置比较

::: warning凡是使用了迭代器的循环体,都不能向迭代器所属的容器添加元素。:::

数组

(1)数组、指针使用数组下标的时候,通常将其定义为size_t类型。::: warning定义数组必须指定数组的类型,不允许用auto推断。不存在引用的数组。如果两个指针分别指向不相关的对象,则不能进行对这2个指针进行比较。:::

多维数组

多维数组实际上是数组的数组。

size_t cnt = 0;
for(auto &row : a)
for (auto &col : row){
col = cnt;
++cnt;
}
int *ip[4];    // 整型指针的数组
int (*ip)[4]; // 指向含有4个整数的数组

术语

begin string和vector的成员,返回指向第一个元素的迭代器。也是一个标准库函数,输入一个数组,返回指向该数组首元素的指针。end string和vector的成员,返回一个尾后迭代器。也是一个标准库函数,输入一个数组,返回指向该数组尾元素的下一个位置的指针。
第四章 表达式
P120-P151

4.1 基础

重载运算符:为已经存在的运算符赋予了另外一层含义。左值、右值:当一个对象用作右值得时候,用的是对象的值(内容)。当对象被用作左值得时候,用的是对象的身份(在内存中的位置)。

4.2 算术运算符

%:参与取余运算的运算对象必须是整数类型。

4.3 逻辑和关系运算符

&& 运算符和 || 运算符都是先求左侧运算对象的值再求右侧运算对象的值。   
::: warning进行比较运算的时候,除非比较的对象是bool类型,否则不要使用布尔字面值true,false作为运算对象。:::

4.4 赋值运算符

赋值运算符满足右结合律。不要混淆相等运算符和赋值运算符

if (i = j)

if (i == j)

4.5 递增和递减运算符

递增运算符 ++递减运算符 —

4.6 成员访问运算符

点运算符和箭头运算符

n = (*p).size();
n = p->size();

4.7 条件运算符

condition ? expression1 : expression2;

4.8 位运算符

4.9 sizeof运算符

sizeof运算符返回一条表达式或一个类型名字所占的字节数,其所得值是一个size_t类型,是一个常量表达式。

sizeof (type)
sizeof expr

4.10 逗号运算符

逗号运算符含有两个运算对象,按照从左向右的顺序依次求值。

4.11 类型转换

隐式转换显式转换命名的强制类型转换

cast-name<type>(expression)

// cast-name是static_cast,dynamic_cast,const_cast,reinterpret_cast

::: tip由于强制类型转换干扰了正常的类型检查,因此建议避免强制类型转换。:::

4.12 运算符优先级表

第五章 语句
P154-P1785.1 简单语句(1)空语句

;    // 空语句

(2)复合语句复合语句是指用花括号括起来的(可能为空的)语句和声明的序列,复合语句也被称作块(block)。

{}

5.2 语句作用域

定义在控制结构当中的变量只在相应语句的内部可见,一旦语句结束,变量就超出其作用范围。

5.3 条件语句

(1)if 语句(2)switch 语句case关键字和它对应的值一起被称为case标签。case标签必须是整形常量表达式。如果某个case标签匹配成功,将从该标签开始往后顺序执行所有case分支,除非程序显示的中断了这一过程。dedault 标签:如果没有任何一个case标签能匹配上switch表达式的值,程序将执行紧跟在default标签后面的语句。

5.4 迭代语句

(1)while 语句

while (condition)
statement

(2)传统 for 语句

for (initializar; condition; expression)
statement

for 语句中定义的对象只在for循环体内可见。(3)范围 for 语句

for (declaration : expression)
statement

(4)do while 语句

do 
statement
while (condition)

5.5 跳转语句

breakbreak只能出现在迭代语句或者switch语句内部。仅限于终止离它最近的语句,然后从这些语句之后的第一条语句开始执行。continuecontinue语句终止最近的循环中的当前迭代并立即开始下一次迭代。gotogoto的作用是从goto语句无条件跳转到同一函数内的另一条语句。容易造成控制流混乱,应禁止使用。return

5.6 try语句块和异常处理

C++中异常处理包括:throw表达式、try语句块。try和catch,将一段可能抛出异常的语句序列括在花括号里构成try语句块。catch子句负责处理代码抛出的异常。throw表达式语句,终止函数的执行。抛出一个异常,并把控制权转移到能处理该异常的最近的catch字句。
第六章 函数
P182-P225

6.1 函数基础

(1)形参和实参:实参的类型必须与对应的形参类型匹配。函数的调用规定实参数量应与形参数量一致。(2)局部对象形参和参数体内部定义的变量统称为局部变量,它们对函数而言是”局部”的,仅在函数的作用域内可见,同时局部变量还会隐藏外层作用域中同名的其他变量。自动对象:只存在于块执行期间的对象。局部静态对象:在程序的执行路径第一次经过对象定义语句时候进行初始化,并且直到程序终止才会被销毁。

size_t count_calls()
{
static size_t ctr = 0;
return ++ctr;
}

(3)函数声明函数的三要素:(返回类型、函数名、形参类型)。函数可被声明多次,但只能被定义一次。(4)分离式编译分离式编译允许把程序分割到几个文件中去,每个文件独立编译。编译->链接

6.2 参数传递

当形参是引用类型,这时它对应的实参被引用传递或者函数被传引用调用。当实参被拷贝给形参,这样的实参被值传递或者函数被传值调用。(1)传值参数(2)被引用传参(3)const形参和实参(4)数组形参为函数传递一个数组时,实际上传递的是指向数组首元素的指针。

void print(const int*);
void pring(const int[]);
void print(const int[10]);
// 以上三个函数等价

数组引用实参:f(int (&arr)[10])

int *matrix[10];   // 10个指针构成的数组
int (*matrix)[10]; // 指向含有10个整数的数组的指针

(5)含有可变形参的数组initializer_list

for err_msg(initializer_list<string> li)

6.3 返回类型和return语句

2种:无返回值函数和右返回值函数。

return;
return expression;

函数完成后,它所占用的存储空间也会随着被释放掉。::: warning返回局部对象的引用是错误的;返回局部对象的指针也是错误的。:::

6.4 函数重载

重载函数:同一作用域内的几个函数名字相同但形参列表不通,我们称之为重载函数。(overloaded)。不允许2个函数除了返回类型外其他所有的要素都相同。重载与作用域如果在内存作用域中声明名字,它将隐藏外层作用域中声明的同名实体。

6.5 特殊用途语言特性

(1)默认实参函数调用时,实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参。

typedef string::size_type sz;
string screen(sz ht = 24, sz wid = 80, char background = ' ');

::: tip当设计含有默认实参的函数时,需要合理设置形参的顺序。一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。:::(2)内联函数使用关键词inline来声明内联函数。内联用于优化规模较小,流程直接,频繁调用的函数。(3)constexpr函数constexpr函数是指能用于常量表达式的函数。

6.6 函数匹配

Step1:确定候选函数和可选函数。Step2:寻找最佳匹配。

6.7 函数指针

函数指针指向的是函数而非对象。

void useBigger (const string &s1, const string &s2, bool pf(const string &, const string &));
等价于
void useBigger (const string &s1, const string &s2, bool (*pf)(const string &, const string &));

第七章 类
P228-P273类的基本思想是数据抽象和封装。抽象是一种依赖于接口和实现分离的编程技术。封装实现了类的接口和实现的分离。

7.1 定义抽象数据类型

(1)this任何对类成员的直接访问都被看作this的隐式引用。

std::string isbn() const {return bookNo;}

等价于

std::string isbn() const {return this->bookNo;}

(2)在类的外部定义成员函数类外部定义的成员的名字必须包含它所属的类名。

double Sales_data::avg_price() const {
if (units_sol)
return revenue/units_sols;
else
return 0;
}

(3)构造函数定义:类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数没有返回类型;构造函数的名字和类名相同。类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数。编译器创建的构造函数被称为合成的默认构造函数。::: tip只有当类没有声明任何构造函数的时,编译器才会自动的生成默认构造函数。一旦我们定义了一些其他的构造函数,除非我们再定义一个默认的构造函数,否则类将没有默认构造函数:::

7.2 访问控制与封装

(1)访问控制

说明符用途
public使用public定义的成员,在整个程序内可被访问,public成员定义类的接口。
private使用private定义的成员可以被类的成员函数访问,但是不能被使用该类的代码访问,private部分封装了类的实现细节。

(2)友元类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元。以friend关键字标识。友元不是类的成员,不受访问控制级别的约束。::: tip友元的声明仅仅制定了访问的权限,而非通常意义的函数声明。必须在友元之外再专门对函数进行一次声明。:::

// Sales_data.h

class Sales_data {
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&);
}

// nonmember Sales_data interface functions
Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

//Sales_data.cpp

Sales_data
add(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs; // copy data members from lhs into sum
sum.combine(rhs); // add data members from rhs into sum
return sum;
}

// transactions contain ISBN, number of copies sold, and sales price
istream&
read(istream &is, Sales_data &item)
{
double price = 0;
is >> item.bookNo >> item.units_sold >> price;
item.revenue = price * item.units_sold;
return is;
}

ostream&
print(ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}

7.3 类的其他特性

(1)重载成员变量

Screen myScrren;
char ch = myScreen.get();
ch = myScreen.get(0,0);

(2)类数据成员的初始化类内初始值必须使用=或者{}的初始化形式。

class Window_mgr{
private:
std::vector<Screen> screens{Screen(24, 80, ' ')};
}

(3)基于const的重载

class Screen {
public:
// display overloaded on whether the object is const or not
Screen &display(std::ostream &os)
{ do_display(os); return *this; }
const Screen &display(std::ostream &os) const
{ do_display(os); return *this; }
}

当某个对象调用display的时候,该对象是否是const决定了应该调用display的哪个版本。(3)类类型对于一个类来说,在我们创建他的对象之前该类必须被定义过,而不能仅被声明。(4)友元友元类如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员。

class Screen {
// Window_mgr的成员可以访问Screen类的私有部分
friend class Window_mgr;
}

令成员函数作为友元

class Screen {
// Window_mgr::clear必须在Screen类之前被声明
friend void Window_mgr::clear(ScreenIndex);
}

7.4 类的作用域

一个类就是一个作用域。

7.5 构造函数再探

(1)构造函数的初始值有时必不可少::: tip如果成员是const、引用,或者属于某种未提供默认构造函数的类类型化。我们必须通过构造函数初始值列表为这些成员提供初值。:::

class ConstRef{
public:
ConstRef (int i);
private:
int i;
const int ci;
int &ri;
};

ConstRef:ConstRef(int ii) : i(ii), ci(ii), ri(i){ }

(2)成员初始化的顺序成员初始化的顺序与它们在类定义中出现 的顺序一致。P259(3)委托构造函数使用它所述类的其他构造函数执行它自己的初始化过程。(4)如果去抑制构造函数定义的隐式转换?在类内声明构造函数的时候使用explicit关键字。

7.6 类的静态成员

(1)声明静态成员在成员的声明之前加上关键词static。类的静态成员存在于任何对象之外,对象中不包含任何与静态成员有关的数据。(2)使用类的静态成员

double r;
r = Account::rate();

小结

类有两项基本能力:一是数据数据抽象,即定义数据成员和函数成员的能力;二是封装,即保护类的成员不被随意访问的能力。
第八章 IO库
P278-P290C++语言不直接处理输入输出,而是通过一组定义在标准库中的类型来处理IO。

  • iostream处理控制台IO
  • fstream处理命名文件IO
  • stringstream完成内存string的IO

ifstream和istringstream继承自istreamofstream和ostringstream继承自ostream

8.1 IO类

(1)IO对象无拷贝或复制。进行IO操作的函数通常以引用方式传递和返回流。(2)刷新输出缓冲区flush刷新缓冲区,但不输出任何额外的字符;ends向缓冲区插入一个空字符,然后刷新缓冲区。

8.2 文件输入输出

作用
ifstream从一个给定文件读取数据
ofstream从一个给定文件写入数据
fstream读写给定文件

8.3 string流

作用
istringstream从string读取数据
ostringstream向string写入数据
stringstream既可从string读数据也可以向string写数据
// will hold a line and word from input, respectively
string line, word;

// will hold all the records from the input
vector<PersonInfo> people;

// read the input a line at a time until end-of-file (or other error)
while (getline(is, line)) {
PersonInfo info; // object to hold this record's data
istringstream record(line); // bind record to the line we just read
record >> info.name; // read the name
while (record >> word) // read the phone numbers
info.phones.push_back(word); // and store them
people.push_back(info); // append this record to people
}
// for each entry in people
for (vector<PersonInfo>::const_iterator entry = people.begin();
entry != people.end(); ++entry) {
ostringstream formatted, badNums; // objects created on each loop

// for each number
for (vector<string>::const_iterator nums = entry->phones.begin();
nums != entry->phones.end(); ++nums) {
if (!valid(*nums)) {
badNums << " " << *nums; // string in badNums
} else
// ``writes'' to formatted's string
formatted << " " << format(*nums);
}
if (badNums.str().empty()) // there were no bad numbers
os << entry->name << " " // print the name
<< formatted.str() << endl; // and reformatted numbers
else // otherwise, print the name and bad numbers
cerr << "input error: " << entry->name
<< " invalid number(s) " << badNums.str() << endl;
}

第九章 顺序容器
P292-P332顺序容器为程序员提供了控制元素存储和访问顺序的能力。

9.1 顺序容器概述

类型作用
vector可变数组大小。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。
deque双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。
list双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。
forward_list单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。
array固定大小数组。支持快速随机访问。不能添加或删除元素。
string与vector相似的容器,但专门用于保存字符、随机访问快。在尾部插入/删除速度快。

9.2 容器库概述

一般,每个容器都定义在一个头文件中。容器均定义为模板类。

类型别名
iterator此容器类型的迭代器类型
const_iterator可以读取元素,但不能修改元素的迭代器类型
size_type无符号整数类型,足够保存此种容器类型最大可能容器的大小
difference_type带符号整数类型,足够保存两个迭代器之间的距离
value_type元素类型
reference元素的左值诶性:与value_type&含义相同
const_reference元素的const左值类型(即,const value_type&)
构造函数
C c;默认构造函数,构造空容器
C c1(c2)构造c2的拷贝c1
C c(b, e)构造c,将迭代器b和e指定的范围内的元素拷贝到c(array不支持)
C c{a, b, c…}列表初始化c
赋值与swap
c1=c2将c1中的元素替换为c2中元素
c1 = {a, b, c…}将c1中的元素替换为列表中元素(不适用于array)
a.swap(b)交换a和b的元素
swap(a, b)与a.swap(b)等价
大小
c.size()c中元素的数组(不支持forward_list)
c.max_size()c中可保存的最大元素数目
c.empty()若c中存储了元素,返回false,否则返回true
添加/删除元素(不适用于array)
c.insert(args)将args中的元素拷贝进c
c.emplace(inits)使用inits构造c中的一个元素
c.erase(args)删除args指定的元素
c.clear()删除c中的所有元素,返回void
关系运算符
==, !=所有容器都支持相等(不等运算符)
<,<=,>,>=关系运算符(无序关联容器不支持)
获取迭代器
c.begin(), c.end()返回指向c的首元素和尾元素之后位置的迭代器
c.cbengin(),c.cend()返回const_iterator
反向容器的额外成员(不支持forward_list)
reverse_iterator按逆序寻址元素的迭代器
const_reverse_iterator不能修改元素的逆序迭代器
c.rbegin(), c.rend()返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(), c.crend()返回const_reverse_iterator

(1)迭代器标准库的迭代器允许我们访问容器中的元素,所有迭代器都是通过解引用运算符来实现这个操作。一个迭代器返回由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置。它们标记了容器中元素的一个范围。左闭合区间:[begin, end)

while (begin !=end){
*begin = val;
++begin;
}

(2)容器类型成员见概述通过别名,可以在不了解容器中元素类型的情况下使用它。(3)begin和end成员begin是容器中第一个元素的迭代器end是容器尾元素之后位置的迭代器(4)容器定义和初始化P290

C c;            // 默认构造函数
C c1(c2)
C c1=c2
C c{a,b,c...} // 列表初始化
C c={a,b,c...}
C c(b,e) // c初始化为迭代器b和e指定范围中的元素的拷贝
// 只有顺序容器(不包括array)的构造函数才能接受大小参数
C seq(n)
C seq(n,t)

将一个容器初始化为另一个容器的拷贝:当将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。不过,当传递迭代器参数来拷贝一个范围时,就不要求容器类型相同,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。标注库array具有固定大小:不能对内置数组类型进行拷贝或对象赋值操作,但array并无此限制。P301(5)赋值与swaparrray类型不允许用花括号包围的值列表进行赋值。

array<int, 10> a2={0}; //所有元素均为0
s2={0}; // 错误!

seq.assign(b,e)   // 将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素。swap用于交换2个相同类型容器的内容。调用swap之后,两个容器中的元素将交换。(6)容器大小操作size 返回容器中元素的数目empty 当size为0返回布尔值true,否则返回falsemax_size 返回一个大于或等于该类型容器所能容纳的最大元素数的值(7)关系运算符关系运算符左右两边的元素符对象必须是相同类型的容器。::: tip只有当元素类型也定义了相应的比较运算符,才可以使用关系元素安抚来比较两个容器:::

9.3 顺序容器操作

(1)向顺序容器添加元素表格P305使用push_back:追加到容器尾部使用push_front:插入到容器头部在容器中的特定位置添加元素:使用insert

vector <string> svec;
svec.insert(svec.begin(), "Hello!");

插入范围内元素:使用insert使用emplace操作:emplace_front、emplace和emplace_back分别对应push_front、insert和push_back。emplace函数直接在容器中构造函数,不是拷贝。(2)访问元素P309注意end是指向的是容器尾元素之后的元素。

在顺序容器中访问元素的操作
c.back()返回c中尾元素的引用。若c为空,函数行为未定义
c.front()返回c中首元素的引用。若c为空,哈数行为未定义
c[n]返回c中下标为n的元素的引用,n是一个无符号整数。若n>=size(),则函数行为未定义
c.at[n]返回下标为n的元素的引用。如果下标越界,则抛出out_of_range异常

(3)删除元素

顺序容器的删除操作
c.pop_back()删除c中尾元素。若c为空,则函数行为未定义。返回返回void
c.pop_front()删除c中首元素。若c为空,则函数行为未定义。返回void
c.erase(p)删除迭代器p所指定的元素,返回一个指向被删除元素之后元素的迭代器,如p指向尾元素,则返回尾后(off-the-end)迭代器。若p是尾后迭代器,则函数行为未定义
c.erase(b, e)删除迭代器b和e所指定范围内的元素。返回一个指向最后一个被删除元素之后元素的迭代器。若e本身就是尾后迭代器,则函数也返回尾后迭代器
c.claer()删除c中的所有元素。返回void

(4)特殊的forwar_list操作P313befor_begin();cbefore_begin();insert_after;emplace_after;erase_after;(5)改变容器大小reseize用于扩大或者缩小容器。resize操作接受一个可选的元素值参数,用来初始化添加到容器内的元素。如果容器保存的是类类型元素,且resize向容器中添加新元素,则必须提供初始值,或者元素类型必须提供一个默认构造函数。

9.4 vector对象是如何增长的

为了避免影性能,标准库采用了可以减少容器空间重新分配次数的策略。当不得不获取新的内存空间时,vector和string通常会分配比新的新的空间需求更大的内存空间。容器预留这些空间作为备用,可以用来保存更多的新元素。容器管理的成员函数:

容器大小管理操作
c.shrink_to_fit()请将capacity()减少为与size()相同大小
c.capacity()不重新分配内存空间的话,c可以保存多少元素
c.reverse()分配至少能容纳n个元素的内存空间。reverse并不改变容器中元素的数量,它仅影响vector预先分配多大的内存空间。调用reverse永远不减少容器占用的内存空间。

capcacity和size:区别:容器的size是指它已经保存的元素的数目;capcacity则是在不分配新的内存空间的前提下它最多可以保存多少元素。注意:只有当迫不得已时才可以分配新的内存空间。

9.5 额外的string操作

(1)构造string的其他方法

构造string的其他方法
string s(cp, n)s是cp指向的数组中前n个字符的拷贝
string s(s2, pos2)s是string s2从下标pos2开始的字符的拷贝。
string s (s2, pos2, len2)s是string s2从下标pos2开始len2个字符的拷贝

substr操作:substr操作返回一个string,它是原始string的一部分或全部的拷贝。s.substr(pos, n)  返回一个string,包含s中从pos开始的n个字符的拷贝。pos的默认值为0。n的默认值为s.size() – pos, 即拷贝从pos开始的所有字符(2)改变string的其他方法assign  替换赋值,总是替换string中的所有内容insert  插入append 末尾插入,总是将新字符追加到string末尾replace 删除再插入(3)string搜索操作

string搜索操作
s.find(args)查找s中args第一次出现的位置
s.rfind(args)查找s中args最后一次出现的位置
s.find_first_of(args)在s中查找args中任何一个字符第一次出现的位置
s.find_last_of(args)在s中查找args中任何一个字符最后一次出现的位置
s.find_first_not_of(args)在s中查找第一个不在args中的字符
s.find_last_not_of(args)在s中查找最后一个不在args中的字符

(4)compare函数compare有6个版本,P327(5)数值转换P328tostringstod

9.6 容器适配器

顺序容器适配器:stack; queue; priority_queue;适配器是一种机制,能使某种事物看起来像另外一种事物。定义一个适配器:适配器有2个构造函数:1、默认构造函数创建一个空对象2、接受一个容器的构造函数栈适配器:

栈的操作
s.pop()删除栈顶元素,但不返回该元素值
s.push(item)创建一个新元素压入栈顶,该元素通过拷贝或移动item而来,或者由args构造
s.emplace(args)由arg构造
s.top()返回栈顶元素,但不将元素弹出栈

队列适配器:

queue和priority_queue操作
q.pop()返回queue的首元素或priority_queue的最高优先级的元素,但不删除此元素
q.front()                  q.back()返回首元素或尾元素,但不删除此元素。只适用于queue
q.top()返回最高优先级元素,但不删除该元素。只适用于priority_queue
q.push(item) q.empalce(args)在queue末尾或priority_queue中恰当的位置创建一个元素,其值为item,或者由args构造

术语

begin容器操作:返回一个指向容器首元素的迭代器,如果容器为空,则返回尾后迭代器。是否返回const迭代器依赖于容器的类型。cbegin容器操作:返回一个指向容器尾元素之后的const_iterator。
第十章 泛型算法
P336-P371标准库并未给每个容器添加大量功能,而是提供了一组算法。这些算法是通用的,可以用于不同类型的容器和不同类型的元素。

10.1 概述

头文件:algorithm、numeric算法不依赖于容器,但算法依赖于元素类型的操作。

10.2 初识泛型算法

(1)只读算法accumulate  求和equal 是否相等(2)写容器元素的算法算法不检查写操作拷贝算法:copy重排容器元素的算法:sort::: tip标准库函数对迭代器而不是容器进行操作。因此,算法不能直接添加或删除元素:::

10.3 定制操作

标准库允许我们提供自己定义的操作来代替默认运算符。(1)向算法传递函数谓词:谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。标准库算法的谓词分为两类:1、一元谓词:只接受单一参数。2、二元谓词:接受两个参数。

bool isShorter(const string &s1, const string &s2)
{
retrun s1.size() < s2.size();
}

sort(words.begin(), words.end(), isShorter);

排序算法:stable_sort算法维持相等元素的原有顺序。(2)lambda表达式lamba:lambda表达式表示一个可调用的代码单元。一个lambda具有一个返回类型、一个参数列表和一个函数体。

[capture list](parameter list) -> return type {function body}
// capture list 捕获列表,lambda所在函数中定义的局部变量
// 捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字
// lambda必须使用尾置返回来指定返回类型

(3)lambda捕获和返回两种:值捕获、引用捕获::: warnning当以引用方式捕获一个变量时,必须保证在lambda执行时变量是存在的。一般的,应该尽量减少捕获的数据量,来避免潜在的问题。如果可能,避免捕获指针或引用。:::隐式捕获:当混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。显式捕获的变量必须使用与隐式捕获不同的方式。lambda捕获列表 P352可变lambda:若希望改变一个被捕获的变量的值,必须在参数列表首加上关键字mutable。指定lambda返回类型:当需要为lambda定义返回类型时,必须使用尾置返回类型。(4)参数绑定标准库bind函数:

auto newCallable = bind(callable, arg_list);
// 调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数

10.4 再探迭代器

插入迭代器、流迭代器、反向迭代器、移动迭代器(1)插入迭代器back_inserter:创建一个使用push_back的迭代器front_inserter:创建一个使用push_front的迭代器inserter:创建一个使用inserter的迭代器(2)iostream迭代器istream_iterator 读取输入流ostream_iterator 向一个输出流写数据istream_iterator操作:

istream-iterator操作
istream_iterator<T> in(is);in从输入流is读取类型为T的值
istream_iterator<T> end;读取类型为T的值得istream_iterator迭代器,表示尾后位置
in1 == in2              in1 != in2in1和in2必须读取相同类型。如果它们都是尾后迭代器,或绑定到相同的输入,则两者相等
*in返回从流中读取的值
in->mem与(*in).mem含义相同
++in, in++用>>从输入流读取下一个值

ostream_iterator操作:

ostream_iterator操作
ostream_iterator<T> out(os);out将类型为T的值写到输出流os中
ostream_iterator<T> out(os, d);out将类型为T的值写到输出流os中,每个值后面都输出一个d。d指向一个空字符串结尾的字符数组
out = val用<<将val写入到out所绑定的ostream中
*out, ++out, out++

(3)反向迭代器反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。

10.5 泛型算法结构

迭代器类别
输入迭代器只读、不写;单遍扫描,只能递增
输出迭代器只写,不读;单遍扫描,只能递增
前向迭代器可读写;多遍扫描,只能递增
双向迭代器可读写,多遍扫描,可递增递减
随机访问迭代器可读写,多遍扫描,支持全部迭代器运算

10.6 特定容器算法

对于list、forward_list,应该优先使用成员函数的算法而不是通用算法。

术语

cref标准库函数:返回一个可拷贝的对象,其中保存了一个指向不可拷贝类型的const对象的引用第十一章 关联容器
P374-P397关联容器支持高效的关键字查找和访问。

类型备注
map关联数组,保存关键字-值对
set值保存关键字的容器
multimap关键字可重复出现的map
multiset关键字可重复出现的set
unordered_map用哈希函数组织的map
unordered_set用哈希函数组织的set
unordered_multimap哈希组织的map;关键字可以重复出现
unordered_multiset哈希组织的set;关键字可以重复出现

11.1 使用关联容器

map是关键词-值对的集合。为了定义一个map,我们必须指定关键字和值的类型。

// 统计每个单词在输入中出现的次数
map<string, size_t> word_count;
string word;
while (cin >> word)
++word_count[word];
for (const auto &w : word_count)
count << w.first << " cccurs " < w.second
<< ((w.second > 1) ? " times" : "time") << endl;

set是关键字的简单集合。为了定义一个set,必须指定其元素类型。

// 统计输入中每个单词出现的次数,并忽略常见单词
map<string, size_t> word_count;
set<string> exclude = {"the", "But"};
string word;
while (cin >> word)
// 只统计不在exclude中的单词
if (exclude.find(word) == exclude.end())
++word_count[word]; //获取并递增word的计数器

11.2 关联容器概述

(1)定义关联容器定义map时,必须指明关键字类型又指明值类型;定义set时,只需指明关键字类型。(2)pair类型pair标准库类型定义在头文件utility中。一个pair保存两个数据成员。当创建一个pair时,必须提供两个类型名。

pair<string, string> anon; // 保存两个string
pair<string, string> author{"James", "Joyce"}; // 也可为每个成员提供初始化器

pair的数据类型是public的,两个成员分别命名为first和second。pair上的操作,见表,P380

11.3 关联容器操作

关联容器额外的类型别名
key_type此容器类型的关键字类型
mapped_type每个关键字关联的类型,只适用于map
value_type对于set,与key_type相同;对于map,为pair<const key_type, mapped_type>

(1)关联容器迭代器set的迭代器是const的set和map的关键字都是const的遍历关联容器:map和set都支持begin和end操作。使用beigin、end获取迭代器,然后用迭代器来遍历容器。(2)添加元素

关联容器insert操作
c.insert(v)v是value_type类型的对象;
c.emplace(args)args用来构造一个元素
c.insert(b, e)
c.insert(il)
c.insert(p, v)
c.emplace(p ,args)

(3)删除元素

从关联容器删除元素
c.erase(k)从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量
c.erase(p)从c中删除迭代器p指定的元素。p必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回.end()
c.erase(b, e)删除迭代器b和e所表示的范围中的元素。返回e

(4)map的下标操作

map和unorder_map的下标操作
c[k]返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其进行值初始化
c.at[k]访问关键字为k的元素,带参数检查;若k不在c中,抛出一个out_of_range异常

::: tipmap进行下标操作,会获得mapped_type对象;当解引用时,会得到value_type对象。:::(5)访问元素

c.find(k)  // 返回一个迭代器,指向第一个关键字k的元素,如k不在容器中,则返回尾后迭代器
c.count(k) // 返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1
c.lower_bound(k) // 返回一个迭代器,指向第一个关键字不小于k的元素;不适用于无序容器
c.upper_bound(k) // 返回一个迭代器,指向第一个关键字大于k的元素;不适用于无序容器
c.equal_bound(k) // 返回一个迭代器pair,表示关键字等于k的元素的范围。如k不存在,pair的两个成员均等于c.end()

11.4 无序容器

无序容器使用关键字类型的==运算符和一个hash<key_type>类型的对象来组织元素。无序容器在存储上组织为一组桶,适用一个哈希函数将元素映射到桶。无序容器管理操作,表格,P395还可以自定义自己的hash模板 P396

using SD_multiset = unordered_multiset<Sales_data, decltype(hasher)*, decltype(eqOp)*>;
SD_multiset bookStore(42, haser, eqOp);

第十二章 动态内存
P400-P436

12.1 动态指针与智能指针

智能指针用途
shared_ptr提供所有权共享的智能指针:对共享对象来说,当最后一个指向它的shared_ptr被销毁时会被释放。
unique_ptr提供独享所有权的智能指针:当unique_ptr被销毁的时,它指向的独享被释放。unique_ptr不能直接拷贝或赋值。
weak_ptr一种智能指针,指向由shared_ptr管理的对象。在确定是否应释放对象视,shared_ptr并不把weak_ptr统计在内。

(1)shared_ptr类

shared_ptr<string> p1;

make_shared函数:make_shared在动态内存中分配一个对象并初始化它,返回此对象的shared_ptr。

share_ptr<int> p3 = make_shared<int>(42);

shared_ptr的拷贝和赋值:每个shared_ptr都有一个关联的计数器,称为引用计数。一旦一个shared_ptr的引用计数变为0,就会自动释放自己所管理的对象。(2)直接管理内存运算符new分配分配内存,delete释放new分配的内存。使用new动态分配和初始化对象:

// 默认情况下,动态分配的对象是默认初始化的
int *pi = new int; // pi指向一个动态分配的、未初始化的无名对象
// 直接初始化方式
int *pi = new int(1024); // pi指向的对象的值为1024
// 对动态分配的对象进行值初始化,只需在类型名之后加上一对空括号
int *pi1 = new int; // 默认值初始化;*pi1的值未定义
int *pi2 = new int(); // 值初始化为0;*pi2为0

动态分配的const对象:

const int *pci = new const int(1024);

释放动态内存:

delete p;

delete表达式执行两个动作:销毁给定的指针指向的对象;释放对应的内存。(3)unique_ptr某个时刻,只能有一个unique_ptr指向一个给定对象。当unique_ptr销毁时,它所指向的对象也被销毁。

unique_ptr操作
unique_ptr<T> u1
unique_ptr<T, D> u2
unique_ptr<T, D> u(d)
u = nullptr
u.release()
u.reset()
u.reset(p)
u.reset(nullptr)

(4)weak_ptrweak+ptr是一种不受控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,而且不会改变shared_ptr的引用计数。

weak_ptr 操作
weak_ptr<T> w
weak_ptr<T> w(sp)
w = p
w.reset()将w置空
w.use_count()与w共享对象的shared_ptr的数量
w.expired()
w.lock()

使用weak_ptr之前,需要调用lock,检查weak_ptr指向的对象是否存在。

12.2 动态数组

(1)new和数组在类型名之后跟一对方括号,在其中指明要分配的对象的数目。释放动态数组:

delete p;      // p必须指向一个动态分配的对象或为空
delete [] pa; // pa必须指向一个动态分配的数组或为空

智能指针和动态数组

unique_ptr<T []> u;
unique_ptr<T []> u(p);
u[i];

(2)allocator类标准库allocator类定义在头文件memory中,帮助将内存和对象构造分离开来。

allocator<string> alloc;
auto const p = alloc.allocate(n);
表达式作用
allocator[T] a定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存
a.allocate(n)分配一段原始的、未构造的内存,保存n个类型为T的对象
a.construct(p, args)为了使用allocate返回的内存,我们必须使用construct构造对象。使用未构造的内存,其行为是未定义的。
a.destroy(p)p为T*类型的指针,此算法对p指向的对象执行析构函数

术语

new : 从自由空间分配内存。new T 分配并构造一个类型为T的指针。如果T是一个数组类型,new 返回一个指向数组首元素的指针。类似的,new  [n]  T 分配 n 个类型为T的对象,并返回指向数组首元素的指针。空悬指针:一个指针,指向曾经保存一个对象但现在已释放的内存。智能指针:标准库类型。负责在恰当的时候释放内存。
第十三章 拷贝控制
P440-P486五种拷贝控制操作:拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符、析构函数。拷贝构造函数、移动构造函数定义了当用同类型的另一个对象初始化本对象时做什么。拷贝赋值运算符、移动赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么。析构函数定义了当此类型对象销毁时做什么。

13.1 拷贝、赋值与销毁

(1)拷贝构造函数拷贝构造函数的第一个参数必须是一个引用类型。

class Foo {
public :
Foo(); // 默认构造函数
Foo(const Foo&); // 拷贝构造函数
}

合成拷贝构造函数:若未定义拷贝构造函数,编译器会定义一个。拷贝初始化:拷贝初始化,要求编译器将右运算对象拷贝到正在创建的对象中。拷贝初始化通常使用拷贝构造函数来完成。(2)拷贝赋值运算符重载赋值运算符:oprator=合成拷贝赋值运算符:若一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。(3)析构函数析构函数:用于释放对象使用的资源,销毁对象的非static数据成员。

class Foo {
public:
~Foo(); // 析构函数,一个类只会有唯一一个析构函数。
}

在一个析构函数中,不存在类似构造函数中初始化列表的东西来控制成员如何销毁,析构部分是隐式的。销毁类类型的成员需要执行成员自己的析构函数。合成析构函数:当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。析构函数体本身并不直接销毁成员。(4)三五法则P447需要析构函数的类也需要拷贝和赋值操作需要拷贝操作的类也需要赋值操作,反之亦然(5)使用default=将拷贝控制成员定义为=dafault来显式地要求编译器生活才能合成的版本。

class Sales_data {
public:
Sales_data(const Sales_data&) = default;
}

(6)阻止拷贝在函数参数列表后面加上=delete。=delete必须出现在函数第一次声明的时候。析构函数不能是删除的成员合成的拷贝控制成员可能是删除的:如果一个类有数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除的。

13.2 拷贝控制和资源管理

(1)行为像值的类为了提供类值的行为,对于类管理的对象,每个对象都应该拥有一份自己的拷贝。类值拷贝赋值运算符:通常组合了析构函数和构造函数的操作。

HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
auto newp = new string(*rhs.ps);
delete ps;
ps = newp;
i = rhs.i;
return *this;
}

(2)行为像指针的类如果需要可直接管理资源,可以使用引用计数。

13.3 交换操作

swap

13.4 拷贝控制示例

P460

13.5 动态内存管理类

P464

13.6 对象移动

与任何赋值运算符一样,移动赋值运算符必须销毁左侧运算对象的旧状态。(1)右值引用可通过move函数开获得绑定到左值上的右值引用。

int && rr3 = std::move(rr1);

(2)移动构造函数和移动赋值运算符移动构造函数的第一个参数是该类类型的一个右值引用。移动赋值运算符:

StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{

}

合成的移动操作:若一个类定义了自己的拷贝构造函数、拷贝赋值运算符或者析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符。如果一个类没有移动操作,类会使用对应的拷贝操作来代替移动操作。移动迭代器:移动迭代器的解引用运算符生成一个右值引用。(3)右值引用和成员函数::: tip区分移动和拷贝的重载函数通常有一个版本接受一个const T&,而另一个版本接受一个T&&。:::右值和左值引用成员函数:指出this的左值/右值属性的方式与定义const成员函数相同,在参数列表后放置一个引用限定符。P483::: tip如果一个成员函数有引用限定符,则具有相同参数列表的所有版本都必须有引用限定符。P485:::

术语

引用限定符:被&限定的函数只能用于坐值;被&&限定的函数只能用于右值。
第十四章 重载运算与类型转换
P490-P523通过运算符重载可重新定义该运算符的含义。

14.1 基本概念

定义:重载运算符是具有特殊名字的函数。名字由operator和符号组成。重载运算符包含返回类型、参数列表和函数体。::: tip当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的显式参数数量比运算对象的数量少一个。对于一个运算符来说,它或者是类的成员,或者至少含有一个类类型的参数。我们只能重载已有的运算符。:::直接调用一个重载的运算符函数

data1 + data2;
operator+(data1, data2);
// 以上2个调用等价

14.2 输入和输出运算符  

(1)重载输出运算符<<

ostream &operator<<(ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.unites_sold << " " << item.revenue << " " << item.avg_price();
return os;
}

(2)重载输入运算符>>

istream &operator>>(istream &is, Sales_data &item)
{
double price;
is >> item.bookNo >> item.units_sold >> price;
if (is)
item.revenue = items.units_sold * price;
else
item = Sales_data();
return is;
}

14.3 算术和关系运算符

(1)相等运算符

bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{
return lhs.isbn() == rhs.isbn() &&
lhs.unites_sold == rhs.units_sold &&
lhs.revenue == rhs.revenue;
}

(2)关系运算符operator<

14.4 赋值运算符

operator=operator+=

14.5 下标运算符

operator[]下标运算符必须是成员函数。

class StrVec{
public:
std::string& operator[](std::size_t n){
return elements[n];
}
const std::string& operator[](std::size_t n) const{
return elements[n];
}
private:
std::string *elements;
}

14.6 递减和递增运算符

递增运算符(++)递减运算符(–)定义前置递增/递减运算符:

class StrBlobPtr{
public:
StrBlobPtr& operator++(); // 前置运算符
StrBlobPtr& operator--();
}

区分前置和后置运算符:

class StrBlobPtr{
public:
StrBlobPtr operator++(int); // 后置运算符
StrBlobPtr operator--(int);
}

14.7 成员访问运算符

operator*operator->

14.8 函数调用运算符

如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。

struct absInt{
int operator()(int val) const {
return val < 0 ? -val : val;
}
};

absInt absObj;
int ui = absObj(i);

如果定义了调用运算符,则该类的对象称为函数对象。

14.9 重载、类型转换与运算符

(1)类型转换运算符类型转换运算符是类的一种特殊成员函数,将一个类类型的值转换成其他类型。形式:operator type() const;(2)避免有二义性的类型转换(3)函数匹配与重载运算符::: warning如果对同一个类既提供了转换目标是算术类型的类型转换,也提供了重载的运算符,将会遇到重载运算符与内置运算符的二义性问题。:::

术语

类类型转换:由构造函数定义的从其他类型到类类型的转换以及由类型转换运算符定义的从类类型到其他类型的转换。
第十五章 面向对象程序设计
P526-P575

15.1 OOP:概述

(1)面对对象程序设计(object-oriented programming)的核心思想:数据抽象、继承和动态绑定。(2)继承:继承是一种类联系在一起的一种层次关系。这种关系中,根部是基类,从基类继承而来的类成为派生类。基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。虚函数:virtual function。基类希望派生类各自定义自身版本的函数。

class Quote {
public:
std::string isbn() const;
virtual double net_price(std::size_t n) const;
}

(3)动态绑定:::: tip在C++语言中,当我们使用基类的引用(或者指针)调用一个虚函数时将发生动态绑定(也称运行时绑定)。P527:::

15.2 定义基类和派生类

(1)定义基类虚函数:基类希望派生类进行覆盖的函数。基类将该函数定义为虚函数(virtual)。基类通过在其成员函数的声明语句之前加上关键词virtual使得该函数执行动态绑定。关键词virtual只能出现在类内部的声明语句之前而不能用于类外部的函数定义。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式的也是虚函数。(2)定义派生类派生类必须通过派生类列表明确指出它是从哪个基类继承而来的。

class Bulk_quote : public Quote {
... // 省略
}

对于派生类中的虚函数的处理:若派生类未覆盖基类中的虚函数,则该虚函数的行为类似其他普通成员。C++允许派生类显式注明覆盖了基类的虚函数,可通过添加override关键字。派生类对象:一个派生类对象包含多个部分:自己定义的成员的子对象,以及基类的子对象。派生到基类的类型转换:由于派生类对象中含有与其基类对象的组成部分,因此可以进行隐式的执行派生类到基类的转换。

Quote item;        // 基类
Bulk_quote bulk; // 派生类
Quote *p = &item; // p指向Quote对象
p = &bulk; // p指向bulk的Quote部分
Quote &r = bulk; // r绑定到bulk的Quote部分。

派生类构造函数:每个类控制自己的成员的初始化过程。派生类首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。派生类使用基类的成员:派生类可以访问基类的公有成员和受保护成员。::: tip派生类对象不能直接初始化基类的成员。派生类应该遵循基类的借口,通过调用基类的构造函数来初始化从基类继承来的成员。:::被用作基类的类:若使用某个类作为基类,则该类必须已被定义而非仅仅声明。派生类包含它的直接基类的子对象以及每个间接基类的子对象。防止继承发生:在类名后面跟着一个关键字final。

class NoDerived final {};   // NoDerived不能作为基类

(3)类型转换与继承我们可以将基类的指针或引用绑定到派生类对象上。静态类型与动态类型:静态类型:在编译时已知,是变量声明时的类型或表达式生成的类型。动态类型:运行时才可知,是变量或表达式表示的内存中的对象的类型。如果表达式既不是引用也不是指针,则动态类型与静态类型永远一致。不存在基类向派生类隐式类型转换:

Quote base;
Bulk_quote *bulkP = &base; // 错误!
Bulk_quote *bulkRef = base; // 错误!

::: warning当我么用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分会被忽略掉。:::

15.3 虚函数

C++的多态性:使用这些类型的多种形式,而无须在意它们的差异。派生类中的虚函数:一个派生类如果覆盖了某个继承而来的虚函数,则它的形参类型必须与被它覆盖的基类函数完全一致。final和override说明符:如果用override标记了某个函数,但是该函数并没有覆盖已存在的虚函数,此时编译器将报错。如果用final标记了某个函数, 则之后任何尝试覆盖该函数的操作都将错误。虚函数与默认实参:如果虚函数某次被调用使用了默认实参,则该实参值由本次调用的静态类型决定。

15.4 抽象基类

纯虚函数:书写=0可以将一个虚函数说明为纯虚函数(pure virtual),纯虚函数无须定义。不能在类的内部为一个=0的函数提供函数体。

class Disc_quote : public Quote {
public:
double net_price(std::size_t) const = 0;
}

抽象基类:含有纯虚函数的类是抽象基类。不能创建抽象基类的对象。

15.5 访问控制与继承

受保护的成员:派生类的成员和友元只能访问派生类对象中的基类部分的受保护成员;对于普通的基类对象中的成员不具有特殊的访问权限。P543公有、私有和受保护继承:派生访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员无影响;对基类成员的访问权限只与基类中的访问说明符有关。派生访问说明符的目的是控制派生类用户对于基类成员的访问权限。改变个别成员的可访问性:通过在类的内部使用using声明语句,我们可以将该类的直接或间接基类中的任何可访问成员标记出来。

class Derived : private Base {
public:
using Base::size;
}

::: tip派生类只能为它可访问的名字提供using声明。:::默认的继承保护级别:使用class关键字定义的派生类是私有继承的;使用struct关键字定义的派生类是共有继承的。

class Base {};
struct D1 : Base {}; // 默认public继承
class D2 : Base {}; // 默认private继承

15.6 继承中的类作用域

在编译时进行名字查找:一个对象、引用或指针的静态类型决定了该对象的哪些成员是可见的。名字冲突与继承:派生类的成员将隐藏同名的基类成员。::: tip出了覆盖继承而来的虚函数外,派生类最好不雅重用其他定义在基类中的名字。:::如果派生类的成员函数与基类的某个成员函数同名,则派生类将在其作用域内隐藏掉该基类成员函数。::: tip非虚函数不会发生动态绑定。:::

15.7 构造函数与拷贝控制

(1)虚析构函数在基类中将析构函数定义成虚函数以确保执行正确的析构函数版本。

Quote *itemP = new Quote;
delete itemP; // 调用Quote的析构函数
itemP = new Bulk_quote;
delete itemP; // 调用Bulk_quote的析构函数

虚析构函数会阻止合成移动操作。(2)合成拷贝控制与继承基类缺少移动操作会阻止派生类拥有自己的合成移动操作,所以当确实要执行移动操作的时候就要首先在基类中进行显式定义。P554(3)派生类的拷贝控制成员派生类的拷贝或移动构造函数:::: tip默认情况下,基类默认构造函数初始化派生类对象的基类部分。如果我们想拷贝(或移动)基类部分,则必须在派生类的构造函数初始值列表中显式的使用基类的拷贝(或移动)构造函数。:::派生类的赋值运算符:派生类的赋值运算符必须显式的为其基类部分赋值。派生类的析构函数:派生类函数只负责销毁由派生类自己分配的资源。

15.8 容器与继承

当使用容器存放继承体系中的对象时,必须采用间接存储的方式。因为不允许在容器中保存不同类型的元素。

术语

覆盖:override,派生类中定义的虚函数如果与基类中定义的同名虚函数与相同的形参列表,则派生类版本将覆盖基类的版本。多态:程序能够通引用或指针的动态类型获取类型特定行为的能力。
第十六章 模板与泛型编程P578-P630(1)控制实例化当编译器遇到extern模板声明时,它不会在本文件中生成实例化代码。将一个实例化声明为extern就表示承诺在程序其他位置有该实例化的一个非extern声明(定义)。对于一个给定的实例化版本,可能有多个extern声明,但必须只有一个定义。(2)模板是标准库的基础。生成特定类或者函数的过程称为实例化。(3)术语类模板:模板定义,可从它实例化出特定的类。类模板的定义以关键词template开始,后面跟尖括号对<和>,其内为一个用逗号分隔的一个或多个模板参数的列表,随后是类的定义。函数模板:模板定义,可从它实例化出特定函数。函数模板的定义以关键词template开始,后跟尖括号<和>,其内以一个用逗号分隔的一个或多个模板参数的列表,随后是函数的定义。

转自:https://mp.weixin.qq.com/s/VQURTxhoDzWgPbclBJttNw