作者| 猫哥
来源| 大猫财经(ID:caimao_shuangquan)



分享个人经验,保留阅读记录,做时间的朋友
作者| 猫哥
来源| 大猫财经(ID:caimao_shuangquan)



在上一章我们探讨了C++的类型系统,并提出了从低到高,又从高到低的学习思路,本文就是一篇从高到低的学习指南,希望能提供一种新的视角。
编程语言一般分为两个部分,一部分是语法部分,如上一章的类型系统,另一部分则是用这套语法完成的预定义的工具集,如本文的主题——标准库。标准库是一堆我们写代码时直接可以用的代码,就像是我们提前写好的一样,不仅如此,标准库还是跨平台的,还是经过工业级测试的,所以标准库有着靠谱,安全的特点。
C++标准库包括很多方面,有类vector、string等,有对象std::cin,std::cout等,还有函数move,copy等,所以一般按功能来对它们分类
容器类
算法类
智能指针
线程相关
其他
当然,这些还不是全部,标准库是在不断扩充和完善的,学习标准库的宗旨也应该是学习它们的使用场景,而不是深入用法。比如容器类中就有很多功能类似的类,不同的业务场景有不同的选择。通过对它们的了解,我们更容易写出高效,简洁的代码。
容器类就是帮助管理一组数据的类,根据实现方式的不同,分为有序列表,无序列表和映射。
有序列表中的有序是指,数据组保存在一块连续的内存区域里,可以通过插入时的索引直接定位到原数据。因为数据是按顺序存入的,所以中途假如需要删除或者新增数据,在操作位置右边的数据都需要移动,操作的代价就比较大。由此也可看出它们的优势是顺序插入和尾部修改,还有直接查找,这方面的代表就是array,vector。array是对原始数组的封装,并且解决了传递数组变成指针这样的问题,但是缺点是它的大小是固定的,适合用在数据量已知的情况。而vector又是对array的增强,不仅能完成所有array的操作,并且大小可变,所以绝大部分情况下,选择vector都是理想的选择。

无序列表的元素是单独存储的,相互之间用指针来查找相邻元素,由于指针可以轻易修改指向的指,所以对相邻元素的修改就变得很快捷。同样的道理,查找相邻元素只能靠指针跳转,查找某个值需要从一个指针开始查找,一次跳转一条数据,直到找到目标或者没有数据为止。所以无序列表的优势是快速地删除和插入新数据,不适合查找,其代表有list,forward_list。显然,有序列表和无序列表是互补的,我们在实际项目中,应该根据数据的操作来确定选择哪种容器。

映射则融合了有序列表和无序列表的优点,既可以快速插入和删除,又可以快速查找。为了满足各种使用场景,C++提供了map,multimap,unordered_map,unordered_multimap。从名字上就能看出来它们的差别。为了直观,我直接列了一个表
| 是否排序 | 是否支持相同值 | 速度 | |
|---|---|---|---|
| unordered_map | ❌ | ❌ | ❤️❤️❤️❤️ |
| map | ✅ | ❌ | ❤️❤️ |
| multimap | ✅ | ✅ | ❤️ |
| unordered_multimap | ✅ | ✅ | ❤️❤️❤️ |
映射存储的是两个值,不同的类型实现方式不一样。由于map是需要排序的,所以通常它的实现是一种平衡二叉树,键就是它排序的依据。

而unordered_map是不需要排序的,所以它的实现通常是哈希表,即根据哈希函数的确定索引位置继而确定存储位置。

综上,容器类提供了一种操作多个同类型数据的接口,开发者通过对容器类方法的调用,可以实现对容器内数据的增删改查。大部分情况下,vector都是靠谱的选择,它提供了全功能的数据操作接口,支持动态长度,索引查询,并且简单高效。如果需要频繁地插入或者删除操作,也可以考虑list或者forward_list。map可以让数据保持有序,需要更快的速度而不是排序的话unorderer_map是更好的选择,如果相同值会出现多次就可以使用对应的multi版本。另外容器类也是很好的数据结构学习资源,C++的容器类几乎提供了数据结构中所有的形式,对数据结构越熟悉选择的容器类就越完美。
之所以将算法放在容器类后面,是因为算法大部分是对容器类操作的加强,算法都定义在algorithm文件头里。这些算法都是短小精悍的,可以大大增加代码可读性,并且妥善处理了很多容易遗忘的边界问题。功能上可以分为增删改查几种操作,可以在实际有需要的时候在查看文档,具体可以参阅这里
很早以前,我对智能指针的态度不是很好。因为刚开始学习C++时我就知道,不能单独使用指针,要把指针封装在类里,利用类的构造函数和析构函数管理指针,也就是RAII。最开始我以为这就够了,直到我遇到下面这种情况
1public:
2 Ptr():p{ new int } {}
3 ~Ptr() {
4 delete p;
5 }
6 int& get() {
7 return *p;
8 }
9
10 void set(const int value) {
11 *p = value;
12 }
13private:
14 int* p;
15};
16
17void use(Ptr p) {
18 //传进来的是复制构造出来的p',函数返回后p'被销毁啦,两个指针指向的地址被回收,外面的p指针成为了野指针
19}
20int main() {
21 Ptr p;
22 p.set(1);
23 use(p); //p按值传递,调用了Ptr的复制构造函数,构造出了新对象p',它的指针和p的指针指向同一个地方
24 std::cout << p.get() << std::endl; //p已经被销毁了,访问p的地址非法
25 return 0;
26}
调用use时,变量p被拷贝,也就出现了两个指针同时指向一块内存地址的情况。use函数执行完后,它的参数p被回收。也就是调用了Ptr的析构函数,也就是两个指针指向的地址被回收。所以24行调用get读取那个已经被回收了的地址就是非法操作,程序崩溃。
这可能是新手比较常遇到的一个问题,当然,解决这个问题也很简单,还用不到智能指针,只需要将函数use的参数改为引用类型就可以了,因为引用只是别名,不会产生新的指针,这也是我在类型系统篇中极力推荐引用为首选参数类型的原因之一。对于此例,数据不大,直接重写复制构造函数,重新申请一块内存也是一种思路。
此例中用到Ptr的地方只有一个,实际项目中Ptr往往需要用到很多次,我们不能保证不会出现忘记使用引用类型的情况,这种情况下重新申请内存也不适用,所以这个时候就需要智能指针来帮忙了。
现在思考另一种情况,某些操作我们不得不暴露出我们的指针供外部使用,随着业务的嵌套和调用链增加,很多时候会忘记或者不确定在什么时候调用delete释放内存。这也是用智能指针的一个场景。以上两种情况都是需要分享指针,对应智能指针中的shared_ptr。shared_ptr顾名思义,它可以帮助开发者完成指针共享的问题,并且完美解决提前释放,不知何时释放,谁负责释放的问题。它的对应关系是一对多,一个实际的内存可以被多个shared_ptr共享

另外一种场景是我们希望自始至终某个指针某个时刻只属于一个对象,外部想要使用它要么通过拥有该指针的对象方法,要么把指针的所有权转移到自己身上,这种场景对应智能指针中的unique_ptr。

unique_ptr的对应关系是一对一,无论哪个时刻,只能有一个管理者拥有指针,也就只能由它负责释放了。假如想转移这种对应关系,只能通过std::move操作,不过这个操作之后,原先对象的指针就失效了,它也不再负责管理,所有的任务移交给了新的对象。这种特性特别适合资源敏感型的应用。
除了内存,线程是开发中另一个重要的课题。线程的难点在于不仅要管理线程对象,还要管理线程对象管理的资源,并且保证线程间数据同步。当然标准库已经做得足够好了,我们需要理解的是使用场景的问题。线程库主要包括线程对象thread,条件对象condition_variable,锁对象mutex。
使用thread可以很方便地把程序写成多线程,只需要三步:
1void plus(int a,int b){ //第一步:定义线程中要运行的函数
2 std::cout<<"running at sub thread"<<std::endl;
3 std::cout<<"a + b = "<<a+b<<std::endl;
4}
5
6int main(){
7 std::thread thread{plus,1,1}; //第二步,定义std::thread对象,将函数作为参数
8 std::cout<<"continue running at main thread"<<std::endl;
9 thread.join(); //第三步调用线程对象的join函数或者detach函数
10 std::cout<<"sub thread finished!"<<std::endl;
11}
12//输出
13// continue running at main thread
14// running at sub thread
15// a + b = 2
16// sub thread finished!
难点在线程间通信,也就是解决两个问题
线程1更新了变量v的值
线程2马上能读取到正确的变量v的值,即线程1更新的那个最新值
为了协调这两个过程,就出现了锁对象mutex和条件对象condition_variable。锁对象mutex保证变量按照正确的顺序更改。条件对象condition_variable保证更改能被其他线程监听到。
1int a,b;
2bool ready = false;
3std::mutex mux;
4std::condition_variable con;
5
6void plus() {
7 std::cout << "running at sub thread" << std::endl;
8 //因为我们要读取ready的最新值,所以要用锁保证读取结果的有效性
9 std::unique_lock<std::mutex> guard{ mux };
10 if (!ready) {
11 //数据没准备好,休息一下!
12 con.wait(guard);
13 }
14 //这里就可以正确读变量a,b了
15 std::cout << "a + b =" << a + b << std::endl;
16}
17
18int main() {
19 std::thread thread{ plus};
20 std::cout << "continue running at main thread" << std::endl;
21 std::cout << "input a = ";
22 std::cin >> a;
23 std::cout << "input b = ";
24 std::cin >> b;
25 {
26 //数据准备好了,该通知子线程干活了,用大括号是因为想让锁因为guard的销毁即使释放,从未保证plus里面能重新获得锁
27 std::unique_lock<std::mutex> guard{ mux };
28 //更新数据
29 ready = true;
30 //通知
31 con.notify_all();
32 }
33 thread.join();
34 std::cout << "sub thread finished!" << std::endl;
35}
多线程另一个需要注意的问题就是死锁。死锁的前提是有两个锁
线程1得到了锁a,还想得锁b
线程2得到了锁b,还想得锁a
然后,再加上一个前提:某一时刻,只有一个线程能拥有某个锁,就不难得出以下结论:线程a,b除非某一个放弃已得的锁,不然两个线程都会因为没得到需要的锁而一直死等,形成死锁。同时解决死锁的思路也呼之欲出:既然一个得了a,一个得了b,而锁同一时间只能被一个线程得到,那么所有线程都按先得a,再得b的顺序来就不会有锁被占用的问题了。另一个思路则可以从放弃上入手,既然都得不到,那么接下来的任务也做不了,不如直接放弃已经得到的,所以可以考虑使用timed_mutex。
还有很多常用的库,如字符串string,时间chrono,还有在定义函数变量时常用的functional,异常exception,更多的内容可以在cplusplus找的参考。
总的来说,标准库提供了一个展现C++语言能力的平台:帮助开发者更好更快完成开发任务的同时,还能启迪开发者实现更好的抽象和实践。如我就从标准库中学到了更规范地定义函数参数,更好的封装,以及其他好的思路。学习标准库不仅更好地掌握了语言本身,还掌握了更全面地分析问题,解决问题的方法,是值得花费一段时间学习的。
容器类是几乎所有项目都会用到的,也是比较好掌握的,主要可以从数据结构方面对照学习;智能指针则是处理指针问题的好帮手;线程相关的库是比较难掌握的,关键是要想明白使用场景和极端情况下的边界问题。很多时候边界问题可能不那么直观。如线程要求获得锁的情况就分为:锁空闲,锁被其他线程占有,锁被自己占有。不同的边界对于不同的锁,预期结果也是不同的,只有在明确场景的情况下,才能更好地理清锁的关系,从而解决好问题。
最好的学习还是在实践中主动使用。对于我,通常在遇到新问题的时候会先查查标准库有没有相应的库,有的话就是学习这个库的好时机。可以先概览库的定义和解决的问题,然后分析它提供的类,函数,对象等,再将自己的理解转换为项目中的代码,最后在实际效果中检验和修正想法,完成库的学习。
转自:https://mp.weixin.qq.com/s/e61DYod_0oypXismlumHpQ




*题图及文中配图来源于unsplash。应受访者要求,文中小鱼、张蓉、桐雨、江汉为化名。

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

7月数据一出,让人直接眼前一黑:实在是太差了!
开发商拿地开工的意愿还在减弱。1-7月,全国房地产开发投资增速同比下降6.4%,其中住宅投资下降下降5.8%,继续坐滑梯。

7月本就是楼市淡季,销售数据并未出现上扬。1-7月商品房销售额同比下降28.8%与上月持平,销售面积同比跌幅扩大到23.1%。

至于一直用来衡量房地产开发景气程度的指数,已经跌了一整年,我建议直接改名为“不景气指数”。
刚刚出炉的70城房价数据显示:
7月份新房下跌城市有40个,又多了2个;
二手房环比下跌城市多达51个,新增了3个。
各大城市的成交数据,只能用“惨不忍睹”来形容。全国30个重点城市,所有城市前七月的累计成交同比都在下滑。

虽然房地产还躺在ICU里吸氧,但你永远不要低估官方和专家的解(wan)读(zun)能力:
房地产在筑底了。


但你仔细想想,不对劲啊,虽说筑底意味着不再下跌,可也没说什么时候开始反弹啊!
这个结果,基本可以宣告上半年的轰轰烈烈的“百城大救市”,效果并不如人意。
半死不活的房地产,又被打回了原形。

信心降至冰点,大家似乎已经不想买房了。
于是,我们迎来了个人住房贷款的又一次下跌。整个7月份的新增房贷只有1486亿元,比上个月少了将近3000亿,比去年同期少了差不多2500亿。
前7个月新增房贷仅仅只有1.71万亿,足足比去年同期少了2万亿。个人住房贷款余额增速几乎降至历史冰点。

全民去杠杆,提前还贷成了一股风潮。去年在售楼处前排队看房的人,今年在银行柜台排队提前还贷。
银行也急了,于是想方设法“制造障碍”。交通银行还因为宣布提前还款要加收1%,被怼上了热搜,赶紧把相关通知撤下。
另外一场席卷全国多个省市的TD潮,进一步加深了购房人的恐惧。
曾经叱咤风云的百强房企,一个个捉襟见肘接连爆雷。
据统计,今年前七个月已有24家房地产发债主体信用债首次违约,已经超过了2021年全年违约数量。
就在上周,“地王专业户”、千亿房企龙光集团爆雷,甚至连一笔境外债的利息都还不上了。
在它之前,已经有一长串名单:阳光城、奥园、正荣、融创、中梁、世茂、融信……

过去我们以为“绿档”房企是安全的,身为绿档房企的弘阳地产,刚刚宣布美元债利息违约。
以前我们相信国企是安全的,有国企背景的绿地也爆雷了。
在刚刚结束的博鳌房地产论坛上,前阳光城总裁朱荣斌发出了灵魂拷问:地产国企央企就安全了吗?
当务之急,是尽快恢复市场信心。
如果不能提振信心,市场购买力不足,会进一步加剧开发商的资金周转困难,后续可能会爆发更多交付问题,让市场信心再度受到打击。
如此一来若是形成恶性循环,楼市回暖也就更加遥遥无期。
绝对不能形成这样的恶性循环。
房地产毕竟是盖章认证的“支柱产业”。房地产一蹶不振,经济却一骑绝尘,怎么想都不可能。这才是今年各种救市的底层逻辑。
7月底高层会议重申“稳地产”,并且提到要“用足用好政策工具箱”,标志着新一轮大救市正式开启。
更多更猛的救市大招,正在排队向我们走来!

在上一轮救市大潮中纹丝不动的一线城市,突然不约而同地有了动作。
可见救市决心有多强!
在帝都密不透风的限购高墙上,终于悄悄凿出了第一条缝隙。北京开始搞试点鼓励老年人购房,甚至在官方口径中允许“接力贷”。

上海限购的核弹级武器——二手房贷款“三价就低”政策,也开始松动了,嗷嗷待哺的改善客们终于不用再“准全款”买房,一大波购买力正待释放。
广州本就是一线城市中购房门槛最低的一个,这次更是直接祭出了超级大招:本科落户“零门槛”!
如此一来,这座一线城市的购房门槛,竟然已经比很多强二线城市还要低了。

全国的本科毕业生们,只要来到广州,几乎不用费什么力气就能获得一张房票,而且是可以买限购区、核心区的那种。
我之前说过,高能级城市的松绑,意味着对低能级城市的利空。
一线城市都开门迎客了,二三四线城市只有拿出更优惠的政策,才能抢到一点肉吃。
接下来,各个都市圈、各个城市会更加内卷!
虽然北京这次松绑,信号意义远大于时机作用,天子脚下的廊坊“北三县”却彻底沸腾了。它们的房价惨遭腰斩,市场一蹶不振,却迟迟不能放开限购。
而这一次,终于没有了阻碍。
上海已经开始行动,长三角的城市们更坐不住了。
网传宁波已经在酝酿全面放开限购,南京、苏州和无锡在同一天宣布降低二套房首付利率,“认房又认贷”即将成为历史。
来自大湾区的最新消息显示:惠州首套房比例,已经要降低到两成了。

新一轮救市大潮,已然扬帆起航。

央行这次大降息,就是个先兆。幸福来得太突然了!
连续7个月按兵不动之后,央妈母爱爆棚,宣布MLF降息10个基点。
考虑到企业贷款利率已经处于历史低位,再加上结构性通胀压力,以及全球加息等多重因素,此前市场预测MLF利率大概率会维持不变。
果然,只要救市决心足够大,就没有什么不可能的!
LPR=MLF+银行平均加点。MLF利率下调,大概率会影响到LPR利率,眼下已经是历史低点的房贷利率,有望继续下探。
答案将会在下周一揭晓。如果5年期LPR利率降息10个基点,则为4.35%;如果5年期LPR利率降息15个基点,则为4.30%。
而根据此前央行、银保监会发布的《关于调整差别化住房信贷政策有关问题的通知》,全国范围内的新发放首套住房商业性个人住房贷款,利率下限从LPR调整为LPR-20BP。
也就是说,首套房贷利率有望低至4.10%。
翻遍我国商品房历史,几乎找不到比这还划算的利率了。
在此我大胆预测一波:
未来还会有更多城市取消“认房又认贷”,
有更多强二线城市全面取消限购,
有更多城市调低首付比例,
甚至连“限跌令”也会有所松动……
也会有越来越多人“经不住诱惑”,买房上车。房地产的战车,终将轰隆隆重启。

最近一段时间,不少读者在后台留言询问:xxx的xxx能不能买?
我只能说,大部分都是坑。
这些地方,要么是产业和人口没有竞争优势的三四线城市,要么是强二线城市的卫星城,甚至还有一线城市的非限购区。
购房门槛越来越低,信贷限制越来越少,看似“能买”的区域越来越多。
越是乱花渐欲迷人眼,也就越容易犯迷糊。你以为是“捡漏”,却不想被套牢。
在当下,买错房比不买房更可怕,一定不要草率决策。
烈火烹油的楼市,一失足成千古恨。
◎本文作者 | 瓜片