<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Program Monkey</title>
    <link>http://coder.amazingdemo.top/</link>
    <description>Recent content on Program Monkey</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <managingEditor>peach5460@qq.com (Peach)</managingEditor>
    <webMaster>peach5460@qq.com (Peach)</webMaster>
    <copyright>他强由他强，清风拂山岗；他横由他横，明月照大江。他自狠来他自恶，我自一口真气足。</copyright>
    <lastBuildDate>Mon, 11 May 2026 15:57:39 +0800</lastBuildDate>
    
        <atom:link href="http://coder.amazingdemo.top/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>地球上还剩一个鸡蛋</title>
      <link>http://coder.amazingdemo.top/post/%E5%9C%B0%E7%90%83%E4%B8%8A%E8%BF%98%E5%89%A9%E4%B8%80%E4%B8%AA%E9%B8%A1%E8%9B%8B/</link>
      <pubDate>Mon, 11 May 2026 15:57:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%9C%B0%E7%90%83%E4%B8%8A%E8%BF%98%E5%89%A9%E4%B8%80%E4%B8%AA%E9%B8%A1%E8%9B%8B/</guid>
      
        <description>&lt;p&gt;&lt;strong&gt;《地球上还剩一个鸡蛋》作者是童话大王郑渊洁。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整个鸡家族都不甘心自己在地球上的地位——任人宰割，供人食用。&lt;/p&gt;
&lt;p&gt;从100年前起，鸡家族的有识之士就试图摆脱鸡家族的困境。但每次行动都以鸡的失败告终。人类大获全胜，还不断完善着吃鸡的艺术：现代化养鸡场填鸡、肯德基、扒鸡、烧鸡，等等，真是到了炉火纯青的境界。&lt;/p&gt;
&lt;p&gt;这还不算，鸡被人类吃得最多，可人类却发明出“黄鼠狼给鸡拜年”的成语嫁祸于黄鼠狼，鸡们确实咽不下这口气。&lt;/p&gt;
&lt;p&gt;鸡王决定在本世纪末之前扭转鸡家族的处境，他召集幕僚想办法。&lt;/p&gt;
&lt;p&gt;“要想让人类放心吃鸡很难。”一位幕僚说。他曾经用10斤鸡蛋收买了人类中一位写东西的人，让他在报上写文章说，吃鸡容易患癌症，可人类照吃不误。&lt;/p&gt;
&lt;p&gt;“依我看，人吃咱们并不是因为恨咱们，而是说明他们离不开咱们。”另一位年纪大一些的幕僚说，他曾经发明过鸡瘟，但被人类用药物治住了。&lt;/p&gt;
&lt;p&gt;鸡王点头，示意这位幕僚继续往下说。&lt;/p&gt;
&lt;p&gt;“大王下一道圣旨，命令所有母鸡停止下蛋。保准人类不会再像现在这样对待咱们了。”幕僚说。&lt;/p&gt;
&lt;p&gt;“停止下蛋？”鸡王吃了一惊，这可是断子绝孙的举动呀。&lt;/p&gt;
&lt;p&gt;“没有鸡蛋，就不会再有新的鸡出生，光这一点，就够人类伤脑筋的。”幕僚说。&lt;/p&gt;
&lt;p&gt;鸡王点点头。&lt;/p&gt;
&lt;p&gt;5分钟后，鸡王向整个鸡家族下达了圣旨：停止下蛋。&lt;/p&gt;
&lt;p&gt;当人类发现全世界所有的母鸡都不下蛋了时，地球上只剩下一个鸡蛋了。&lt;/p&gt;
&lt;p&gt;人类意识到问题的严重性了，地球上的鸡将吃一只少一只，最终灭绝。人们将不再有鸡肉吃，不再有鸡蛋吃。&lt;/p&gt;
&lt;p&gt;科学家们想尽一切方法动员母鸡们恢复下蛋，什么音乐刺激法，红外线紫外线这线那线照射啦，遗憾的是母鸡们不为所动，不管科学家们想出什么绝活儿，她们就是不下蛋。&lt;/p&gt;
&lt;p&gt;人类专门为此召开了一个紧急会议，会议的结果是制定了一部关于保护珍贵动物——鸡的法律。鸡终于改变了卑微的地位，加入珍贵动物的行列。&lt;/p&gt;
&lt;p&gt;人们以拥有一只鸡为荣，狗和猫的地位一落千丈。在公园里时常见到牵着鸡散步的达官贵人。一只鸡的售价已高达上千美元。&lt;/p&gt;
&lt;p&gt;科学家们研制的延长鸡的寿命的药物刚一问世就被抢购一空。有几个国家的国旗上出现了鸡的图案。这一切都因为母鸡不下蛋了。&lt;/p&gt;
&lt;p&gt;经营炸鸡的快餐店统统倒闭，老板不是跳楼就是离婚。&lt;/p&gt;
&lt;p&gt;地球上唯一的那个鸡蛋更是价值连城，光是守护它的军队就有两个旅之多。这个鸡蛋的妈妈是最后一个听到鸡王的圣旨的，当时她正在下蛋，想把这个蛋退回到肚子里已经不可能了，于是她成了地球上唯一的鸡蛋的母亲。&lt;/p&gt;
&lt;p&gt;她因此身价百倍，人们将延续鸡的生命的期望寄托在她身上，人们把最好的食物给她吃，为她建造了超豪华住宅，还让她上报纸上电视上电影。&lt;/p&gt;
&lt;p&gt;鸡王的臣民们不再被人类杀戮，不再供人类食用，人类视他们为珍宝。鸡王满意了。&lt;/p&gt;
&lt;p&gt;那个唯一鸡蛋的母亲的显赫地位渐渐引起了鸡家族成员的注意，他们羡慕她嫉妒她。同样是鸡，为什么她住的房子比同类的好？为什么她能上电视还享有鸡皇后的称号？就因为她生了地球上最后一个鸡蛋！&lt;/p&gt;
&lt;p&gt;没人甘心比同类活得差。&lt;/p&gt;
&lt;p&gt;于是，在一个上午，一只母鸡违抗鸡王的圣旨，她公然下了一个蛋。&lt;/p&gt;
&lt;p&gt;这只母鸡立刻成为全球新闻的焦点，她的待遇立即超过了那只“鸡皇后”。&lt;/p&gt;
&lt;p&gt;鸡家族受到了强烈的震动。&lt;/p&gt;
&lt;p&gt;第三个鸡蛋出生了。&lt;/p&gt;
&lt;p&gt;第四个鸡蛋问世了。&lt;/p&gt;
&lt;p&gt;第五个鸡蛋降临了。&lt;/p&gt;
&lt;p&gt;鸡王感到大事不妙，他连降圣旨制止母鸡们下蛋，但他已经控制不了他的臣民了。&lt;/p&gt;
&lt;p&gt;所有的母鸡都想当鸡皇后，所有的母鸡都想比别的鸡活得好，她们玩命地下蛋。&lt;/p&gt;
&lt;p&gt;当鸡蛋的数量多到派不过来军队保护时，人类醒悟到鸡不会灭绝了。人们嘴里出现了口水，他们仿佛又闻到炸鸡的香味了。&lt;/p&gt;
&lt;p&gt;保护鸡的法律取消了。&lt;/p&gt;
&lt;p&gt;鸡又重新出现在餐桌上。&lt;/p&gt;
&lt;p&gt;没人牵着鸡逛公园了。&lt;/p&gt;
&lt;p&gt;鸡王想再发一道不下蛋的圣旨，幕僚进谏劝住了他。幕僚告诉鸡王，人类已经有了对付鸡不下蛋的办法：他们留出了一个鸡蛋，只要鸡们再不下蛋，他们就拼命抬举这个鸡蛋和它的妈妈。&lt;/p&gt;
&lt;p&gt;鸡家族算是没有出头之日了。鸡王哀叹。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么一去高档场所就浑身不自在?</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E5%8E%BB%E9%AB%98%E6%A1%A3%E5%9C%BA%E6%89%80%E5%B0%B1%E6%B5%91%E8%BA%AB%E4%B8%8D%E8%87%AA%E5%9C%A8/</link>
      <pubDate>Fri, 01 May 2026 15:57:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E5%8E%BB%E9%AB%98%E6%A1%A3%E5%9C%BA%E6%89%80%E5%B0%B1%E6%B5%91%E8%BA%AB%E4%B8%8D%E8%87%AA%E5%9C%A8/</guid>
      
        <description>&lt;p&gt;你走进一个地方，立刻能感觉到：这里是不是为我准备的。&lt;/p&gt;
&lt;p&gt;说得学术一点：空间是有阶级的。&lt;/p&gt;
&lt;p&gt;不是只有人有阶级，空间也有。有些空间是为你开放的，有些空间是为你关闭的。关闭不一定是有形的门，更多是无形的墙——你看不见，但你能感觉到。&lt;/p&gt;
&lt;p&gt;高档商场，就是这种无形墙最密集的地方。&lt;/p&gt;
&lt;p&gt;它不是不让你进。它让你进，但它让你觉得——你不该进。&lt;/p&gt;
&lt;p&gt;这种感觉是怎么来的？不是玄学，是设计。&lt;/p&gt;
&lt;p&gt;每一个细节，都在悄悄地说一句话：你，不属于这里。&lt;/p&gt;
&lt;p&gt;你去过那种商场吗？一进去，头顶是十几米高的挑空，还有巨大的玻璃穹顶。&lt;/p&gt;
&lt;p&gt;这种层高，不是为了实用，选择浪费那么多空间，是因为它只有一个目的：让你觉得渺小。&lt;/p&gt;
&lt;p&gt;心理学里有一个概念叫 “崇高感” 。当你面对巨大的空间、巨大的建筑、巨大的自然景观时，你会产生一种混合着敬畏和渺小的感觉。&lt;/p&gt;
&lt;p&gt;这种感觉，本来是留给大教堂的。中世纪的人走进教堂，仰望高耸的穹顶，会觉得神是伟大的，自己是渺小的。这是一种宗教体验，目的是让你臣服。&lt;/p&gt;
&lt;p&gt;高档商场，借用了同样的手法。&lt;/p&gt;
&lt;p&gt;你不是在购物，你是在朝圣。&lt;/p&gt;
&lt;p&gt;那些高不可攀的穹顶，那些望不到边的走廊——它们都在告诉你一句话：这个地方，比你大。&lt;/p&gt;
&lt;p&gt;你大不过它，所以你只能接受它。&lt;/p&gt;
&lt;p&gt;其实最让人不自在的，是人。&lt;/p&gt;
&lt;p&gt;不是顾客，是店员。&lt;/p&gt;
&lt;p&gt;高档商场的店员，是经过训练的。她们（大部分是她们）站在那里，穿着统一的黑色套装，化着精致的妆，站姿挺拔，表情管理极好。&lt;/p&gt;
&lt;p&gt;她们不会像普通商场那样喊“欢迎光临”，也不会热情地迎上来问“想看点什么”。她们只是站在那里，看着你。&lt;/p&gt;
&lt;p&gt;那种看，是一种审判。&lt;/p&gt;
&lt;p&gt;你的衣服、你的鞋、你的包、你的发型、你的气质——她们扫一眼，几秒钟，就已经完成了评估。&lt;/p&gt;
&lt;p&gt;你通没通过，你自己能感觉到。&lt;/p&gt;
&lt;p&gt;如果你通过了，她们会上前，语气温柔地问：“有什么可以帮您的吗？”如果你没通过，她们只是看着你，什么都不说。&lt;/p&gt;
&lt;p&gt;那种“什么都没说”，比说了更可怕。&lt;/p&gt;
&lt;p&gt;因为它在告诉你：你不值得我说。&lt;/p&gt;
&lt;p&gt;有一个做奢侈品销售的人跟我说，她们培训的时候，有一课叫“识别顾客”。&lt;/p&gt;
&lt;p&gt;“不是看谁有钱，是看谁适合。”她说，“有些人进来，一看就知道不是目标客户。这种人不用理，理了也是浪费时间，还可能影响其他客人。”&lt;/p&gt;
&lt;p&gt;我问她：“怎么识别？”&lt;/p&gt;
&lt;p&gt;她说：“看整体。穿着、气质、眼神。真正买得起的人，走路是稳的，眼神是定的。不买的人，走路飘，眼神躲闪，不敢看人。”&lt;/p&gt;
&lt;p&gt;我听完，有点悟了。&lt;/p&gt;
&lt;p&gt;如果你去高档商场不自在，那不是你的问题，是设计的问题。&lt;/p&gt;
&lt;p&gt;那些灯光、层高、店员、品牌、入口——它们都是精心设计过的。设计的目的是什么？是让你不自在，是让你觉得自己不配，是让你在掏钱的时候，觉得自己“终于配得上了”。&lt;/p&gt;
&lt;p&gt;这是一种销售策略，不是对你个人的审判。&lt;/p&gt;
&lt;p&gt;你什么都没做错。&lt;/p&gt;
&lt;p&gt;你只是走进了一个为你设计的不自在。&lt;/p&gt;
&lt;p&gt;所以，如果你下次再走进那样的商场，可以对自己说一句话：“不是我不配，是这个空间在说话。”&lt;/p&gt;
&lt;p&gt;它可以说话，你也可以不听。&lt;/p&gt;
&lt;p&gt;你可以昂起头，慢慢走，想看什么看什么。你可以直视那些店员的凝视，用同样的眼神看回去。你可以不买任何东西，就只是逛，然后离开。&lt;/p&gt;
&lt;p&gt;因为商场是给人逛的，不是给人跪的。&lt;/p&gt;
&lt;p&gt;而且，去多了，你就会发现，那些灯光没那么可怕，那些层高没那么压迫，那些店员没那么重要。你会发现，那些你曾经觉得高不可攀的地方，其实也只是卖东西的。&lt;/p&gt;
&lt;p&gt;只是卖得更贵一点而已。&lt;/p&gt;
&lt;p&gt;而那些贵的东西，你也可以不买。&lt;/p&gt;
&lt;p&gt;你可以只是逛，只是看，只是体验。&lt;/p&gt;
&lt;p&gt;然后离开。&lt;/p&gt;
&lt;p&gt;这是你的权利。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>有什么极为典型的穷人思维？</title>
      <link>http://coder.amazingdemo.top/post/%E6%9C%89%E4%BB%80%E4%B9%88%E6%9E%81%E4%B8%BA%E5%85%B8%E5%9E%8B%E7%9A%84%E7%A9%B7%E4%BA%BA%E6%80%9D%E7%BB%B4/</link>
      <pubDate>Sun, 05 Apr 2026 12:57:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%9C%89%E4%BB%80%E4%B9%88%E6%9E%81%E4%B8%BA%E5%85%B8%E5%9E%8B%E7%9A%84%E7%A9%B7%E4%BA%BA%E6%80%9D%E7%BB%B4/</guid>
      
        <description>&lt;p&gt;穷人都喜欢说自己能吃苦。 夏天40度送外卖，冬天零下送快递，一天干12小时不叫苦。扛水泥、搬砖头、站流水线，累到腰直不起来不叫苦。&lt;/p&gt;
&lt;p&gt;过年回家，还跟亲戚吹牛，说自己在外面「能吃苦」。&lt;/p&gt;
&lt;p&gt;你可以受所有的穷，忍所有的气。但你不能看书。你不能学技能。你不能花两个小时让自己变好。&lt;/p&gt;
&lt;p&gt;你这叫能吃苦？老法告诉你，你这叫贱。&lt;/p&gt;
&lt;p&gt;你那是吃苦吗？你那是被生活强奸了还不敢还手。&lt;/p&gt;
&lt;p&gt;《当幸福来敲门》里的威尔·史密斯主演的克里斯就是个典型例子，真人真事改编。&lt;/p&gt;
&lt;p&gt;克里斯·加德纳，医疗器械推销员，卖一种叫骨密度扫描仪的东西。比X光机贵一倍，效果还没好到哪去，根本卖不出去。他家里堆满了这种破机器，老婆打两份工养不活这个家，最后老婆跑了，留下他一个人带着两岁多的儿子。&lt;/p&gt;
&lt;p&gt;穷到什么地步？交不起房租，被房东赶出来。带着儿子住收容所，睡教堂的走廊，最后睡地铁厕所。&lt;/p&gt;
&lt;p&gt;有一天晚上，外面有人疯狂砸厕所的门，克里斯一脚死死抵着门，一手捂着儿子的耳朵，眼泪哗哗地往下掉，不敢出声。他怕一开门，儿子就连厕所都没得睡了。&lt;/p&gt;
&lt;p&gt;这苦不苦？苦到家了。但老法告诉你，这种苦，还不是克里斯最狠的地方。&lt;/p&gt;
&lt;p&gt;他最狠的地方，是他明明已经惨成这样了，还敢去做一件更苦的事。&lt;/p&gt;
&lt;p&gt;他看到一个男人开着法拉利停在路边，冲上去问「你是干什么的」。那人说，我是股票经纪人。克里斯说，我能干这个吗？那人说，只要你好好学，谁都能干。克里斯拿着简历就去了。&lt;/p&gt;
&lt;p&gt;迪恩·威特的实习，20个人争1个转正名额，实习期整整6个月，一分钱工资都没有。他没学历，没背景，没资源，连件像样的衬衫都没有。&lt;/p&gt;
&lt;p&gt;实习期间，他每天比别人早到一小时，电话听筒夹在脖子上一整天，不喝水、不上厕所——就为了省下那几分钟，在别人下班之前多做几个客户，然后狂奔去幼儿园接儿子。&lt;/p&gt;
&lt;p&gt;晚上，儿子在收容所的床上睡着了，克里斯借着走廊昏黄的灯光，啃那本厚厚的证券教材。他不认识那些单词，就查字典，一行一行地啃。&lt;/p&gt;
&lt;p&gt;老法问你，被赶出家门、睡厕所，和每天不喝水不尿尿还要啃书，这两种苦，是一样的吗？&lt;/p&gt;
&lt;p&gt;不一样。第一种苦，生活塞给你的，你躲不掉，不得不吃，谁都会。第二种苦，是你自己找的，没人逼你，你可以不吃，但你吃了。&lt;/p&gt;
&lt;p&gt;穷人是什么？穷人是第一种苦吃得越多，越感动。但第二种苦，碰都不敢碰。&lt;/p&gt;
&lt;p&gt;主动吃苦不一样，它需要你动脑子。&lt;/p&gt;
&lt;p&gt;《hello树先生》看过吧？&lt;/p&gt;
&lt;p&gt;树先生在村里，谁都能使唤他，谁都能笑话他。他爹亲手弄死了他哥，这事儿像座山压在他头上。他在村里晃来晃去，东家串西家，红白喜事他都去帮忙，忙前忙后像个孙子。别人拿他当猴耍，他嘿嘿笑。别人拿他当傻子，他也嘿嘿一笑。&lt;/p&gt;
&lt;p&gt;他被动吃了多少苦？穷、被人看不起、亲人离世、精神创伤。他全咽下去了。&lt;/p&gt;
&lt;p&gt;但他主动吃过什么苦？&lt;/p&gt;
&lt;p&gt;他主动学过什么手艺？&lt;/p&gt;
&lt;p&gt;他主动想过怎么离开那个村子？&lt;/p&gt;
&lt;p&gt;他主动争取过什么？&lt;/p&gt;
&lt;p&gt;没有。他连一个念头都没有。&lt;/p&gt;
&lt;p&gt;最后他疯了。他在幻觉里当上了风水大师，在幻觉里被人尊重，在幻觉里娶了媳妇盖了房。现实里他连一条狗都不如，只能在疯掉的脑子里给自己盖一座皇宫，当自己的王。&lt;/p&gt;
&lt;p&gt;老法告诉你，这就是只吃被动苦的结局。&lt;/p&gt;
&lt;p&gt;你不是被生活打垮的，你是被生活调教成了一个只会挨打、不会还手的废物。&lt;/p&gt;
&lt;p&gt;你说老法，那我从小也没错啊，怎么就这样了？&lt;/p&gt;
&lt;p&gt;因为你从小被训练成这样。&lt;/p&gt;
&lt;p&gt;你爹妈告诉你，要忍耐，要听话，要老实。&lt;/p&gt;
&lt;p&gt;学校告诉你，要服从，要标准答案，别做出格的事&lt;/p&gt;
&lt;p&gt;上班，领导告诉你，好好干，吃亏是福。&lt;/p&gt;
&lt;p&gt;这些东西加起来，把你训练成了一台完美的被动吃苦机器。&lt;/p&gt;
&lt;p&gt;被动吃苦有个好处：不用思考。不用选择。不用承担失败的风险。生活把苦塞给你，你咽下去就行了，脑子可以不动。&lt;/p&gt;
&lt;p&gt;但主动吃苦不一样，你得自己想清楚要干什么，你得自己做选择，你得承担后果。&lt;/p&gt;
&lt;p&gt;扛水泥不需要动脑子， 这就是你为什么能扛16小时水泥，却看不下2小时书的原因。&lt;/p&gt;
&lt;p&gt;《肖申克的救赎》里，老布在监狱里待了50年，最后被释放了却自杀了。&lt;/p&gt;
&lt;p&gt;为什么？因为他被体制化了。50年，有人告诉他什么时候吃饭，什么时候睡觉，什么时候拉屎。他不需要思考，不需要选择。突然门打开了，告诉你，你自由了。他走到外面，发现不知道怎么生活，不知道怎么办。于是他上吊了。&lt;/p&gt;
&lt;p&gt;别笑老布傻，老法告诉你，大多数人都是老布。被生活强奸了20年、30年，已经不会反抗了。&lt;/p&gt;
&lt;p&gt;既然这样，那我们现在该咋办？&lt;/p&gt;
&lt;p&gt;老法告诉你，先从一件小事开始。&lt;/p&gt;
&lt;p&gt;找到一件「你可以不吃，但你想吃」的苦。&lt;/p&gt;
&lt;p&gt;下班之后，别人刷视频，你看一小时能让你值钱的书。周末，别人打麻将，你学一个能赚钱的技能。这苦没人逼你，是你自己找的。比如我定了个目标，不管有多忙，每天都必须写一篇知乎文章，每天保持写作输出的热情&lt;/p&gt;
&lt;p&gt;刚开始你会浑身难受。因为你已经被调教成了「不逼就不动」的动物。但你得逼自己。不是逼自己扛更多水泥，是逼自己去动脑子。&lt;/p&gt;
&lt;p&gt;克里斯·加德纳在最惨的时候，还在看书。树先生在同样惨的时候，在村里晃荡。&lt;/p&gt;
&lt;p&gt;最后克里斯成了百万富翁，树先生成了疯子。&lt;/p&gt;
&lt;p&gt;你的命，是你自己选的。&lt;/p&gt;
&lt;p&gt;你吃的苦，决定了你是克里斯，还是树先生。&lt;/p&gt;
&lt;p&gt;共勉&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么一些人总是爱在小事上斤斤计较，大事上却犯糊涂？</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E4%BA%9B%E4%BA%BA%E6%80%BB%E6%98%AF%E7%88%B1%E5%9C%A8%E5%B0%8F%E4%BA%8B%E4%B8%8A%E6%96%A4%E6%96%A4%E8%AE%A1%E8%BE%83%E5%A4%A7%E4%BA%8B%E4%B8%8A%E5%8D%B4%E7%8A%AF%E7%B3%8A%E6%B6%82/</link>
      <pubDate>Thu, 02 Apr 2026 16:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%80%E4%BA%9B%E4%BA%BA%E6%80%BB%E6%98%AF%E7%88%B1%E5%9C%A8%E5%B0%8F%E4%BA%8B%E4%B8%8A%E6%96%A4%E6%96%A4%E8%AE%A1%E8%BE%83%E5%A4%A7%E4%BA%8B%E4%B8%8A%E5%8D%B4%E7%8A%AF%E7%B3%8A%E6%B6%82/</guid>
      
        <description>&lt;p&gt;以前，我读钱钟书的《围城》时，一直很不理解：方鸿渐和孙柔嘉，他们俩明明受过高等教育，为什么却在婚后闹得不可开交、变得歇斯底里？&lt;/p&gt;
&lt;p&gt;他们俩可以为了谁去买报纸、佣人的一句闲话、以及亲戚间的人情往来等琐碎小事情，吵得天翻地覆。&lt;/p&gt;
&lt;p&gt;可是，在面对职业选择、人生走向甚至婚姻存续这种真正的大事上，他们却毫无主见，得过且过，任由命运把他们推向深渊……&lt;/p&gt;
&lt;p&gt;这，真是天意弄人吗？&lt;/p&gt;
&lt;p&gt;后来出社会后，我发现像方鸿渐这样“抓小放大”的人，比比皆是。&lt;/p&gt;
&lt;p&gt;现实生活里，有人会为了一张免费赠送的鼠标垫，去买一台自己根本用不上的电脑的。&lt;/p&gt;
&lt;p&gt;也有人，跟着中介看房两三周，就糊里糊涂就购置了几百万的房产，签下30年的贷款。&lt;/p&gt;
&lt;p&gt;更有人，一辈子省吃俭用，转头就相信亲戚的一句话，就把养老钱全砸进无资质的理财产品……&lt;/p&gt;
&lt;p&gt;他们把在小事上很精明、很会过日子，却从大事上像个傻子一样糊里糊涂。&lt;/p&gt;
&lt;p&gt;无论他们怎么将自己这种行为合理化，都在某种程度上证明了一点：&lt;/p&gt;
&lt;p&gt;他们的大脑无法处理高风险、高复杂度的系统性问题，只能在低维度的琐事上寻找安全感。&lt;/p&gt;
&lt;p&gt;现代社会其实给我们提供了极其丰富的信息工具，然而大部分人依旧把精力耗费在最末节的摩擦里。&lt;/p&gt;
&lt;p&gt;网络上那么多关于宏观经济走向的分析、关于资产配置的底层逻辑、关于行业周期的深度研报，他们连一分钟都看不进去。&lt;/p&gt;
&lt;p&gt;但如果是某家超市发了五毛钱的优惠券，或者邻里之间的一点八卦，他们能津津乐道一整天。&lt;/p&gt;
&lt;p&gt;他们只敢在那些一眼就能看到底、无论输赢都无关痛痒的小事里挥洒聪明才智。&lt;/p&gt;
&lt;p&gt;我曾一度以为，那些很会抓大放小的人，一定是比较清醒、或者是天生充满智慧的。&lt;/p&gt;
&lt;p&gt;后来见过的人多了，才发现：&lt;/p&gt;
&lt;p&gt;人与人是不同的，每个人的时间成本和容错率是不一样的。&lt;/p&gt;
&lt;p&gt;当你的时间越来越值钱，你自然对赠送的鼠标垫、鸡蛋亦或是那几块钱、几句闲话等小事不再执着了。&lt;/p&gt;
&lt;p&gt;因为你很清楚，你的时间和心智带宽，是有巨大成本的。&lt;/p&gt;
&lt;p&gt;记得《稀缺》一书时，还提到一个很契合这里的概念，叫做：管窥效应。&lt;/p&gt;
&lt;p&gt;说的是，当一个人长期处于某种资源的匮乏中，无论是缺钱、缺爱、缺安全感还是缺认知，他的视野就会变得像通过一根管子看世界一样狭窄。他的大脑带宽被眼前最紧迫、最琐碎的得失全部占满。&lt;/p&gt;
&lt;p&gt;这其实是人类的本能，小事虽然琐碎，但它们是具象的、可控的，能提供即时的反馈。而大事是抽象的、充满未知的，思考大事意味着要承担巨大的不确定性和失败的风险。&lt;/p&gt;
&lt;p&gt;所以，在小事上斤斤计较，在大事上犯糊涂，本质上是一种心理防御机制。&lt;/p&gt;
&lt;p&gt;因为面对大事时的无力感太痛苦了，他们只能像把头埋进沙子里的鸵鸟，通过在小事上的赢，来掩盖自己在人生大事上的失控。&lt;/p&gt;
&lt;p&gt;反过来，如果你跳出了那个生存压力的隧道，你的容错率变高了，你就会发现试图在每一件小事上争个输赢是多么短视的行为。&lt;/p&gt;
&lt;p&gt;因为捡起了芝麻却丢了西瓜，换个维度看，所有的蝇头小利都抵不过一次大方向的溃败。&lt;/p&gt;
&lt;p&gt;可是，不是每个人都有资本和运气去拥有充裕的心智带宽。&lt;/p&gt;
&lt;p&gt;总有人因为原生家庭、成长环境或生活的重担，一辈子都在为了眼前的几两碎银疲于奔命。&lt;/p&gt;
&lt;p&gt;这些人看不远，想不深，他们痛恨自己生活的逼仄，却又没有余力去思考破局的宏大命题，最后只能在鸡毛蒜皮的算计里了此一生。&lt;/p&gt;
&lt;p&gt;归根到底，抓大放小的格局不是什么多么了不得的天赋，当你被逼着扛过几次大责任，当你体会过做对一次大决策带来的巨大时代红利，你就会发现，服务员的态度其实根本没有那么重要，超市的打折也没有那么重要。&lt;/p&gt;
&lt;p&gt;等到了那个时候，一点小钱的损失也好，别人的几句闲言碎语也罢，都只是你庞大人生系统里的微小摩擦力，请问你又怎么会把宝贵的精力浪费在跟它们缠斗上呢？&lt;/p&gt;
&lt;p&gt;因此，当你发现自己开始为了几块钱、几句意气之争而彻夜难眠时，也许你该停下来审视一下自己的人生坐标了。&lt;/p&gt;
&lt;p&gt;或者去学一点真正有门槛的东西，只要你愿意，这个时代能撬动人生杠杆的支点实在太多，多到你全力以赴去学习都嫌时间不够。&lt;/p&gt;
&lt;p&gt;当你意识到这点就会发现，在琐事上的算计和争执其实是多么盲目、可悲且消耗生命。&lt;/p&gt;
&lt;p&gt;人生短短三万天，不要在小事情在难为自己，消耗生命。&lt;/p&gt;
&lt;p&gt;更不要用战术上的精明，来掩盖战略上的偷懒。&lt;/p&gt;
&lt;p&gt;别在琐碎中雕花，不值得。&lt;/p&gt;
&lt;p&gt;祝好。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>解决小米电力猫黄灯闪</title>
      <link>http://coder.amazingdemo.top/post/%E8%A7%A3%E5%86%B3%E5%B0%8F%E7%B1%B3%E7%94%B5%E5%8A%9B%E7%8C%AB%E9%BB%84%E7%81%AF%E9%97%AA/</link>
      <pubDate>Sat, 29 Mar 2025 12:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E8%A7%A3%E5%86%B3%E5%B0%8F%E7%B1%B3%E7%94%B5%E5%8A%9B%E7%8C%AB%E9%BB%84%E7%81%AF%E9%97%AA/</guid>
      
        <description>&lt;p&gt;出租屋的网有点问题，路由器覆盖不够全面，在卧室里信号不怎么好。&lt;/p&gt;
&lt;p&gt;于是从咸鱼上买了一对小米电力猫。&lt;/p&gt;
&lt;p&gt;但是买回来以后，把电力猫接到同一个插板上，主猫接了路由器LAN口，主猫和小猫一直是黄灯狂闪。在米家里面尝试添加电力猫也失败。&lt;/p&gt;
&lt;p&gt;尝试过电力猫直接接光猫，尝试过换不同的网线，都不行。&lt;/p&gt;
&lt;p&gt;在网上搜了好多教程没无非是按住Rest键五秒，重置。&lt;/p&gt;
&lt;p&gt;但是实际是，按住五秒以后，也不行，还是闪黄灯。&lt;/p&gt;
&lt;p&gt;都怀疑咸鱼卖家是不是发了个坏的给我，都准备找卖家扯皮了。&lt;/p&gt;
&lt;p&gt;最终，终于搜到一个老帖子，说电力猫的重置不是按住Rest是快速按三下Rest键。&lt;/p&gt;
&lt;p&gt;原来不是长按N秒，是连点。连点三下，主猫灯蓝灯闪，小猫点三下，也蓝灯闪。过一会，两蓝灯都常亮了，好了。&lt;/p&gt;
&lt;p&gt;果然，快速按三下以后，蓝灯常亮，开始了配对模式，正确接入了手机上的米家APP。&lt;/p&gt;
&lt;p&gt;小米自己的说明书都是些的长按不松，真的是坑爹。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>2023年的奇葩车祸事件</title>
      <link>http://coder.amazingdemo.top/post/23%E5%B9%B4%E5%A5%87%E8%91%A9%E8%BD%A6%E7%A5%B8/</link>
      <pubDate>Wed, 27 Dec 2023 12:12:12 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/23%E5%B9%B4%E5%A5%87%E8%91%A9%E8%BD%A6%E7%A5%B8/</guid>
      
        <description>&lt;p&gt;遇到个奇葩车险。&lt;/p&gt;
&lt;p&gt;昨天上班快迟到了，到单位楼下慌着倒车入库，不小心把旁边的车前脸擦了，当时对方不在车里。&lt;/p&gt;
&lt;p&gt;我通过116114联系对方，三通电话没人接，于是我自己报了交警和保险。&lt;/p&gt;
&lt;p&gt;但是因为一直联系不上对方车主，保险就让我留个字条写上我的电话，让我先离开现场。&lt;/p&gt;
&lt;p&gt;一个半小时后，保险让我拍损伤照片，下楼跑去拍，正好碰到对方车主了，原来大家在一栋楼上不同楼层打工的。&lt;/p&gt;
&lt;p&gt;车主声称自己车车后面都擦过，就前脸没擦，我正好就擦了，然后说擦得蛮严重的，都露底漆了。&lt;/p&gt;
&lt;p&gt;我说不慌，我有保险，已经报了，交警也报了，我全责无异议，会跟你修的。&lt;/p&gt;
&lt;p&gt;他很诧异说你这么快就报了？然后说没必要报保险，私了给个三五百算了，他自己找个路边修理厂随便补补漆，你明年保费也不涨。&lt;/p&gt;
&lt;p&gt;我说一个多小时前就擦了，我不报，人走了，算肇事逃逸不是亏大发了，报都报了，走保险吧。&lt;/p&gt;
&lt;p&gt;当晚，保险流程就走完了，保险办结案了。&lt;/p&gt;
&lt;p&gt;今天对方又联系我，说建议我保险撤销，大家私了。&lt;/p&gt;
&lt;p&gt;我说保险昨晚都办结案了，别搞麻烦了，你去修车吧，不好意思啊，把你车擦了。&lt;/p&gt;
&lt;p&gt;然后他在电话里再三确认，那我真去修啦，你确定我去修啦，我去修以后就不能反悔啦，保费要涨啦。&lt;/p&gt;
&lt;p&gt;在我再三确认下，他终于去修车了。过了一个多小时，他又打电话给我，说他去了附近的途虎，途虎估价1000，要我去现场交钱。&lt;/p&gt;
&lt;p&gt;我感觉很诧异，以前我出险不都是修理厂跟保险对接，车送去修就完了，怎么还要我去现场送钱。我没经历过啊？&lt;/p&gt;
&lt;p&gt;我跟保险打电话，让保险跟对面对接对接。保险说途虎跟平安没合作，推荐他去合作修理厂。&lt;/p&gt;
&lt;p&gt;我又打电话给对方司机，说推荐他去附近的修理厂。对方司机说他不信任修理厂，修理厂修不好。&lt;/p&gt;
&lt;p&gt;我又去打保险电话，要求保险必须跟我把这件事解决掉，磨了半天，保险说，好吧，他跟对方谈一下。&lt;/p&gt;
&lt;p&gt;过了一会儿，保险打电话跟我说对方极难伺候。保险跟对方说给他300，就此了断，你爱去哪修去哪修，对方不同意，非要保险赔完以后，我私人再赔100.&lt;/p&gt;
&lt;p&gt;保险说他只能保证保险赔的，别的他做不了主，就把电话挂了。&lt;/p&gt;
&lt;p&gt;然后保险跟我说，要么让对方去附近平安能合作的修理厂，要么去他自己的4S。他不管了，这人太难缠了。&lt;/p&gt;
&lt;p&gt;然后我就接到对方的电话，问我怎么修车。我说修理厂你不乐意去，那就去4S呗。&lt;/p&gt;
&lt;p&gt;对方跟我说，4S很远，要我额外给他报销送车以后的士费用。还说自己全新的车，开了一年不到，就没出过险，这次出险损失可大了，都没找我要折旧费，对我很和气了。&lt;/p&gt;
&lt;p&gt;我说我都报保险了，没道理还要私下给钱，要不然我买保险干嘛。送车以后，给你带来不便，我只能抱歉，但是给钱，这不合规矩。而且我也一直很配合你的呀，主动找你人找不到，又是留电话，你修车我也是积极配合。&lt;/p&gt;
&lt;p&gt;对方觉得，我只是让你出来去的的士费，又不是讹你，这你都不乐意，那就没得谈了？&lt;/p&gt;
&lt;p&gt;对方现在开始，话里话外开始威胁我，你这样办事是吧，那以后你车在底下停着，要是出点什么擦碰啊之类的，我也只能先说声对不起，抱歉了。&lt;/p&gt;
&lt;p&gt;我说你没必要威胁我，大家一个楼上班，抬头不见低头见的，没必要。而且，你昨天说大家私了，你随便找个修理厂，怎么咧，换成是保险赔钱，就看不上修理厂啦？而且昨天还说车哪里哪里都擦过，就牵连没擦过，今天就成全新车子，没出过险了？有些话别说太绝，有些事别做太绝。&lt;/p&gt;
&lt;p&gt;他又说，谁敢威胁你呀，现在法制社会，反正以后你车擦了，你就找保险呗，给你带来不便，你也说抱歉好了。反正就是各种阴阳怪气。&lt;/p&gt;
&lt;p&gt;我擦过别人，也被人擦过，出了这么多次车险，还是第一次碰到这种人！！！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如何选用化痰止咳药物</title>
      <link>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E9%80%89%E7%94%A8%E5%8C%96%E7%97%B0%E6%AD%A2%E5%92%B3%E8%8D%AF%E7%89%A9/</link>
      <pubDate>Wed, 22 Nov 2023 11:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E9%80%89%E7%94%A8%E5%8C%96%E7%97%B0%E6%AD%A2%E5%92%B3%E8%8D%AF%E7%89%A9/</guid>
      
        <description>&lt;p&gt;咳、咳、咳，咳嗽在「阳」群中此起彼伏，不少网友表示，感染新冠退烧后的主要症状就是咳嗽，有的是干咳停不下来，有的则是脓痰咳不出去。&lt;/p&gt;
&lt;p&gt;咳嗽咳痰在许多呼吸道病毒感染后都会出现。感染早期的表现（如发烧、鼻塞、流鼻涕等）改善后，仍然遗留咳嗽、咳痰的症状，一般称为感染后咳嗽（详情文章）或者感冒后咳嗽。其原因主要由于病毒损伤了气道黏膜，黏膜下神经暴露导致平滑肌反应性增强，容易出现气道收缩，导致咳嗽。&lt;/p&gt;
&lt;p&gt;有些人表现为干咳，是嗓子发痒的刺激性咳嗽，用一些对症的止咳药就可以了。有些人表现为有痰，早期是白色泡沫痰，后期出现黏痰甚至黄浓痰，这种情况一般主张用化痰药。&lt;/p&gt;
&lt;p&gt;如果咳嗽几天后，痰液颜色由白色变为黄色，且痰量增加，出现了脓性痰，这有可能是由于上呼吸道的病毒感染导致了呼吸道防御能力的下降，出现了合并细菌感染，这种情况建议可以用一些抗生素治疗，一般 3 到 5 天就可以了。&lt;/p&gt;
&lt;p&gt;咳嗽其实是呼吸道的保护性生理反射，有利于清除呼吸道分泌物和有害因子，一定程度是「有益的」， 轻度咳嗽不需进行镇咳治疗。但严重的咳嗽，如剧烈干咳或频繁咳嗽影响工作和休息时，则可适当给予镇咳治疗，痰多患者宜使用祛痰药物治疗。&lt;/p&gt;
&lt;h1 id=&#34;常见止咳药物用药方案及注意事项&#34;&gt;常见止咳药物用药方案及注意事项&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;止咳1&#34;&gt;
&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;止咳2&#34;&gt;
&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;止咳3&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;常见祛痰药物用药方案及注意事项&#34;&gt;常见祛痰药物用药方案及注意事项&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;祛痰1&#34;&gt;
&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;祛痰2&#34;&gt;
&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;祛痰3&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;小结&#34;&gt;小结&lt;/h1&gt;
&lt;p&gt;咳嗽是机体的重要防御性反射，不一定都是坏事，如果是轻微的干咳，建议多喝温水、蜂蜜水润喉，睡觉时垫高头部；如果是严重的咳嗽，且已影响生活质量，建议及时就诊，服用适宜的止咳药和（或）祛痰药，并排查是否伴有其他疾病影响。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>孩子在学校里的冲突事件</title>
      <link>http://coder.amazingdemo.top/post/%E5%AD%A9%E5%AD%90%E5%9C%A8%E5%AD%A6%E6%A0%A1%E8%B5%B7%E5%86%B2%E7%AA%81/</link>
      <pubDate>Thu, 07 Sep 2023 12:12:12 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%AD%A9%E5%AD%90%E5%9C%A8%E5%AD%A6%E6%A0%A1%E8%B5%B7%E5%86%B2%E7%AA%81/</guid>
      
        <description>&lt;h1 id=&#34;放学时&#34;&gt;放学时&lt;/h1&gt;
&lt;p&gt;今天放学，我去接Iris。接到孩子以后，快走到校门口时，被同班的另一个家长突然拦住，说Iris用水杯带把她家孩子（下称为B）手抽了一下，她家孩子疼哭了，问我：“你看怎么办吧。”&lt;/p&gt;
&lt;p&gt;当时我有点懵，于是蹲下来问Iris，是不是你打的；Iris说是。&lt;/p&gt;
&lt;p&gt;我问为什么你要打人呢，Iris低着头不说话；我说你不要害怕，有什么问题就跟爸爸说，你是因为什么原因要打她一下呢。Iris想了一下说是因为B小朋友和另外一个小朋友（下称为A），一直抢Iris的水杯，并且B做了一个很过分的事，把Iris惹毛了，Iris就抡起水杯甩了一下，水杯带子就把B抽到了。但是B当时具体做了什么事，Iris想不起来了。&lt;/p&gt;
&lt;p&gt;在我询问Iris事情经过的时候，B小朋友的妈妈不断地向我投诉说，“我家孩子从来不哭的，她很坚强的，这是要打得多狠她才会哭呀”。此时有另一个小朋友C全程站在B旁边，不断地补充说，我看到Iris打B了，我看到Iris打B了。&lt;/p&gt;
&lt;p&gt;于是，我问B，是你抢Iris水杯了吗？此时，B承认，是抢了。然后我又问Iris，你是因为她们抢你水杯你很生气吗？Iris说我让她们不要抢，她们不听，还要抢，非要把水泼出来。此时另一个小朋友D说，是的，我看到A和B抢Iris的水杯。&lt;/p&gt;
&lt;p&gt;我此刻，觉得事情好像不那么简单，我跟对方家长说，打人肯定是不对的，要不Iris先跟她道个歉，事情经过我回去了解一下再说。然后B同学突然改口了，说我没动她水杯，都是A同学动的。然后C同学也突然说，我知道的都跟你们说了，你们谈我就先走了，然后就离场了。&lt;/p&gt;
&lt;p&gt;对方家长此刻突然开始输出，说“你家还是小组长啊，小组长怎么能打人呢。还是少先队员，少先队员更不能打人了，打人不能当少先队员，说出去好丑啊。”&lt;/p&gt;
&lt;p&gt;当时我听了很生气，但是我也没多说什么，只说你先别这么说，事情是怎么样的都还没了解清楚呢。B家长说，总之我们家挨打了，是你们家打的，你想了解自己去了解吧，然后气鼓鼓就走了。&lt;/p&gt;
&lt;p&gt;当时这件事发生的地点就在校门口，有几个家长还围观了很久。&lt;/p&gt;
&lt;h1 id=&#34;iris口述&#34;&gt;Iris口述&lt;/h1&gt;
&lt;p&gt;以下是我单独和Iris在一起时，梳理出来的孩子眼中的事实：&lt;/p&gt;
&lt;p&gt;放学了，在班级门口站队准备由老师带出教学楼。&lt;/p&gt;
&lt;p&gt;Iris跑去接了一满杯水（&lt;em&gt;插播一下，这几天，Iris每天放学的时候接一杯水，然后放学走路队的时候就把水杯里的水倒到路边的花坛里，美其名曰浇花。这一点事后我教育了，这么玩不好。但是我确认了一点，她没有故意往走道上倒水，都是倒在路边的花坛里，至少主观意愿上不是想倒水致人摔跤&lt;/em&gt;）&lt;/p&gt;
&lt;p&gt;接水回来以后，A小朋友就跑来抢Iris的水杯。A非要把Iris水杯盖子打开，然后晃水杯，让水荡出来，Iris一直护着水杯，并将杯盖盖上。&lt;/p&gt;
&lt;p&gt;B小朋友（被打的孩子）此刻看着觉得好好玩啊，也加入抢水杯的队伍，两个人一起抢Iris水杯。&lt;/p&gt;
&lt;p&gt;期间Iris多次言语制止她们不要抢水杯，但是A和B没有任何停手的意思。然后B不知道怎么把Iris弄疼了，水杯也因此掉到了地上，Iris捡起水杯以后很气愤，就向A和B甩了水杯三下，前两下没有打到人，第三下水杯的背带打到了B，在B右手小臂上留下了一个粉红色印子。&lt;/p&gt;
&lt;p&gt;当时Iris就懵了，站在那里呆着。此时B走过来，伸手就要揪Iris的脸，Iris伸手挡了一下，但是B很执着，很大力的把手伸过来，非要掐Iris的脸。&lt;/p&gt;
&lt;p&gt;Iris觉得我打人了，理亏，就让她揪了一下脸。B捏着Iris的脸狠狠的揪了一下，然后又狠用力的打了一巴掌Iris的背，然后面无表情的走了，此刻B并没有哭。（&lt;em&gt;为此我拍了拍Iris的肩膀说，B是摸了一下你的脸，像我一样拍拍你的背，还是很用力的报复，Iris说是报复，不是拍拍&lt;/em&gt;）&lt;/p&gt;
&lt;p&gt;然后就是上面提到的，接到孩子以后，我跟Iris突然被B的妈妈拦下来，她妈妈说B哭了，当时B的确有泪痕。&lt;/p&gt;
&lt;p&gt;回家后，我问Iris，别人掐你脸，还打一巴掌你的背，疼不疼，Iris说放学的时候脸和背都很疼，现在好多了。我问她，那你为什么不反抗或者阻止她呢？Iris说觉得自己打了人，理亏，让人家打两下，扯平了，自己也没往心里去。&lt;/p&gt;
&lt;h1 id=&#34;老师的调查&#34;&gt;老师的调查&lt;/h1&gt;
&lt;p&gt;这件事情，我最终求助了老师，因为我也不知道该如何和那位家长理论，那位家长一看就是没办法讲道理的人，在校门口利用群众不分青红皂白的对未成年人进行公判。&lt;/p&gt;
&lt;p&gt;我将以上事实说给了老师听，老师说会调查的。第二天，老师电话我公布了调查结果。&lt;/p&gt;
&lt;p&gt;老师说，首先Iris的记忆有一些混淆，当时A和B抢水杯是为了把杯子盖上，而Iris当时是打开杯盖的那个人。并且Iris会把杯子里面的水洒出来。&lt;/p&gt;
&lt;p&gt;此时我打断了老师的话，我问在A和B抢杯子之前，Iris主动洒水吗？老师说，那没有，Iris只是把杯子端着，杯盖打开。&lt;/p&gt;
&lt;p&gt;我又问老师，依我的了解，Iris只是想往草地里洒水，美其名曰浇花，是这样吗？老师没有否认这一点，但是老师提到，在走路队的时候，杯子端得没那么稳，虽然主观上只想往路过的草地里洒水，但是也会漏到地上（瓷砖），然后地上的水会造成地滑，容易摔跤。&lt;/p&gt;
&lt;p&gt;我说我知道了，然后老师继续说。A和B是看到了Iris打开杯盖，于是想去阻止她，就上前去抢，并且是直接上手，没有任何语言沟通。这是不对的，她已经跟A和B都说了，不能因为自己想法是对的，就直接上手，要先沟通。&lt;/p&gt;
&lt;p&gt;然后，后面的经过基本和我所说的那样，拉扯中，Iris被惹毛了，于是胡乱抡了几下，打到了B。然后B还击了两下，Iris就让她打了两下，自认为扯平了。&lt;/p&gt;
&lt;p&gt;但是，老师也提到了一个我很在意的点。老师说B同学自己跟老师说，放学的时候，也跟她妈妈清晰地表达了，自己抢Iris的杯子，自己被打了，自己立马还击了。&lt;/p&gt;
&lt;p&gt;所以我就很搞不懂啦，你作为一个家长，你都知道自家孩子没吃亏，还要来找我闹一通，还要在大庭广众之下，使用语言暴力对一个孩子进行类似于公审一样的行为，你是何居心呢？&lt;/p&gt;
&lt;p&gt;老师在这件事情上，其实我个人认为也是有失偏颇的。洒水这个行为是有问题的，我接受我家孩子有错，但是对方上手，我觉得问题更大吧？&lt;/p&gt;
&lt;p&gt;我认为一个正常人，在一个未知的情况下，突然有人来抢我东西，我奋力保护自己和自己的财产，怎么看都是很合理的。由此产生了矛盾升级这个锅难道是我的锅？&lt;/p&gt;
&lt;p&gt;在我和老师的沟通，以及孩子第二天回家，我旁敲侧击的追问中。我发现老师的调节点在于，老师告诉Iris，你洒水是错误的，万一有人因此摔倒了骨折了，这罪过就大了，这事在学校里也不是没发生过。所以你先有错，然后B来阻止你犯错，虽然行为不当，但是B是正义的。你无意中打到了B，所以B很委屈告诉了她的家长，矛盾升级了。&lt;/p&gt;
&lt;p&gt;我觉得，这是什么歪理呢？我犯了一个小错，别人因为这个小错犯了个大错，但是因为我是矛盾的起点，所以我的罪过是小错加大错的和？况且，其实我不是很在意小学生之间的打闹升级，双方也没怎么样，小学生嘛，今天吵架明天和，都很正常。&lt;/p&gt;
&lt;p&gt;我更在意的是，对方家长的胡搅蛮缠以及无理取闹，并且手段恶劣。&lt;/p&gt;
&lt;p&gt;也就是Iris当时自认为，我抽了你，你揪了我，双方扯平了，所以Iris这人心大，没把事放心上。结果对面突然哭着告妈妈，把我拦下来，我问她打人没，把她搞懵了，当场一下脑子空白，没说出事情原委。&lt;/p&gt;
&lt;p&gt;在当时拼凑的事情点滴上，Iris的确是理亏的一方，所以对方那么说，我也不好反驳，只好带着Iris赶快逃离现场。但凡我要是指导是这个么事，或者当时我知道自己不理亏，我一定要跟对方家长对着闹，看谁嗓门大，谁占理。&lt;/p&gt;
&lt;p&gt;玛德！贱人！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>提问的智慧</title>
      <link>http://coder.amazingdemo.top/post/%E6%8F%90%E9%97%AE%E7%9A%84%E6%99%BA%E6%85%A7/</link>
      <pubDate>Thu, 07 Sep 2023 09:38:23 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%8F%90%E9%97%AE%E7%9A%84%E6%99%BA%E6%85%A7/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;提问的智慧&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>直播声卡和录音声卡</title>
      <link>http://coder.amazingdemo.top/post/%E7%9B%B4%E6%92%AD%E5%A3%B0%E5%8D%A1%E5%92%8C%E5%BD%95%E9%9F%B3%E5%A3%B0%E5%8D%A1/</link>
      <pubDate>Sun, 27 Aug 2023 17:57:37 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%9B%B4%E6%92%AD%E5%A3%B0%E5%8D%A1%E5%92%8C%E5%BD%95%E9%9F%B3%E5%A3%B0%E5%8D%A1/</guid>
      
        <description>&lt;h1 id=&#34;直播声卡&#34;&gt;直播声卡&lt;/h1&gt;
&lt;p&gt;说明：市面上的直播声卡不止支持LOOP BACK通道功能，还带有丰富的虚拟音频通道。可以对每个虚拟音频通道进行跳线等控制操作。可以把系统声音，QQ声音，微信声音，音乐播放器声音，直播平台声音，等分配给不同的虚拟音频通道。你可以自由设置哪些虚拟音频通道LOOP BACK，哪些虚拟音频通道不LOOP BACK，哪些虚拟音频通道进入机架。避免卡麦。&lt;/p&gt;
&lt;h1 id=&#34;录音声卡&#34;&gt;录音声卡&lt;/h1&gt;
&lt;p&gt;说明：市面上大部分入门级录音声卡没有LOOP BACK通道功能，就算是部分录音声卡带有LOOP BACK通道功能，它也没有虚拟音频通道。只有一条总音频输出通道。拿福克斯特4I4为例，它有LOOP BACK通道功能，但是它只要开启LOOP BACK功能，就是把你电脑内所有的声音全部LOOP BACK。这时候，只要你直播连麦必卡麦。甚至于你用QQ语音或电脑微信语音都会卡麦。&lt;/p&gt;
&lt;p&gt;又比如雅马哈UR22系列声卡，它带有LOOP BACK功能，但是它的LOOP BACK功能会占用1和2的通道，开启LOOP BACK功能后就不能使用机架了，那么这个声卡带的LOOP BACK功能根本不能用于直播。&lt;/p&gt;
&lt;p&gt;大家不要看到录音声卡介绍里写着支持LOOP BACK功能或支持内录通道，就以为可以玩直播，其实，支持LOOP BACK功能或支持内录通道与支持完美直播是完全不同的概念。&lt;/p&gt;
&lt;p&gt;所以，如果要用录音声卡玩直播，我只推荐娃娃脸声卡，它有灵活的通道控制功能。&lt;/p&gt;
&lt;h1 id=&#34;手机直播&#34;&gt;手机直播&lt;/h1&gt;
&lt;p&gt;说明：手机直播要连接声卡的话，有一个东西叫直播一号手机转换器。它的作用就是把声卡的声音传输到手机，而手机输出的声音再返回给直播一号上面的耳机。所以，理论上手机直播用户，只要用直播一号，任何声卡都可以直播。连麦也不会卡麦，因为直播一号根本也没有LOOP BACK功能。但是，如果你用录音声卡直播，还是不能灵活的通道控制，但是也不会影响你直播。还有另一个缺点，如果你用不带LOOP BACK功能录音声卡玩手机直播，声卡调试只能你自己进行，因为脱离手机平台，在电脑上，调音师是无法听到你的效果的，也就无法进行效果调试。如果你用带LOOP BACK功能的录音声卡玩直播，脱离手机平台的时候，调音师给你调试的时候，你与调音师连语音都会卡麦，相当难受。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>了解话筒，声卡，的作用</title>
      <link>http://coder.amazingdemo.top/post/%E4%BA%86%E8%A7%A3%E8%AF%9D%E7%AD%92%E5%A3%B0%E5%8D%A1%E7%9A%84%E4%BD%9C%E7%94%A8/</link>
      <pubDate>Thu, 24 Aug 2023 12:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%BA%86%E8%A7%A3%E8%AF%9D%E7%AD%92%E5%A3%B0%E5%8D%A1%E7%9A%84%E4%BD%9C%E7%94%A8/</guid>
      
        <description>&lt;p&gt;如果用摄影设备，比喻录音K歌设备的话：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;话筒=镜头（话筒分电容，动圈，铝带，驻极体。镜头分长焦，短焦，不同光圈）&lt;/li&gt;
&lt;li&gt;声卡=相机机身（声卡主要作用ADDA，提供高精度数模转换。相机机身有cmos图像传感器，提供高像素）&lt;/li&gt;
&lt;li&gt;宿主软件=PS软件（宿主软件配合VST插件可以给人声做各种效果。PS软件可以给照片做各种效果）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;直播人声信号流程原理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;话筒是一个声能转换电能的转换器，你人声质感的好坏，主要取决于话筒的好坏。你的人声会通过话筒转换成电能，这时候的电能是模拟信号，发送给声卡集成话放。&lt;/li&gt;
&lt;li&gt;声卡的集成话放会把你的人声模拟电信号进行放大。&lt;/li&gt;
&lt;li&gt;声卡负责数模转换（ADDA），话筒转换的电能只是模拟信号，要经过声卡的ADDA芯片转换成数字信号，才能被计算机识别，计算机把人声数字信号送进宿主软件（机架），经过各种VST插件（效果器插件）后，你的人声就是带效果的了，然后带效果的人声数字信号分为两组，一组直接走网络，给了直播平台的服务器。第二组会重新回到声卡ADDA芯片，转换成模拟信号，发送给集成耳放。&lt;/li&gt;
&lt;li&gt;集成耳放会把带效果的人声模拟信号进行放大后发送给耳机。&lt;/li&gt;
&lt;li&gt;耳机是一个电能转换声能的转换器。带效果的人声模拟信号经过耳机后会变成声音让我们听到。（所谓的声卡监听延迟，指的就是信号走完这全部流程后所花的时间。而增加监听延迟最大的因素就是VST插件。这就是为什么听湿录干要比听干录干的时候，监听延迟大的原因。）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;录音人声信号流程原理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;话筒是一个声能转换电能的转换器，你人声质感的好坏，主要取决于话筒的好坏。你的人声会通过话筒转换成电能，这时候的电能是模拟信号，发送给声卡集成话放。&lt;/li&gt;
&lt;li&gt;声卡的集成话放会把你的人声模拟电信号进行放大。&lt;/li&gt;
&lt;li&gt;声卡负责数模转换（ADDA），话筒转换的电能只是模拟信号，要经过声卡的ADDA芯片转换成数字信号，才能被计算机识别，计算机把人声数字信号分为两组，一组声音信号录进宿主软件，然后就可以进行后期混音了。第二组会重新回到声卡ADDA芯片，转换成模拟信号，发送给集成耳放。（录音声卡普遍带有直接硬件监听功能，可以大大缩减监听延迟，就是第二组人声模拟信号不走ADDA芯片，直接发送给耳放，实现超低延迟监听。）&lt;/li&gt;
&lt;li&gt;集成耳放会把带效果的人声模拟信号进行放大后发送给耳机。&lt;/li&gt;
&lt;li&gt;耳机是一个电能转换声能的转换器。带效果的人声模拟信号经过耳机后会变成声音让我们听到。（所谓的声卡监听延迟，指的就是信号走完这全部流程后所花的时间。而增加监听延迟最大的因素就是VST插件。这就是为什么听湿录干要比听干录干的时候，监听延迟大的原因。）&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>拇指测距是什么原理？</title>
      <link>http://coder.amazingdemo.top/post/%E6%8B%87%E6%8C%87%E6%B5%8B%E8%B7%9D%E6%98%AF%E4%BB%80%E4%B9%88%E5%8E%9F%E7%90%86/</link>
      <pubDate>Tue, 15 Aug 2023 20:33:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%8B%87%E6%8C%87%E6%B5%8B%E8%B7%9D%E6%98%AF%E4%BB%80%E4%B9%88%E5%8E%9F%E7%90%86/</guid>
      
        <description>&lt;p&gt;拇指测距的方法常常在军队中可见，被用作辅助测距离。&lt;/p&gt;
&lt;p&gt;用拇指测距的方法在军事上叫做跳眼测距法，是基于三角函数的测距方法，而这种方法对于炮兵来说就是最基本的常识，也是军队使用最简单的一个测距法。使用这种方法，可以快速并有效地帮助士兵测出目标与身体之间的距离。&lt;/p&gt;
&lt;p&gt;拇指测距的方基于几何学原理，具体来说，是利用三角视差。视差就是把手指放在两眼之间，分别用左右眼单独观察远方，会看见固定的手指头发生了移动，这就是我们说的视差。&lt;/p&gt;
&lt;p&gt;拇指测距的方法的具体操作，首先要把左胳膊伸直（左右手哪一只都可以），然后把拇指竖起来放在左眼的正前方， 拇指挡住目标的一边。接下来，再用右眼去观测目标，并测出拇指所移动的距离。最后，物体的距离等于拇指移动距离乘以10，就能估算出目标的距离了。&lt;/p&gt;
&lt;p&gt;这是个对顶角平行三角形，△AOB∽△COD，OC/OA=CD/AB，AB=7cm，OA=70cm，OC=10CD，CD=280m。&lt;/p&gt;
&lt;p&gt;拇指测距的方法虽然简单，但却无法保证最佳精度，因此，现在部队里已经逐渐使用上了先进的测距装置，精度更是传统拇指测距不能相比的。但不可否认的是，拇指测距却是早期炮兵最有效的测距方法！&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;三角函数测距&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如果你是IT技术人员，请思考这15个问题</title>
      <link>http://coder.amazingdemo.top/post/%E5%A6%82%E6%9E%9C%E4%BD%A0%E6%98%AFit%E6%8A%80%E6%9C%AF%E4%BA%BA%E5%91%98%E8%AF%B7%E6%80%9D%E8%80%83%E8%BF%9915%E4%B8%AA%E9%97%AE%E9%A2%98/</link>
      <pubDate>Wed, 09 Aug 2023 09:42:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%A6%82%E6%9E%9C%E4%BD%A0%E6%98%AFit%E6%8A%80%E6%9C%AF%E4%BA%BA%E5%91%98%E8%AF%B7%E6%80%9D%E8%80%83%E8%BF%9915%E4%B8%AA%E9%97%AE%E9%A2%98/</guid>
      
        <description>&lt;p&gt;行内的人自嘲是程序猿、屌丝和码农，行外的人也经常拿IT人调侃，那么究竟是IT人没有价值，还是没有仔细思考过自身的价值？&lt;/p&gt;
&lt;h1 id=&#34;搞it的是屌丝码农程序猿&#34;&gt;搞IT的是屌丝、码农、程序猿？&lt;/h1&gt;
&lt;p&gt;人们提到IT人的时候，总会想到他们呆板、不解风情，专注于IT技术，就算性感的美女躺在旁边也无动于衷。事实真的是这样吗？虽说不能完全否定有这样的情况 存在，但这是IT人普遍的特点吗？而其它行业也有很多这样的人，那为什么人们总是拿程序员说事？&lt;/p&gt;
&lt;p&gt;而实际上，问题并不在于IT行业拥有这些固有的标签，而是行业内的人看待自己的态度。IT行业大多都很辛苦，“朝九晚五”只是听说过，很多IT人都没有见过，这是大家都知道的事实。那么，不排除某些程序员需要在苦中找乐子，好让辛苦的工作多一份乐趣，这当然是可以理解的了，生活总不能像程序一样执行。&lt;/p&gt;
&lt;p&gt;而正因为IT行业很辛苦，整天只能与机器打交道，慢慢地就不想跟人说话、不想出门，经常就会有程序员在社交网络吐槽。“程序猿”是用来形容IT人呆板、情商低的特征；“码农”用来形容程序员的工作跟农民一样辛苦，赚不到钱；“屌丝”就是前面两者的结合了。&lt;/p&gt;
&lt;p&gt;因此，程序员为自己贴这几个标签的原因主要有两个，一个是找乐子，另一个就是吐槽了。那为什么行外的人也来凑热闹？记得多年前，当笔者还是个朦胧的高中生的时候，就特别崇拜程序设计师，在我心里他们就是社会的精英。而其他的人，对IT行业也很有神秘感。当时的人要想去编程，要么有兴趣和天赋，要 么毕业于相关专业，否则是难以胜任的。而现在的情况就不同了，外面的软件开发培训机构都在面向初中和高中毕业生招生了。越来越多的人可以接触到软件开发， 而进入这个领域的人中，能力参差不齐，目的各不相同，有发展得很好的，也有发展得不理想的。所以，行外的人印象中的高薪行业，行内却有不少人并没有拿到高 薪；行外的人觉得这是一个精英行业，而行内不少人认为自己跟工地上的搬砖工差不多。&lt;/p&gt;
&lt;p&gt;那为何面对行外的调侃时，很多程序员表现得如此淡定？原因就很简单了，如果程序员对自己都是这种“调侃”的态度，别人怎么调侃都无所谓了，甚至还表现出欢迎或者引起共鸣。&lt;/p&gt;
&lt;h1 id=&#34;如何看待工作中的加班以及确保自身健康&#34;&gt;如何看待工作中的加班以及确保自身健康？&lt;/h1&gt;
&lt;p&gt;加班可以分为主动加班和被动加班。&lt;/p&gt;
&lt;p&gt;先谈谈主动加班，主动加班也是有不同的动机，很多时候分为两类，一类是热衷于自己的事业，愿意奉献更多的时间和精力在事业上面；另一类是，回家后就找不 到成就感与幸福感，还不如留在公司，可以做一些工作，也可以玩一会儿游戏，一般不会有领导去干涉员工在下班时间做的事情，而且还可以节约一点空调的电费， 有些公司还会提供加班补贴，因此他们觉得多在公司呆几个小时也挺好。&lt;/p&gt;
&lt;p&gt;而被动加班的原因就没那么简单了。有可能造成被动加班的原因很多，它可能来自公司、领导、团队、个人以及一些不可抗拒的因素。&lt;/p&gt;
&lt;p&gt;在创业公司，因为业务变动频繁，公司的决策和方向，也会瞬息万变，这就需要团队成员花更多的时间去应对这些变化，因此正常的八小时工作制一般不适合创业 公司，除非创始团队足够牛，能保证非常好的工作效率、市场洞察力和执行力。当然在大公司一般不会出现业务频繁变动的情况，不然这家公司就是濒临倒闭了。在 国内某些互联网巨头中，加班不仅已经成为家常便饭，而且有时候可以以“变态”来形容。某985高校毕业生A在毕业后进入某互联网巨头（为了保护相关人员或 组织的隐私，本文尽量不出现特定人员或组织的名称）承担开发工作，在试用期三个月里兢兢业业，每天晚上24:00左右下班回家，试用期结束以后，转正考核 以优秀通过。A凭借较强的学习能力，这时对自己项目组的业务和技术非常熟悉，已经可以提前完成领导安排的任务，甚至还主动去改进项目组的程序。随着工作效率的提高，A 感觉没有必要跟其他同事一样必须呆到23:00之后才回家了，所以慢慢的他提前离开了，23:30，23:00,22:30,22:00,21:30。随 着时间一点一点提前，虽然他的工作任务都保质保量完成了，但是他在领导眼里被贴上了“不尽职”的标签，月度考核从最初的A滑到了C。&lt;/p&gt;
&lt;p&gt;而团队所带来的加班有些时候也是不可避免的，这涉及到团队的分工与合作，如果经常出现团队之间的协作导致的加班，那一定是团队成员工作的耦合度太大了，就有可能是技术架构或者团队分工出现严重问题。&lt;/p&gt;
&lt;p&gt;个人导致的加班，可能是由于自己没有较为准确地预估工作量，也有可能是自己拖延症严重，还有可能是自己对技术不够熟悉等原因，其实个人原因最好解决，因为自己可以轻松地找到这类加班问题的症结，并对症下药。&lt;/p&gt;
&lt;p&gt;其它一些不可抗拒的因素，包括需求变动、硬盘永久性损坏等，都会带来很多额外的工作量。&lt;/p&gt;
&lt;p&gt;而当前国内大多数IT技术人员都是被动加班，而且是强制性质的，只有极少数公司提供加班费。因此，很多技术人员只有两种选择，要么适应，要么走人。而在中国这样一个发展中国家，也很难期待当局会强烈干涉这个现象。&lt;/p&gt;
&lt;p&gt;前不久一则“深圳36岁IT男猝死马桶盖上”的新闻在互联网引起轰动，一个清华毕业的程序员，在长期连续加班之后，终因身体透支过多，年轻的生命就这样倒下了。从尊重生命的角度来说，事业、公司和客户都没有自己的生命重要，若事业与生死只能选择一个，相信绝大多数人会选择生存。而从所谓的“XX比生命还重要”的角度而言，如果你能够承担长期过度加班所带来的后果，或者原意像革命先烈一样为自己的事业献出生命，那谁也没法阻止你。&lt;/p&gt;
&lt;h1 id=&#34;如何平衡工作与家庭&#34;&gt;如何平衡工作与家庭？&lt;/h1&gt;
&lt;p&gt;笔者曾经在参加一期沙龙的时候，一个智能硬件公司创始人对我说：程序员根本就没有生活，他们的生活就是工作。他作为一个技术出身的创始人，这样说也是可以理 解的，但这种说法并不一定正确。一方面，程序员需要争取到家人的大力支持，如果没有他们的支持，程序员在事业的前进途中可能会遇到很多困难；另一方面，可 以想象一下，如果自己生了重病，每天呆在病床旁边照顾你的人是公司领导还是家人？考虑好了这些，也许就知道该怎么办了。&lt;/p&gt;
&lt;h1 id=&#34;信息检索一定得用google&#34;&gt;信息检索一定得用Google？&lt;/h1&gt;
&lt;p&gt;在很多招聘广告中，也许你见到过很多类似这样的职位要求“必须使用Google来搜索技术资料，如果你用Baidu，那么你就不适合我们”。首先，我们知道 Google的搜索引擎比Baidu做得好，对关键词进行的资源定位更加精确，理论上来说，输入同样的关键词，Google匹配得更准确一些，也就是可以 更快速地找到答案。那么研发团队是否需要对工具的使用强制立下规矩，必须用Google搜索，必须用Linux操作系统，必须用机械键盘…这又联想到 了小学的时候学到的文章《摔琴》的故事了，虽然便宜的小提琴在某些音调上表现得不是那么好，而只要演奏者水平足够高，听众根本意识不到演奏者用的是多贵的琴了。再回到主题上来，对于一个信息检索高手来说，他可以利用世界上最糟糕的搜索引擎来查找到Google 上面找不到的内容。也就是说，能否快速查找到需要的结果，并非取决于特定的搜索引擎。况且，当你使用Google和Baidu同时搜索相同的中文关键字 时，呈现的结果都是大同小异的。有人会说，Google的英文搜索比Baidu强，那么你可以试一下，它与没有被墙的Bing、Yahoo等搜出来的英文 结果，也是大同小异的。因此，检索信息人的是一种能力，它并非决定于搜索工具。&lt;/p&gt;
&lt;h1 id=&#34;技术牛人如何对待新手&#34;&gt;技术牛人如何对待新手？&lt;/h1&gt;
&lt;p&gt;在公司里（特别是大公司），一般有很一些技术大牛，他们是公司核心的技术人员，支撑着整个公司的技术平台。那些可以称得上技术专家的员工，一般性情随和，也 表现得非常谦卑，他们对于技术新手的提问特别有耐心。但是也有少数技术还不错的人，对于职场新手各种瞧不起，特别是对于新手程序员犯下的错误，他们会用尽 可能高的音量指出错误，甚至是谩骂，以此来向周围的人表明自己的技术是多么牛。只能说，作为IT技术人员，这样的表现很不成熟。高手或专家都是从小白起家 的，今天的小白也许就是明天的专家，根本没有必要去嘲讽职场新人，那样只会让自己在同事眼里的魅力大打折扣。&lt;/p&gt;
&lt;h1 id=&#34;如何看待it鄙视链&#34;&gt;如何看待IT鄙视链？&lt;/h1&gt;
&lt;p&gt;2014年底的时候，IT界盛传一篇名为“软件工程师的鄙视链”的文章，主要从编程语言、工具、OS、硬件和职场五个方面来介绍IT界的鄙视链。就以编程语言鄙视链为例，静态语言鄙视动态语言，组合鄙视C，C鄙视C++，C++鄙视Java和C#，Java和C#相互鄙视，C#鄙视VB…&lt;/p&gt;
&lt;p&gt;Java、 C和C++在2002年前后使用量很大，但是到了2014年之后，三者都有下降，只是C降幅比较小。在2002年前后，php刚问世就得到大量的应用，而 到了2014年就跌了很远。Python在2002年前后应用较少，而到了2014年应用也很多了…这些变化说明了什么？编程语言日新月异，它终归是 一个工具，编程语言有个很明显的特点就是，它们之间相互借鉴，直接导致了设计思想有很多类似的地方，所以，只要你精通了一门或两门编程语言，其它绝大部分 语言学习成本很低。所以，今天你自鸣得意的编程语言，完全有可能在明天变得冷门了，甚至是消失。没有必要去鄙视使用另一门冷门语言的人，也许他今天使用的 编程语言明天会成为主流语言。很多有编程语言情节的程序员根本就不相信这句话，他们坚信自己使用的语言是世界上最伟大的，会长命百岁。&lt;/p&gt;
&lt;p&gt;换到其它类型的鄙视也是一样，从事运维的技术人员，在能力上并不一定比从事开发的差，也许街上的某位快递员之前的职位就是一名比你还牛的程序员。社会职 位各有分工，各行各业的职位都是不可替代的，否则这个职位就该消失了，没有被鄙视的机会。也许你做的工作他不会做，而他做的工作你也不会做。&lt;/p&gt;
&lt;h1 id=&#34;为何不自称工程师&#34;&gt;为何不自称工程师？&lt;/h1&gt;
&lt;p&gt;在中国，很少有程序员把自己自称为工程师，在这些人中，要么是担心这个标签给自己带来太大的压力，而自己的能力不匹配；要么就是希望外界把自己当做一名普通 的写程序的人员；还有一种就是，希望外界不要称呼他们“程序猿”或者“码农”，他们不喜欢被这样调侃，但也不希望被高估，就喜欢低调行事。&lt;/p&gt;
&lt;h1 id=&#34;薪水在选择工作中的影响力有多大&#34;&gt;薪水在选择工作中的影响力有多大？&lt;/h1&gt;
&lt;p&gt;当前很多IT行业求职者都有一个信条：“做多少事，拿多少钱”。如果公司愿意拿更多的钱，通常求职者在主观上表现出愿意做更多的事。若有两个offer在面 前，offer1钱多，但是你不是特别喜欢它的工作内容，而offer2薪资只有offer1的一半，但是它的工作内容是你擅长并且喜欢的。经常在网上会 有这样的帖子，列出几个offer，让网友提建议。其实，遇到这种情况很好办的。如果你当前最需要的是钱，那么果断地选择钱多的；如果你当前最需要的是一 份你喜欢的工作，肯定选择自己喜欢的了。选择工作的时候，选择自己最需要的，这样工作起来也会更有动力。若听从那些所谓的牛人的建议（比如应届生没必要在 乎工资多少，能学到技术就好之类的，其实对于应届生来说，到哪里都能学到技术，只是学到得多与少的问题），你很难在工作岗位上认真投入的。当前很缺钱，现 在也没有兴趣去干一番事业，那么就不要接受一个创业团队的低薪+画的大饼这样的待遇，因为你加入公司之后，对于公司和你自己都没有好处。&lt;/p&gt;
&lt;h1 id=&#34;编程语言不重要重要的是设计思想&#34;&gt;编程语言不重要，重要的是设计思想？&lt;/h1&gt;
&lt;p&gt;这个是那些所谓的技术牛人给新手的建议，学校的老师也会给出这样的建议。当新手在咨询学哪门语言的时候，那些所谓的专家建议新手随便学一门语言，门门语言都 想通，哪门语言精通以后都可以找到好工作。虽然这个建议没有完全错，但是也没用完全正确。不同的语言适应着不同的业务需要，比如做企业开发Java语言更 合适，php和python在中小型网站开发中更加快速，Objective-C主要用于开发ios…况且不同的语言还有不同的特性，底层的实现通常 并非相同，这就需要开发者根据自己喜欢的业务领域来选择编程语言，需要对所使用的语言相当熟悉。&lt;/p&gt;
&lt;h1 id=&#34;是否经常把自己的思想强加给同事&#34;&gt;是否经常把自己的思想强加给同事？&lt;/h1&gt;
&lt;p&gt;程序员群体有个比较普遍的现象就是，总觉得自己的想法是最好的。而人人都有自己的想法，只是有些人喜欢表达出来，而有些是埋在心里。允许他人评判你的想法，客观去分析他们的观点，而不是粗暴地强加给他们，这是一种个人魅力。&lt;/p&gt;
&lt;h1 id=&#34;it人可以做多久的技术&#34;&gt;IT人可以做多久的技术？&lt;/h1&gt;
&lt;p&gt;笔者在大学期间，经常听人说IT人是吃青春饭的，过了35岁就写不了代码了。如果干到了35岁还停留在写代码层面上，估计那时是干不过毕业没多久的年轻人了。&lt;/p&gt;
&lt;h1 id=&#34;什么技术热门或赚钱就学什么技术&#34;&gt;什么技术热门或赚钱，就学什么技术？&lt;/h1&gt;
&lt;p&gt;之前Hadoop技术很火，很多公司开出天价招聘Hadoop技术人员，但是如今呢？当Hadoop退烧之后，这个职位的薪资没有之前那么有吸引力了。热门或赚钱的技术很多时候比较短暂，学习自己喜欢的技术才是王道。&lt;/p&gt;
&lt;h1 id=&#34;如果某一天开始计算机不需要人类编程了你还可以做什么&#34;&gt;如果某一天开始计算机不需要人类编程了，你还可以做什么？&lt;/h1&gt;
&lt;p&gt;随着人工智能技术的发展，若未来机器可以代替程序员进行编程了，程序员还能做什么呢？计算机和互联网的发展，消灭了很多传统职位，但随着科技的不断发展，程序员这个职位也许会有一天也被消灭了。当全球的IT公司都宣布废除人工编程时，程序员应该是回家还是转行呢？&lt;/p&gt;
&lt;h1 id=&#34;业务驱动型还是技术驱动型&#34;&gt;业务驱动型还是技术驱动型&lt;/h1&gt;
&lt;p&gt;当前O2O在中国非常火，严格来说，O2O公司不算是一个互联网公司。O2O将传统行业从线下搬一部分到线上，比如以前需要去餐馆吃饭，现在只需要在网上下 订单，餐馆就把食物送到家里来了。很明显，O2O就是一个业务驱动型的公司。在这样的公司里，技术只是业务的一个支撑部门，一般不会用到复杂的技术，但是 需要技术人员懂得较多的线上线下业务。而百度这样的公司，就是典型的技术驱动型的公司，他们在使用和研究比较高深的技术，里面很多科学家级别的人物。所 以，如果想在技术上有深入到专家级别，那么肯定在技术驱动型的公司里更容易做到。而如果想利用简单的技术来改变传统行业，业务驱动型的O2O就是你想找 的。&lt;/p&gt;
&lt;h1 id=&#34;如何定义成功&#34;&gt;如何定义成功&lt;/h1&gt;
&lt;p&gt;毕业多年后，同学之间总会有人讨论谁混得好谁混得差。那么好与差的标准是什么呢？是赚了多少钱，有没有在北上广深买房，当了多大的官、是否在BAT工作？不 同的人有不同的评判标准，但多数是以钱来衡量他是否成功。但成功的标准就这么单一？如果一定得给成功下一个定义，那么成功应该这样来计算：&lt;/p&gt;
&lt;p&gt;成功度(S)＝（工作快乐度&lt;em&gt;工作快乐权重　＋工作薪资&lt;/em&gt;薪资权重　＋ … + 生活快乐度×生活快乐权重 + 家庭和睦度*家庭和睦权重 + …）/n&lt;/p&gt;
&lt;p&gt;这里S最大者才是最成功的人。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>烤花生【空气炸锅版】</title>
      <link>http://coder.amazingdemo.top/post/%E7%A9%BA%E6%B0%94%E7%82%B8%E9%94%85%E7%89%88%E7%83%A4%E8%8A%B1%E7%94%9F/</link>
      <pubDate>Tue, 01 Aug 2023 11:18:51 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%A9%BA%E6%B0%94%E7%82%B8%E9%94%85%E7%89%88%E7%83%A4%E8%8A%B1%E7%94%9F/</guid>
      
        <description>&lt;h1 id=&#34;烤花生空气炸锅版&#34;&gt;烤花生(空气炸锅版)&lt;/h1&gt;
&lt;p&gt;用空气炸锅炸花生米最棒了，只需几分钟就可以吃上香脆味美的花生米！&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;花生米	250克&lt;/li&gt;
&lt;li&gt;食用油	5克&lt;/li&gt;
&lt;li&gt;食用盐	1克&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;取一个碗，把花生米倒入碗中，加5克油和1克盐搅拌均匀！&lt;/p&gt;
&lt;p&gt;空气炸锅180度预热3分钟，再把锅中的花生米倒进炸蓝中，铺平！180度设置8分钟！5分钟时已闻到花生米香味儿！&lt;/p&gt;
&lt;p&gt;好脆，好香，色泽好美！&lt;/p&gt;
&lt;h1 id=&#34;小贴士&#34;&gt;小贴士&lt;/h1&gt;
&lt;p&gt;在炸的过程中可以拉开炸蓝看看，时间是由花生米的多少来定，炸多点可以适当延长点时间，但要掌握火候，千万别炸糊了！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>ACID、Data Replication、CAP与BASE</title>
      <link>http://coder.amazingdemo.top/post/acid_datareplication_cap_base/</link>
      <pubDate>Tue, 18 Jul 2023 10:45:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/acid_datareplication_cap_base/</guid>
      
        <description>&lt;h1 id=&#34;acid&#34;&gt;ACID&lt;/h1&gt;
&lt;p&gt;在传数据库系统中，事务具有ACID 4个属性(Jim Gray在《事务处理：概念与技术》中对事务进行了详尽的讨论)。&lt;/p&gt;
&lt;p&gt;(1)原子性（Atomicity）：事务是一个原子操作单元，其对数据的修改，要么全都执行，要么全都不执行。&lt;/p&gt;
&lt;p&gt;(2)一致性（Consistent）：在事务开始和完成时，数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改，以保持数据的完整性；事务结束时，所有的内部数据结构（如B树索引或双向链表）也都必须是正确的。&lt;/p&gt;
&lt;p&gt;(3)隔离性（Isolation）：数据库系统提供一定的隔离机制，保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的，反之亦然。&lt;/p&gt;
&lt;p&gt;(4)持久性（Durable）：事务完成之后，它对于数据的修改是永久性的，即使出现系统故障也能够保持。&lt;/p&gt;
&lt;p&gt;对于单个节点的事务，数据库都是通过并发控制（两阶段封锁，two phase locking或者多版本，multiversioning）和恢复机制（日志技术）保证事务的ACID特性。对于跨多个节点的分布式事务，通过两阶段提交协议（two phase commiting）来保证事务的ACID。&lt;/p&gt;
&lt;p&gt;可以说，数据库系统是伴随着金融业的需求而快速发展起来。对于金融业，可用性和性能都不是最重要的，而一致性是最重要的，用户可以容忍系统故障而停止服务，但绝不能容忍帐户上的钱无故减少(当然，无故增加是可以的)。而强一致性的事务是这一切的根本保证。&lt;/p&gt;
&lt;h1 id=&#34;data-replication&#34;&gt;Data Replication&lt;/h1&gt;
&lt;p&gt;数据复制(data replication)属于分布式计算的范畴，它并不仅仅局限于数据库，但这里主要是指分布式数据库的复制。&lt;/p&gt;
&lt;p&gt;在多副本构成的分布式数据库系统中， 其事务特性与单个数据库系统的差别主要表现在原子性和一致性两个方 面。在原子性方面， 要求同一分布式事务的所有操作在所有相关副本上要么提交， 要么回滚， 即除了保证原有的局部事务的原子性，还需要控制全局事务的原子性； 在一致性方面，多副本之间需要保证单一副本一致性。&lt;/p&gt;
&lt;p&gt;针对分布式事务的原子性和一致性这两个复制协议中的核心问题， 经过近20年的研究 ， 人们提出了各种各样的复制协议。这些协议在外在功能和内部实现两方面都有较大的差别。据此，我们可以从这两个大的方面进行分类说明。&lt;/p&gt;
&lt;p&gt;从外在功能的角度看， 依据文献[1]， 可以从事务执行的地点和时间两个方面进行分类。从事务执行的地点，可以分为两类： 主从( Priamry / Copy)方式和更新所有( Update-Anywhere ) 方式。&lt;/p&gt;
&lt;p&gt;前者的处理过程一般是系统中仅仅指定一个Primary节点接受更新请求 ， 在事务操作执行完毕后， 在事务提交前或后将操作广播到其他Copy节点。&lt;/p&gt;
&lt;p&gt;后者的处理过程稍微复杂， 系统中的任何副本具有相同的地位，都可以接收Update请求 ，在检测事务冲突、 事务提交前或后将各个节点的Update传播到其他副本节点。&lt;/p&gt;
&lt;p&gt;Primary / Copy方式并发控制较为简单， 由Primary本地的事务控制即可实现， 事务的原子性的实现也较为简单， 一般由Primary节点作为协调节点来实现。但是， 其缺陷也显而易见： 仅仅单个节点提供Update请求处理能力， 对于Update密集类型的应用， 如OLTP， 容易形成单点性能瓶颈。Update-Anywhere方式则与其相辅相成， 可以通过多点提高事务吞吐率， 但随之而来的是多个分布式事务之间复杂的并发控制和原子性问题。&lt;/p&gt;
&lt;p&gt;从事务提交的时间点看， 可以分为积极 (Eager)和消极(Lazy) 两类。其区别在于， 前者是在事务提交前传播更新，后者则是在提交之后才将事务操作传播到其他副本。实际上，前者即通常无谓的同步复制(synchronous replication)，后者即无谓的异步复制(asynchronous replication)。&lt;/p&gt;
&lt;p&gt;异步复制的优点是可以提高响应速度， 但牺牲了一致性 ，一般实现该类协议的算法需要增加额外的补偿机制。同步复制的优点是可以保证一致性(一般通过两阶段提交协议)，但是开销较大，可用性不好(参见CAP部分)，带来了更多的冲突和死锁等问题。值得一提的是Lazy+Primary/Copy的复制协议在实际生产环境中是非常实用的，MySQL的复制实际上就属于这种。&lt;/p&gt;
&lt;h1 id=&#34;cap&#34;&gt;CAP&lt;/h1&gt;
&lt;p&gt;在2000的PODC（Principles of Distributed Computing）会议上，Brewer提出了著名的CAP理论。2002年，Seth Gilbert和Nancy Lynch证明了这一理论。CAP指的是：Consistency、Availability和Partition Tolerance。&lt;/p&gt;
&lt;p&gt;（1）Consistency（一致性）：一致性是说数据的原子性，这种原子性在经典的数据库中是通过事务来保证的，当事务完成时，无论其是成功还是回滚，数据都会处于一致的状态。在分布式环境中，一致性是说多个节点的数据是否一致。&lt;/p&gt;
&lt;p&gt;（2）Availability（可用性）：可用性是说服务能一直保证是可用的状态，当用户发出一个请求，服务能在有限时间内返回结果。&lt;/p&gt;
&lt;p&gt;（3）Partition Tolerance（分区容错性）：Partition是指网络的分区。可以这样理解，一般来说，关键的数据和服务都会位于不同的IDC。&lt;/p&gt;
&lt;p&gt;CAP理论告诉我们，一个分布式系统不可能同时满足一致性，可用性和分区容错性这三个需求，三个要素中最多只能同时满足两点。三者不可兼顾，此所谓鱼与熊掌不可兼得也！而对于分布式数据系统而言，分区容错性是基本要求，否则就不称其为分布式系统了。因此架构设计师不要把精力浪费在设计如何能同时满足三者的完美分布式系统上，而是应该进行权衡取舍。这也意味着分布式系统的设计过程，也就是根据业务特点在C（一致性）和A（可用性）之间寻求平衡的过程，要求架构师真正理解系统需求，把握业务特点。&lt;/p&gt;
&lt;h1 id=&#34;base&#34;&gt;BASE&lt;/h1&gt;
&lt;p&gt;BASE来自于互联网的电子商务领域的实践，它是基于CAP理论逐步演化而来，核心思想是即便不能达到强一致性(Strong consistency)，但可以根据应用特点采用适当的方式来达到最终一致性(Eventual consistency)的效果。BASE是Basically Available、Soft state、Eventually consistent三个词组的简写，是对CAP中C &amp;amp; A的延伸。BASE的含义：&lt;/p&gt;
&lt;p&gt;（1）Basically Available：基本可用；&lt;/p&gt;
&lt;p&gt;（2）Soft-state：软状态/柔性事务，即状态可以有一段时间的不同步；&lt;/p&gt;
&lt;p&gt;（3）Eventual consistency：最终一致性；&lt;/p&gt;
&lt;p&gt;BASE是反ACID的，它完全不同于ACID模型，牺牲强一致性，获得基本可用性和柔性可靠性并要求达到最终一致性。&lt;/p&gt;
&lt;p&gt;CAP、BASE理论是当前在互联网领域非常流行的NoSQL的理论基础。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>请不要对我说 你要马上把这个小问题修改好 </title>
      <link>http://coder.amazingdemo.top/post/%E8%AF%B7%E4%B8%8D%E8%A6%81%E5%AF%B9%E6%88%91%E8%AF%B4_%E4%BD%A0%E8%A6%81%E9%A9%AC%E4%B8%8A%E6%8A%8A%E8%BF%99%E4%B8%AA%E5%B0%8F%E9%97%AE%E9%A2%98%E4%BF%AE%E6%94%B9%E5%A5%BD/</link>
      <pubDate>Mon, 17 Jul 2023 17:53:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E8%AF%B7%E4%B8%8D%E8%A6%81%E5%AF%B9%E6%88%91%E8%AF%B4_%E4%BD%A0%E8%A6%81%E9%A9%AC%E4%B8%8A%E6%8A%8A%E8%BF%99%E4%B8%AA%E5%B0%8F%E9%97%AE%E9%A2%98%E4%BF%AE%E6%94%B9%E5%A5%BD/</guid>
      
        <description>&lt;p&gt;“我需要”，“小问题”，“立刻”。你激怒我了。你的每个词都激怒我了。这种情绪很难翻译成英语表达，但在荷兰，有些人就是用这种方式要求你。翻译成英语，这有点像“你要几分钟内把这个东西修改好”。大家都知道的这样的逻辑，你要么追求好和快，但付出很多，要么追求好但不付出，这样你就别贪图快，要么追求快，但是不想付出，那你只能得到坏的结果。&lt;/p&gt;
&lt;p&gt;荷兰人有一个毛病，干什么事都说“只需要几分钟”。&lt;/p&gt;
&lt;p&gt;让我解释一下这为什么会让我恼怒。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这种事情不是简单的“几分钟”，你打断了我的工作思路，你的“小问题”至少会耽误我10分钟。为什么？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;首先，我要查看和分析你说的问题。第二，我必须去处理你说的问题。然后，我需要提交代码，部署它。一旦部署完成(或编译等类似的)，我需要去检查修改后的代码是否按你要求的方式工作。&lt;/p&gt;
&lt;p&gt;现在1个小时都不够了，因为我需要花时间重回到之前的工作状态中。&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;我不知道你在想什么。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;正如我说的，我需要分析你的思想。如果我把你的想法理解错了，那就浪费了2小时的实际工作时间，因为我还需要去讨论你的真实意图。&lt;/p&gt;
&lt;p&gt;我有一个非常好的例子。一个客户告诉我，“信息面板显示日程信息。它不应该有这个。”猜我怎么做的？我把日常信息给删了。&lt;/p&gt;
&lt;p&gt;而客户的实际意思是什么呢？他的意思是,”信息面板显示日常信息，但上面还有下载表格的选项。不应该有这个。”好吧。交流不畅。我应该再问一句“哦，你指的是什么？”而客户也应该把说完整。&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;就算是只增加一行代码，我也要认真的检查。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;随便的往现有的程序里塞代码，那是在给自己找麻烦。这点请相信我说的。&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;你的“只要一分钟”不可能是一分钟。它会变成我一整天的工作。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我还没有说“流”状态吧？&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;读读这本书吧———&lt;a href=&#34;../%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A8%8B%E5%BA%8F%E5%91%98%E9%83%BD%E6%98%AF%E5%A4%9C%E7%8C%AB%E5%AD%90/&#34;&gt;为什么程序员都是夜猫子&lt;/a&gt;。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么程序员都是夜猫子</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A8%8B%E5%BA%8F%E5%91%98%E9%83%BD%E6%98%AF%E5%A4%9C%E7%8C%AB%E5%AD%90/</link>
      <pubDate>Mon, 17 Jul 2023 17:53:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E7%A8%8B%E5%BA%8F%E5%91%98%E9%83%BD%E6%98%AF%E5%A4%9C%E7%8C%AB%E5%AD%90/</guid>
      
        <description>&lt;p&gt;一种很流行的说法是，程序员是把咖啡因转化成程序代码的机器。&lt;/p&gt;
&lt;p&gt;说的是实情，随便问一个程序员，问他什么时候工作最有状态，估计他很有可能说是深夜。有人稍微早一点，有人更晚。有一种流行的趋势是凌晨4点起床，在破晓之前这段时间里做一些事情。而另一些人喜欢凌晨4点才睡觉。&lt;/p&gt;
&lt;p&gt;所有这些的主要目的是躲避打搅。但是你把自己反锁在屋里不就行了？为什么对夜晚情有独钟？&lt;/p&gt;
&lt;p&gt;我想，这事归纳下来有3点：工人的时间表，疲倦的大脑和明亮的电脑屏幕。&lt;/p&gt;
&lt;h1 id=&#34;工人的时间表&#34;&gt;工人的时间表&lt;/h1&gt;
&lt;p&gt;Paul Graham 在2009年写了一篇关于 工人的时间表的文章——主要是说这个世界(主要)存在两种时间表。传统的管理者的时间表——一天的时间别分成了很多小时，一次十分钟的分心至多会浪费你1个小时的时间。&lt;/p&gt;
&lt;p&gt;而另一种情况是程序员们所说的工人的时间表——生产东西的人的时间表。研究一个大型的抽象的系统，需要把整件事情装进大脑——这样的一些人类似于用珍贵的水晶玻璃搭建一间房子，一旦有人打搅你，整个结构都会滑落，摔成无数的碎片。&lt;/p&gt;
&lt;p&gt;这就是为什么被打扰的程序员会如此的生气。&lt;/p&gt;
&lt;p&gt;因为这是一种巨大的精力上的投资，在没有几个小时不被打搅的环境中的酝酿，你不可能开始工作。如果你好不容易在大脑中构建了整个要思考的事情的模型，而在半个小时后被人摧毁，这是很大的浪费。&lt;/p&gt;
&lt;p&gt;事实上，在跟很多的企业创办人交谈后，你会发现，他们都感觉在白天根本无法做任何事情。持续不断的打扰、重要的事情需要注意、大量的邮件需要处理，环境不允许他们坐下来做事。于是他们大部分人都在夜晚，人们都入睡的时候去完成他们要做的事做完。&lt;/p&gt;
&lt;h1 id=&#34;疲倦的大脑&#34;&gt;疲倦的大脑&lt;/h1&gt;
&lt;p&gt;但是程序员仍然在晚上会困倦。程序员不是超人。程序员甚至在白天都会感到疲劳。&lt;/p&gt;
&lt;p&gt;为什么我们要把智力上最复杂的事情放到我们的大脑想去睡觉的时间段里去完成，而在我们的大脑最敏锐最清醒的时候去做相对简单的事？&lt;/p&gt;
&lt;p&gt;因为疲倦让我们编写出最好的代码。&lt;/p&gt;
&lt;p&gt;跟ballmer峰值相似，疲倦能使我们的精力更容易集中，因为你的大脑疲倦了，不得不集中精力！没有多余的脑能量来提供你去三心二意。&lt;/p&gt;
&lt;p&gt;对我来说，如果喝了太多的茶或在错误的时间喝了提神饮料，我反而干不了什么事。我会过度兴奋，一会查看微博，一会看看新闻，不知道自己该做什么。&lt;/p&gt;
&lt;p&gt;你会认为我应该更有效率的工作——精力充沛，大脑超频。但正好相反，我东一榔头西一耙子，根本无法在一件事情上定神2分钟。&lt;/p&gt;
&lt;p&gt;而反过来，当我稍微有点疲倦时，我却能把屁股安稳的放到椅子上开始编程。在大脑稍微有点疲倦的情况下，我能连续编程数小时，毫不考虑微博或Facebook。好像这互联网根本不存在。&lt;/p&gt;
&lt;p&gt;我感觉大部分程序员都是跟我的感觉是一样的。对于日常工作中80%的任务，我们的脑能力都是过剩的——除非你需要写的是在当前程序运行的环境中让它增速10的算法。即使你开发的是最高级的机器学习想象功能，其中大部分的工作也只是简单的整理数据和以一种合适的方式表现输出结果。&lt;/p&gt;
&lt;p&gt;当你的大脑不是满负荷运转时，它总是想找点事情做。疲乏会让你迟钝，手头的工作就已经够你消化了。&lt;/p&gt;
&lt;h1 id=&#34;明亮的电脑屏幕&#34;&gt;明亮的电脑屏幕&lt;/h1&gt;
&lt;p&gt;这一点非常的简单。在晚上老是盯着明亮的光源，你的睡眠周期会推迟。你忘了该是睡觉的时候了，直到凌晨3点。然后你早上11点才能起来，当晚上再次到来时你不再感到困倦，因为你11点才起床！&lt;/p&gt;
&lt;p&gt;反复这样的作息规律，你的身体会进入另外一个时区。更有趣的是，这种周期不会一直推迟下去，一旦你进入了这种3点或4点才去睡觉的生理平衡状态，你会一直留在这种状态里。&lt;/p&gt;
&lt;p&gt;也许这是一种警讯在起作用，社会在告诉我们，如果我们在下午2点吃早餐，会被认为是很邋遢的人。&lt;/p&gt;
&lt;h1 id=&#34;最后&#34;&gt;最后&lt;/h1&gt;
&lt;p&gt;总结一下，程序员喜欢在晚上工作，是因为这时没有何时应该停止工作的限制，这让人感到更放松，你的大脑不再思考让你分心的事情，明亮的屏幕使人清醒。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>一道题识别优秀的程序员</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%80%E9%81%93%E9%A2%98%E8%AF%86%E5%88%AB%E4%BC%98%E7%A7%80%E7%9A%84%E7%A8%8B%E5%BA%8F%E5%91%98/</link>
      <pubDate>Mon, 17 Jul 2023 13:53:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%80%E9%81%93%E9%A2%98%E8%AF%86%E5%88%AB%E4%BC%98%E7%A7%80%E7%9A%84%E7%A8%8B%E5%BA%8F%E5%91%98/</guid>
      
        <description>&lt;p&gt;在我的面试工具箱中，有一把锤子是我的最爱。一旦在这道面试题上捕捉到积极信号，无论在其他问题上的反馈多么不靠谱，我都会想法设法把候选人收进来。&lt;/p&gt;
&lt;p&gt;期望的结果只有一个，而错误的可能性却有千千万万&lt;/p&gt;
&lt;p&gt;这道题是关于滥用防卫性编程的。和大部人的直觉相反，生产中绝大部分所谓防卫性代码（错误处理代码）自身都是垃圾，根本没有存在的必要，而且错误成堆，一旦不幸运行就会制造灾难！绕过这个坑的工程师写出来的代码通常比一般代码要少三分之一，圈复杂度会下降一半（不是瞎掰，我是通过度量前后版本的差异得到这个数据的），而可靠性反而会有惊人的提高。&lt;/p&gt;
&lt;p&gt;关于防卫性编程，绝大部分软件工程师的成长经历是这样的：&lt;/p&gt;
&lt;p&gt;阶段一, 没有防卫&lt;/p&gt;
&lt;p&gt;这个阶段很快就会过去，因为有无数的书本和”前辈”在提示防卫性编码的必要性。&lt;/p&gt;
&lt;p&gt;阶段二, 疯狂防卫&lt;/p&gt;
&lt;p&gt;无论是模块之间，还是模块内部，处处充满检查，首先是检查参数，其次是检查返回值和出参；最后，给自己写的函数通通加上指示成功失败的返回码。&lt;/p&gt;
&lt;p&gt;如果读者诸君有幸参加过代码评审会，对阶段二的代码应该不会陌生，看看到底哪里不对劲？&lt;/p&gt;
&lt;p&gt;只管汇报，不管处理，不了了之&lt;/p&gt;
&lt;p&gt;这些泛滥的防卫性代码有一个共同的特点–只管检查汇报，不管处理。所谓的处理就是一层一层的return错误码，典型的懒政行为，老子眼界不够高，做不了决定，把皮球丢给上级机关。上级机关面对来自不同下属的千奇百怪的错误码，他也没辙，只好再丢给上上级机关。这样层层上报，一直汇报到最上层。你觉得最上层要维护多大的队伍才能搞得定？人手再多也搞不定，因为用来分析错误的上下文信息已经在层层汇报中丢失了。所以别看费了好大力气汇报错误码，最后除了一死了之或者睁只眼闭只眼也没有更好的办法。&lt;/p&gt;
&lt;p&gt;内外不辨，亲疏不分&lt;/p&gt;
&lt;p&gt;防卫是应该的，但不应该处处为营。国界线上要有边防军巡逻，但是省界呢？县界呢？设防是有成本的，在软件上就体现为交复杂度税。同一个模块内部，假设A调用B，如果B认为A传给它空指针(空对象)是一个错误，那么A应该设计为永远不会把空指针传递给B，而不是在B处设防。模块内部设防，就好比两口子同床异梦，精力都内耗了。&lt;/p&gt;
&lt;p&gt;人格分裂，自相矛盾&lt;/p&gt;
&lt;p&gt;设计这种代码的人一方面认为自己思维缜密，步步设防，另一方面认为自己白痴到底，所以要步步设防，左手要防着右手。左右手互搏的代码散布得纷纷扬扬，几乎无法辨认有限的功能代码。&lt;/p&gt;
&lt;p&gt;顾头不顾尾，屁股擦不干净&lt;/p&gt;
&lt;p&gt;只要评审得足够仔细，总能发现这些防卫性代码的漏洞，要么是文件没关闭，要么是内存没释放。这是必然的，因为一个正常人根本没脑力搞定这么多细节（你要考虑错误处理代码也是层层嵌套，充满了排列组合的）。考虑到天量的路径组合和逻辑的自相矛盾，你也没办法设计测试用例来验证它。这就是为什么真的一不小心命中的话，结局总是很悲惨的原因。写到这里，我想起历史上重大的安全事故调查报告，事故之所以能发生，是因为多道安全闸门同时失效，而软件故障会更加可怕，因为后面的闸门本身就是炸弹。&lt;/p&gt;
&lt;p&gt;可惜的是，无数工程师一脚踏入阶段二这个大坑后再也没有爬出来。&lt;/p&gt;
&lt;p&gt;我每次评审代码，上手第一刀就是检查错误处理。边界处的错误是否都被检查？错误处理是否遵从系统规定的一致策略？模块内部是否存在周伯通？一旦作者把错误处理的评审意见处理完毕，剩下的代码基本上就身段苗条、脉络清晰了，因为用来实现正常功能的代码真的不会很多。&lt;/p&gt;
&lt;p&gt;说了这么多，那么阶段三到底又是什么样的呢？&lt;/p&gt;
&lt;p&gt;边界清晰，御敌于国门之外&lt;/p&gt;
&lt;p&gt;在模块的边界处，无论别人调用你还是你调用别人，一定要明确约定调用规范，一旦违反规范，立即启用错误处理流程（注意不是防卫流程），不要让错误在自己的模块内部流窜。&lt;/p&gt;
&lt;p&gt;不给错误做二传手&lt;/p&gt;
&lt;p&gt;如果发现错误，让错误到此为止，非必要不接力传递错误码&lt;/p&gt;
&lt;p&gt;当断则断，早死早超生&lt;/p&gt;
&lt;p&gt;所有程序的正确运行都是依赖于一定的前提条件的，如果你发现外部模块不工作了，遵从系统的错误处理策略，该报异常就报异常，该立即退出就退出，重要的是搜集好现场的证据。一味的容错处理既不能解决问题还会掩盖问题，导致错误扩散，变形，拖延定位问题的时机。&lt;/p&gt;
&lt;p&gt;和谐社会，简单单纯&lt;/p&gt;
&lt;p&gt;在模块内部，只有不言自明的约定，没有周伯通似的左右手互搏。好比乌龟，龟壳坚硬，但龟壳内部是一个柔软的世界。&lt;/p&gt;
&lt;p&gt;这样的代码，模块内部几乎没有用于实现需求以外的代码，很容易阅读，仅通过黑盒测试就能实现代码覆盖。&lt;/p&gt;
&lt;p&gt;阶段三的程序员靠谱在哪里？&lt;/p&gt;
&lt;p&gt;有些工程师似乎天生就跨过了阶段二，我遇到一个，当时他还是在校研究生，现在已经创业有成了。我发现这些工程师一般有如下特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;刨根究底，不满足于似是而非，对自己程序所依赖的外部环境和自己模块的职责有清晰的认识&lt;/li&gt;
&lt;li&gt;有全局观念，站在整个软件的角度设计单个模块，站在整个系统的角度设计软件&lt;/li&gt;
&lt;li&gt;倾向于写”简单而明显没有错误的程序”&lt;/li&gt;
&lt;li&gt;正确的错误处理设计需要对软件和系统，模块和整体软件之间的职责划分有清晰的认识，阶段三是一个合格软件架构师的起点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;发现这样的候选人也不是太麻烦，让他面试的时候随身带一份代码来就可以了（不是每一个工程师都有在github上留代码的习惯）。不过，最有价值的实践是通过代码评审把工程师从阶段二推向阶段三。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>一年级的孩子被老师放弃了怎么办？</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%80%E5%B9%B4%E7%BA%A7%E7%9A%84%E5%AD%A9%E5%AD%90%E8%A2%AB%E8%80%81%E5%B8%88%E6%94%BE%E5%BC%83%E4%BA%86%E6%80%8E%E4%B9%88%E5%8A%9E/</link>
      <pubDate>Sun, 16 Jul 2023 12:59:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%80%E5%B9%B4%E7%BA%A7%E7%9A%84%E5%AD%A9%E5%AD%90%E8%A2%AB%E8%80%81%E5%B8%88%E6%94%BE%E5%BC%83%E4%BA%86%E6%80%8E%E4%B9%88%E5%8A%9E/</guid>
      
        <description>&lt;h1 id=&#34;问&#34;&gt;问&lt;/h1&gt;
&lt;p&gt;一年级的孩子被老师放弃了怎么办？&lt;/p&gt;
&lt;h1 id=&#34;答&#34;&gt;答&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;知乎&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如何判断自己是否到了该辞职的时候</title>
      <link>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E8%87%AA%E5%B7%B1%E6%98%AF%E5%90%A6%E5%88%B0%E4%BA%86%E8%AF%A5%E8%BE%9E%E8%81%8C%E7%9A%84%E6%97%B6%E5%80%99/</link>
      <pubDate>Thu, 13 Jul 2023 13:53:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E8%87%AA%E5%B7%B1%E6%98%AF%E5%90%A6%E5%88%B0%E4%BA%86%E8%AF%A5%E8%BE%9E%E8%81%8C%E7%9A%84%E6%97%B6%E5%80%99/</guid>
      
        <description>&lt;p&gt;大约6个月前，我辞去了投资公司的工作，去和我的朋友Adrian一起做一个网站。我发现有必要把我的这点经验写一写，希望能给那些有跟我一样想法的人一点忠告和见解。&lt;/p&gt;
&lt;p&gt;我想辞职。但不知道是否应该辞职？&lt;/p&gt;
&lt;p&gt;这绝对是一个最私人的、最耗时间、最困难的你应该问自己的问题。也正因为这样，大多数人放弃回答这样的问题。&lt;/p&gt;
&lt;p&gt;作为一个拿着工作签证的移民，我知道绝不会有一个辞职的“最佳时间”等着我，但一些已经出现的信号使得我知道给别人打工的日子将不会持续太久。不知道这些迹象是否在你身上也有？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;一直在做业余项目。如果你在白天工作了一整天，仍然在业余找一些项目做，很可能是：你从白天的工作中学不到足够的知识，或你对它不感兴趣。我总是在业余时间做一些小项目或经营一些小生意。最终，我认识到，我应该用全部时间来做它们。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我对晋升不感兴趣，也不想去别的公司工作。在企业职场里，职位头衔和官级是让一个人走向事业成功最好的动力。大多数的公司不会为你某个人创造一个新职位。如果你在想5-7年后你的职位，那你的老板的职位是离你最近的目标。而7-15年后，你很可能达到现在老板的老板的地位。但那时你估计有40岁了。如果你对这些不感兴趣，你也许应该在另外一个“更好”的地方工作(比如大多数认为去雅虎或谷歌工作能更快的事业有成)。但如果你已经做到了很高的位置可还是跟CEO或自己成立公司的距离很远，你应该认真的考虑一下在某些方面应该做巨大的调整。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每月的固定工资不能激起我的激情。工资是个很奇怪的东西，它不会根据你的工作成绩而随时的上下变化，它们只会一年变化一两次。当你刚从学校出来步入职场，你能在短时间内进步的很快，但工薪却几乎不会有多大变化。如果你拥有一个公司，或者是有一部分股份，你的成功与否会紧密的跟你的表现和你的合伙人的表现直接相关(再加上一点运气)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;你感觉自己在失去闯劲，虽然是缓慢的，但的确在发生。在大学生活和社会职场生活完全不同。如果你聪明且爱学，你能迅速的完成学业，但工作却需要资历。如果你的工作不是你热心的，那它会慢慢磨损你的意志，虽然很慢，但却是真的。这是我的真切感受。作为一个创业者，你有多聪明或多努力工作并不代表你能成就多少——重要的是你的斗志/锋芒/动力，这才是让你和周围其他人与众不同的地方。永远不允许自己往下沉。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;公司工作的机会成本 (opportunity cost,指为选择一项东西而放弃的其他机会) 让你夜不能寝。这种情况，如果你还没辞职，但估计也快了。从这一点上看，你大概已经认识到了人生不是一场看看自己能往上流社会爬的多高的竞赛。你是想从无到有创造一些东西，你想试试能否做出这世界上最好的东西。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我决定辞职。可在我离职前应该做些什么？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;找个你能信任的合作伙伴。这几乎是可遇不可求的事。你也许有很多值得信赖的老朋友，但大多数在你想做的事情上没有相同的动力、热情、和信心。我很幸运，遇到了Adrian(我和他的故事都在这里，你可以看一下)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选一个简单的创意开始做。我认为找一些你感兴趣的想法创意或问题，着手去做，这很重要。很难说你应该做什么，但我可以告诉你，我的Backspaces网站最初起源于分享一下我在纽约的所见所闻，最终发展成了更大的东西。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;尽可能多的还清助学贷款。如果你有一个报酬很高的工作，你可以很快的还清助学贷款。并不是因为你可以挣更多的钱，你就可以花更多的钱。不要把大把大把的票子都花在酒吧舞厅里，不要买房或买车。我在一年内偿还了所有的贷款。无债一身轻，没有经济负担，我感觉到生活的一种解脱。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;善待自己和家人。他们在你的整个生命中都在支持你。如果你取得了一些成就，向他们表示你对他们提供的支持的感激，因为这种机会很少，更多的时候是你对他们欠下责任和义务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>关于吞吐量、延迟、信号量和互斥锁的精彩比喻</title>
      <link>http://coder.amazingdemo.top/post/%E5%85%B3%E4%BA%8E%E5%90%9E%E5%90%90%E9%87%8F_%E5%BB%B6%E8%BF%9F_%E4%BF%A1%E5%8F%B7%E9%87%8F%E5%92%8C%E4%BA%92%E6%96%A5%E9%94%81%E7%9A%84%E7%B2%BE%E5%BD%A9%E6%AF%94%E5%96%BB/</link>
      <pubDate>Fri, 07 Jul 2023 03:33:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%85%B3%E4%BA%8E%E5%90%9E%E5%90%90%E9%87%8F_%E5%BB%B6%E8%BF%9F_%E4%BF%A1%E5%8F%B7%E9%87%8F%E5%92%8C%E4%BA%92%E6%96%A5%E9%94%81%E7%9A%84%E7%B2%BE%E5%BD%A9%E6%AF%94%E5%96%BB/</guid>
      
        <description>&lt;p&gt;我们知道，计算机中有很多概念并不容易理解，有些时候一个好的比喻能胜过很多句解释。下面两个是我看到的两个很精彩的比喻，拿出来和大家分享一下。&lt;/p&gt;
&lt;p&gt;第一比喻是关于吞吐量（throughput）和延迟（latency）的。如果你要搞网络性能优化，这两个概念是你必须要知道的，它们看似简单实则不是。我相信包括我在内的很多人都曾经认为大的吞吐量就意味着低延迟，高延迟就意味着吞吐量变小。下面的比喻可以解释这种观点根本不对。该比喻来自这里，我来做个大体意译（非逐字翻译）。&lt;/p&gt;
&lt;p&gt;我们可以把网络发送数据包比喻成去街边的 ATM 取钱。每一个人从开始使用 ATM 到取钱结束整个过程都需要一分钟，所以这里的延迟是60秒，那吞吐量呢？当然是 1/60 人/秒。现在银行升级了他们的 ATM 机操作系统，每个人只要30秒就可以完成取款了！延迟是 30秒，吞吐量是 1/30 人/秒。很好理解，可是前面的问题依然存在对不对？别慌，看下面。&lt;/p&gt;
&lt;p&gt;因为这附近来取钱的人比较多，现在银行决定在这里增加一台 ATM 机，一共有两台 ATM 机了。现在，一分钟可以让4个人完成取钱了，虽然你去排队取钱时在 ATM 机前还是要用 30 秒！也就是说，延迟没有变，但吞吐量增大了！可见，吞吐量可以不用通过减小延迟来提高。&lt;/p&gt;
&lt;p&gt;好了，现在银行为了改进服务又做出了一个新的决定：每个来取钱的客户在取完钱之后必须在旁边填写一个调查问卷，用时也是30秒。那么，现在你去取钱的话从开始使用 ATM 到完成调查问卷离开的时间又是 60 秒了！换句话说，延迟是60秒。而吞吐量根本没变！一分钟之内还是可以进来4个人！可见，延迟增加了，而吞吐量没有变。&lt;/p&gt;
&lt;p&gt;从这个比喻中我们可以看出，延迟测量的是每个客户（每个应用程序）感受到的时间长短，而吞吐量测量的是整个银行（整个操作系统）的处理效率，是两个完全不同的概念。用作者的原话说是：&lt;/p&gt;
&lt;p&gt;In short, the throughput is a function of how many stages are in parallel while latency is a function of how many are in series when there are multiple stages in the processing. The stage with the lowest throughput determines the overall throughput.&lt;/p&gt;
&lt;p&gt;正如银行为了让客户满意不光要提高自身的办事效率外，还要尽量缩短客户在银行办事所花的时间一样，操作系统不光要尽量让网络吞吐量大，而且还要让每个应用程序发送数据的延迟尽量小。这是两个不同的目标。&lt;/p&gt;
&lt;p&gt;另外一个比喻是解释信号量（semaphore）和互斥锁（mutex）的区别。该比喻最初来自这里，我先翻译一下，然后对它做个改进。&lt;/p&gt;
&lt;p&gt;互斥锁是一把公共厕所的钥匙。一个人使用厕所的时候可以拿到这把钥匙，用完之后把这把钥匙交给排队的下一个人。&lt;/p&gt;
&lt;p&gt;信号量是没有人使用的厕所的钥匙数量，所有厕所的钥匙都一样。比如有4个厕所有相同的钥匙和锁。信号量的值就是钥匙的数量，一开始是4。当进来一个人的时候数量就是少一个，如果4个厕所都满了，信号量就成0了，出去一个人就增加1，并把钥匙交给排队的下一个人。&lt;/p&gt;
&lt;p&gt;这个比喻并不是太好，尤其是它无法解释 二元（binary）信号量和互斥锁的区别！我把这个比喻做了改进。互斥锁的比喻还是和上面一样，需要指出的是，当你拿到那把钥匙的时候你就是它的拥有者（owner），别人是无法打开厕所门的。&lt;/p&gt;
&lt;p&gt;而信号量到底是什么呢？它就是一个大的公共厕所，里面有若干个位置，外面的大门口有一个可以翻动牌子写着“已满”和“可用”，当里面还有空的位置的时候，进去的人不用翻动这个牌子，直到没有位置时最后一个进去的人必须把它设成“已满”，这时后面的人必须排队等候，然后出去的人必须把牌子翻到“可用”，如果需要的话。&lt;/p&gt;
&lt;p&gt;很好理解对嘛？那么它怎么解释二元信号量呢？也就是当这个厕所里面只能容纳一个人的时候，每个人进去的时候都要把门口的牌子翻到“已满”，出去的时候翻到“可用”。它和互斥锁的区别马上就可以看出来了，翻动的牌子在外面可以被别人翻的，而锁住的锁只有拿钥匙的人才可以开！&lt;/p&gt;
&lt;p&gt;当然了，信号量之所以翻译成“信号”，还是有道理的，因为它（厕所门口的牌子）标示的是资源（厕所空位）的状态，而互斥锁就是锁，它实实在在地锁住了资源。这在生产者消费者的情况下区别更明显。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>蠕虫病毒史话：好奇心是如何害死猫的</title>
      <link>http://coder.amazingdemo.top/post/%E8%A0%95%E8%99%AB%E7%97%85%E6%AF%92%E5%8F%B2%E8%AF%9D_%E5%A5%BD%E5%A5%87%E5%BF%83%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%B3%E6%AD%BB%E7%8C%AB%E7%9A%84/</link>
      <pubDate>Sat, 24 Jun 2023 12:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E8%A0%95%E8%99%AB%E7%97%85%E6%AF%92%E5%8F%B2%E8%AF%9D_%E5%A5%BD%E5%A5%87%E5%BF%83%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%B3%E6%AD%BB%E7%8C%AB%E7%9A%84/</guid>
      
        <description>&lt;p&gt;25 年前，Robert Morris 释放的蠕虫病毒永久改变了互联网。虽然他制作病毒的动机更多是出自好奇心而非恶意，但病毒的传染很快失控，并且获得媒体的广泛关注。他也因此成为第一个被计算机欺诈和滥用法令起诉的人。近日，Washingtonpost 采访相关人士，回顾了多年前的这场风波。&lt;/p&gt;
&lt;p&gt;在进入康奈尔大学之前，Robert Morris 是哈佛大学的学生。他厌烦日常课程，经常把精力放在其它地方。到了康奈尔大学之后，他的学习方式并未改变。他喜欢独自研究，并且把大量的时间用在了编程上。据他的同学说，从入学到发布蠕虫病毒的两个月里，他并没有交上太多的朋友。&lt;/p&gt;
&lt;p&gt;Robert Morris 开始制作蠕虫病毒的时间大概是 1988 年 10 月 15 日。他的蠕虫病毒包括了三种攻击方式。其中一个攻击是针对 finger 服务，另一个攻击是针对用户的弱密码。10 月 20 日，Morris 去哈佛看朋友，呆了两天。回到康奈尔之后，他编写了攻击邮件服务“Sendmail”的代码。&lt;/p&gt;
&lt;p&gt;Morris 曾有过颇具野心的目标。在他的设想中，病毒不仅仅是悄悄地传播复制，而是要把上千的计算机联合起来，执行自己的命令。这正是后来出现的僵尸网络。&lt;/p&gt;
&lt;p&gt;不过，在蠕虫病毒发布的时候，他只实现了一小部分目标。他从未做出一个控制系统，以便向感染病毒的计算机发布命令，而且他在病毒传播的实现上犯了错误。蠕虫病毒在同一个计算机上不断复制，从而耗尽了计算机资源。因此，这个病毒很快被发现了。普度大学的助理教授 Eugene Spafford 是最早发现该病毒的人之一。他说，Morris 是个有经验的 Unix 程序员，但远非人们所说的“Unix 巫师”。在编写蠕虫病毒的时候，Morris 犯了许多新手的错误。&lt;/p&gt;
&lt;p&gt;Morris 在发出蠕虫病毒后，很快就后悔了。11 月 2 日 11 点左右，他给一个在哈佛认识的朋友 Andrew Sudduth 打了电话。Andrew Sudduth 是哈佛大学 Aiken 计算机实验室的技术人员。那时候，他正在和 Paul Graham 交谈。&lt;/p&gt;
&lt;p&gt;Paul Graham 也是 Aiken 实验室的人员，Morris 的朋友。他接到 Morris 的电话，然后告诉 Sudduth 说，Morris 承认自己发布了一个蠕虫病毒。半小时后，Morris 再次打来电话，给出了抵抗病毒的建议。2 点 30 分，Morris 第三次打电话，他显得有些惊慌，并说自己犯了一个天大的错误。他让 Sudduth 发表一份匿名信息，对此次事件表示抱歉，并且解释了抵抗病毒传播的方法。&lt;/p&gt;
&lt;p&gt;一个小时后，Sudduth 在 Usenet 公告牌上发布了信息。不幸的是，他的信息在两天后才被人注意到。&lt;/p&gt;
&lt;p&gt;最早注意到蠕虫病毒的一批人中，包括加州大学伯克利分校的学生。当天，正好是伯克利年度的 Unix Workshop 大会开幕的时间。一些人没有参加大会，而是去分析蠕虫病毒了。到了傍晚的时候，伯克利团队已经搞懂了病毒，并且提出了制止其传播的方案。周五的时候，他们完成了对病毒的深度分析，并且把发现展示给 Unix workshop 大会。&lt;/p&gt;
&lt;p&gt;其它大学也开始对抗病毒。普度大学的助理教授 Eugene Spafford 很快组织了八人的小组，对其进行分析。后来，他还成为各个大学信息交换的中转站。当天结束的时候，他创建了针对病毒的邮件列表，并且在随后的几周内写出了深度分析报告。&lt;/p&gt;
&lt;p&gt;Morris 的蠕虫病毒获得了媒体的广泛关注。对于许多记者来说，这是他们第一次听说互联网和恶意软件。为此，MIT 还举办了一场新闻发布会。“病毒没有任何可见的影响，这让媒体一致地失望，”两位 MIT 研究员说，“我们并不会立刻进入第三次世界大战，这似乎让一些人感到痛苦。”&lt;/p&gt;
&lt;p&gt;Eugene Spafford 接到了南印第安纳报社的电话，记者非常严肃地询问，读者是否需要担心感染上这种病毒。“老天啊，我不知道，”Spafford 不动声色的回复说，“我们这里没有医学院。你应该问问印第安纳大学的人。”&lt;/p&gt;
&lt;p&gt;Morris 的病毒震动了年轻的互联网。当时，互联网只有 10 万台计算机，而且多数是用于工作。“我不能说人们彼此信任，但是当时有一种追求稳定的社区氛围 ，而且对计算机系统的使用比较适度。”Spafford 说。&lt;/p&gt;
&lt;p&gt;网络管理员很少采取防护措施。“那时候没有防护墙这种东西“，Spafford 说，“没有什么破坏分子、无政府主义者或者罪犯。公共服务器很多，因为大学分享大量的数据和资源。”&lt;/p&gt;
&lt;p&gt;在蠕虫病毒的风波之后，人们对计算机安全更为在意了，从而导致了安全行业的兴起。&lt;/p&gt;
&lt;p&gt;Morris 制造蠕虫病毒的时候，或许并没有恶意，但这无法使他避免政府的追究。负责案件的律师 Mark Rasch 说，他和同行们曾仔细考虑过如何处罚的问题。他们相信 Morris 的动机并非恶意，但是，如果政府处罚过轻，可能会纵容其他人做同样的事情。&lt;/p&gt;
&lt;p&gt;1990 年，法官 Howard Munson 判处 Morris 三年监外缓刑，400 小时社区服务，以及 1 万美元的罚款。Morris 的律师们认为量刑过重，并且提出了上诉，但是最终被驳回。&lt;/p&gt;
&lt;p&gt;自此以后，Robert Morris 的人生比较顺利。1995 年 ，他和 Paul Graham 一起创建 Viaweb，最早的电子商务网站之一。由于非常在意公众的目光，Morris 不希望自己的名字出现在 Viaweb 网站上。他使用了假名“John McArtyem“。1998 年，Viaweb 以 4900 万美元的价格出售给 Yahoo。&lt;/p&gt;
&lt;p&gt;随后，Morris 回到哈佛大学，在 1999 年获得博士学位。他后来去 MIT 从事计算机网络的研究，2006 年获得终身职位。2005 年，Morris 和 Graham 一起创办了 Y Combinator。&lt;/p&gt;
&lt;p&gt;在 Eugene Spafford 看来，Robert Morris 的行为证明他当初并没有恶意，“那是一场意外，他为此感到后悔”。 曾参与案件的律师 Mark Rasch 也表示同意，“如果 Robert Morris 被赦免的话，我不会反对。如果他愿意，我可以代表他。他不是一个坏人。在我看来，没有理由让他带着罪过的标记度过自己的人生。”&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>《学会提问》逻辑谬误初识</title>
      <link>http://coder.amazingdemo.top/post/%E5%AD%A6%E4%BC%9A%E6%8F%90%E9%97%AE_%E9%80%BB%E8%BE%91%E8%B0%AC%E8%AF%AF%E5%88%9D%E8%AF%86/</link>
      <pubDate>Wed, 07 Jun 2023 12:38:55 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%AD%A6%E4%BC%9A%E6%8F%90%E9%97%AE_%E9%80%BB%E8%BE%91%E8%B0%AC%E8%AF%AF%E5%88%9D%E8%AF%86/</guid>
      
        <description>&lt;h1 id=&#34;背景&#34;&gt;背景&lt;/h1&gt;
&lt;p&gt;最近在看《学会提问》这本书， 里面主要就是讲了：当别人讲一些观点， 你如何去判断对方的观点是否正确。生活中， 有些人在灌输观点的时候， 可能会采用一些逻辑上的谬误或者其他欺骗性的论证方式来糊弄你， 让你接受他的观点。所以， 还是有必要了解一些逻辑谬误， 一方面可以防止别人用这些逻辑谬误来对付自己， 另外一方面， 在面对不知道的这些谬误的人， 我们某些场景下也可以使用这些误谬去“说服”别人。&lt;/p&gt;
&lt;p&gt;下面就是我简单整理的常见的谬误。&lt;/p&gt;
&lt;h1 id=&#34;常见谬误&#34;&gt;常见谬误&lt;/h1&gt;
&lt;h2 id=&#34;滑坡谬误&#34;&gt;滑坡谬误&lt;/h2&gt;
&lt;p&gt;滑坡谬误：是一种使用连串的因果关系推论的错误思维方式，它夸大了每个环节的因果强度，最终得出不合理的结论。滑坡谬误认为，一种事件的发生会引起另一种事件的发生，而这种事件的发生又会引起更多的事件的发生，最终导致不可控的不利事件发生。然而，滑坡谬误忽略了其他可能的因素和解决方案，从而得出了错误的结论。通常，滑坡谬误将“可能性”转化为“必然性”，以达到某种意图或结论。&lt;/p&gt;
&lt;p&gt;举个常见的例子， 很多父母特别喜欢讲一句， “不能让孩子输在起跑线上”为例。这种思维方式认为，如果孩子在教育上一步落后，那么他们就会步步落后并且将来无法追赶上其他孩子。然而，这种思维方式夸大了“一步落后”对孩子未来成功的影响，而忽略了其他可能的因素和解决方案。&lt;/p&gt;
&lt;h2 id=&#34;追求完美解决方案谬误&#34;&gt;追求完美解决方案谬误&lt;/h2&gt;
&lt;p&gt;追求完美解决方案谬误：错误地认为如果尝试某种解决方案后还有遗留问题未解决，那么这种解决方案根本就不该采用。&lt;/p&gt;
&lt;p&gt;举个例子：比如你在家里面想要安装一个安保系统。但是有人觉得如果贼要光顾你家，他们总归想的到办法能进来，不管你装什么系统都不管用。&lt;/p&gt;
&lt;p&gt;陷入这种谬误的人觉得：我们不应支持针对某问题的解决方案，除非它能从根本上解决这个问题的。&lt;/p&gt;
&lt;p&gt;实际上，如果我们真能找到完美的解决方案，那我们自然该接受它。问题是尝试某种解决方案后还遗留一部分问题，并不意味着这个解决方案欠考虑。能提出一个部分解决问题的方案，总比束手无策要强得多，它可能让我们向彻底解决问题迈进一步。如果坐等完美解决方案出现，我们常常发现自己固守原地，动弹不得。&lt;/p&gt;
&lt;h2 id=&#34;诉诸公众谬误&#34;&gt;诉诸公众谬误&lt;/h2&gt;
&lt;p&gt;诉诸公众谬误：试图通过引述很多人都持有这一观点，以证明某个断言有道理。错误地以为很多人支持的事就是可取的，也是典型的羊群效应。&lt;/p&gt;
&lt;p&gt;举例：是时候让大麻成为人们用来缓解慢性疼痛的一种选择了。当社会对一种毒品的药用价值达成共识时，我们就该同意使用这种毒品。现在显然社会上已经达成了同意使用大麻的共识。最新的民意调查显示，有73%的人认为应该允许使用药用大麻。此外，加州艾滋病受害者治疗协会也支持使抽大麻成为艾滋病病人的一种治疗。&lt;/p&gt;
&lt;p&gt;这个例子里面认为：如果公众都认为怎么样， 那么这个想法就是好的。但是，公众往往没有充分研究过一个问题，不足以提供合理的判断。一定要警惕诉诸流行观点或公众感受的论证。﻿&lt;/p&gt;
&lt;h2 id=&#34;诉诸可疑权威谬误&#34;&gt;诉诸可疑权威谬误&lt;/h2&gt;
&lt;p&gt;诉诸可疑权威谬误：在辩论或者论证中，使用可疑或不可靠的权威来支持自己的观点，从而误导别人。&lt;/p&gt;
&lt;p&gt;举例：某人在辩论中声称某种健康产品能够治愈所有疾病，并且引用了一个自称是医学专家的人士的观点来支持自己的观点。但是，这个所谓的医学专家可能并不具备权威或专业知识，或者可能与该产品的生产商有利益关系，这种情况下就属于诉诸可疑权威谬误。尽管这个人声称是权威，但是他的观点可能是不可信或者有偏见的。﻿&lt;/p&gt;
&lt;h2 id=&#34;虚假的两难选择谬误&#34;&gt;虚假的两难选择谬误&lt;/h2&gt;
&lt;p&gt;虚假的两难选择谬误：在辩论或者论证中，错误地将问题简化为两个极端的选择，而且这两个选择都看似不可避免或不可接受，从而排除了其他选择。&lt;/p&gt;
&lt;p&gt;例如，假设某人在一个关于社会福利的辩论中，试图说服听众，认为提高社会福利开支是不可接受的，因为要么增加税收，要么减少其他政府支出，这两个选择都不可避免，并且都会导致其他问题。这样的说法是虚假的两难选择谬误。这种做法将复杂的问题简化为两个选择，有意忽略了其他可能的选项，并且将听众引向它自己的观点。&lt;/p&gt;
&lt;p&gt;虚假的两难选择谬误的问题在于，它让人们相信只有两种选项，而排除了其他可能的选择。实际上，很少有问题只有两种选择，总会有其他的选项，需要我们进一步探讨。&lt;/p&gt;
&lt;p&gt;要找出两难选择的谬误，就要时时警惕下面这些说法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不是……就是(either…or)&lt;/li&gt;
&lt;li&gt;唯一的选择就是(the only alternative is)·&lt;/li&gt;
&lt;li&gt;两种选择分别是(the two choice are)·&lt;/li&gt;
&lt;li&gt;因为甲不起作用，所以只有乙能(because A has not worked,only B will)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;人身攻击型谬误&#34;&gt;人身攻击型谬误&lt;/h2&gt;
&lt;p&gt;人身攻击谬误：攻击对方的人或个人品质，而不是针对其观点或论据进行辩论。这种错误常见于争论或辩论中，它不仅违反了逻辑规则，也会导致争论的氛围变得恶劣。&lt;/p&gt;
&lt;p&gt;例如，如果在辩论中，对方提出了一个论点，而你却回应说：“你这个人很不可信，你的话不值得相信”，这就是人身攻击谬误。这种回应没有对对方的论点作出任何反驳，而只是针对对方个人进行攻击。&lt;/p&gt;
&lt;p&gt;另一个例子是，假设你对某个政治领袖的政策有不同意见，但你却攻击他的外貌、家庭背景或个人生活，这也是人身攻击谬误。&lt;/p&gt;
&lt;h2 id=&#34;叙事谬误&#34;&gt;叙事谬误&lt;/h2&gt;
&lt;p&gt;叙事谬误：是在没有足够证据支持的情况下，基于一些已知事实构建一个故事或解释，然后错误地认为这个故事或解释可以解释所有相关事实和现象，忽略了其他可能的解释。﻿&lt;/p&gt;
&lt;p&gt;例如，假设有一个人在一家商店偷了一瓶饮料，并被抓住了。这个人可能会说，“我是因为口渴才偷的，我实在是没有钱买。”。但这个解释并不足以解释他为什么也偷了两个香蕉和一包薯片。这里的错误在于，这个人假设只有一个故事能够解释他的行为，而忽略了其他可能的解释，比如他可能只是贪婪或者有偷窃癖好。&lt;/p&gt;
&lt;p&gt;类似地，许多情况下，一个貌似可以解释事实和现象的故事或解释并不具备足够的证据或逻辑支持，忽略了其他可能的解释。在做出判断和决策时，我们应该避免叙事谬误，不要假设只有一个解释可以解释所有事实和现象，而是应该考虑所有可能的解释，并据此进行推理和判断。﻿&lt;/p&gt;
&lt;h2 id=&#34;诉诸情感谬误&#34;&gt;诉诸情感谬误&lt;/h2&gt;
&lt;p&gt;诉诸情感谬误 ：使用带有强烈感情色彩的语言来分散读者或听众的注意力，让他们忽视相关的理由和证据。常被用来加以利用的情感有：恐惧、希望、爱国主义、怜悯和同情。&lt;/p&gt;
&lt;p&gt;举个例子：两位候选人就是否应该实行移民政策展开辩论。当一个候选人支持移民政策时，另一个候选人可能会诉诸情感谬误来攻击他的观点，例如，他可能会说：“这种政策会导致大量外来移民来到我们的国家，他们将占据我们的工作岗位、住房和公共资源，使我们的社会陷入混乱和失序。” 尽管这个候选人没有提供任何事实或数据来支持自己的观点，他使用了情感上的恐惧和不安因素来试图唤起听众的情绪，这就是诉诸情感谬误。﻿&lt;/p&gt;
&lt;p&gt;当然，对许多事情产生情绪化的反应是没有错的。当我们看到医院遭受轰炸时，如果我们情感上一点反应都没有，那就是对人性的背叛。同样，试图用理由和证据说服他人对某件事做出情绪化反应也是没有错的。﻿&lt;/p&gt;
&lt;p&gt;然而，仅仅靠情感诉求就形成一种论点，是一种逻辑上的花招。他们会精心挑选词汇以引发我们的情感共鸣，希望我们的情绪反应会促使我们同意他的结论。当立论者激发人们的情绪反应，然后依靠这种情绪反应而非正当理由来促使人们同意他的结论时。&lt;/p&gt;
&lt;h2 id=&#34;稻草人误谬&#34;&gt;稻草人误谬&lt;/h2&gt;
&lt;p&gt;稻草人谬误：歪曲，夸大对方的观点，使其容易受到攻击，进而攻击事实上根本就不存在的观点。&lt;/p&gt;
&lt;p&gt;这种谬误通常通过虚构对手的观点，并将其描述为极端或容易被攻击的形式来实现。然后，对手的这个虚假观点就被轻松地攻击和击败，而不是真正的观点。这样做的结果是，辩论或论证的过程不再基于真正的观点和证据，而是基于虚假的观点和推论。&lt;/p&gt;
&lt;p&gt;举个例子，假设两个人正在辩论是否应该实施糖税。A认为糖是导致肥胖和糖尿病的罪魁祸首，应该实施糖税来减少人们对含糖饮料的消费；B认为政府不应该干涉人们的饮食选择，而应该鼓励大众更加健康地饮食。然而，A可能会使用稻草人误谬来攻击B的观点，他可能会说：“你不支持糖税就意味着你支持肥胖和糖尿病，这是一种危险的立场，会使人们生病甚至死亡。” 这种做法是将B的观点扭曲为“支持肥胖和糖尿病”，从而将其描述为极端、容易攻击的形式，以便于A能够轻易地击败B。这就是稻草人误谬。&lt;/p&gt;
&lt;p&gt;如果有人攻击一个立场的某些方面，一定记住要查看一下他是否公正全面地呈现了这个立场。如果没有的话，你就找到了一个“稻草人谬误”。稻草人不是真人，很容易被击倒。当一个人犯了稻草人谬误时，他攻击的立场就像稻草人。要检查一个立场被展现得有多公平，最好的办法就是找出反映所有立场的事实。﻿&lt;/p&gt;
&lt;h2 id=&#34;乱扣帽子谬误&#34;&gt;乱扣帽子谬误&lt;/h2&gt;
&lt;p&gt;乱扣帽子谬误：就是有人宣称他们发现了导致某个行为的原因，而实际上所做的不过是为这个行为起了个名字而已。这样的解释常常过于简化问题，而阻碍了对人过事物更深刻的理解。&lt;/p&gt;
&lt;p&gt;下面有2个例子：&lt;/p&gt;
&lt;p&gt;1)作为对爸爸酗酒毛病的回应，当妈妈被大女儿问道“为什么爸爸行为这么古怪”时，她干脆回答说：“他遇到了中年危机。”&lt;/p&gt;
&lt;p&gt;2)有个朋友老是担心其他人在背后谈论他。你问一个心理学家他为什么会这样，心理学家回答说：“因为这个人有妄想症。”&lt;/p&gt;
&lt;p&gt;两个回答问题的人都没有对所发生的行为提供让人满意的解释。比如说，爸爸基因的具体情况、工作上的压力以及夫妻间的争吵等，都有可能是老爸酗酒的合理解释。“中年危机”的解释不但不充分，而且容易误导别人。我们本以为自己知道了爸爸为什么酗酒，实际上却什么都不知道。&lt;/p&gt;
&lt;p&gt;“解释”需要对一个行为为什么发生进行分析。解释是个耗时费力的活儿，经常考验我们脑子里有多少知识。当别人要我们解释为什么某个特定的行为会发生时，我们常常忍不住想要隐藏自己对很多复杂因果关系的无知，干脆给那种行为贴个标签或起个名字。然后，我们错误地以为，因为我们知道那个标签或名字，所以我们知道了事情的起因。&lt;/p&gt;
&lt;p&gt;我们之所以这样做，是因为乱扣帽子欺骗了我们，让我们相信自己找到了那个人的特点或发现那个人是什么样的人，相信是这些促使他做出相应的行为。例如，比起详细列举出导致一个人大发脾气的一套又一套复杂的内因和外因，如人际关系出了问题、父母给他的强化或者孤立无援的感觉等，我们可能更倾向于干脆说那个人脾气不好，或者说他就是喜欢与人为敌。这样的解释过度简化了问题，阻碍我们取得更深邃的理解。﻿&lt;/p&gt;
&lt;h2 id=&#34;粉饰谬误&#34;&gt;粉饰谬误&lt;/h2&gt;
&lt;p&gt;粉饰谬误：使用模糊、引发人们强烈情感认同的描述品行的词语，使我们倾向于同意某件事而不去细查其理由。&lt;/p&gt;
&lt;p&gt;看一下下面这个例子：&lt;/p&gt;
&lt;p&gt;政治演说：在即将到来的选举中，你迎来了为一位女性投票的良机，她代表了这个伟大国家的未来，她为实现民主长期奋斗，为捍卫国家利益不遗余力，她为追寻美国梦而当机立断、充满信心、勇往直前。这位女性充满爱心，为儿童福利出力，为环境保护奔走，为推动国家迈向和平、繁荣和自由而出谋划策。投古德哈特(Goodheart)一票就是投真理一票，投梦想一票，投常识一票。&lt;/p&gt;
&lt;p&gt;听起来好像古德哈特女士是个完人，是不是？但是这一演说没有提供任何具体的细节，说明这位参议员的过去记录和对重大事件的立场。取而代之的是一连串描述品行的词，它们全都倾向于和我们心中积极的情感产生联系。我们把这类词称为粉饰，因为它们都能让人产生积极的联想，而且它们都很笼统，简直可以说读者想它们是什么意思，它们就是什么意思。这种粉饰的手法会让我们赞同或接受一个结论，而根本不去检查相应的理由、证据或具体的优缺点。“粉饰谬误”(glittering generality fallacy)就好像把乱扣帽子的方法颠倒过来，后者的重点是要我们形成负面的评价，而不去检查相应的证据。品行词的使用是政客们经常玩弄的手腕，这个手法可以分散读者或听众的注意力，让他们不去关注具体的行动或政策，这些行动和政策更容易招致反对和批评。﻿&lt;/p&gt;
&lt;h2 id=&#34;转移话题谬误&#34;&gt;转移话题谬误&lt;/h2&gt;
&lt;p&gt;转移话题谬误：在辩论或论证中，将讨论的焦点从原问题转移到不相干的话题上，以此来误导别人或者避免回答问题。这种做法试图移动目标或标准，使对方的证据或观点无法通过或接受。&lt;/p&gt;
&lt;p&gt;例如，如果两个人在辩论是否应该提高最低工资时，一个人可能会说“提高最低工资会对经济造成负面影响”，而另一个人可能会反驳“这并不是真的，提高最低工资可以有助于减少贫困和增加消费”。然而，如果第一个人使用转移话题谬误，例如“但是提高最低工资可能会导致更多的通货膨胀”，就避免接受对方的证据或观点。&lt;/p&gt;
&lt;p&gt;转移话题谬误的问题在于，它试图移动目标或标准，以避免回答问题或接受对方的证据。这种做法可能使辩论变得无意义，因为双方无法就问题达成共识或接受对方的观点。为避免转移话题谬误，我们需要确保讨论的目标和标准是明确和一致的，并避免改变标准或要求以避免接受对方的证据或观点。﻿&lt;/p&gt;
&lt;h2 id=&#34;循环论证谬误&#34;&gt;循环论证谬误&lt;/h2&gt;
&lt;p&gt;循环论证谬误：在论证中，使用所要证明的观点本身来支持这个观点，而不是提供新的证据或理由。这种做法试图通过重复一个观点来证明它，而忽略了需要外部证据和逻辑推理的事实。&lt;/p&gt;
&lt;p&gt;例如，一个人可能会说，“我是正确的，因为我总是说真话。” 这种论证并没有提供其他证据或理由来支持这个观点，而是用观点本身来支持它。这种做法试图通过重复论点来证明它，而忽略了需要外部证据和逻辑推理的事实。&lt;/p&gt;
&lt;p&gt;循环论证谬误的问题在于，它并没有提供新的证据或理由来支持观点，而是使用观点本身来支持它。这种做法可能会误导别人，使人们相信一个没有被证明的观点。为避免循环论证谬误，我们需要从外部获得证据，而不是只重复观点本身，以支持或反驳论点。﻿&lt;/p&gt;
&lt;h1 id=&#34;结尾&#34;&gt;结尾&lt;/h1&gt;
&lt;p&gt;逻辑误谬还有很多很多， 这边也只是罗列了一些常见的。希望大家在这个信息繁杂的社会上， 对于接收的信息， 都能保持自我的批判性思考， 不要被某些有意为之的言论带偏。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>毁灭程序员效率的 15 个障碍</title>
      <link>http://coder.amazingdemo.top/post/%E6%AF%81%E7%81%AD%E7%A8%8B%E5%BA%8F%E5%91%98%E6%95%88%E7%8E%87%E7%9A%8415%E4%B8%AA%E9%9A%9C%E7%A2%8D/</link>
      <pubDate>Sun, 28 May 2023 17:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%AF%81%E7%81%AD%E7%A8%8B%E5%BA%8F%E5%91%98%E6%95%88%E7%8E%87%E7%9A%8415%E4%B8%AA%E9%9A%9C%E7%A2%8D/</guid>
      
        <description>&lt;p&gt;会议，什么都不懂的经理，生产效率指标——这就是你和下一个伟大软件之间的天堑。&lt;/p&gt;
&lt;p&gt;昨天必须得发布产品。用户争闹和咆哮某个缺失的功能。老板的老板说，我们最好迅速行动起来否则就炒我们的鱿鱼。感觉一切都有心无力。&lt;/p&gt;
&lt;p&gt;没有人满意开发人员这种已经“竭尽全力”改变世界的速度，每个人都希望代码像消防水管里的水一样能够源源不断地流出来，但没有人愿意提供给开发人员更好地完成工作的条件。正如那个想要我们昨天就完成工作的老板，他不愿意雇佣更多的人，不愿意购买速度更快的机器，也不愿意做任何其他可以让程序员专注于编程的事情，又想马儿跑，又不给马儿吃草。&lt;/p&gt;
&lt;p&gt;下面就是现实世界中的15个编程障碍。&lt;/p&gt;
&lt;h1 id=&#34;no1会议&#34;&gt;No.1：会议&lt;/h1&gt;
&lt;p&gt;最常见的抱怨是打断开发人员编码思绪的会议。如果老板信任该程序员，就会要求他们时不时地去那间数周甚至数年昏昏暗暗的会议室闲聊有关细节。尽管程序员通常归咎于是管理人员毁了会议，但他们偶尔也会指责其他的程序员老是跑过来询问有关或bug或功能或架构策略的问题。&lt;/p&gt;
&lt;p&gt;虽然有些抱怨是愚蠢的——但程序员依然会埋怨，如果老板让他们自己在黑暗中摸索，没有一点沟通——任他们自己在软件的抽象世界里埋头苦干，自己去面对各种困境。快餐厨师和咖啡调配师或许还能够兼顾不同的需求，但如果是切换大脑到正确的模式来操作抽象算法则通常需要时间。从会议模式中切换回编码模式，可能会浪费一个小时左右的工作时间。&lt;/p&gt;
&lt;h1 id=&#34;no2答复所有的电子邮件&#34;&gt;No.2：答复所有的电子邮件&lt;/h1&gt;
&lt;p&gt;如果说会议很糟糕，那么这一种可能更糟糕：需要查看发来的无穷无尽的邮件。回复邮件需要时间，而且没人会对回复结果表示满意。然后那些最不耐烦的开发人员或许会选择简单的回复——“tl;dr”(即too long，didn’t read。篇幅过长，没有阅读)。&lt;/p&gt;
&lt;p&gt;有的团队试图开设每周一天的禁邮日。还有的团队就完全不用邮件。虽然解决了邮件过载的问题，但却是以沟通为代价的。要是突然不在一起工作。这还能算是好办法吗？&lt;/p&gt;
&lt;h1 id=&#34;no3试图衡量生产力&#34;&gt;No.3：试图衡量生产力&lt;/h1&gt;
&lt;p&gt;总会有管理团队受那些所谓“你不能管理你无法衡量的东西”的书籍启发，于是开始衡量提交的或代码库或软件代码行或bug修复。他们认为，计数就是衡量，而且衡量一定是好事。&lt;/p&gt;
&lt;p&gt;但是程序员并不是砌砖工，不能数数砌了多少砖就知道其效率。相反，为了写出更好的代码，程序员需要或专注于编写的代码行，或解决bug，或提交到代码仓库，或做一些无法计数的事情。如果bug修复可以加分，那么一些微小bug的报告就会激增，bug修复也会如此。有人因为报告bug得到了奖励，然后另一个人因为修复它也能得到奖励。或者，如果是计数代码行数，那么那些可以用10行代码解决问题的程序员，可能就会转而表示5000行的代码将更灵活或功能更兼容——任何可以添加到5000行中的都加进去。&lt;/p&gt;
&lt;p&gt;衡量效率实际上会因为鼓励功能丰富，代码过度设计的长文件，而让代码库变得更糟。&lt;/p&gt;
&lt;p&gt;对于此问题还没有真正的解决方法。我们需要跟踪bug。我们需要组织工作流程，协调软件的创建。这种优雅是无法衡量的。&lt;/p&gt;
&lt;h1 id=&#34;no4妄自尊大的开发人员&#34;&gt;No.4：妄自尊大的开发人员&lt;/h1&gt;
&lt;p&gt;对于程序员而言，有这样一个同事比Boss更难以忍受：创建了代码的最后一次迭代，却不再工作于这个项目。正如每个房屋装修承包商会贬低上一个木匠的技能，每个程序员也会快速指出可怕的，不可原谅的，完全是死脑筋的上一代的行为。&lt;/p&gt;
&lt;p&gt;当然，这可能是事实，但它很少像程序员说得那么糟糕。如果有什么区别的话，问题通常也不是由于技能匮乏而引起的。主要还是风格的不同，并且风格还会随着时间而改变。上一代和我们今天访问的库不同。他们也不曾阅读过有关最佳做法的最新著作。&lt;/p&gt;
&lt;p&gt;妄自尊大的编程态度往往会减缓项目。骄傲和利己主义的混合发酵会导致程序员抛弃完全能够胜任的代码，只为了按照他们认为的“正确方式”重建。&lt;/p&gt;
&lt;h1 id=&#34;no5以后修复的思维定式又名技术债&#34;&gt;No.5：“以后修复”的思维定式，又名“技术债”&lt;/h1&gt;
&lt;p&gt;我们总感觉不够时间在项目中按计划构建我们想要构建的东西。于是，我们偷工减料，给代码打补丁，缠满了虚拟胶带。曾有明智的经理将此称为是“技术债”，因为“债”是以后必须要还的。即使他们不理解代码，也知道“债”的含义。&lt;/p&gt;
&lt;p&gt;每个项目都有一定的技术债务。有时它会快速见效，但通常直到下一代才会发现这已经成为了一个坑。他们需要构建上一代没有做到的东西。就像滚雪球一样，越滚越大。&lt;/p&gt;
&lt;h1 id=&#34;no6非程序员经理&#34;&gt;No.6：非程序员经理&lt;/h1&gt;
&lt;p&gt;总会有那些面带微笑，西装笔挺，却不是主修计算机科学，也不懂编程项目的家伙成为了经理。也许他们娶了老板的女儿；也许他们正好在“正确”的时间出现在了“正确”的地方。但是，老板让他们担任了经理，即使他们一窍不通。更糟的是，他们会用外行人的眼光来看待问题，哪怕不伦不类，文不对题。&lt;/p&gt;
&lt;p&gt;有一些程序员表示很欢迎这样的经理，因为愚弄他们很容易。而且他们还承担了来自于更高管理层的炮火。但也有人承认，这些人只会不断地开会，只会妨碍编程。他们几乎给不了任何有用的指导，他们可以提供的只是那么一点质量检测。&lt;/p&gt;
&lt;h1 id=&#34;no7程序员经理&#34;&gt;No.7：程序员经理&lt;/h1&gt;
&lt;p&gt;虽然程序员可能会因为不得不与非程序员经理打交道而抱怨，但他们经常悄悄地表示，编程人员去做管理人员更糟糕——有时甚至更糟糕得多。&lt;/p&gt;
&lt;p&gt;他们是前任的天才，可能会决定微观管理项目，然后果决地撕裂大片的代码，因为他们有了一个新的展望。或者，也许他们会闲谈，对于同样的事情，他们是如何用8080汇编或C或Java编程写了一半的代码。在任何情况下，他们更痴迷于技术细节而不是大局，虽然他们被雇来的目的是盯牢后者。&lt;/p&gt;
&lt;h1 id=&#34;no8善于社交的程序员又名brogrammer&#34;&gt;No.8：善于社交的程序员，又名“brogrammer”&lt;/h1&gt;
&lt;p&gt;虽然程序员可以将每个问题和任何中断的责任归咎于巧言令色的销售团队，但编程人员也必须承认，有一些问题在于他们自己。程序员被聘请的目的在于他们的计算机技术，而不是他们的人际交往能力。&lt;/p&gt;
&lt;p&gt;程序员通常不善于沟通，不知道如何表达他们的感受和思维。他们可以准确抓住技术参数，就像庖丁解牛一样迎刃有余。无论客户想要改变什么都不要紧：程序员总是时刻思索着技术参数，即使是在公司野餐上也不外如是。&lt;/p&gt;
&lt;p&gt;尽管程序员通常可以过滤掉对方的特质，但当程序员之间发生磕磕绊绊时也会让团队失败。当同一个团队中两个人有着不同的政治观点，比方说，动态语言或NoSQL，那么团队就会永无宁日。一切都像是在战场一样，战火纷飞，硝烟弥漫。&lt;/p&gt;
&lt;h1 id=&#34;no9自私或牛仔程序员&#34;&gt;No.9：自私或牛仔程序员&lt;/h1&gt;
&lt;p&gt;你从他的代码里发现一个空指针？捕捉空指针于是成为了你的工作。你最好多想一遍要不要传递一个零，因为自私的程序员不会检查除以零错误。这也成为了你的工作。&lt;/p&gt;
&lt;p&gt;牛仔程序员的工作又酷又快，但这是因为他的代码中遗留了许多漏洞，并且没有经过测试。于是这也成为了你的工作，因为如果你不处理这些琐事的话，代码就会崩溃。&lt;/p&gt;
&lt;p&gt;很多团队在最终认识到这一点的时候已经为时已晚。代码块在早期测试中运行良好，但当输入真正的数据之后，各种问题就开始暴露出来。真是一场灾难。&lt;/p&gt;
&lt;h1 id=&#34;no10可怜的文档&#34;&gt;No.10：可怜的文档&lt;/h1&gt;
&lt;p&gt;写文档需要时间。但由于老板雇我们来是来写代码的，并且通常通过我们写的代码行数来衡量我们的效率。因此既然你想要结果，那么我们就只做你想要的那部分。当然最终我们还是会写文档的，但质量的好坏就不论了。&lt;/p&gt;
&lt;p&gt;有时候，文档虽然很多，但却是几个月或几年前老代码的版本。我们只是还没来得及修改这些旧文档而已，但是，以后我们会同步的——相信我。&lt;/p&gt;
&lt;h1 id=&#34;no11成为文档的奴隶&#34;&gt;No.11：成为文档的奴隶&lt;/h1&gt;
&lt;p&gt;虽然我们都经历过没有文档的项目，但是空话太多、编码太少反而导致项目失败也很常见。曾有几个人指着满满一书架的文件夹，向我炫耀说：“我专门请人来写文档。”然而要读完这么多文档需要一年的时间。&lt;/p&gt;
&lt;p&gt;程序员通常在处理需求时，会写一些评论和注释，之后充作文档。因此这样的文档，都是一些微小的细节，没有经过认真地总结或没有说到要点上。这在文档中将可能是致命的，当他们没有提供太多的抽象和理解，就只写代码流水账的时候。这样的文档并不具启发性，只是翻译下代码而已。&lt;/p&gt;
&lt;h1 id=&#34;no12很容易导致分心的环境&#34;&gt;No.12：很容易导致分心的环境&lt;/h1&gt;
&lt;p&gt;有一个客户坚持要我每天去他们的办公室，坚持要我使用他们的电脑。然后，他们没有提供任何的办公空间，所以我只能和六个实习生在会议室写代码，此外，这些实习生还需要我用半天的时间回答他们前一天晚上碰到的问题。另外半天的时间则用来指示今天晚上做什么。于是，我基本上做不来自己的工作。&lt;/p&gt;
&lt;p&gt;虽然销售和营销团队可以在背景噪音的环境下茁壮成长，但程序员通常需要图书馆般安静的背景。闲聊，令人心烦意乱的敲击声，或铃声将驱逐程序员的思维走出抽象的工作区，回到现实中。然后，需要几分钟的时间才能重新沉浸于工作区。&lt;/p&gt;
&lt;p&gt;有一位开发人员告诉我，他恨他的新办公桌，因为它靠拢空调出风口，噪音令人难以置信的响，使得他真的很难集中注意力。这可能略有夸张，但的确是一个事实。&lt;/p&gt;
&lt;p&gt;虽然许多企业会提供程序员类似乒乓球桌的娱乐活动，但他们往往忘记了开发人员需要在安静的氛围中集中精神。甚至，他们还将程序员转移到大房间，认为这可以促进合作，殊不知却会导致一有风吹草动，整个房间的程序员都受到干扰。&lt;/p&gt;
&lt;h1 id=&#34;no13文化契合&#34;&gt;No.13：“文化契合”&lt;/h1&gt;
&lt;p&gt;你想拥有自己的办公室？或者你更喜欢团队化的办公室，这样你就可以直接喊出你的问题？你喜欢在清晨开始工作，亦或是你更喜欢熬夜？&lt;/p&gt;
&lt;p&gt;如果团队成员之间的风格相似。那么这支团队往往才能更好地工作。无法找到共同点的团队很快就会失败。没有沟通，最后只会南辕北辙，不知所谓。&lt;/p&gt;
&lt;h1 id=&#34;no14死守传统技术&#34;&gt;No.14：死守传统技术&lt;/h1&gt;
&lt;p&gt;很多捍卫者认为古老的技术依然很伟大，依然能够完成任务。因此对于为什么要重写代码表示疑虑重重。&lt;/p&gt;
&lt;p&gt;他们想得没错，但他们忘记了保持这些古老代码的成本。所有一切通常都需要用自定义代码进行翻译。某些代码甚至写在ASCII之前，这意味着需要转换输入和输出。旧系统经常会计数空格字符只是为了在数据库中指出这是什么。这就更加需要转换了。&lt;/p&gt;
&lt;p&gt;当然程序员可以通过屏幕抓取，重新格式化，临时构建系统来做大量的工作，但一段时间以后，他们往往需要花费更多的工作来清理混沌的逻辑，以致于腾不出时间来写新的逻辑。&lt;/p&gt;
&lt;h1 id=&#34;no15对最新的渴望&#34;&gt;No.15：对最新的渴望&lt;/h1&gt;
&lt;p&gt;最新的工具自然有意思，但却在没有经过大量时间再次编码以往的工作之前，是不会被开发工作室采用的。走在时代尖端的人总是会扔掉API的整个部分，并重新编写，从而迫使我们这些下游的程序员不得不跟着一起改写代码。我厌烦过，当我不得尽力用Python 2.7的代码对付Python 3.0的代码时，因为依现在的情况，Python已经是一种相对稳定的代码库。&lt;/p&gt;
&lt;p&gt;在许多情况下，新的工具并没有战斗化。例如，Node.js，虽然说相当快，但是只有当你重新学习所有关于死锁的经验教训之后，知道线程优先的时候才能发挥作用。世上没有免费的午餐，工具虽好但都是有代价的。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>魔方手法练习</title>
      <link>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95%E7%BB%83%E4%B9%A0/</link>
      <pubDate>Wed, 24 May 2023 21:43:44 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95%E7%BB%83%E4%B9%A0/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%AD%94%E6%96%B9_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FSC · Finger Shortcut&lt;/strong&gt; ：字面翻译是手指抄近路，也就是魔方的快速手法，即通过手指来转动魔方，以减少操作时间。魔方手法对于魔方提速是非常重要的 不管是公式的学习 还是动作的连贯 都很重要，魔方手法练习好了，才能从根本上提升速度。&lt;/p&gt;
&lt;p&gt;手法，主要有几个要点（以一般的左手握法来说）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;左拇指放在靠近自己这面的中心块上，左中指无名指放在后面的中心块和中心块靠下方，这3个手指捏着魔方。左尾指放在后面或者放在底面。如果有L和L&amp;rsquo;之类的转动，就需要换成右手指捏住魔方了，和左手的握法是一样的。&lt;/li&gt;
&lt;li&gt;右手的准备动作有两个，一个是右拇指放在中右棱的前面，右食指放在上右后角的后面，右中指无名指尾指依次放在右边层的后面。还有一个准备动作就是第一个准备动作做了一个R&amp;rsquo;后的样子。如果有L L&amp;rsquo;等之类的动作需要右手捏着魔方时，左手的准备动作和以上说到的右手准备动作是一样的。&lt;/li&gt;
&lt;li&gt;U&amp;rsquo;一般用左食指拨动，拨动时感觉手指是在划圆弧；如果有RU&amp;rsquo; R，RU&amp;rsquo;U&amp;rsquo;（RU&amp;rsquo;U&amp;rsquo;和R U2的效果一样）之类的手法，可以在R后顺势用右拇指推动顶层以完成一个U&amp;rsquo;的动作。&lt;/li&gt;
&lt;li&gt;U一般用右食指拨动，拨动时感觉手指是在划圆弧；很少用左拇指推动以完成U转的。&lt;/li&gt;
&lt;li&gt;F时，按照要点2中说到的右手的第二种准备动作，用右食指拨动。&lt;/li&gt;
&lt;li&gt;F&amp;rsquo; 时，按照要点2中说到的右手的第二种准备动作，用右拇指拨动。&lt;/li&gt;
&lt;li&gt;R,R&amp;rsquo; L, L&amp;rsquo;等动作，就是准备动作状态下捏着整层转动；注意不要只用一根手指拨动这层，整个转动过程和转动后，手指都是不离开魔方的（食指可以离开）。&lt;/li&gt;
&lt;li&gt;要克服一个最常见的习惯，就是整个手包着魔方。只能用手指捏着魔方，掌心是远离魔方的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意！练习的时候，&lt;strong&gt;手指要放在合适的位置，才能更快衔接后面的动作&lt;/strong&gt;，具体练习案例看下方表格：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;手法练习&#34;&gt;&lt;/p&gt;
&lt;p&gt;FSC的变化非常多，需要靠每个人自己去探索，找出自己习惯的手法，但这是你以后要做的事，现在就有事没事把上面这些FSC转得滚瓜烂熟吧！手法公式练习熟悉了，学习公式也会更加简单，很多公式其实就是手法公式的集合，甚至在学习F2L的时候 就可以自己研究出一些公式来。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>魔方手法</title>
      <link>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95/</link>
      <pubDate>Mon, 22 May 2023 22:33:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%AD%94%E6%96%B9_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;字母含义及手法&#34;&gt;字母含义及手法&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;R L U D F B&lt;/strong&gt;分别对应Right，Left，Up，Down，Front，Back，对应每个面的顺时针转动。&lt;/p&gt;
&lt;p&gt;在一个字母后面加上一撇&amp;rsquo;，那就表示反过来做这个字母的动作（逆时针转动）。&lt;/p&gt;
&lt;p&gt;在一个字母后面加一个2表示这个动作做两次。&lt;/p&gt;
&lt;p&gt;在一个括号后面加一个2表示括号内的字母动作做两次。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;字母含义&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;转动手法&#34;&gt;转动手法&lt;/h1&gt;
&lt;p&gt;如果你想让你的复原更连贯、动作优雅，那就学。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;转动手法&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;术语&#34;&gt;术语&lt;/h1&gt;
&lt;p&gt;另外必须知道什么是角块、棱块、中心块，后面教程都是用的这些术语。&lt;/p&gt;
&lt;p&gt;每个角块有三个色块，每个棱块有两个色块。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;术语&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>魔方 - 目录</title>
      <link>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E7%9B%AE%E5%BD%95/</link>
      <pubDate>Sun, 21 May 2023 16:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%AD%94%E6%96%B9_%E7%9B%AE%E5%BD%95/</guid>
      
        <description>&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95/&#34;&gt;手法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%AD%94%E6%96%B9_%E6%89%8B%E6%B3%95%E7%BB%83%E4%B9%A0/&#34;&gt;手法练习&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>新冠 儿童线上问诊记录</title>
      <link>http://coder.amazingdemo.top/post/%E5%84%BF%E7%AB%A5%E7%BA%BF%E4%B8%8A%E9%97%AE%E8%AF%8A%E8%AE%B0%E5%BD%95/</link>
      <pubDate>Fri, 19 May 2023 11:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%84%BF%E7%AB%A5%E7%BA%BF%E4%B8%8A%E9%97%AE%E8%AF%8A%E8%AE%B0%E5%BD%95/</guid>
      
        <description>&lt;p&gt;目前北京妈妈圈里流传着一份15分钟的线上问诊记录录音，大夫是协和医院儿科副主任医师李主任，讲解地非常细致、中肯，我根据问诊录音整理了文字概要，分享给大家，希望能帮助到焦虑的妈妈们，真的阳了也不要怕，心中有数，沉着应对。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;除了退烧药，不需要使用任何药物，过度用药反而容易出问题，儿童不需要镇咳，中成药不要服用。&lt;/li&gt;
&lt;li&gt;睡着了 不需要叫起来吃退烧药，烧不出脑炎肺炎心肌炎等问题，让孩子继续睡，是我们的重要选择。&lt;/li&gt;
&lt;li&gt;一般发烧3天左右，第二天最高，抵抗力好的第二天就会降温。&lt;/li&gt;
&lt;li&gt;退烧药阻挡不住高热惊厥的发生，高热惊厥发生在前12小时，如果12小时后没有高热惊厥，基本不会发生。&lt;/li&gt;
&lt;li&gt;发热时，从呼吸道和皮肤水分流失特别快，水分给到位是最重要的第一条，充分休息，是最好的治疗方法。&lt;/li&gt;
&lt;li&gt;退烧药美林、泰诺选一种用，1岁以下不建议两种都用，肾脏无法负荷。24小时不超过4次。&lt;/li&gt;
&lt;li&gt;退烧药退不下来也没关系，退不下去不追加服用退烧药。&lt;/li&gt;
&lt;li&gt;退烧后开始咳嗽，不用镇咳。有鼻涕可以用生理盐水、海盐水。&lt;/li&gt;
&lt;li&gt;中成药没有母乳安全喂养级别，布洛芬可以安全哺乳。&lt;/li&gt;
&lt;li&gt;体温上升时身体发抖或手脚冰凉，不要泡澡，儿童会不舒服。&lt;/li&gt;
&lt;li&gt;物理降温：不用泡澡擦身，少穿少盖，降低环境温度就可以。温水擦拭可能加重儿童不适，降温贴也容易过敏。&lt;/li&gt;
&lt;li&gt;少吃多餐，多喝水、奶、粥、汤面等有液体的东西，冷的热的都行，孩子愿意吃就可以。&lt;/li&gt;
&lt;li&gt;呕吐没关系，不耽误吃。化整为零，全天保证水分和能量，原来可以吃的食物仍旧可以吃，没必要压缩食物种类，不能只喝粥，没营养，什么都能吃。&lt;/li&gt;
&lt;li&gt;这种情况容易出问题：家长不给吃不给喝硬扛，尿黄，拿包被捂汗，容易出心肌炎、脑炎、肺炎等严重问題。&lt;/li&gt;
&lt;li&gt;感染后3-6个月不会重复感染，除非感染其他亚型病毒。&lt;/li&gt;
&lt;li&gt;环境的消毒，都比不过手部消毒。&lt;/li&gt;
&lt;li&gt;人体转阴，环境中的病毒也消失了，不需要大规模消杀。&lt;/li&gt;
&lt;li&gt;一家人不太容易不被传染，都感染后，在家不用再戴口罩。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>怎么才能让孩子在不牺牲睡眠和运动时间的前提下，成绩名列前茅？</title>
      <link>http://coder.amazingdemo.top/post/%E6%80%8E%E4%B9%88%E6%89%8D%E8%83%BD%E8%AE%A9%E5%AD%A9%E5%AD%90%E5%9C%A8%E4%B8%8D%E7%89%BA%E7%89%B2%E7%9D%A1%E7%9C%A0%E5%92%8C%E8%BF%90%E5%8A%A8%E6%97%B6%E9%97%B4%E7%9A%84%E5%89%8D%E6%8F%90%E4%B8%8B%E6%88%90%E7%BB%A9%E5%90%8D%E5%88%97%E5%89%8D%E8%8C%85/</link>
      <pubDate>Tue, 16 May 2023 15:34:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%80%8E%E4%B9%88%E6%89%8D%E8%83%BD%E8%AE%A9%E5%AD%A9%E5%AD%90%E5%9C%A8%E4%B8%8D%E7%89%BA%E7%89%B2%E7%9D%A1%E7%9C%A0%E5%92%8C%E8%BF%90%E5%8A%A8%E6%97%B6%E9%97%B4%E7%9A%84%E5%89%8D%E6%8F%90%E4%B8%8B%E6%88%90%E7%BB%A9%E5%90%8D%E5%88%97%E5%89%8D%E8%8C%85/</guid>
      
        <description>&lt;h1 id=&#34;问&#34;&gt;问&lt;/h1&gt;
&lt;p&gt;怎么才能让孩子在不牺牲睡眠和运动时间的前提下，成绩名列前茅？&lt;/p&gt;
&lt;h1 id=&#34;答&#34;&gt;答&lt;/h1&gt;
&lt;p&gt;很简单，帮孩子降低焦虑水平，提升其学习的效率。&lt;/p&gt;
&lt;p&gt;一个低年级小学生做作业过程，真的可以把亲妈气炸：40分钟的作业时间里有30分钟是在做各种准备工作，削铅笔，喝开水，屁股痒再挠一挠，摸摸自己的小玩具，再看看窗外的风景……&lt;/p&gt;
&lt;p&gt;面对这个现象，大人常见的做法当然就是严厉斥责，督促孩子抓紧时间。但这种做法只会让孩子的学习效率越来越低。&lt;/p&gt;
&lt;p&gt;其实啊，孩子的这种模式在很多成年人身上也常见，只是我们磨皮擦痒的手段变成了手机之类的东西而已。随便到一个公司去抓一群员工来问：“你们有谁在写年终报告时没有玩过手机？”估计有80%的人都会心虚地低下头。&lt;/p&gt;
&lt;p&gt;这个现象也不难理解，人在焦虑的时候注意力很难集中，就会出现这种行为模式。&lt;/p&gt;
&lt;p&gt;那孩子的焦虑点在哪呢？他们的很多知识都是不成体系的，面对一些新的作业练习时，那种巨大的不熟悉感就会让人很烦躁。这个时候如果家长再去责骂孩子，只会导致焦躁感升级，而且一旦孩子觉得自己是被逼着在做作业，那种烦躁感还会再提升一个等级。&lt;/p&gt;
&lt;p&gt;但很多家长无法理解这个问题，他们只能观察到孩子“越骂越皮”，所以一种常见的误区就会广泛流传于老师和家长之间——孩子的学习习惯一开始就要养好，否则后面很难办！&lt;/p&gt;
&lt;p&gt;本来孩子就很无助焦躁了，这下好了，家长听风就是雨，觉得要是一开始孩子就没管教好，就已经输在起跑线上了，于是比孩子还焦躁。不辅导功课时还能母慈子孝 ，一辅导功课就鸡飞狗跳。&lt;/p&gt;
&lt;p&gt;要知道，像焦躁这种负面情绪是会传染的，而且由于长期难以集中注意力，孩子会觉得学习是一件非常困难而且耗能的事情，反而越发的对学习感到畏惧起来。所以那些试图赢在起跑线上的家长，通常最后输得尤其的惨。&lt;/p&gt;
&lt;p&gt;其实要提升孩子的学习效率非常简单——不要试图改造孩子，而是陪着孩子一起去应对困难，在孩子感受到压力的时候，去安抚孩子、鼓励孩子。有条件的家庭甚至可以专门请作业托管老师或者家教老师。&lt;/p&gt;
&lt;p&gt;有兴趣的朋友可以观察一个现象，看看在陪着孩子做作业时，孩子会有什么反应？&lt;/p&gt;
&lt;p&gt;他们通常会做出一道题，就想要抬起头来看看家长。&lt;/p&gt;
&lt;p&gt;其实这个现象说明孩子急于想要获得反馈——我到底做对了没有啊，要是我做错了，妈妈会有什么反应呀？&lt;/p&gt;
&lt;p&gt;对幼年的孩子来说，什么为了中华之崛起而读书，为了出人头地而读书，统统都是扯淡，几乎所有的孩子都是“为了大人的快乐而读书”，所以他们会特别在意权威对自己的看法。&lt;/p&gt;
&lt;p&gt;我们很多家长非常害怕承认这一点，似乎要是孩子是为了家长而读书，那读书就失去神圣的意义了，孩子就不会用功了，所以必须要给孩子树立一些伟大理想，孩子才会有出息。&lt;/p&gt;
&lt;p&gt;拜托有这种想法的家长睁开眼睛好好看看吧！孩子如果某科成绩好，十有八九是因为喜欢那个任课老师。孩子又不知道什么叫中华之崛起，他们连历史课都还没开始上呢！红色电影看了一堆的孩子，也只会认为“打死了日本鬼子就崛起了”，这跟读书有啥关系？&lt;/p&gt;
&lt;p&gt;而家长一直强调“读书是你的事”，这其实是在把孩子往外推，让孩子一个人到知识的海洋里去冲浪。只是孩子看到的这片海洋完全没有大人描述的那么美好，反而到处都是狂风暴雨。&lt;/p&gt;
&lt;p&gt;所以啊，真的没有什么高深的技巧，孩子的学习效率能否提升，拼的就是家长的认知水平和耐心。要是家长自己耐心不够，那就请家教，或者找托管，让专业人士来帮助自己完成辅导工作，自己只需要跟在后面鼓励孩子，在孩子受到挫败的时候抱抱孩子，在孩子取得成绩的时候亲亲孩子。&lt;/p&gt;
&lt;p&gt;当孩子完成了几年的基础教育，打好了基础后，他们对新知识的恐惧也会随之下降，这个时候才会有真正的学习自主性被培养出来。&lt;/p&gt;
&lt;p&gt;这样培养出来的孩子不一定就能稳定地“名列前茅”，但他们会有真正的自信产生。他们知道自己可以承受失败的后果，可以重新对目标发起挑战，没有人会逼迫他们，没有人会责怪他们，如果再次失败受伤，还有人会帮自己疗伤。&lt;/p&gt;
&lt;p&gt;耗子敢挑衅猫，一定是因为身后有洞，而不是打赢猫后可以回家吹牛逼。&lt;/p&gt;
&lt;h2 id=&#34;更新一下&#34;&gt;更新一下&lt;/h2&gt;
&lt;p&gt;光说道理很难消化和理解，分享一个案例来解析孩子和大人的心态是如何产生矛盾的吧。&lt;/p&gt;
&lt;p&gt;小明是一个二年级的小学生，马上面临要期末考试了。老师为了让家长监督孩子复习，在钉钉群里发布了一个作业，要求只有这么一句话：&lt;/p&gt;
&lt;p&gt;“请家长在周末辅导孩子，根据书本内容完成一张思维导图。具体要求已经告知孩子。”&lt;/p&gt;
&lt;p&gt;周末，小明的妈妈正在忙着洗衣做饭，爸爸加班未归。于是妈妈要求小明自己先去把思维导图的作业做了。&lt;/p&gt;
&lt;p&gt;一个小时以后，当妈妈洗完衣服走进书房，发现小明正在看漫画书，作业一个字都没有动。顿时火冒三丈，厉声质问小明：“我刚才是怎么跟你说的？一天到晚就知道玩！妈妈早上起来到现在忙得连板凳都没挨一下，你自己去看那一大盆衣服是谁的？全是你的！你怎么这么不听话！？”&lt;/p&gt;
&lt;p&gt;妈妈越说越气，突然抢过孩子的漫画书一把撕成了两半。&lt;/p&gt;
&lt;p&gt;小明低着头不说话，拿着笔迟迟不肯写，不停抠着手里的橡皮擦。&lt;/p&gt;
&lt;p&gt;妈妈急了，拿出了小明的课本指着上面的目录对小明吼道：“你怎么这么笨，照着书把目录抄下来，这很难吗？这么简单的事情都不会做，一天到晚就知道玩！”&lt;/p&gt;
&lt;p&gt;小明迫于压力，开始勉强做了起来，但动作依然很慢，经常在一些奇怪的地方停顿，比如一句话写了一半就不写了。妈妈为此又是一顿催促，情急之下还拍了小明脑瓜子几下：“快点写啊，抄都不会抄吗？你怎么这么笨？”&lt;/p&gt;
&lt;p&gt;在煎熬了半小时以后，小明终于完成了作业，妈妈也长舒一口气，后悔于刚才对孩子太凶了，于是语气稍微缓和地对小明说：“你看吧，作业做完再去玩哪里不好了？下次不能这样了，知道吗？走，去吃饭！”&lt;/p&gt;
&lt;p&gt;但是小明并没有动，而是突然哇哇哭了起来，并一边哭一边摇头：“我不想吃！”&lt;/p&gt;
&lt;p&gt;这时妈妈急了：“你说什么呢？快点吃完饭下午还要去练画画！”&lt;/p&gt;
&lt;p&gt;小明：“我今后不想去画画了。”&lt;/p&gt;
&lt;p&gt;妈妈更急了：“这画画不是你自己选的吗？说不去就不去，男子汉大丈夫怎么能言而无信！你太任性了！”&lt;/p&gt;
&lt;p&gt;于是小明又挨了一顿打……而打完小明的妈妈也气得直哭，结果这时爸爸回来了，于是妈妈开始向爸爸发泄怒火：“一天到晚就知道瞎忙，钱也没赚到几个，孩子也不管，难道这个家是我一个人的吗？我上辈子是做了什么孽，摊上你们这两个货！”&lt;/p&gt;
&lt;h2 id=&#34;案例解析&#34;&gt;案例解析&lt;/h2&gt;
&lt;p&gt;其实，如果妈妈有读心术的话，就会发现孩子并不是贪玩才不做作业的，但思维导图这种东西，一个二年级的小朋友咋理解得到？他们只能死记硬背老师的要求，但遗憾的是没有哪个小朋友能够完全背下老师的要求，所以当小明在面对作业的时候，就会产生两种认知：&lt;/p&gt;
&lt;p&gt;“老师的要求很多，这很难！”&lt;/p&gt;
&lt;p&gt;“如果我不能完成那些要求，老师就不会满意，我会挨骂。”&lt;/p&gt;
&lt;p&gt;这两种情绪都是夹杂着焦虑和害怕的，还记得我们前面说的很多人写工作总结时总是忍不住耍手机吗？对，当我们没有办法知道权威的明确要求，又渴望得到认可时，就特别害怕自己做得不够好，于是压力就特别大，不自觉地想要搞点娱乐活动来缓解情绪。&lt;/p&gt;
&lt;p&gt;结果这个时候妈妈冲进来了，小明知道自己被抓了个现行，情绪更加紧张了。当妈妈询问他怎么就不做作业时，他的想法：&lt;/p&gt;
&lt;p&gt;“如果我直接告诉妈妈我不知道老师的要求是什么，她会骂我上课不听讲！”&lt;/p&gt;
&lt;p&gt;所以就憋住没法解释，也没法求助，更不可能跟妈妈商量该怎么做。即使妈妈简单告诉了画图要领，孩子依然是有些忐忑的，他会觉得：“这个要求过于简单，跟老师说的那种感觉不一样，老师可能不会满意的！”&lt;/p&gt;
&lt;p&gt;于是还是动不起来。&lt;/p&gt;
&lt;p&gt;小明后来迫于压力边做边停顿，其实也是因为随着思维导图的不断形成，小明的疑虑也越来越大，他会不断想：“真的这样就可以了吗？感觉不对劲啊！”但是回头看看妈妈，又感觉自己没有退路。&lt;/p&gt;
&lt;p&gt;最终，在妈妈的强压下，勉强完成了作业，整整半个小时都在直面老师和妈妈的压力，小明再也绷不住了。而且因为紧张时血糖升高，他也感觉没有胃口，甚至有些反胃，所以他本能地告诉妈妈：“不想吃饭。”&lt;/p&gt;
&lt;p&gt;但妈妈为此感到压力，于是继续用画画来施压——你不吃，就会耽误下午的画画。这种做法不仅没有缓解小明的情绪，反而让小明压力更大了，于是他只有本能地想要逃，所以才会说出：“今后我再也不想画画。”这种话，他不能将不满发泄给作业、老师和妈妈，那就只能攻击画画了。其实我们很多成年人也都经常说：“我真是再也不想干这个工作了。”但又有几个人是真的分不清轻重呢？&lt;/p&gt;
&lt;p&gt;遗憾的是小明的反应让妈妈信以为真，于是接着施压，甚至给小明强调她为小明付出的牺牲，希望小明能因为愧疚而改变主意，结果原来的压力情绪再叠加上愧疚感，反而导致小明压力更大了。&lt;/p&gt;
&lt;p&gt;随后，妈妈进一步情绪崩溃，不满的情绪迁怒到了刚回家的爸爸身上，夫妻之间免不了又是一顿争吵，孩子在这个时候会感觉是因为自己的无能而导致的这一切。一旦这种信念一形成，他下次再面对作业时的压力会更大——因为如果自己太笨，导致父母吵架，最后都没人爱自己了，自己就死定了。&lt;/p&gt;
&lt;p&gt;这得有多大的压力？虽然孩子无法用语言来表述这种认知，但那种“会被抛弃”的感觉是非常真实的，甚至基于这种感觉形成的行为习惯可以被带到成年以后。&lt;/p&gt;
&lt;p&gt;这就是为什么孩子越责骂和讲道理，就越皮的心理反应过程。&lt;/p&gt;
&lt;p&gt;其实，在小明没有好好做作业的时候，妈妈能够平常心接纳这个事实，并且耐心询问小明在担心些什么，并不断告诉小明：“先试一试，妈妈陪着你改。”小明扭捏一会后是可以开始动起来的。&lt;/p&gt;
&lt;p&gt;很多家庭练习靠孩子一个人是没有办法独立完成的，只有当家长把自己视为孩子的协作者，而不是监督者，耐心去感受孩子的每一寸焦虑，不要把孩子说的一些情绪性的话语当真，辅导孩子才不会变得那么可怕。&lt;/p&gt;
&lt;p&gt;最终还是那句话，如果家长自己不想改变，那至少找个专业人士来帮自己辅导，把压力交给专业人士来扛。千万不要以为自己已经洞悉一切，本着“人性本恶”的原则不停用道德大棒击打孩子，这种做法是真的容易造成心理创伤的。&lt;/p&gt;
&lt;h2 id=&#34;再更&#34;&gt;再更&lt;/h2&gt;
&lt;p&gt;看到有多条评论都是关于“到底要不要陪孩子做作业”，我的看法：需不需要陪，得看孩子的具体需求，有时候他们需要，有时候他们不需要，这是灵活处理的。&lt;/p&gt;
&lt;p&gt;通常过于困难的任务会导致一些紧张害怕的情绪，这个时候有人陪着一起面对可以降低压力，提升信心。但太简单的任务，孩子不喜欢有人看着自己做。你想呀，连大人在做太简单的任务时都忍不住想要摸个鱼，如果孩子发现身后有大人在监督自己，摸鱼都不方便，这就太别扭了，反而会导致精力更加不集中。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>竞业协议有什么办法破解吗？</title>
      <link>http://coder.amazingdemo.top/post/%E7%AB%9E%E4%B8%9A%E5%8D%8F%E8%AE%AE%E6%9C%89%E4%BB%80%E4%B9%88%E5%8A%9E%E6%B3%95%E7%A0%B4%E8%A7%A3%E5%90%97/</link>
      <pubDate>Sun, 14 May 2023 17:24:42 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%AB%9E%E4%B8%9A%E5%8D%8F%E8%AE%AE%E6%9C%89%E4%BB%80%E4%B9%88%E5%8A%9E%E6%B3%95%E7%A0%B4%E8%A7%A3%E5%90%97/</guid>
      
        <description>&lt;h1 id=&#34;问&#34;&gt;问&lt;/h1&gt;
&lt;p&gt;竞业协议有什么办法破解吗？&lt;/p&gt;
&lt;h1 id=&#34;答&#34;&gt;答&lt;/h1&gt;
&lt;p&gt;据说互联网大厂疯狂挖人的时候，早就把招式都试过了，一方面自然是想方设法上竞业，另一方面大家都疯狂的挖人，那你想挖的肯定也背了竞业，你也要想办法给员工规避竞业。&lt;/p&gt;
&lt;p&gt;首先，竞业协议再怎么仔细，你不可能预见到员工离职以后新成立的公司对吧，所以这些大厂会不停的成立新的公司，反正现在成立公司又不需要验资了，就是壳公司走账发工资就行了。&lt;/p&gt;
&lt;p&gt;然后竞业协议你不可能把所有的行业都ban了吧，那就成禁止就业协议了，总有你允许的行业，一把互联网公司无非就是生活啊金融啊软件开发啊之类，我成立一个农业研究院你管不着吧？&lt;/p&gt;
&lt;p&gt;你查社保，没问题，他就是我农业研究院的研究员，你没辙，不过，他人还是在我 x 大厂做软开。等过了竞业期，再用真实身份转回公司。&lt;/p&gt;
&lt;p&gt;当然，在实际工作的 xx 互联网公司，他会是化名工作，oa 里面只能看到英文名或者花名，真名只有hr 知道，对外联络的手机号用老婆的或者爹妈的身份证实名申请一个新的就行了，这样他就有两个身份：一个张三（真名），xx 农业研究院研究员（只是交社保发工资）；一个张三丰（花名），实际工作岗位xx 大厂工程师，对外联系手机号（老婆实名新申请）。&lt;/p&gt;
&lt;p&gt;这种公司基本都是封闭办公区，外人无预约禁入，即使有预约也是去单独的会客区，不能进办公区，只要他不浪，主动在社交媒体自首，基本没辙。&lt;/p&gt;
&lt;p&gt;当然，为了破解这个办法，很多互联网公司会采用给员工寄快递包裹或者蹲守公司入口的方式钓鱼取证。&lt;/p&gt;
&lt;p&gt;不过总有见招拆招：比如公司前台一律不代收包裹（员工自己也要有预防，比如自己网购的地址不留公司地址，这样直接寄到公司的快递肯定有鬼一律不收，公司可以在办公区外的公共区域设置一个代收点），外人来访必须提前和本人预约，前台没有在系统里查到预约的拜访一律查无此人，再比如分出一个办公区“租给“农业研究院挂牌，内部再打通，上班的时候有竞业的人刷卡进农业研究院的门，再通过内部通道到实际工位，你在门蹲守到了也没辙。&lt;/p&gt;
&lt;p&gt;当然就是最后终极招数了：冒充猎头钓鱼招聘，你想跳槽，那你肯定不能用农业研究员的身份，你肯定要主动招自己的真实岗位，这就是赌人性了，如果你动了这个心思，那现公司也帮不了你了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>我是女生，为什么我觉得看了咪蒙的文章觉得很励志呢？</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%A7%89%E5%BE%97%E7%9C%8B%E4%BA%86%E5%92%AA%E8%92%99%E7%9A%84%E6%96%87%E7%AB%A0%E8%A7%89%E5%BE%97%E5%BE%88%E5%8A%B1%E5%BF%97%E5%91%A2/</link>
      <pubDate>Wed, 10 May 2023 15:57:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%A7%89%E5%BE%97%E7%9C%8B%E4%BA%86%E5%92%AA%E8%92%99%E7%9A%84%E6%96%87%E7%AB%A0%E8%A7%89%E5%BE%97%E5%BE%88%E5%8A%B1%E5%BF%97%E5%91%A2/</guid>
      
        <description>&lt;h1 id=&#34;问&#34;&gt;问&lt;/h1&gt;
&lt;p&gt;我是女生，为什么我觉得看了咪蒙的文章觉得很励志呢？&lt;/p&gt;
&lt;h1 id=&#34;答&#34;&gt;答&lt;/h1&gt;
&lt;p&gt;接下来我说的话可能挺狠，而且不好听，但都是实话。&lt;/p&gt;
&lt;p&gt;有一种写作方式叫穷人乐，盛行于上世纪七十年代至今。&lt;/p&gt;
&lt;p&gt;咱们先不说咪蒙，咱们假设一个人物A：&lt;/p&gt;
&lt;p&gt;这个人物叫小A，你只是上网搜到这个人的，对于这个人的一切都一无所知。&lt;/p&gt;
&lt;p&gt;你发现小A被几篇“谢顶的男人都很温柔”“头发多少并不能丝毫影响一个人的价值”“致25岁以后还有头发的人：你不趁年轻起早贪黑奋斗吗？”感动的跟孙子似得。&lt;/p&gt;
&lt;p&gt;你估计一下这个人现在是个什么发型？&lt;/p&gt;
&lt;p&gt;穷人乐狠就狠在这一点上，其定点收割能力无出其右。&lt;/p&gt;
&lt;p&gt;想写一篇受穷人欢迎的文章怎么写？：《有钱人只能坐在奔驰里哭》&lt;/p&gt;
&lt;p&gt;在自视甚高的穷人眼里，所有有钱人的精神生活都极度空虚，没有例外。&lt;/p&gt;
&lt;p&gt;想写一篇受低学历人士欢迎的文章怎么写？还记得那篇一个群的物理博士推测一滴水从多高落下来能砸死人，然后一个屌丝强力打脸“你们没淋过雨吗”那个故事吗？在屌丝眼里，所有博士生都必须是学习学傻了的书呆子，常识一定低于正常人，没有例外。&lt;/p&gt;
&lt;p&gt;他们未必真的这么想，而仅仅是不这么想根本就没法活，所以逼着自己这么认为的。&lt;/p&gt;
&lt;p&gt;那么，想写一篇受自视甚高又对伴侣要求高，但自身水平低下的人欢迎的文章怎么写？&lt;/p&gt;
&lt;p&gt;这其实是一个极度庞大的群体，而咪蒙给了我们最好的解释。&lt;/p&gt;
&lt;p&gt;只要告诉看的人其实你非常优秀，是外界环境的影响耽误了你就可以了。同时解决了俩问题：即自视甚高和自己实际水准直接的巨大落差感，并合理解释自己长期以来混的不如意的原因。&lt;/p&gt;
&lt;p&gt;然后就造成了最恶劣的影响：这群自我催眠的人真的就自认为无比优秀了。&lt;/p&gt;
&lt;p&gt;真狠，我能想象出对仇家最损的报复，也不过如此了。&lt;/p&gt;
&lt;p&gt;就好比一个钱包只有五六块钱的穷人，其实没什么惨的。&lt;/p&gt;
&lt;p&gt;咪蒙让这个穷人相信自己钱包里有五六万，然后这个穷人乐悠悠的去豪华酒店了。&lt;/p&gt;
&lt;p&gt;这名粉丝一边胡吃海塞，一边念着咪蒙的好。&lt;/p&gt;
&lt;p&gt;是啊，直到结账之前，他一定都是无比快乐的。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么我的母亲从来都不快乐，而且总是轻而易举的夺走全家人的快乐？</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84%E6%AF%8D%E4%BA%B2%E4%BB%8E%E6%9D%A5%E9%83%BD%E4%B8%8D%E5%BF%AB%E4%B9%90%E8%80%8C%E4%B8%94%E6%80%BB%E6%98%AF%E8%BD%BB%E8%80%8C%E6%98%93%E4%B8%BE%E7%9A%84%E5%A4%BA%E8%B5%B0%E5%85%A8%E5%AE%B6%E4%BA%BA%E7%9A%84%E5%BF%AB%E4%B9%90/</link>
      <pubDate>Mon, 01 May 2023 15:57:39 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84%E6%AF%8D%E4%BA%B2%E4%BB%8E%E6%9D%A5%E9%83%BD%E4%B8%8D%E5%BF%AB%E4%B9%90%E8%80%8C%E4%B8%94%E6%80%BB%E6%98%AF%E8%BD%BB%E8%80%8C%E6%98%93%E4%B8%BE%E7%9A%84%E5%A4%BA%E8%B5%B0%E5%85%A8%E5%AE%B6%E4%BA%BA%E7%9A%84%E5%BF%AB%E4%B9%90/</guid>
      
        <description>&lt;h1 id=&#34;问&#34;&gt;问&lt;/h1&gt;
&lt;p&gt;为什么我的母亲从来都不快乐，而且总是轻而易举的夺走全家人的快乐？&lt;/p&gt;
&lt;p&gt;就连我考上大学她也哭丧着脸，从来没见她开怀大笑过，我跟她分享快乐她也冷脸相对……对什么事情不满从来都直接不会提出来，而且憋着，然后抱怨……经常大年三十晚上翻旧账，搞得大家一起哭……我服了&lt;/p&gt;
&lt;h1 id=&#34;答&#34;&gt;答&lt;/h1&gt;
&lt;p&gt;这个问题其实很有意思，&lt;/p&gt;
&lt;p&gt;你或许曾注意到过这样一个现象：&lt;/p&gt;
&lt;p&gt;老一辈的人，似乎特别喜欢“自虐”，然后用“自虐”的成果来彰显她的伟大，最后用她的“伟大”来指责你。&lt;/p&gt;
&lt;p&gt;比如同样是节约用水，&lt;/p&gt;
&lt;p&gt;如果是正在看这篇文章的你，会做出的行为无非是避免浪费、及时关水，宗旨是“该用就用，该省就省”，&lt;/p&gt;
&lt;p&gt;但我亲眼见过一个老人的用水流程：&lt;/p&gt;
&lt;p&gt;她把洗脸刷牙后的水用来洗菜，洗完菜的水用来洗衣服，洗完衣服的水用于拖地，剩下的水用来浇花&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;p&gt;最后，整个屋子里都是污水的味道，花盆里的植物也因为长期的碱性水浇灌而枯萎，&lt;/p&gt;
&lt;p&gt;这时，老人的孙子放学回来了，或许是夏天太热，小男孩回家后第一件事就是冲进厕所，打开水龙头放水洗脸，用水量瞬间超过老人一上午省下的量，&lt;/p&gt;
&lt;p&gt;老人痛心疾首，跳着脚指责孩子铺张浪费。&lt;/p&gt;
&lt;p&gt;这个现象其实很奇怪，因为这位老人的家庭无论是在过去还是现在，都不能算贫困，至少不至于为了几分钱的水费而如此节省，&lt;/p&gt;
&lt;p&gt;后来，我与老人子女闲聊时无意中了解到：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;老人以前没这么节省，&lt;/li&gt;
&lt;li&gt;老人独居时的那段时间也没这么节省，&lt;/li&gt;
&lt;li&gt;自从子女各自成家、收入稳定，把她接到儿子的家里住后，她反而变得“自虐”般节省，并且比以前更热衷于指责他们铺张浪费、不会过日子。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根据子女的描述，老人变得“自虐”般节省的全程复盘如下：&lt;/p&gt;
&lt;p&gt;老人的子女均已婚，为了方便照顾，老人住在儿子家。&lt;/p&gt;
&lt;p&gt;最初，儿子一家确实比较喜欢浪费水，老人在此时是正常用水，并未过度节省，&lt;/p&gt;
&lt;p&gt;于是，正常用水的老人指责爱浪费水的子女“不会过日子”，&lt;/p&gt;
&lt;p&gt;儿子觉得很有道理，这样浪费确实不太好，于是正常用水，不再浪费；&lt;/p&gt;
&lt;p&gt;老人那段时间总是闷闷不乐，但一段时间之后，老人开始极其节省用水，然后在儿子一家正常用水的时候，指责他们铺张浪费。&lt;/p&gt;
&lt;p&gt;聪明的你很可能已经发现了一个有趣的矛盾：&lt;/p&gt;
&lt;p&gt;这件事表面上看起来是“老人指责子女浪费”，但在儿子一家改正后，他们依然会被指责，&lt;/p&gt;
&lt;p&gt;这似乎意味着：&lt;/p&gt;
&lt;p&gt;子女浪不浪费根本无所谓，老人想要的只是“指责”而已。&lt;/p&gt;
&lt;p&gt;当子女确实在浪费时，老人就光明正大地指责他们浪费；&lt;/p&gt;
&lt;p&gt;当子女只是在正常用水时，老人就降低自己的用水标准，用“自虐”般的节省把子女正常的用水衬托得很“浪费”，再光明正大地指责子女浪费。&lt;/p&gt;
&lt;p&gt;我们有理由相信：就算这家人把用水标准也降到了“自虐”的地步，老人还是会另辟蹊径，从其他方面来指责他们“不会过日子”。&lt;/p&gt;
&lt;p&gt;题主提问：为什么我的母亲从来都不快乐，而且热衷于夺走别人的快乐？&lt;/p&gt;
&lt;p&gt;答案其实特别简单：&lt;/p&gt;
&lt;p&gt;他的母亲并非是故意要夺走自己和家人的快乐，事实上，她这样做与快乐或不快乐根本无关，她只是想通过一些行为获得内心的满足感和掌控感。&lt;/p&gt;
&lt;p&gt;我们不妨剖析一下这种有趣的心理，&lt;/p&gt;
&lt;p&gt;在上文中提到的案例里，那位老人是典型的“强势者”，在过去的十几年里，她是家庭中的主要收入来源与家务承担者，同时也享受着家庭中最高的话语权，&lt;/p&gt;
&lt;p&gt;但在老人退休没有收入、儿女成家立业后，她发现自己反而成了“寄人篱下”的那个人，地位落到家庭最底层，话语权也一落千丈，&lt;/p&gt;
&lt;p&gt;经过了十几年，她已经习惯处于家庭中的最高地位，而现在地位一落千丈，让她根本无法适应。&lt;/p&gt;
&lt;p&gt;她不甘心、无所适从，她想要做出改变，但此时的她已经退休，无法通过“创造营收”的方式来提高地位了，&lt;/p&gt;
&lt;p&gt;她只能通过降低自身生活标准，来表现出一种“节省”“持家”的美德，从另一个角度显得自己比其他家庭成员都优秀，进而提升地位，缓解内心的焦虑，&lt;/p&gt;
&lt;p&gt;为了避免家人没有注意到她的努力，她会下意识地通过“指责”来提醒家人，她虽然嘴上说的是“你们太浪费了，真是不会过日子”，&lt;/p&gt;
&lt;p&gt;但潜台词其实是：“你们快看，我是多么的勤俭持家啊！”&lt;/p&gt;
&lt;p&gt;她希望子女夸她，&lt;/p&gt;
&lt;p&gt;她不想让自己看起来这么没用。&lt;/p&gt;
&lt;p&gt;但对于我们中的大部分人而言，是根本get不到这一点的，我们只会觉得她像在自虐一样节省。&lt;/p&gt;
&lt;p&gt;于是，一个恶性循环就形成了：&lt;/p&gt;
&lt;p&gt;老人为自身低家庭地位而焦虑，所以开始节省→子女认为老人过于节省，指责老人→老人受到指责，感觉家庭地位更低了，于是更节省→受到子女更严苛的指责&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;p&gt;当然，“节省”可以替换成任意家庭问题，比如题主提到的“快乐”，&lt;/p&gt;
&lt;p&gt;题主考上了大学，父亲做生意很成功，按理来说是应该得到夸奖，但由于母亲不想让自己显得很没用，于是通过打击家人来彰显自己的聪明，&lt;/p&gt;
&lt;p&gt;比如母亲对题主说：“考上大学了又怎么样？有好多人进了大学，还不是照样因为挂科毕不了业”，&lt;/p&gt;
&lt;p&gt;如果题主运气不好，真的不小心挂科了，她的母亲就可以理直气壮地说：“你看，你看！我就说你要挂科吧，你还不信我！”&lt;/p&gt;
&lt;p&gt;母亲通过实现自己的“预言”，显得自己很聪明，进而缓解了自己内心的“无能”焦虑。&lt;/p&gt;
&lt;p&gt;“预言”的成功实现，可以满足母亲的心理需求，但并不是每个“预言”都能成功实现，&lt;/p&gt;
&lt;p&gt;所以她会把生活中所有包含不确定性因素的事都预言一遍，在偶尔运气好而说中某件事后，就会拿出来大肆宣扬：“你看，我当初是怎么说的，我让你别这么做你偏不听&amp;hellip;&amp;hellip;”&lt;/p&gt;
&lt;p&gt;她本质上其实是希望你夸她，让你认为她很聪明，&lt;/p&gt;
&lt;p&gt;但我们中的绝大多数人根本get不到这层意思，我们只会觉得她很烦，总是打击人，&lt;/p&gt;
&lt;p&gt;于是，一个恶性循环就形成了：母亲因为感到自己的无能而焦虑→通过“预言”家人会失败，来彰显自己的聪明，进而缓解这种“无能焦虑”→家人觉得很烦，指责母亲→母亲更焦虑了&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;p&gt;不过，我们还可以把这个心理机制进行更深层次的剖析：&lt;/p&gt;
&lt;p&gt;既然“预言”可以让她自我感觉良好，那她为什么一定要做“失败预言”呢？&lt;/p&gt;
&lt;p&gt;她完全可以做“成功预言”呀，&lt;/p&gt;
&lt;p&gt;比如在题主高考时，她可以拉着题主，很认真地告诉他：“这次高考，你一定能成功考上大学，妈妈相信你”&lt;/p&gt;
&lt;p&gt;然后题主果真稳定发挥，考上大学。题主考完试后还会高兴地抱住她，说谢谢妈妈的鼓励让他充满信心，&lt;/p&gt;
&lt;p&gt;题主收获了鼓励和信心，母亲收获了内心的满足感，双赢，&lt;/p&gt;
&lt;p&gt;可母亲为何偏偏要做“失败预言”呢？&lt;/p&gt;
&lt;p&gt;答案其实很简单：&lt;/p&gt;
&lt;p&gt;因为她的内心深处，藏着很深刻的自卑。&lt;/p&gt;
&lt;p&gt;一个人越缺乏某种事物，就越会对那种事物产生超出寻常的渴求。她迫切地希望家人认为她“不差劲”，但实际上，真正认为她很差劲的人，恰恰是她自己，&lt;/p&gt;
&lt;p&gt;她很多看似荒谬的行为，其实都是被一种“自卑心理”作为底层逻辑而推动的，&lt;/p&gt;
&lt;p&gt;因为自卑，她认为自己不配享受，&lt;/p&gt;
&lt;p&gt;因为自卑，她认为自己和自己的家人不配成功，&lt;/p&gt;
&lt;p&gt;因为自卑，所以她打心眼里认为自己与一切美好的事物都不配，&lt;/p&gt;
&lt;p&gt;她的认知是：自己不配享受、不配成功、不配拥有美好的事物，&lt;/p&gt;
&lt;p&gt;所以她“自虐”般节约用水，因为她认为自己不配享受，&lt;/p&gt;
&lt;p&gt;她“诅咒”般认为孩子考不上大学，因为她认为自己和家人不配成功，&lt;/p&gt;
&lt;p&gt;她会对所有让家人快乐的事进行全盘否定，因为她认为自己与一切美好的事物都不配。&lt;/p&gt;
&lt;p&gt;但现实是：家里实现了用水自由，孩子考上了大学，家里偶尔会发生好事，&lt;/p&gt;
&lt;p&gt;现实与她的认知有巨大的差异，所以她会通过不断“自虐”和否定来打击家人，从而达到现实和自我认知之间的平衡。&lt;/p&gt;
&lt;p&gt;所有看似很荒谬的行为，其实都是被“自卑心理”驱动而产生。&lt;/p&gt;
&lt;p&gt;这个问题下，有答主讽刺说“中国人似乎跟快乐有仇”，我认为说的很有道理，但同时也觉得这只是一个表面现象，&lt;/p&gt;
&lt;p&gt;表象的背后所隐藏的心理学机制，或许远比它表现出来的部分更有意思。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>黑客传说</title>
      <link>http://coder.amazingdemo.top/post/%E9%BB%91%E5%AE%A2%E4%BC%A0%E8%AF%B4/</link>
      <pubDate>Mon, 24 Apr 2023 12:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%BB%91%E5%AE%A2%E4%BC%A0%E8%AF%B4/</guid>
      
        <description>&lt;p&gt;今年，Linux 迎来了它的20岁生日。据称从4月开始，全球各地都在筹划着如何庆祝这一生日。但到底哪一天才是这款史上惟一可与Windows比肩的电脑操作系统确切的 诞生之日，众说纷纭 ，有人说是1991年8月25日发明者本人李纳斯·托沃兹(Linus Torvalds)宣布项目启动那天，还有人认为应该是1991年10月5日Linux Kernel 0.02发布之日。“教父”本人在今年4月接受采访时倒是提出了4个有意义的日子：7月3日、8月25日、9月17日、10月5日，并且说，“哪天都可以”。看来，要给Linux一个不漏地过生日，得需要4个生日蛋糕！&lt;/p&gt;
&lt;p&gt;在1998年的奥斯卡颁奖典礼上，当詹姆斯·卡梅隆手握“最佳导演奖”的小金人，模仿《泰坦尼克》中的男主角杰克发出“我是世界之王”的宣言时，全世 界无不为之倾倒。影片中那被重新描绘过的“唯美”海难，使人们惊叹于卡氏的神奇。但浪漫的爱情桥段和震撼的视觉效果，也使人们忽略了另一个伟大事实：电影史上崭新的技术时代悄然来临。而这一时代，是由Linux开启的。&lt;/p&gt;
&lt;p&gt;对于这一切，李纳斯同样始料未及。尽管作为Linux的发明者，这位黑客出身的IT精英一直对这款操作系统信心十足。他可能并未想到，诞生7年来一直默默无闻的Linux，会以众星云集的奥斯卡颁奖礼作为其征服世界之旅的转折点。&lt;/p&gt;
&lt;p&gt;从那时开始，梦工厂、迪斯尼、皮克斯和维塔数码等公司都逐渐将各自的工作平台转向Linux，无数伟大的银幕经典因此横空出世。从《魔戒》到《金 刚》，从《哈利波特》到《霍比特人》，从《怪物史莱克》到《纳尼亚传奇》，再从《指环王》到《阿凡达》，Linux几乎以一己之力缔造了无数人梦中的玄幻 王国。也是从那时开始，Linux终将埋葬微软就开始成为江湖中著名的传说之一。&lt;/p&gt;
&lt;p&gt;与十几年前相比，今天的Linux依然是无数IT人士心中的图腾。苹果（NASDAQ：AAPL）与谷歌（NASDAQ：GOOG）都曾经严词批评微 软（NASDAQ：MSFT）的垄断，都曾以崇尚开源（开放源代码）的自由斗士形象示人。但多年来，只有Linux真正坚持向所有人公布其核心代码，尽管 在这个过程中，推动其发展的主要对象已经从自娱自乐的电脑黑客变成了追求利润的软件公司。&lt;/p&gt;
&lt;p&gt;与那时相比，今天的Linux依然小众，其市场份额只有1%，根本无法与微软动辄90%的市场份额相提并论。惟一的安慰来自Android，这款源自 Linux的操作系统在过去1年中独领风骚，不但令微软的Windows Phone望尘莫及，甚至还将苹果的iOS挑落马下：现在，每天激活的Android设备高达55万台；而仅仅3个月前，这一数字还仅为35万。&lt;/p&gt;
&lt;p&gt;而这一切早有伏笔，比尔·盖茨早在微软巅峰时期，就对Linux赞誉有加。&lt;/p&gt;
&lt;p&gt;就在卡梅隆扬名奥斯卡颁奖礼半年后，在一份名为《鬼节前夕》的秘密备忘录中，曾无视Linux的微软对这款操作系统大加褒奖，“Linux是最优秀的 Unix系统，它免费提供了几乎所有应用程序，其在个人设置、可用性、可靠性、扩展性和性能表现等方面均超过了Windows NT。而由于其开放性，Linux将超过其他所有操作系统。”在那一年的联邦反垄断诉讼中，微软的律师搬出了一整箱Linux以证明Windows 并没有垄断操作系统市场。&lt;/p&gt;
&lt;h1 id=&#34;linux是黑客留给世界的美好回忆&#34;&gt;Linux是黑客留给世界的美好回忆。&lt;/h1&gt;
&lt;p&gt;对大多数黑客而言，简洁优雅的程序和干净清晰的代码，抑或是某个强大实用的程序，都足以成为他们毕生得意之作。但1969年12月28日出生的芬兰人 李纳斯却选择了另一条道路，他不但重新定义了一流程序、代码和软件的标准，而且重新定义了黑客对人类的贡献——他将绚烂的效果和精简的资源、强大的能力和 自由的思想融为一体。仅此一项，已无愧传奇的声望。&lt;/p&gt;
&lt;h1 id=&#34;平淡的开场&#34;&gt;平淡的开场&lt;/h1&gt;
&lt;p&gt;对于芬兰很多为人熟知的传奇故事，1990年都是一个重要的转折点。&lt;/p&gt;
&lt;p&gt;这一年，最后一年征战F3的哈基宁勇夺英国F3冠军；在澳门格兰披治大赛最后一圈，他被当年的德国F3冠军舒马赫撞出赛道，痛失冠军，世界车坛随后长 达10年的“双雄会”自此开始。几乎同时，诺基亚（NYSE：NOK）的全球通技术进入最后攻关阶段。1年后，世界上首次全球通对话在诺基亚的 Radiolinja网络中完成，诺基亚征服世界的脚步已经无法遏制。&lt;/p&gt;
&lt;p&gt;这年的李纳斯刚刚拥有第一台完全属于自己的电脑，这使他不必再忍受学校机房漫长的等待，也可以学习安德鲁·塔南鲍姆（Andrew Tanenbaum）的著作《操作系统：设计与实现》——一本Minix操作指南。作为Unix的变种，彼时的Minix正凭借低廉的价格和简易的操作在 大学大行其道。&lt;/p&gt;
&lt;p&gt;“每个人都会有一本改变其一生的书籍，比如《圣经》或《资本论》。对我而言，那本红色的简装本教科书就充当了这样的角色。”李纳斯为自己的发现兴奋，但却不知道此时的塔南鲍姆正陷入窘境，因为他无法满足全球用户对于增加功能近乎永无止境的要求。&lt;/p&gt;
&lt;p&gt;李纳斯很快也意识到了这一点。于是，他开始编写自己的操作系统内核。他将其视为Minix的复制品，为此，他写信给Minix的一个新闻组，“我在编写一个操作系统。这是一个个人爱好，不会很大”。现在看来，当时的李纳斯完全低估了他作品的价值。&lt;/p&gt;
&lt;p&gt;仅仅两个月后，一个“千疮百孔但却碰巧可以使用”的磁盘驱动程序和一个小到不能再小的文件系统就诞生了，这就是第0.01版的Linux。尽管幼稚得近乎可笑，但李纳斯完全沉迷其中，他意识到，一个比Minix更好的操作系统即将诞生。&lt;/p&gt;
&lt;p&gt;两个月后的10月5日，Linux的第一个正式版本0.02版正式发布。这时的Linux已经可以运行C编译器，但还是什么现实的事情都做不了。因为李纳斯将全部精力都集中于系统内核，却没有开发任何应用程序，这使Linux看上去就像一个十足的黑客操作系统。&lt;/p&gt;
&lt;p&gt;李纳斯将操作系统上传至FTP，并公布了全部源代码。在USENET讨论区，李纳斯这样阐述其初衷：在新操作系统中，“人们可以自己编写驱动程序，可以随意修改操作系统以适应不同需求，可以尝试在Minix上运行所有程序，这是Minix从未有过的美好一天。”&lt;/p&gt;
&lt;p&gt;到1992年1月，尽管Linux的实际用户只有100人，但就是这100人，为Linux提供了至关重要的洗礼。这些黑客们借助电子邮件和新闻组不 断表达他们对这款操作系统的理解和批判，并将修改错误代码的补丁上传到了网上。李纳斯则亲自动手，将散落于互联网上的补丁拼凑起来，并继续确保任何人都可 以得到基本的Linux文件。&lt;/p&gt;
&lt;p&gt;1994年3月，李纳斯对外发布了Linux 1.0。这个终于完整起来的操作系统如同网络世界的“自由宣言”，将遍布全球的软件精英召唤在自己旗下。在这个版本操作系统所包含的数千个文件中，有一个 名为Credits的文件，里面详细列出了为Linux做出卓越贡献的100多名黑客的名字、地址以及职业，李纳斯用这种独特的方式向他的同行们表示敬意 与谢意。&lt;/p&gt;
&lt;p&gt;更重要的是，李纳斯从人们热烈的反馈中看到了完美操作系统的魅影，他决心将自由软件的理想进行到底&lt;/p&gt;
&lt;h1 id=&#34;庶民的胜利&#34;&gt;庶民的胜利&lt;/h1&gt;
&lt;p&gt;同为百年一遇的软件天才，李纳斯与盖茨宛如软件世界里的两极。盖茨中途辍学，虽然因此失去了哈佛学位，却一举成为全球首富。李纳斯则不同，为集中精力推进Linux，他用了整整7年时间才获得赫尔辛基大学的硕士学位，因为他从未视Linux为赚钱工具。&lt;/p&gt;
&lt;p&gt;这当然不是因为李纳斯家境富裕。李纳斯的祖父是一家报纸记者，叔叔供职于芬兰电视台，父亲效力于一家电台，母亲则以翻译报纸新闻为生。由于父母离异， 跟着妈妈的李纳斯一直生活拮据。在他记忆中，为贴补家用，母亲不得不经常抵押她惟一的投资——价值500美元的赫尔辛基电话公司股票。而在芬兰，只要拥有 一部该公司的电话就能拥有一张公司的股票。&lt;/p&gt;
&lt;p&gt;李纳斯是一个彻底的平民主义者。他从不买精装书，因为他相信精装书一定偏贵。他更喜欢好莱坞的大众娱乐片，而不是欧洲的艺术电影。这种价值观在他的事业上得到了更加淋漓尽致的展现：在投身Linux后，李纳斯一直致力于打破软件的垄断定价方式。&lt;/p&gt;
&lt;p&gt;“一个每月挣50美元的人，会为一个软件花250美元吗？如果花一点钱买非法拷贝，而把5个月的工资用于吃饱肚子，我一点不觉得他不道德。相反，去追 捕这种‘侵权者’是不道德的，甚至是愚蠢的。”李纳斯因此赋予了Linux一个全新的定义，“就Linux而言，那些想藉此赚大钱的人才是不道德的，不管 他在美国还是非洲，也不管程度如何。”&lt;/p&gt;
&lt;p&gt;李纳斯信奉自由软件的力量，而开放的思想则给Linux带来了巨大的回报。&lt;/p&gt;
&lt;p&gt;1992年，在Linux Kernel平台上工作的开发者只有100位，平台中的核心代码只有几万行。但到了2010年，在Linux Kernel平台上工作的开发者已经多达1000人，人员的背景也从最初的黑客扩散至更多的行业，平台中的核心代码则已经超过1400万行。&lt;/p&gt;
&lt;p&gt;1998年，全球前500台超级计算机中还只有1台运行Linux。但13年后的今天，Linux已经在这块市场上确立了绝对的优势：在全球前500台超级计算机中，有413台选用Linux。这些计算机遍布世界各地的多个行业，共同主宰着这个智慧的地球。&lt;/p&gt;
&lt;p&gt;尽管在很多人看来，“可自由扩散”总是近似等同于“缺乏支持”和“业余水平”，但Linux却是一个完全不同于其他的特例。从一开始，Linux就主 要流传于软件高手中间，并且很快就在全球范围内网罗了一大批职业和业余的技术专家，形成了一个数量庞大而且非常热心主动的支持者群体，他们几乎能够通过网 络即时响应和解决你的任何请求。比如Pentium II设计上的漏洞刚被发现，Linux就在所有操作系统中第一个提供了完整的解决方案。1997年，Linux支持者群体在众多的软件公司中一举胜出，荣获了美国《InfoWorld》杂志的最佳技术支持奖，而这一奖项原本是为商业公司设立的。&lt;/p&gt;
&lt;p&gt;人们惊叹于Linux的飞速成长，但很多人并不知道，最初的Linux远非今天的模样&lt;/p&gt;
&lt;h1 id=&#34;自由之精神&#34;&gt;自由之精神&lt;/h1&gt;
&lt;p&gt;Linux 1.0问世的时候，是按完全自由扩散版权进行扩散的。李纳斯要求所有的源代码必须公开，而且任何人均不得从Linux交易中获利。显然，他还记得在他还是个穷学生的时候，由于买不起Unix商业版而遭遇到的种种尴尬和苦恼。&lt;/p&gt;
&lt;p&gt;但半年后，李纳斯就意识到，这种纯粹的自由软件理想严重影响了Linux的传播，因为无法借助磁盘等形式传播，很多网络状况不好的地区根本无法使用Linux。这种规定也阻碍了商业公司参与Linux的开发和维护，这使Linux的稳定性天然地受到社会的质疑。&lt;/p&gt;
&lt;p&gt;就在李纳斯苦恼需要改变之时，另一个伟大的黑客——理查德·斯托曼（Richard Stallman）登场。&lt;/p&gt;
&lt;p&gt;1983年，斯托曼发起了“GNU（GNU’s Not Unix的递归缩写）”计划，其目的是创建一套完全自由的操作系统，以“重现软件界合作互助的团结精神”。斯托曼深信，包括软件在内的知识确实不应是“免 费的面包”，但却必须是“自由的思想”。为此，他以“著佐权”（copyleft）标准为范本拟定了一份通用公用版权协议(General Public License，GPL)。&lt;/p&gt;
&lt;p&gt;与强调个人版权但限制自由传播的著作权（copyright）不同，著佐权更强调公共版权和鼓励自由传播，它允许用户修改程序、复制软件和销售获利，但前提是必须保证自由思想的传递——修改后的全部源代码，也必须像它的来源一样免费向社会公开。&lt;/p&gt;
&lt;p&gt;GNU计划激发了软件界极大的热情，世界各地的软件奇才们纷纷参与其中。到1990年，GNU计划已经开发出包括文字编辑器Emacs、C语言编译器 GCC以及大部分UNIX系统程序库和工具在内的绝大多数软件，很多免费软件的水平甚至都已经超过了相应的付费版本。对斯托曼而言，GNU计划惟一的也是 “致命”的欠缺，就是操作系统内核。&lt;/p&gt;
&lt;p&gt;后面的故事情节顺理成章：在各自的领域奋战多年后，李纳斯率领Linux加盟斯托曼的GNU计划，这一“联姻”堪称是软件界的天作之合。&lt;/p&gt;
&lt;p&gt;这一转变对Linux至关重要，它促进了Linux在商用领域的繁荣。虽然软件可以免费下载得到，但几十块一套的CD更方便、更经济也更快速。这也大大刺激了程序员的积极性，因为这种许可方式不但保证了他们的工作成果可以得到更广泛的传播，还确保了这些成果不会被逐利之徒所利用，成为牟利的工具。&lt;/p&gt;
&lt;p&gt;Linux谱系之庞大，在世界软件史上前无古人，也几乎再难有来者。Ubuntu来自南非，openSUSE来自德国，Mandriva来自澳大利 亚，Mint来自爱尔兰，Fedora、PCLinuxOS、Gentoo、CentOS和Slackware来自美国，Debian的开发者则遍及全 球。这些操作系统各有所长，但全部秉承了Linux一贯的低价策略。&lt;/p&gt;
&lt;p&gt;Linux的开发主体也从黑客转向了商业软件公司。这些公司技术雄厚又善于市场运作，它们用更友好的图形化界面和更高效的开发工具磨光了纯粹自由软件粗糙不平的棱角，使Linux从网络黑客和业余爱好者自娱自乐的工具，变成了一个具有全球影响力的软件王国。&lt;/p&gt;
&lt;p&gt;与这些同样重要的是，GNU计划从制度上保证了Linux的平价策略。由于任何版本都可以在互联网免费得到，高价的软件几乎从未在Linux的世界里 出现过。迄今为止，Linux分销商的收入仍主要来自系统维护，但Linux稳定的表现则使这一部分利润同样难称丰厚。&lt;/p&gt;
&lt;p&gt;对于这段历史，李纳斯本人也颇为自得，“使Linux成为GPL的一员，是我一生中做过的最漂亮的一件事”。&lt;/p&gt;
&lt;h1 id=&#34;颠覆windows&#34;&gt;颠覆Windows？&lt;/h1&gt;
&lt;p&gt;1996年，美国一所大学准备换掉一套过时的NetWare Server，另外安装一套新的操作系统。当时的首选目标当然是Windows NT 4.0，然而人们没有料到，在安装了NT之后，竟会遇到那么多难以解决的麻烦，多次寻求技术支持又让学校破费了很多钱。&lt;/p&gt;
&lt;p&gt;该校系统部经理决定试用Linux，结果却令他大感意外。从1997年1月至7月，这部服务器仅当过3次，其中两次是因为楼里的电源线路故障，另一次 则是因为操作人员的愚蠢失误。更不可思议的是，在原先那套旧的NetWare上都已无法支持5位用户同时登陆的硬件配置，在更换为Linux之后居然可以 轻松自如地支持40个用户同时登录做专业出版。&lt;/p&gt;
&lt;p&gt;人们好奇这个毫无保留的系统竟然能够拥有如此卓越的表现，也好奇这么一个强劲的对手居然没有湮灭在微软强大的攻势之下，更好奇这个系统是否能有颠覆微软的机会。&lt;/p&gt;
&lt;p&gt;其实，Linux今天的表现并不意外。Linux与芬兰民族史诗《卡勒瓦拉》有着惊人的相识。它们的最初版本都幼稚可笑，成长、成熟与成功也都完全依 靠来自民间的养料。最后，就像《卡勒瓦拉》影响了芬兰的民族语言与民族文化一样，Linux也对全世界技术精英的生存方式和价值判断产生了深远的影响。&lt;/p&gt;
&lt;p&gt;而在Linux痛苦而缓慢的成长阶段，之所以能抵御微软众多名震江湖的杀招，并不单单因为这款操作系统完全成型、性能良好、功能齐备，或许更关键的在于知易行难的四个字：无欲则刚。&lt;/p&gt;
&lt;p&gt;李纳斯从未想过以此牟利，他也没有普世理想，“对我来说，Linux仍然不是什么大事情。在我看来，人们使用它固然是好事情，能够从中获得反馈意见也 很棒，同时这些却并不是那么重要。我不想传播什么福音。我为人们使用我的代码而感到骄傲，但我从来也没有过要与所有人共同分享它的念头。我从来没有认为这 是世界上最重要的事情。我也不认为有几百人用我的软件有任何的重要性可言，以至要告诉我爸爸。不是的，它对我来说更多的是一种乐趣。直到今天我也还是这么 认为。”&lt;/p&gt;
&lt;p&gt;或许正因为如此，商业巨人微软才对其无可奈何。因为Linux几乎惟一的企图只是：技术。而技术的驱动力并不只有利益，还有金钱买不来的热爱和兴趣。&lt;/p&gt;
&lt;p&gt;事实上，关于Linux会否颠覆Windows的猜测从未停止，尤其当Linux经常性地在某些方面展现出自己相对于Windows的巨大优势时。 Linux可以让用户拥有更多的操作界面和更炫的视觉效果，却不会让用户的电脑中毒，也不会让用户体会到应用程序诡异而突然的崩溃，更不会让用户的电脑在 使用一段时间之后出现莫名其妙的减速。&lt;/p&gt;
&lt;p&gt;但Linux也有自己的苦恼。&lt;/p&gt;
&lt;p&gt;现在，由李纳斯本人所控制的主要版本已经进化到3.0，而各商业软件公司自行开发的扩散版本更是不计其数。如何对这种开发模式进行有效控制和管理，以 减少软件本身不必要的膨胀，已经成为Linux未来必须面对的一个关键问题。很多人担心，Linux独具分布式开发模式将使它最终乱成一团。至于战胜微 软，在李纳斯那里似乎从未成为问题。&lt;/p&gt;
&lt;p&gt;“我根本没有打算威胁微软，因为我根本没有把微软真正视为对手，尤其没有把Windows视为对手——因为Linux和Windows的目标完全不一 样。我从Windows中找不到任何技术上令人感兴趣的东西。微软做得更多的是怎么去挣钱，而不是去制作一个更好的操作系统。”&lt;/p&gt;
&lt;p&gt;对于Linux的未来，李纳斯也充满信心：“Linux一直就是最棒的。从技术方面看，Linux肯定会越变越好；而从非技术方面看，我更看不出有什么需要担忧。”&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>超级解霸为什么当年那么火？后来又是为什么衰落的？</title>
      <link>http://coder.amazingdemo.top/post/%E8%B6%85%E7%BA%A7%E8%A7%A3%E9%9C%B8%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BD%93%E5%B9%B4%E9%82%A3%E4%B9%88%E7%81%AB/</link>
      <pubDate>Sat, 15 Apr 2023 09:45:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E8%B6%85%E7%BA%A7%E8%A7%A3%E9%9C%B8%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BD%93%E5%B9%B4%E9%82%A3%E4%B9%88%E7%81%AB/</guid>
      
        <description>&lt;p&gt;超级解霸的前身叫金山影霸, 它实际上是盗版软件 (不知道当时北京金山的老大 是否知道这件事). 金山影霸的VCD 解压部分是盗用当时国外的一个叫 Xing 的收费共享软件里面的解码 DLL (动态链接库). 金山影霸播放帧速高的原因是它在 Windows 操作系统播放时把屏幕设置成 320x200 全屏 (VCD 标准分辨率是 352x240, 所以抽行抽列显示后对画质影响不大)， 省去了图像放大和复制显示大图的步骤. 金山影霸本质上是盗版 Xing 的 DLL 外面套一个320x200 的全屏播放模式.&lt;/p&gt;
&lt;p&gt;后来梁肇新单干后推出了可以解码 VCD 和 DVD 的超级解霸. 超级解霸的 MPEG2 解码部分是拿开源软件 mpeg2dec (后来改名叫 libmpeg2, 一直是 GPL License, 梁肇新又违反开源协议了) 改出来的. 超级解霸里强调的主要优点其实没有太多技术含量.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;强力纠错 (实际上是遇到读盘错误后直接跳过几秒钟,读下一段, CD/DVD 读取光盘的默认行为是反复重读)&lt;/li&gt;
&lt;li&gt;解码速度快 (实际上是梁肇新用汇编重写了部分 C 代码, 比如像素从 YUV 转换到 RGB 的代码)&lt;/li&gt;
&lt;li&gt;梁后来自称的自主版权解码器实际上是把GPL 协议的 mpeg2dec 重构了一遍.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;后来为超级解霸什么被淘汰了呢? 因为超级解霸本质上是基于 mpeg2dec 的软件解码. 2000 年后图形加速卡逐渐成为主流, MPEG2 解码, 视频显示的多数功能被显卡硬件直接支持. 第三方软件通过微软 DirectX/MediaX 接口直接调用显卡图形加速功能, 效果比基于 mpeg2dec/libmpeg2 的软解压好一大截,而且加速卡支持自动缩放,直接 YUV 通道显示. 梁肇新的汇编优化在显卡图形加速功能面前就成了无用功.&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如何让代码段只运行在 Debug 模式下 </title>
      <link>http://coder.amazingdemo.top/post/dotnet_%E8%AE%A9%E4%BB%A3%E7%A0%81%E5%8F%AA%E6%89%A7%E8%A1%8C%E5%9C%A8debug%E6%A8%A1%E5%BC%8F%E4%B8%8B/</link>
      <pubDate>Fri, 24 Feb 2023 15:12:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/dotnet_%E8%AE%A9%E4%BB%A3%E7%A0%81%E5%8F%AA%E6%89%A7%E8%A1%8C%E5%9C%A8debug%E6%A8%A1%E5%BC%8F%E4%B8%8B/</guid>
      
        <description>&lt;p&gt;在C#下,如何让一段代码只执行在DEBUG模式下&lt;/p&gt;
&lt;h1 id=&#34;conditional-特性&#34;&gt;Conditional 特性&lt;/h1&gt;
&lt;p&gt;这个特性会告诉编译器除非遇到指定的编译符号，否则将会被忽略，参考下面的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static void Main(string[] args)
{
    [Conditional(&amp;#34;DEBUG&amp;#34;)]
    static void Method() { }
 
    Method();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;预处理指令&#34;&gt;预处理指令&lt;/h1&gt;
&lt;p&gt;当你用了 #if &amp;hellip; #end if 成对预处理指定时，当遇到编译符号与定义的环境一致时将会执行此if。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#if DEBUG
    static int testCounter = 0;
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;需要提醒一下，这个预处理指令不像 C, C++ 那种可以带逻辑条件， C# 中的if预处理指令只能是一种 boolean 型表达式。&lt;/p&gt;
&lt;h1 id=&#34;diagnostics下的isattached属性&#34;&gt;Diagnostics下的IsAttached属性&lt;/h1&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if (System.Diagnostics.Debugger.IsAttached)
{
    // Code here
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;属性解释如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//
// Summary:
//     Gets a value that indicates whether a debugger is attached to the process.
//
// Returns:
//     true if a debugger is attached; otherwise, false.
public static bool IsAttached
{
    get
    {
        throw null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>幼儿园，你真的准备好了吗？</title>
      <link>http://coder.amazingdemo.top/post/%E5%B9%BC%E5%84%BF%E5%9B%AD%E4%BD%A0%E5%87%86%E5%A4%87%E5%A5%BD%E4%BA%86%E5%90%97/</link>
      <pubDate>Wed, 11 Jan 2023 09:09:09 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%B9%BC%E5%84%BF%E5%9B%AD%E4%BD%A0%E5%87%86%E5%A4%87%E5%A5%BD%E4%BA%86%E5%90%97/</guid>
      
        <description>&lt;p&gt;上幼儿园，需要做哪些准备？&lt;/p&gt;
&lt;h1 id=&#34;长期准备&#34;&gt;长期准备&lt;/h1&gt;
&lt;h2 id=&#34;语言能力的培养&#34;&gt;语言能力的培养&lt;/h2&gt;
&lt;p&gt;大多数小朋友在上幼儿园的年纪已经拥有不错的语言沟通能力了，但仍有部分儿童，由于语言发展较慢或者害羞等原因沟通能力较弱。&lt;/p&gt;
&lt;p&gt;培养语言能力主要在两方面。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;教会小朋友表达自己的生理需求.如上厕所，渴了，饿了，肚子疼，累，困等。&lt;/li&gt;
&lt;li&gt;要教会孩子表达自己的感受.帮助孩子认知并表达自己的情绪，开心，生气，难过等。这样在小朋友回家后，就可以更好的与我们交流在幼儿园的情况啦。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;自理能力&#34;&gt;自理能力&lt;/h2&gt;
&lt;p&gt;儿童在上幼儿园之前，必须具备一定的生活技能，如自己吃饭，上厕所，睡觉等。&lt;/p&gt;
&lt;p&gt;这些小朋友力所能及的事，应当尽早去训练小朋友做，一定要分清爱和溺爱的区别。&lt;/p&gt;
&lt;p&gt;如上述这些生活技能在小朋友两岁多时即可开始锻炼。&lt;/p&gt;
&lt;h2 id=&#34;锻炼分离的能力全职妈妈们注意啦&#34;&gt;锻炼分离的能力（全职妈妈们注意啦）&lt;/h2&gt;
&lt;p&gt;要在日常生活中，刻意锻炼小朋友与父母，尤其是妈妈的分离能力。&lt;/p&gt;
&lt;p&gt;前期可以在保证小朋友安全的情况下，让小朋友独立的玩耍，妈妈呆在另外一个房间。&lt;/p&gt;
&lt;p&gt;在小朋友逐渐适应短时间的分离之后，可以与小朋友做好约定。&lt;/p&gt;
&lt;p&gt;比如妈妈下楼一趟，等你拼好拼图就回来；妈妈出去给你买好吃的，你睡醒了就回来。&lt;/p&gt;
&lt;p&gt;在这个过程中，让小朋友适应分离。同时，建立亲子之间的信任感。让小朋友知道，妈妈走了，是一定会再回来的。&lt;/p&gt;
&lt;h2 id=&#34;自我保护的能力&#34;&gt;自我保护的能力&lt;/h2&gt;
&lt;p&gt;事故频发，皮哥在此不做讨论。只给建议。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安全人员名单.给小朋友做一个名单，告诉小朋友哪些人才是安全的人，哪些人可以相信。&lt;/li&gt;
&lt;li&gt;身体安全，小朋友之间难免发生摩擦。第一，教会小朋友勇敢大声的说“不”。第二，告诉小朋友在玩耍时，保护好自己的脆弱部位，如头部，眼睛。第三，自己的私密部位，禁止任何人触碰！即使是父母，也要在小朋友同意之后。&lt;/li&gt;
&lt;li&gt;对一切陌生人说“不”。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;规则感和听从指令的能力&#34;&gt;规则感和听从指令的能力&lt;/h2&gt;
&lt;p&gt;要在小朋友两到三岁之间，逐渐培养小朋友的规则感和听从指令的能力。&lt;/p&gt;
&lt;p&gt;也就是要给小朋友“立规矩”了。&lt;/p&gt;
&lt;p&gt;具体的方式就是要跟小朋友约法三章，提前告知那些行为是可以做的，哪些是不能做的。&lt;/p&gt;
&lt;p&gt;让小朋友知道这就是规矩，以及打破了规则之后会有哪些惩罚。&lt;/p&gt;
&lt;p&gt;剩下的就是贯彻落实了，做错了那就要承担相应的后果，用温和而坚定的态度去纠正小朋友违反规矩的行为。&lt;/p&gt;
&lt;p&gt;最终将规则意识内化，让小朋友拥有自律的能力。&lt;/p&gt;
&lt;p&gt;切记在“执法”时要对事不对人。&lt;/p&gt;
&lt;p&gt;如：“妈妈是爱你的，但是妈妈不喜欢你刚才的行为，因为我们说过，那是错误的行为。所以现在你要和妈妈一起在这个小椅子上一起坐十分钟，不能玩任何的玩具”。&lt;/p&gt;
&lt;h1 id=&#34;短期准备&#34;&gt;短期准备&lt;/h1&gt;
&lt;h2 id=&#34;上学前&#34;&gt;上学前&lt;/h2&gt;
&lt;p&gt;与小朋友的沟通，以及心理疏导。&lt;/p&gt;
&lt;p&gt;让小朋友知道即将发生什么，用正面的方式去疏导小朋友入园前的焦虑。如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;绘本引导，如《我爱幼儿园》《一口袋的吻》等。&lt;/li&gt;
&lt;li&gt;实地参观，让小朋友看到幼儿园小朋友快乐玩耍的情形。&lt;/li&gt;
&lt;li&gt;场景模拟，在家里模拟幼儿园的场景，爸妈来扮演老师。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;调整作息：幼儿园有其作息表，家长至少提前一个月开始调整小朋友与作息表不符的习惯。&lt;/p&gt;
&lt;p&gt;家长的心理准备：家长切勿表现出焦虑不安，小朋友能敏感的感知到我们的情绪。试着用轻松，愉快的态度来感染小朋友。&lt;/p&gt;
&lt;h2 id=&#34;放学后&#34;&gt;放学后&lt;/h2&gt;
&lt;p&gt;按时接！！&lt;/p&gt;
&lt;p&gt;一个大大的拥抱！&lt;/p&gt;
&lt;p&gt;听听小朋友一天的经历。&lt;/p&gt;
&lt;p&gt;切不可一上来就问：有没有小朋友欺负你？&lt;/p&gt;
&lt;p&gt;待小朋友平静下来后，慢慢引导他说出这一天的经历，并且强化那些小朋友喜欢的部分。&lt;/p&gt;
&lt;p&gt;如“对呀，如果你上幼儿园的话，你就能天天跟你说的那个小妹妹一起玩了。”&lt;/p&gt;
&lt;p&gt;与老师的简要沟通小朋友当天的表现，同时告知老师小朋友的性格特征以及一些生活习惯。&lt;/p&gt;
&lt;h1 id=&#34;最后我想说&#34;&gt;最后我想说&lt;/h1&gt;
&lt;p&gt;我们在讨论起分离焦虑的时候，总是把孩子默认为问题的主体。&lt;/p&gt;
&lt;p&gt;其实，孩子远比我们想的要坚强，比我们想的要独立。&lt;/p&gt;
&lt;p&gt;有时候这个焦虑，说的其实是父母。&lt;/p&gt;
&lt;p&gt;我们总是期待着成长，也畏惧着离别。&lt;/p&gt;
&lt;p&gt;我们希望看到孩子的羽翼日渐丰满，&lt;/p&gt;
&lt;p&gt;又希望我们的庇护能够多一点，再多一点。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>2022年12月 疫情放开</title>
      <link>http://coder.amazingdemo.top/post/%E7%96%AB%E6%83%85%E6%94%BE%E5%BC%80%E5%9B%9E%E7%9C%B8/</link>
      <pubDate>Sat, 24 Dec 2022 06:06:06 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%96%AB%E6%83%85%E6%94%BE%E5%BC%80%E5%9B%9E%E7%9C%B8/</guid>
      
        <description>&lt;p&gt;2022年12月，三年防疫之后，全国以一种突然而迅速的姿态，吹枯拉朽般的放开了，令我始料未及。&lt;/p&gt;
&lt;p&gt;现在，不要求做核酸，出门乘坐公共交通无需扫码，进出场所也绝对的自由。&lt;/p&gt;
&lt;p&gt;外面全是小羊人，社会上的健康人群正以极快的速度不断地被传染着。网友们都笑称，阴性清零。&lt;/p&gt;
&lt;p&gt;我全家在小心翼翼，做好防护的十五天后（20221218），还是阳了。&lt;/p&gt;
&lt;p&gt;不知道政府这三年来做的动态清零防疫，以及现在猛然一下猝不及防的放开，是对是错。&lt;/p&gt;
&lt;p&gt;也不知道那些所谓的专家一天一个发言，到底哪些人是昧着良心的，哪些人是真为国为民。&lt;/p&gt;
&lt;p&gt;这些都只能交给时间，留给后人说。&lt;/p&gt;
&lt;p&gt;现在全民阳是迟早的，跑不脱的事。在我难受的几天里，我好奇，翻看了一下19年底，武汉的点滴。&lt;/p&gt;
&lt;p&gt;现在奥密克戎毒株比武汉初期的毒株，据说毒性弱了20多倍。不知道19年的武汉人民是怎么挺过来的。&lt;/p&gt;
&lt;p&gt;听说那个时候医护形容死者死状为“陆地淹死”。肺里全是积液，努力呼吸，却得不到一丝氧气。&lt;/p&gt;
&lt;p&gt;看到之前保存的这张图片，谨以此记录一下这段特殊的历史吧。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;小杭&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>关于HP打印机提示用户干预</title>
      <link>http://coder.amazingdemo.top/post/%E5%85%B3%E4%BA%8Ehp%E6%89%93%E5%8D%B0%E6%9C%BA%E6%8F%90%E7%A4%BA%E7%94%A8%E6%88%B7%E5%B9%B2%E9%A2%84/</link>
      <pubDate>Sun, 04 Dec 2022 15:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%85%B3%E4%BA%8Ehp%E6%89%93%E5%8D%B0%E6%9C%BA%E6%8F%90%E7%A4%BA%E7%94%A8%E6%88%B7%E5%B9%B2%E9%A2%84/</guid>
      
        <description>&lt;p&gt;首先，我想说，网上常见搜到的，在服务里找到什么PrintSpooler的，都是垃圾&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解决的步骤：&lt;/p&gt;
&lt;p&gt;1、“计算机/此电脑”——单击右键“管理”——左侧点击“服务和应用程序”——“服务”——双击“PrintSpooler”服务——点击“停止”；&lt;/p&gt;
&lt;p&gt;2、键盘上按win+r——弹出运行框输入“spool”确定——选择“PRINTERS＂文件夹，并将其中的文件全部删除；&lt;/p&gt;
&lt;p&gt;3、返回第一步双击“PrintSpooler”服务——“启动”并“确定”；&lt;/p&gt;
&lt;p&gt;4、重新打开打印机的任务窗，需要用户干预的提示消失。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以上，并不解决问题。&lt;/p&gt;
&lt;p&gt;最后我在HP官网论坛找到了答案&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;比如 ：打印机的无线ip是192.168.1.2&lt;/p&gt;
&lt;p&gt;如果之前此端口已经存在，右击打印机图标&amp;ndash;打印机属性&amp;ndash;端口&amp;ndash;选择standard tcp/ip  192.168.1.2&lt;/p&gt;
&lt;p&gt;如果之前没有此端口，右击打印机图标&amp;ndash;打印机属性&amp;ndash;端口&amp;ndash;创建端口&amp;ndash;standard tcp/ip&amp;ndash;输入打印机ip&amp;ndash;下一步&amp;ndash;下一步&amp;ndash;直到完成&lt;/p&gt;
&lt;p&gt;然后重新测试&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从这个解决方案上来看，应该是WIN10的打印机接口设置错误，导致无法和打印机通讯，所以把打印机的接口设置成正确的参数就好了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C/C&#43;&#43; 基本类型32位和64位字节大小</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B32%E4%BD%8D%E5%92%8C64%E4%BD%8D%E5%AD%97%E8%8A%82%E5%A4%A7%E5%B0%8F/</link>
      <pubDate>Fri, 16 Sep 2022 20:32:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B32%E4%BD%8D%E5%92%8C64%E4%BD%8D%E5%AD%97%E8%8A%82%E5%A4%A7%E5%B0%8F/</guid>
      
        <description>&lt;h1 id=&#34;64位编译器&#34;&gt;64位编译器&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;char ：1个字节&lt;/li&gt;
&lt;li&gt;char*(即指针变量): 8个字节&lt;/li&gt;
&lt;li&gt;short int : 2个字节&lt;/li&gt;
&lt;li&gt;int：  4个字节&lt;/li&gt;
&lt;li&gt;unsigned int : 4个字节&lt;/li&gt;
&lt;li&gt;float:  4个字节&lt;/li&gt;
&lt;li&gt;double:   8个字节&lt;/li&gt;
&lt;li&gt;long:   8个字节&lt;/li&gt;
&lt;li&gt;long long:  8个字节&lt;/li&gt;
&lt;li&gt;unsigned long:  8个字节&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;32位编译器&#34;&gt;32位编译器&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;char ：1个字节&lt;/li&gt;
&lt;li&gt;char*（即指针变量）: 4个字节（32位的寻址空间是2^32, 即32个bit，也就是4个字节。同理64位编译器）&lt;/li&gt;
&lt;li&gt;short int : 2个字节&lt;/li&gt;
&lt;li&gt;int：  4个字节&lt;/li&gt;
&lt;li&gt;unsigned int : 4个字节&lt;/li&gt;
&lt;li&gt;float:  4个字节&lt;/li&gt;
&lt;li&gt;double:   8个字节&lt;/li&gt;
&lt;li&gt;long:   4个字节&lt;/li&gt;
&lt;li&gt;long long:  8个字节&lt;/li&gt;
&lt;li&gt;unsigned long:  4个字节&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;16位编译器&#34;&gt;16位编译器&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;char ：1个字节&lt;/li&gt;
&lt;li&gt;char*(即指针变量): 2个字节&lt;/li&gt;
&lt;li&gt;short int : 2个字节&lt;/li&gt;
&lt;li&gt;int：  2个字节&lt;/li&gt;
&lt;li&gt;unsigned int : 2个字节&lt;/li&gt;
&lt;li&gt;float:  4个字节&lt;/li&gt;
&lt;li&gt;double:   8个字节&lt;/li&gt;
&lt;li&gt;long:   4个字节&lt;/li&gt;
&lt;li&gt;long long:  8个字节&lt;/li&gt;
&lt;li&gt;unsigned long:  4个字节&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>2022 武汉外小游园记</title>
      <link>http://coder.amazingdemo.top/post/%E6%AD%A6%E6%B1%89%E5%A4%96%E5%B0%8F2022%E6%B8%B8%E5%9B%AD%E8%AE%B0/</link>
      <pubDate>Tue, 05 Jul 2022 08:08:08 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%AD%A6%E6%B1%89%E5%A4%96%E5%B0%8F2022%E6%B8%B8%E5%9B%AD%E8%AE%B0/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;2022录取&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;2022分布&#34;&gt;&lt;/p&gt;
&lt;p&gt;我是在6月12日提交游园申请截止的当天，晚上十点多才想起来要去写小作文的。&lt;/p&gt;
&lt;p&gt;主要是那个星期自驾带孩子出去旅游了，玩嗨了，周日回来洗完澡才记起来还有这个事，险些错过了。&lt;/p&gt;
&lt;p&gt;幸亏系统不计前嫌，还是把我选上了去参加游园，万幸万幸。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;顺便说一句，我成功上岸了。预祝大家心想事成&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;生活篇&#34;&gt;生活篇&lt;/h1&gt;
&lt;p&gt;20日晚，得知被选上去游园，我还是很兴奋的。基于之前三年的&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/&#34;&gt;骑车生涯&lt;/a&gt;，我们跑遍了全国各地参加各种平衡车比赛，积累了丰富的带孩子远征经验，我和孩子麻麻当晚立刻制定了游园当天的详细计划安排。&lt;/p&gt;
&lt;p&gt;依据计划，我在24日晚就把热水什么的都提前烧好了，要蒸的鸡蛋，玉米什么的都洗好放在冰箱里，第二天拿出来就蒸；避免第二天早上起来烧水洗菜弄出响动，影响孩子休息。&lt;/p&gt;
&lt;p&gt;同时，我把所有的资料，全都打印了两份，以备不时之需。而且，入校陪同的家长资料，我做了一份孩子妈妈的，做了一份我自己的。防止万一出于各种原因，孩子妈妈被拦着不让进的话，能快速切换为我陪着进。&lt;/p&gt;
&lt;p&gt;25日早上七点不到我就醒了，轻手轻脚的洗漱，然后悄悄开门出去买了热干面和包子，还买了一些面包作为干粮，又买了一条巧克力。回来冲好奶，摆好早餐，再把孩子轻轻唤醒，起床吃早饭。吃完早饭就立刻出发了。&lt;/p&gt;
&lt;p&gt;之前考虑过，外小附近肯定很多家长，很堵。所以导航直接定到了湘隆广场，把车开下去地下停车场以后，再出来坐地铁去了学校。（事实证明，真的堵红了，外小周围全是车，好多交警都在指挥维持秩序）&lt;/p&gt;
&lt;p&gt;到了学校外面以后，还是很震惊的，乌泱泱的家长围在那里，颇有一种送孩子高考的感觉。&lt;/p&gt;
&lt;p&gt;我们赶紧找了个僻静点的地方，安排孩子吃面包，喝点水，吃完了以后去上厕所，再洗洗脸，整理下着装。临进校，又让她咬了一大口巧克力补充高糖。&lt;/p&gt;
&lt;p&gt;目送孩子和孩子妈妈进去以后，我就观察了一下周围。&lt;/p&gt;
&lt;p&gt;今年并没有像往年一样，让家长在外面排队，改成了在学校里排队。所以之前计划的，带小板凳接力排队的场景，这次没用上。（本来计划，我先去校外排队，让孩子可以在树荫下休息，等排到进校了，再换孩子进去，以免孩子提前过度消耗体力。结果今年不像往年可以在校外排一轮，进去了再排一轮。今年直接就可以由一位直系家长进校，然后一直排队到贴号码牌。）&lt;/p&gt;
&lt;p&gt;学校里面围着操场搭建了一条长棚，家长带着小孩就在里面排着队煎熬着。感觉这更加考验孩子的专注力和忍耐力了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;外小入场是提前了一个小时入场，迟到二十分钟禁止入场的。&lt;/strong&gt; 比如说十半点场的，九点多就已经可以进学校了；但是进去了，也是在里面围着操场排着队，等着分发号码牌（而且号码牌也是十点半才开始发放的）。然后十点五十，外面的入场通道就会关闭，迟到的人就进不了校门了，也就意味着失去了游园机会。&lt;/p&gt;
&lt;p&gt;我个人感觉，没必要那么早进去，只要不迟到就好。进去了也是大太阳下面晒着，等着叫号，把人都晒蔫了。&lt;/p&gt;
&lt;p&gt;贴号码牌的时候是按照排队先后顺序，30人一组进行的分组，2022年的分组名是字母，比如A组，B组。分好组以后，孩子们就会被带离家长，由老师带领去参加游园，大人们会按孩子的分组，被分配到高年级教室里看纪录宣传片，并且等待喇叭里喊某个组可以接孩子了，再出去接孩子。&lt;/p&gt;
&lt;p&gt;通知家长领孩子的时候，就是游园结束了，可以出学校了。从进校门，在操场那边排队等着贴号牌，这个过程大概等了接近一个小时。从贴号牌孩子离开家长算起，到通知领孩子，大概一到一个半小时。所以你要做好，进校门以后，鏖战两到三个小时的准备，这对孩子和家长的体力，注意力都是不小的考验。&lt;/p&gt;
&lt;p&gt;门口不断地有各种突发事件发生，比如这个家长着装不对，进不去啊；那个家长资料打印不合规范，不让进啊；险象环生。&lt;/p&gt;
&lt;p&gt;门口还有各种卖水的，卖小板凳的，培训机构推销的，一片繁华。&lt;/p&gt;
&lt;h1 id=&#34;考试篇&#34;&gt;考试篇&lt;/h1&gt;
&lt;p&gt;终于说到游园考试了，外小游园流传着很多都市传说。&lt;/p&gt;
&lt;p&gt;比如说 &lt;em&gt;老师给每个孩子一颗糖，让保管好，最后要还，没有还的小孩都被淘汰。有的小孩玩丢了，有的小孩忍不住吃了，就都淘汰了。&lt;/em&gt; 还有人说 &lt;em&gt;孩子一进去就跟别的小孩子打架，然后还没开始游就双双被遣返，失去游园资格。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这些我也不知道真的假的，也不知道是不是众口铄金，人传人传成了演义。&lt;/p&gt;
&lt;p&gt;就我个人的经历，孩子出来的描述（ &lt;strong&gt;不排除小孩子的记忆偏差和以及遗忘&lt;/strong&gt; ），我个人感觉如下（ &lt;em&gt;考核项目发生有点乱序，我也不知道项目谁先谁后&lt;/em&gt; ）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;老师要求本组孩子们合唱一首歌，并且从一号小朋友开始挨个询问你想唱什么。我家孩子排号比较靠后，还没问到我们家的时候，前面的孩子们大部分都说要唱《孤勇者》，然后老师还没问完，前面的孩子们就已经开始自顾自的唱起来了。我家孩子因为我们只给她听过，没教过她唱，老师问到她的时候，她说她不想唱孤勇者，她可以唱一首别的歌。但是此时前面的都唱开了，老师对此也没有干预的意思，于是我家孩子就看着别人唱，自己也没搭话。&lt;/li&gt;
&lt;li&gt;老师放了一段视频，然后还是从一号小朋友开始问视频里的内容。我家又吃了排号靠后的亏。她说，刚看完，还记得一点，等老师问到她那里的时候，她就忘了一部分了，然后她就只把她记得的给老师说了。（ &lt;em&gt;我不确定她有没有跟老师表达我排位靠后，所以忘了一些&lt;/em&gt; ）&lt;/li&gt;
&lt;li&gt;参观宿舍的时候，老师进宿舍之前规定不许爬上铺。但是他这组还是有小朋友一进去就爬了，她铭记老师的教诲没有爬，但是老师越说不让爬，她就越好奇上铺有什么，所以她一直踮脚伸着脖子往上铺够着看，还时不时的蹦一下看上铺是啥样子的。&lt;/li&gt;
&lt;li&gt;在图书馆里，老师说让大家自己选本书坐着看，或者也可以在这里休息。她去拿书的时候，发现书架旁边都围着瑜伽垫，于是就脱了鞋上去拿书。很多小朋友就直接穿着鞋踩上去了，这一定是扣分项。另外，她选了一本书，好像是《我第一次自己睡觉》（大概是这个内容的一本书），我不知道老师会不会在这个环节去记录孩子看的什么类型的书，或者说我不清楚阅读的选材会不会影响考试成绩。&lt;/li&gt;
&lt;li&gt;有一个冬瓜，一个茄子，放到篓子里，篓子还有破洞。（ &lt;em&gt;反正她描述了半天，我不知道是个啥游戏&lt;/em&gt; ）&lt;/li&gt;
&lt;li&gt;看视频，要求跟着视频跳舞（ &lt;em&gt;这应该是体测，以及身体协调性&lt;/em&gt; ）&lt;/li&gt;
&lt;li&gt;老师带到厕所问谁想上厕所，结果她说不是很想上，然后她这组有几个小朋友就一直站着没去，其他也有很多小孩去上了厕所。（ &lt;em&gt;可能是考上厕所洗手？&lt;/em&gt; ）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当时考完出来，我一听她描述的题目，我就觉得糟了。&lt;/p&gt;
&lt;p&gt;第一个问题，你不会唱，你跟着哼嘛，你傻站着，不会被认为没有团队协作能力吗？第二个问题，你可以跟老师说呀，你排得太后了，不是能力不行。第三个问题，老师不会认为你仪态不好，很调皮吗？&lt;/p&gt;
&lt;p&gt;反正就是刚考完出来，感觉孩子哪哪都是问题。于是安慰自己，亲生的，录取率又低，能游园那就是运气爆棚了，要淡定。自己也就混得这么个熊样，为啥要求孩子事事都得最好？人不能起贪念，不要有不切实际的幻想。&lt;/p&gt;
&lt;p&gt;但是过了几天，我总反复回想孩子说的话，我再回头冷静的审视这些问题。&lt;/p&gt;
&lt;p&gt;第一个，老师都没说开始，也没有说让大家讨论下确定一个歌曲再唱，甚至老师都没问完话，你们就乱起头开始。我家孩子不同流合污，不是一件美德么？&lt;/p&gt;
&lt;p&gt;第二个，我排在靠后，还能记得视频里发生了什么，不是证明我记忆力还不错么，六岁孩子，你想要求多高？&lt;/p&gt;
&lt;p&gt;第三个，她并没有违反规则，也满足了自己的好奇心，这不是聪明的一种体现么？&lt;/p&gt;
&lt;p&gt;所以我个人觉得，虽然外界传得沸沸扬扬，都市传说说得真真假假的。但是外小考核更多的还是看眼缘，观察的是孩子的整体表现吧。&lt;/p&gt;
&lt;p&gt;就像一千个人眼中有一千个哈姆雷特，孩子的表现也许不是标答，但也不是坏的啊，这得看你从何种角度去看她的行为。或者说老师的考核本来就没有标答，他们只是在关注孩子的临场表现，选择与学校气质相符的孩子，仅此而已。&lt;/p&gt;
&lt;h1 id=&#34;后记篇&#34;&gt;后记篇&lt;/h1&gt;
&lt;p&gt;总结起来，借用网上的话就是&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自控力，安静排队，能听老师指令。&lt;/li&gt;
&lt;li&gt;领导力，小组活动时，如果想当组长，就当好组长，承担责任不推脱；如不想当组长，那也要乖巧，听老师话。&lt;/li&gt;
&lt;li&gt;如果需要休息，安静坐好，不要乱跑或者打扰别人。&lt;/li&gt;
&lt;li&gt;如果需要做运动，即使完成度不高，也不要发脾气，不要放弃，尽力完成。&lt;/li&gt;
&lt;li&gt;如果看视频，一定要专注，不要做小动作，只会看一遍，视频里一定有答案。&lt;/li&gt;
&lt;li&gt;如果有休息环节，安静睡觉，就算自己不想睡觉，也不要打扰别人。（包括，你看到别的孩子很吵闹，也不要去管别人，管好你自己，千万千万不要打扰别人，哪怕是阻止别人讲话）&lt;/li&gt;
&lt;li&gt;小孩子容易受周围影响，你没办法决定你的同组人，只能叮嘱自己孩子不要受别人影响。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外说几个的经验总结：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;着装上一定要尽量要满足要求。&lt;/strong&gt; 这一点外小还是很魔幻的。&lt;/p&gt;
&lt;p&gt;我在校外等孩子出来，就听到旁边的一位妈妈一脸焦急的拦住了一个刚游完了出来的孩子家长，说他们家着装裤子不合要求，学校不让进；她想现场买这个刚出来的孩子的裤子。&lt;/p&gt;
&lt;p&gt;但是据我长时间的观察，从外小游园出来的孩子着装也是五花八门，各种不合要求的着装都有。貌似也最后参加了游园呀。比如说，通知说穿短裤，结果穿的七分裤甚至九分裤；通知白T恤，但是穿得衣服各种花里胡哨的，白色不是主色调；通知穿T恤，但是穿的衬衣 &lt;em&gt;这些看起来不合规的着装，不知道会不会在游园过程中暗暗的被扣分，但是有些人因为衣着被拦着不让进，有的人看着不合规又能进，感觉更多的是看老师心中的尺度？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;建议大家手上备点干粮&lt;/strong&gt; ，尤其是临近中午场的孩子。我们这次游园，当时十点半那场进去的孩子，最晚的一直游园到下午一点半才出来，而且学校是不管饭的。大人和孩子全都被整得饥肠辘辘的，很影响游园表现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要以为老师没有盯着就没事&lt;/strong&gt; 外小游园是有摄像头全程监控的，你的成绩不是只有带队老师打分的，镜头后面还有很多老师也在同步观察的，不要让孩子以为，我做坏事，老师没看见就可以了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;自我介绍和才艺表演不是必须的&lt;/strong&gt; ，我这一场就没这两项，但是别的场次据说游园整体结束了，但是还没通知家长来带领的时候，老师会问，有没有同学想做这些。我个人理解的话，这两项是个加分项，但是你没有十足的把握，就不要轻易去尝试，免得成了个扣分项。&lt;/p&gt;
&lt;p&gt;虽然近几年说不考核家长了，但是家长和小朋友分开去教室休息的时候也不要瞎搞哟。&lt;/p&gt;
&lt;p&gt;最后，祝大家都游园顺利。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220625上午0800&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220625下午1330&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220625下午1600&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220626上午0800&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220626上午1030&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220626下午1330&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;20220626下午1600&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43; 判断线段和矩形是否相交</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E5%88%A4%E6%96%AD%E7%BA%BF%E6%AE%B5%E5%92%8C%E7%9F%A9%E5%BD%A2%E7%9B%B8%E4%BA%A4/</link>
      <pubDate>Sun, 06 Mar 2022 13:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E5%88%A4%E6%96%AD%E7%BA%BF%E6%AE%B5%E5%92%8C%E7%9F%A9%E5%BD%A2%E7%9B%B8%E4%BA%A4/</guid>
      
        <description>&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public static bool isLineIntersectRectangle(float linePointX1,
                                      float linePointY1,
                                      float linePointX2,
                                      float linePointY2,
                                      float rectangleLeftTopX,
                                      float rectangleLeftTopY,
                                      float rectangleRightBottomX,
                                      float rectangleRightBottomY)
    {
        float lineHeight = linePointY1 - linePointY2;
        float lineWidth = linePointX2 - linePointX1;  // 计算叉乘 
        float c = linePointX1 * linePointY2 - linePointX2 * linePointY1;
        if ((lineHeight * rectangleLeftTopX + lineWidth * rectangleLeftTopY + c &amp;gt;= 0 &amp;amp;&amp;amp; lineHeight * rectangleRightBottomX + lineWidth * rectangleRightBottomY + c &amp;lt;= 0)
            || (lineHeight * rectangleLeftTopX + lineWidth * rectangleLeftTopY + c &amp;lt;= 0 &amp;amp;&amp;amp; lineHeight * rectangleRightBottomX + lineWidth * rectangleRightBottomY + c &amp;gt;= 0)
            || (lineHeight * rectangleLeftTopX + lineWidth * rectangleRightBottomY + c &amp;gt;= 0 &amp;amp;&amp;amp; lineHeight * rectangleRightBottomX + lineWidth * rectangleLeftTopY + c &amp;lt;= 0)
            || (lineHeight * rectangleLeftTopX + lineWidth * rectangleRightBottomY + c &amp;lt;= 0 &amp;amp;&amp;amp; lineHeight * rectangleRightBottomX + lineWidth * rectangleLeftTopY + c &amp;gt;= 0))
        {
 
            if (rectangleLeftTopX &amp;gt; rectangleRightBottomX)
            {
                float temp = rectangleLeftTopX;
                rectangleLeftTopX = rectangleRightBottomX;
                rectangleRightBottomX = temp;
            }
            if (rectangleLeftTopY &amp;lt; rectangleRightBottomY)
            {
                float temp1 = rectangleLeftTopY;
                rectangleLeftTopY = rectangleRightBottomY;
                rectangleRightBottomY = temp1;
            }
            if ((linePointX1 &amp;lt; rectangleLeftTopX &amp;amp;&amp;amp; linePointX2 &amp;lt; rectangleLeftTopX)
                || (linePointX1 &amp;gt; rectangleRightBottomX &amp;amp;&amp;amp; linePointX2 &amp;gt; rectangleRightBottomX)
                || (linePointY1 &amp;gt; rectangleLeftTopY &amp;amp;&amp;amp; linePointY2 &amp;gt; rectangleLeftTopY)
                || (linePointY1 &amp;lt; rectangleRightBottomY &amp;amp;&amp;amp; linePointY2 &amp;lt; rectangleRightBottomY))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        else
        {
            return false;
        } 
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>详细解析竞业限制</title>
      <link>http://coder.amazingdemo.top/post/%E8%AF%A6%E7%BB%86%E8%A7%A3%E6%9E%90%E7%AB%9E%E4%B8%9A%E9%99%90%E5%88%B6/</link>
      <pubDate>Sat, 19 Feb 2022 19:43:27 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E8%AF%A6%E7%BB%86%E8%A7%A3%E6%9E%90%E7%AB%9E%E4%B8%9A%E9%99%90%E5%88%B6/</guid>
      
        <description>&lt;blockquote&gt;
&lt;p&gt;竞业限制是指用人单位在劳动合同或者保密协议或者竞业限制协议中，与掌握本单位商业秘密和与知识产权相关的保密事项的劳动者约定，在劳动合同解除或者终止后的一定期限内，不得到与本单位生产或者经营同类产品、从事同类业务的有竞争关系的其他用人单位任职，也不得自己开业生产或者经营同类产品、从事同类业务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;用通俗的话来说，竞业限制就是用人单位要求劳动者在离职后一段时间内不得到与用人单位有竞争关系的企业上班，也不能自己做与用人单位有竞争关系的业务。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;用人单位与劳动者约定竞业限制有法律依据目的是保护用人单位的商业秘密&#34;&gt;用人单位与劳动者约定竞业限制有法律依据，目的是保护用人单位的商业秘密。&lt;/h1&gt;
&lt;p&gt;有些劳动者掌握了用人单位的经营和技术秘密，离职后通常选择从事与用人单位相同或者近似的业务。劳动者一旦从事这些业务，不但容易成为用人单位强劲的竞争对手，而且劳动者由于自身的便利和业务的需要，往往会情不自禁地使用用人单位的商业秘密。为防止出现这种情况，用人单位为维护自己的利益，就会选择与劳动者签署竞业限制协议，约定劳动者离职后一段时间不能从事与用人单位有竞争关系的业务，法律对此行为是保护的。&lt;/p&gt;
&lt;p&gt;《劳动合同法》第二十三条规定：“对负有保密义务的劳动者，用人单位可以在劳动合同或者保密协议中与劳动者约定竞业限制条款，并约定在解除或者终止劳动合同后，在竞业限制期限内按月给予劳动者经济补偿。劳动者违反竞业限制约定的，应当按照约定向用人单位支付违约金。”&lt;/p&gt;
&lt;h1 id=&#34;竞业限制协议可以在劳动者入职时劳动合同履行期间或是离职时任一时间签署劳动者也可以不签&#34;&gt;竞业限制协议可以在劳动者入职时、劳动合同履行期间或是离职时任一时间签署，劳动者也可以不签。&lt;/h1&gt;
&lt;p&gt;用人单位可以选择在劳动者入职时、劳动合同履行期间或是离职时任一时间签署，法律没有强制规定。因为离职时劳动者一般不愿意签署该协议，很多用人单位基本都选择在入职时就让劳动者签署，或是劳动合同履行期间趁着升职加薪的时候说服劳动者签署。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;竞业限制不是法定义务，而是用人单位与劳动者之间的约定义务，劳动者可以选择签署，也完全可以选择不签。劳动者不签署竞业限制协议不违法，用人单位不能以此为由解除劳动合同，否则属于违法解除，用人单位需要向劳动者支付双倍的经济补偿金。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;竞业限制仅适用于用人单位的高级管理人员高级技术人员和其他负有保密义务的人员&#34;&gt;竞业限制仅适用于用人单位的高级管理人员、高级技术人员和其他负有保密义务的人员。&lt;/h1&gt;
&lt;p&gt;《劳动合同法》第二十四条规定：“&lt;strong&gt;竞业限制的人员限于用人单位的高级管理人员、高级技术人员和其他负有保密义务的人员。&lt;/strong&gt;”&lt;/p&gt;
&lt;p&gt;虽然劳动合同法明确限定了竞业限制的人员只有以上三类，但法律没有对这三类人员进行客观定义，司法实践中没有一个统一标准，用人单位选择全员签署竞业限制协议，除非有特别不合理之处，例如对用人单位前台、保洁员进行竞业限制，否则法院一般不会以劳动者不是竞业限制主体为由认定协议无效。&lt;/p&gt;
&lt;h1 id=&#34;竞业限制的范围是有竞争关系的业务期限最长为二年&#34;&gt;竞业限制的范围是有竞争关系的业务，期限最长为二年。&lt;/h1&gt;
&lt;p&gt;竞业限制只能限制劳动者的择业范围，不能剥夺劳动者的就业权，所以竞业限制协议只能约定劳动者离职后不得到与用人单位有竞争关系的企业上班，也不能自己做与用人单位有竞争关系的业务。劳动者从事与用人单位没有竞争关系的业务是不受限制的，如果竞业限制协议约定劳动者不能从事的业务超出了有竞争关系的范围，该约定无效。&lt;/p&gt;
&lt;p&gt;《劳动合同法》第二十四条规定：“在解除或者终止劳动合同后，前款规定的人员到与本单位生产或者经营同类产品、从事同类业务的有竞争关系的其他用人单位，或者自己开业生产或者经营同类产品、从事同类业务的竞业限制期限，不得超过二年。”&lt;/p&gt;
&lt;p&gt;所以**竞业限制期限最长为劳动者离职后二年，超过二年的约定无效。**至于具体的期限是一个月还是一年或二年，可以由用人单位和劳动者协商确定，只要不超过二年都是合法的。&lt;/p&gt;
&lt;h1 id=&#34;用人单位要求劳动者竞业限制必须向劳动者支付经济补偿最低标准为劳动者离职前十二月平均工资的30&#34;&gt;用人单位要求劳动者竞业限制，必须向劳动者支付经济补偿，最低标准为劳动者离职前十二月平均工资的30%。&lt;/h1&gt;
&lt;p&gt;劳动合同法第二十三条规定，用人单位应当在竞业限制期限内按月给予劳动者经济补偿。&lt;/p&gt;
&lt;p&gt;《最高人民法院关于审理劳动争议案件适用法律若干问题的解释(四)》第六条规定：“当事人在劳动合同或者保密协议中约定了竞业限制，但未约定解除或者终止劳动合同后给予劳动者经济补偿，**劳动者履行了竞业限制义务，要求用人单位按照劳动者在劳动合同解除或者终止前十二个月平均工资的30%按月支付经济补偿的，人民法院应予支持。**前款规定的月平均工资的30%低于劳动合同履行地最低工资标准的，按照劳动合同履行地最低工资标准支付。”&lt;/p&gt;
&lt;p&gt;所以，用人单位要求劳动者履行竞业限制义务的，必须向劳动者按月支付经济补偿，经济补偿的最低标准为劳动者离职前十二个月平均工资的30%，可高不可低（例如，宁波、深圳、珠海均规定竞业限制经济补偿不得低于劳动者离职前十二个月平均工资的二分之一），且不能低于当地最低工资标准。&lt;/p&gt;
&lt;h1 id=&#34;劳动者违反竞业限制应当向用人单位支付违约金竞业限制违约金应在合理范围内劳动者支付违约金后用人单位有权要求劳动者继续履行竞业限制义务&#34;&gt;劳动者违反竞业限制，应当向用人单位支付违约金，竞业限制违约金应在合理范围内。劳动者支付违约金后，用人单位有权要求劳动者继续履行竞业限制义务。&lt;/h1&gt;
&lt;p&gt;《劳动合同法》第二十三条规定：“劳动者违反竞业限制约定的，应当按照约定向用人单位支付违约金。”从上述规定可以看出，违约金的数额是以约定为准的。&lt;/p&gt;
&lt;p&gt;但签署竞业限制时，用人单位往往占优势地位，约定的经济补偿是越低越好，约定的违约金却是动不动就50万、100万甚至更高。司法实践中，为平衡用人单位与劳动者之间的权利义务，如果约定的违约金明显过高，法院会予以适当调整。&lt;/p&gt;
&lt;p&gt;法律并没有规定竞业限制违约金的上限是多少，当竞业限制违约金明显过高时，法院通常会结合竞业限制经济补偿的标准、劳动者过错程度以及违反竞业限制造成的后果，并参考全国人大常委会2006-03-20发布的《中华人民共和国劳动合同法（草案）》（征求意见稿）十六条规定：“劳动者违反竞业限制约定的，应当向用人单位支付违约金，其数额不得超过用人单位向劳动者支付的竞业限制经济补偿的3倍”，综合确定违约金的具体数额。&lt;/p&gt;
&lt;p&gt;并不是劳动者支付了违约金就可以不履行竞业限制义务了，根据《最高人民法院关于审理劳动争议案件适用法律若干问题的解释(四)》第十条规定：“&lt;strong&gt;劳动者违反竞业限制约定，向用人单位支付违约金后，用人单位要求劳动者按照约定继续履行竞业限制义务的，人民法院应予支持&lt;/strong&gt;”，即使劳动者支付了违约金，用人单位也有权要求劳动者继续履行竞业限制义务。&lt;/p&gt;
&lt;h1 id=&#34;在职期间签署了竞业限制协议离职时用人单位有权决定是否要履行&#34;&gt;在职期间签署了竞业限制协议，离职时，用人单位有权决定是否要履行。&lt;/h1&gt;
&lt;p&gt;**用人单位与劳动者虽然签署了竞业限制协议，离职时，如果用人单位认为不需要对该劳动者进行竞业限制，不想浪费竞业限制经济补偿，需要书面通知劳动者，明确告知不需要劳动者履行竞业限制协议。**如果用人单位没有明确告知劳动者不需要履行竞业限制协议，劳动者履行了竞业限制义务后可以要求用人单位支付经济补偿。&lt;/p&gt;
&lt;p&gt;《最高人民法院关于审理劳动争议案件适用法律若干问题的解释(四)》第七条规定：“当事人在劳动合同或者保密协议中约定了竞业限制和经济补偿，当事人解除劳动合同时，除另有约定外，用人单位要求劳动者履行竞业限制义务，或者劳动者履行了竞业限制义务后要求用人单位支付经济补偿的，人民法院应予支持。”&lt;/p&gt;
&lt;h1 id=&#34;竞业限制期限内用人单位可以随时通知劳动者解除竞业限制协议但需向劳动者额外支付3个月的竞业限制经济补偿&#34;&gt;竞业限制期限内，用人单位可以随时通知劳动者解除竞业限制协议，但需向劳动者额外支付3个月的竞业限制经济补偿。&lt;/h1&gt;
&lt;p&gt;《最高人民法院关于审理劳动争议案件适用法律若干问题的解释(四)》第九条规定：“在竞业限制期限内，用人单位请求解除竞业限制协议时，人民法院应予支持。在解除竞业限制协议时，劳动者请求用人单位额外支付劳动者三个月的竞业限制经济补偿的，人民法院应予支持。”&lt;/p&gt;
&lt;p&gt;所以**只要额外支付3个月的竞业限制经济补偿，用人单位就可以解除竞业限制协议，**该规定对用人单位是有利的，对劳动者不利。&lt;/p&gt;
&lt;h1 id=&#34;用人单位不支付竞业限制经济补偿劳动者可以解除竞业限制协议&#34;&gt;用人单位不支付竞业限制经济补偿，劳动者可以解除竞业限制协议。&lt;/h1&gt;
&lt;p&gt;《最高人民法院关于审理劳动争议案件适用法律若干问题的解释(四)》第八条规定：“当事人在劳动合同或者保密协议中约定了竞业限制和经济补偿，劳动合同解除或者终止后，因用人单位的原因导致三个月未支付经济补偿，劳动者请求解除竞业限制约定的，人民法院应予支持。”&lt;/p&gt;
&lt;p&gt;如果用人单位未支付竞业限制经济补偿达3个月的，劳动者获得竞业限制协议的解除权，可以通知用人单位解除竞业限制协议或向法院申请解除竞业限制协议，并要求用人单位支付已履行竞业限制期间的经济补偿。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;司法实践中，用人单位在劳动者离职后3个月内未按时足额支付竞业限制经济补偿的，并不当然导致竞业限制协议无效，如果劳动者在3个月内从事有竞争关系的业务，将很大可能认定劳动者违反竞业限制协议，需要向用人单位支付违约金。用人单位未支付竞业限制经济补偿达3个月，劳动者未行使竞业限制解除权即未通知用人单位解除竞业限制协议或向法院申请解除竞业限制协议，直接从事有竞争关系的业务，还是有可能被认定劳动者违反了竞业限制协议，对劳动者非常不利。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;竞业限制协议能不签署就不要签署，签署后，是否履行或解除的权利都在用人单位一方，劳动者非常被动。&lt;/li&gt;
&lt;li&gt;如果签署了竞业限制协议，一定要在离职时跟用人单位确认是否需要劳动者履行，如果不需要履行竞业限制协议，要拿到用人单位的书面文件，避免3个月内入职了有竞争关系的企业，被认定违反竞业限制协议而支付高额违约金。&lt;/li&gt;
&lt;li&gt;如果签署了竞业限制协议，离职时用人单位未通知是否需要履行或通知需要履行竞业限制协议，都是需要履行。建议不管用人单位有没有支付竞业限制经济补偿，离职之日起三个月内，都不要入职与用人单位有竞争关系的企业，否则很大可能被认定违反了竞业限制协议，需要向用人单位支付违约金。&lt;/li&gt;
&lt;li&gt;超过三个月用人单位还未支付竞业限制经济补偿的，在从事与用人单位有竞争关系的业务前，最好立即向法院申请解除竞业限制协议，并要求用人单位支付已履行竞业限制期间的经济补偿。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>浏览器 STATUS_INVALID_IMAGE_HASH 错误状态码解决办法</title>
      <link>http://coder.amazingdemo.top/post/%E6%B5%8F%E8%A7%88%E5%99%A8status_invalid_image_hash%E9%94%99%E8%AF%AF%E7%8A%B6%E6%80%81%E7%A0%81%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
      <pubDate>Thu, 10 Feb 2022 08:08:32 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%B5%8F%E8%A7%88%E5%99%A8status_invalid_image_hash%E9%94%99%E8%AF%AF%E7%8A%B6%E6%80%81%E7%A0%81%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
      
        <description>&lt;p&gt;解决方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开注册表&lt;/li&gt;
&lt;li&gt;进入\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome（Policies没有Google和Chrome就新建项）&lt;/li&gt;
&lt;li&gt;然后新建DWORD(32位)，将名称改为RendererCodeIntegrityEnabled，值为0&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;重启浏览器问题解决。&lt;/p&gt;
&lt;p&gt;因为edge和chrome内核相同，如果是Micro Edge遇到此问题，可以使用同样方法。对应文件夹名要改如下图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;注册表&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如何面试程序员</title>
      <link>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E7%A8%8B%E5%BA%8F%E5%91%98/</link>
      <pubDate>Wed, 22 Sep 2021 17:54:33 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E7%A8%8B%E5%BA%8F%E5%91%98/</guid>
      
        <description>&lt;h1 id=&#34;提问之前的准备&#34;&gt;提问之前的准备&lt;/h1&gt;
&lt;p&gt;首先，最重要的是，你自己一开始就应该想清楚：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;需要新员工完成什么样的任务？&lt;/li&gt;
&lt;li&gt;怎样的人能完成这样的任务？&lt;/li&gt;
&lt;li&gt;哪些途径和方法可以发现这样的人？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;只有明确这些根本性的问题，才能正确高效地完成面试。&lt;/p&gt;
&lt;h1 id=&#34;提问的原则&#34;&gt;提问的原则&lt;/h1&gt;
&lt;p&gt;假定你对上一节的三个问题，已经有了清晰的想法，那么接下来就可以设计如何提问了。&lt;/p&gt;
&lt;p&gt;有一些提问的原则，是你应该遵循的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每一个面试问题都有明确的目的。你不仅自己了解，还能向其他面试官解释清楚。&lt;/li&gt;
&lt;li&gt;多提一些开放性（Open-ended）的问题，而不是那种用Yes/No就可以回答的问题。这样做使你有机会与面试者展开讨论，并且提出后续的问题，尽可能多地了解对方。&lt;/li&gt;
&lt;li&gt;不要问宗教、家庭、健康、个人隐私等方面的问题。&lt;/li&gt;
&lt;li&gt;不要问太复杂的问题。因为面试者没有太多思考时间，所以无法周全地回答，你也就无从判断他的能力了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;考察专业能力&#34;&gt;考察专业能力&lt;/h1&gt;
&lt;p&gt;为了确认面试者是胜任的，你可以问一些与职位相关的专业方面的问题。（不过通常来说，一次面试不足以看出一个人的专业能力。）&lt;/p&gt;
&lt;p&gt;比如，你的招聘职位是系统管理员，你可以问&amp;quot;如何快速地在50台机器上部署Linux？&amp;quot;（提示：正确答案不是刻录50张安装光盘。）&lt;/p&gt;
&lt;p&gt;另外，你还应该向面试者了解他的过去，因为过去是未来的最好预测依据。不过，提问的重点不要仅仅是他过去的成果，更要关注在当时的环境中，他是如何决策和实施的。&lt;/p&gt;
&lt;h1 id=&#34;考察综合素质&#34;&gt;考察综合素质&lt;/h1&gt;
&lt;p&gt;因为人是会发展的，所以某种程度上，面试者的综合素质要比他的专业能力更重要。&lt;/p&gt;
&lt;p&gt;所以，具体的技术问题（如何调用API、什么是设计模式、编程语言的语法等等）可以少问一些，更应该关注面试者的事业心、对工作的热情、进取心、自律能力、毅力等方面。&lt;/p&gt;
&lt;p&gt;下面是一些典型问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why did you get into development?
你为什么开发软件？&lt;/p&gt;
&lt;p&gt;How many technical books did you read in the past year?
去年你读了几本技术书籍？&lt;/p&gt;
&lt;p&gt;What was your favorite technical book in the past year? What did you learn from it?
去年你最喜欢的技术书籍是哪本？你从中学到了什么？&lt;/p&gt;
&lt;p&gt;What websites do you read regularly, related to development?
平时你经常访问哪些编程类网站？&lt;/p&gt;
&lt;p&gt;Do you maintain any open-source projects?
你有自己的开源项目吗？&lt;/p&gt;
&lt;p&gt;Do you code in your spare-time?
业余时间你编程吗？&lt;/p&gt;
&lt;p&gt;Do you love programming, or do you do it for the money?
对于你来说，编程是一种爱好，还是一种谋生手段？&lt;/p&gt;
&lt;p&gt;Have you accomplished anything important in your career yet? Do you want to?
你的职业生涯之中有什么重要的成就？它是你主导的吗？&lt;/p&gt;
&lt;p&gt;What would make you feel that you have done something important?
什么事情会让你很有成就感？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;考察理性思维&#34;&gt;考察理性思维&lt;/h1&gt;
&lt;p&gt;某些情况下，你可能需要了解面试者的分析判断能力，看他能否全面地思考问题、客观地评价自己。&lt;/p&gt;
&lt;p&gt;那么，你可以依次提出这样三个问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&amp;rsquo;s your favorite programming language? Why?
你最喜欢的编程语言是哪种？为什么？&lt;/p&gt;
&lt;p&gt;If you could add one feature to your favorite language, what would it be? Why?
如果允许你为这种语言加一种功能，你会加什么功能？为什么？&lt;/p&gt;
&lt;p&gt;If you could remove one feature from it, what would it be? Why?
如果允许你取消一种功能，会是什么功能？为什么？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里的重点是，让面试者从正反两方面评价一件自己熟悉的东西，看看他的思维是否片面。答案无所谓对错，只要面试者有一个明确的立场，能够从正反两方面说出令人信服的理由，就可以了。比如，某个软件的口碑不好，但是面试者说他很喜欢，而且说得出一大堆理由，清楚地解释了这种软件的优点和缺点在哪里，这样就很好。&lt;/p&gt;
&lt;p&gt;你还可以把这些问题，套用在其他东西上面，比如操作系统、文字编辑器等等。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>硅谷公司是如何培训面试官的</title>
      <link>http://coder.amazingdemo.top/post/%E7%A1%85%E8%B0%B7%E5%85%AC%E5%8F%B8%E6%98%AF%E5%A6%82%E4%BD%95%E5%9F%B9%E8%AE%AD%E9%9D%A2%E8%AF%95%E5%AE%98%E7%9A%84/</link>
      <pubDate>Tue, 21 Sep 2021 23:55:03 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%A1%85%E8%B0%B7%E5%85%AC%E5%8F%B8%E6%98%AF%E5%A6%82%E4%BD%95%E5%9F%B9%E8%AE%AD%E9%9D%A2%E8%AF%95%E5%AE%98%E7%9A%84/</guid>
      
        <description>&lt;h1 id=&#34;编程篇&#34;&gt;编程篇&lt;/h1&gt;
&lt;p&gt;面试就好像是在第一次约会的时候就要决定是不是要结婚&lt;/p&gt;
&lt;p&gt;这是一个真实的笑话。在每日高强度的工作当中，我们和一些同事相处的时间甚至会比家人还要长。所以面试是工作中十分重要的一个环节。&lt;/p&gt;
&lt;p&gt;正文开始之前，请你先想像一个场景：有一天，你的老板找到你，让你和另一名工程师合作完成一个项目，那么你希望这名的工程师具有什么样的品质？开发速度快(coding machine)？代码清晰易懂？接口易用？系统搞设计能力强？知识渊博？靠谱？有激情？容易相处？自我驱动力强？或者以上所有？那么你所想看到的优点也就同样是你在面试时候需要考察的重点。面试首先要明确目标，再通过目标反向推导出面试的考察点和内容。硅谷大公司的面试分为三种类型：编程面试，系统设计面试，行为面试。一般在编程面试或者系统设计面试中不会涉及行为面试，但是在行为面试中可能会加入简单的系统设计以及编程。本文主要分享的是编程面试的经验总结，所以不会涉及过多的系统设计或者行为面试的内容。其他类型的面试会在之后的文章中讨论。&lt;/p&gt;
&lt;h2 id=&#34;编程面试中我们应该考察面试者的什么能力呢&#34;&gt;编程面试中我们应该考察面试者的什么能力呢？&lt;/h2&gt;
&lt;p&gt;编程面试主要测试面试者的如下几点能力和品质：解决复杂问题的能力，沟通能力，编程能力，调试能力，测试能力，知识储备等。除此之外，面试者的态度，习惯等也在考察范围之内。&lt;/p&gt;
&lt;p&gt;解决问题：解决问题的能力包括是否能把复杂问题简单化，比如把一个大的问题分解成若干个小的问题。面对复杂问题的时候，面试者能否先想出一个简单的解法而非最优的解法。在工作中，我们经常会遇到复杂的项目和问题，需要工程师把问题分解，先想出一个方案可以快速满足需求。&lt;/p&gt;
&lt;p&gt;沟通能力：沟通能力十分重要。我相信很多人都经历过因为沟通中出现的问题导致项目进展不顺利或者做了无用的努力。在系统设计和行为面试两轮会对面试者的沟通能力有更全面的评估，代码轮更多的是考察面试者解释思路的能力。&lt;/p&gt;
&lt;p&gt;编程能力：这个涉及的范围就非常广泛了：代码的正确性，结构是否清晰，设计是否合理，编程习惯是否良好，代码是否易读，速度是否快。对于代码的要求和在平时工作当中是一样的，并不会因为是在面试而降低要求。&lt;/p&gt;
&lt;p&gt;调试能力：我很少见到有人在面试的时候能把代码一次写对，编译和运行的过程当中总会有错误。我们需要每一位工程师都有能快速找出代码问题的能力。&lt;/p&gt;
&lt;p&gt;测试能力：这里最重要的一点是能否找出所有的edge case以提高测试的覆盖率。我们每天在做代码审查的时候会尽量考虑到所有可能的情况。然而现实中，大部分的edge case都是由代码编写者发现并处理。所以写好测试的能力是十分重要的。&lt;/p&gt;
&lt;h2 id=&#34;什么样的题目算是一个好的面试题&#34;&gt;什么样的题目算是一个好的面试题？&lt;/h2&gt;
&lt;p&gt;一个好的面试题应该能够考察面试者以上所有的能力。&lt;/p&gt;
&lt;p&gt;首先，一道题目不能太过直接简单。简单的题目既不能测试出面试者的能力，又不能拉开优秀和普通工程师的距离。&lt;/p&gt;
&lt;p&gt;其次，问题应该具有延展性。有些面试者会比其它人更快的做出题目。这个时候如果重新问一道全新的题需要花时间解释。然而在已经完成的题目上做延伸就不会有这样的问题。 比如可以禁止一些操作，或者对复杂性提出更高的要求。更难的问题可以帮你找出真正优秀的工程师。&lt;/p&gt;
&lt;p&gt;第三，题目可以具有一些不确定性。比如在解释题目的时候漏掉一些信息。举个例子，输入integer的范围和正负性。这种方式可以帮助你考察面试者思考问题是不是全面（测试能力），以及是否有良好的沟通能力。&lt;/p&gt;
&lt;p&gt;第四，要避免智力题和脑筋急转弯。有些问题太过于依赖面试者的智力表现。举个例子，25匹马赛跑，最少几次找出前3名。不推荐这类问题有几个原因：1. 通过智力题目选出来的工程师不一定能写出优秀的代码。上文中的提到的绝大部分能力都无法考察。2. 尤里卡时刻依赖灵光一现。面试的时候有没有尤里卡时刻有一定的运气成分。一个优秀的工程师很有可能因为客观的原因没有想到答案而被淘汰。&lt;/p&gt;
&lt;p&gt;第五，请时不时的去网上搜索一下贵公司的面经，看看自己的题目有没有被泄露。如果被泄露，为了公平起见，请及时更换题目。&lt;/p&gt;
&lt;h2 id=&#34;要注意面试者的哪些错误&#34;&gt;要注意面试者的哪些错误？&lt;/h2&gt;
&lt;p&gt;有一些常见的错误是面试者的通病，需要在面试反馈中记下来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在没弄清楚题目之前就开始写代码。比如题目的输入是一个数字，面试者有可能假设这个数字是整数而直接写代码。&lt;/li&gt;
&lt;li&gt;没有想清楚思路就开始写代码。经常有面试者写着写着发现思路不对，于是删了代码重写。这个时候时间已经过了一半。大部分情况下，做开发之前都要弄清楚需求，不然很有可能会做无用功。以上两个问题都可以反映出面试者是不是有犯这类问题的潜质。&lt;/li&gt;
&lt;li&gt;代码没有处理一些明显的edge case。如果一个人的代码只能在80%的情况下运行，环境稍微特殊一点就会crush，那么你以后审核他的代码必须要小心翼翼，这样的人招到组里来大家都很累。&lt;/li&gt;
&lt;li&gt;写代码很慢。大部分公司都需要程序员可以高效的开发。这里就不赘述了。&lt;/li&gt;
&lt;li&gt;代码不规范。写出清晰漂亮的代码应该是深深刻在每个程序员骨子里的东西。面试的时候虽然时间紧张，但是有些时候一个空格，一个换行都应该是习惯性的肌肉记忆。如果代码写的很乱一定程度上反映出了面试者平时的编程的习惯。&lt;/li&gt;
&lt;li&gt;基础知识缺乏。从简单的时间和空间复杂性到常用的数据结构，都应该是各个层次的面试者要熟练掌握的。&lt;/li&gt;
&lt;li&gt;沟通能力较差。无法清晰的解释自己的思路或者代码。这个在工作沟通当中效率会受到影响，招进来需要进一步培训。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;如何写好面试反馈&#34;&gt;如何写好面试反馈？&lt;/h2&gt;
&lt;p&gt;Facebook最常用的是“加减号”记录法：按时间顺序记录面试者的data points，用加减号区分加分项和减分项。比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;+清晰的描述了解题思路，并确认了输入数值的范围。&lt;/li&gt;
&lt;li&gt;-没有处理输入值是0的情况&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样时候我们可以清晰明了的看出面试者的优缺点。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;要有一个概括总结。上一条提到我们提到按时间顺序记录面试的过程，但是这个过程往往很长，我们还需写出简单干练的总结，概括出面试者在各项能力上的表现。这样别人在看你写的反馈的时候就不需要通读全文了。&lt;/li&gt;
&lt;li&gt;备份面试者的代码。如果是在线面试，只需要把代码复制一下。如果是在白板上，那么请照相，然后贴在反馈里。&lt;/li&gt;
&lt;li&gt;为了提高效率，我会准备一个反馈模板。填空要比每次重头写更有效率。也可以防止漏掉重要的信息和考察点。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;如何决定是不是给offer&#34;&gt;如何决定是不是给offer？&lt;/h2&gt;
&lt;p&gt;无论是否给offer，很多时候都是一个艰难的决定。当两难的时候，我通常问自己的问题是：我愿不愿意在未来的几年里，和这个人坐在一起，共同开发一个项目? 这个通常是一个很实用的标准。如果遇到十分极端的情况，你在问完自己这个问题以后还是不确定，那么就不要给offer。一个好的hire一定是让你和他/她一起工作你会很兴奋。如果做不到的话，宁缺毋滥。&lt;/p&gt;
&lt;h2 id=&#34;面试官要注意什么&#34;&gt;面试官要注意什么？&lt;/h2&gt;
&lt;p&gt;面试是双向的。面试不仅是我们考察面试者是否合格，也同样是面试者考察这个公司或者这个组是否值得加入。我们经常看到因面试体验很差在网上吐槽的事情。这对公司来说是个极其负面的宣传。所以请在面试的时候尊重面试者，无论他看起来是否符合你的招聘标准。通常Facebook要求面试官做到一下几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关闭手机的消息提醒。频繁的震动会给面试者带来不必要的压力，也会打断面试者的思路。&lt;/li&gt;
&lt;li&gt;用电脑之前告诉面试者你是在记笔记，而不是在处理自己的事情。&lt;/li&gt;
&lt;li&gt;态度要友善，专业。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;如何培训面试官&#34;&gt;如何培训面试官？&lt;/h2&gt;
&lt;p&gt;Facebook一般在面试官上任之前要经过如下四个步骤：1， 在公司呆满三个月（曾经是一年），这样对公司的文化才有一定的了解。2，经过一个小时的面试培训，培训的内容已经包括在上文中了。3, shadow interview 一轮，也就是在旁边看资深面试官是怎么面试的。4. Reverse-shadow inteview 这轮面试的时候有资深面试官在旁边观察你，如果你通过考察之后就可以独立面试了。&lt;/p&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;乔布斯曾经说过“一流的人只愿跟一流的人在一起，如果有二流的人进来，就会有三流、四流、不入流的人进来”。找到一流的人才会给公司带来更多一流的人才。所以作为公司的面试官，一定要为公司把好关。&lt;/p&gt;
&lt;h1 id=&#34;系统设计篇&#34;&gt;系统设计篇&lt;/h1&gt;
&lt;p&gt;硅谷大部分公司（FB、 Amazon、 Uber 等）会有专门的一场面试考察候选人的系统设计能力。一些公司（如Google）会时常把系统面试和编程面试或者行为面试混在一起考察。无论是哪种形式，过硬的系统设计能力都是优秀程序员必备的能力，所以系统设计则是面试中的必不可少的一个环节。候选人等级越高，对候选人的系统设计能力要求也就越高。根据候选人的等级（初级，中级，高级，staff等）和岗位（比如产品，infra等），面试官需要对系统设计题目的内容和难度做出一定的调整，在下面的文章中会做出详细说明。本文不会涉及具体某个系统，而是把重点放在面试方法上。因为技术更新的很快，而面试题目也会因为被用的次数太多而被抛弃，但是面试的方法和思路是很少改变的。&lt;/p&gt;
&lt;h2 id=&#34;所有的问题解决方案都应该从问题本身出发&#34;&gt;所有的问题解决方案都应该从问题本身出发&lt;/h2&gt;
&lt;p&gt;开发系统就是为了解决问题。那么在设计系统之前就要分析问题的本身。我发现越是有经验的候选人，越是会在开始设计前弄清楚要解决的问题是什么。比如：”我们为什么要设计这个系统？“， ”我们具体要解决什么问题？“。这就好比一个人管你要锤子，帮助这个人的最优的方式不是给他一个锤子，也不是问他要什么样的锤子，而是问清他到底要做什么？如果这个人的目的是开核桃，给他一个核桃夹子更能帮助到他。有些候选人会认为问题的本身不言而喻。然而在现实中，开发的系统不解决用户问题的例子比比皆是。我们在面试中要寻找那些会从用户角度出发思考问题的人。&lt;/p&gt;
&lt;p&gt;除此之外，候选人应当主动去了解问题的细节。比如DAU是多少？具体要提供哪些功能？系统需要支持什么操作？是不是大部分情况下是读操作？这些细节问题会直接影响整个系统的设计。这个环节面试官可以让候选人自己提出假设。重点不是什么假设，而是假设是否合理。面试官在一开始描述问题时，可以选择模糊细节以鼓励候选人提问。比如问题可以是：设计谷歌地图、设计redis。这些系统和产品不可能在短时间内设计出来，因此面试者必须首先确定问题的范围。&lt;/p&gt;
&lt;h2 id=&#34;定义成功&#34;&gt;定义成功&lt;/h2&gt;
&lt;p&gt;另外一个候选人经常忽略的问题是如何定义系统的成功？根据要设计系统的不同，成功的衡量标准可以是具体的数字指标：比如延时在200毫秒以内，吞吐率在1k/s 等。也可以是功能的实现：比如最终一致或者提供搜索功能。或者是定义成功的指标：比如DAU、MAU，留存率等。有经验的候选人一般会很自然的提出这点，因为在日常的工作中，定义成功是系统设计中十分重要的一个环节。这个部分答案本身并不重要，重要的是为什么是这个答案。比如候选人定的目标是延时在200毫秒以内，重要的是候选人能否说清楚为什么要在200毫秒以内，而不是500毫秒或者1秒。&lt;/p&gt;
&lt;h2 id=&#34;系统组件设计&#34;&gt;系统组件设计&lt;/h2&gt;
&lt;p&gt;候选人应该具有从全局出发的能力，思考系统需要什么组件，每一个组件的职责是什么。常见的错误包括：1. 组件的职责不清楚：候选人无法解释清楚一个组件到底是做什么的。2. 一个组件负责的功能太多：候选人应当拆分组件以增加可复用性。3. 功能放在了错误的组件上。 4. 组件高度耦合：一个组件过于依赖另外一个组件的具体实现。产品工程师可以把重点放在组件API的设计上。&lt;/p&gt;
&lt;h2 id=&#34;估算&#34;&gt;估算&lt;/h2&gt;
&lt;p&gt;容量估算是系统设计中重要的一个环节。候选人需要根据之前所给的需求量、数据量等指标来预测设计是否可行以及需要消耗多少资源。如果候选人没有预估，面试官应该主动要求候选人做出计算。比如：我们需要部署多少台机器？每天需要新增多少存储？给出的答案不重要，重要的是候选人的预估是否合理。如果做出了假设，假设是否合理？预估很有可能会影响系统设计中所做出的选择，所以要及时讨论可能存在的问题。有经验的候选人会时常停下来做预估。&lt;/p&gt;
&lt;h2 id=&#34;提出多种方案利弊权衡做出选择&#34;&gt;提出多种方案，利弊权衡，做出选择&lt;/h2&gt;
&lt;p&gt;面试官应当鼓励候选人给出不同的解决方案，并询问每种方案的优缺点。候选人应当最终选择一种方案，然后给出选择的理由。面试过程中偶尔会遇到优柔寡断的候选人，面试官应该鼓励候选人做出最终决定。因为不选择很多时候比做错误的选择代价还要高。&lt;/p&gt;
&lt;h2 id=&#34;挖掘t型人才&#34;&gt;挖掘T型人才&lt;/h2&gt;
&lt;p&gt;T型人才是指一个人的知识点既有深度又有广度。他对全栈的每一个部分都有一定的了解，但是又对某一个或几个领域有很深的研究。T型人才的深度可以帮助公司解决复杂的问题。而他们的广度可以让他们更好的和他人合作。想找到T型人才就需要在面试过程中深度探讨一些技术细节。建议面试官提前阅读面试者的简历，并找到面试者所熟知的领域。比如数据库、网络、系统安全等，在面试的过程中可以花多一点时间讨论这些领域，深挖细节。所选的领域最好是面试者熟悉的，否则很难对候选人做出正确的评估。但是注意避免因讨论过多的细节而没能给面试者足够的时间设计出端到端的解决方案。&lt;/p&gt;
&lt;h2 id=&#34;根据候选人的等级和岗位调整题目&#34;&gt;根据候选人的等级和岗位调整题目&lt;/h2&gt;
&lt;p&gt;一般本科毕业生（对应Google level 3）并不具备很强的复杂系统设计能力。上学阶段很少有机会设计大型系统，所以OOP设计比较合适初级工程师。对与中级工程师（level 4）的要求是可以做小型项目，所以可以问service oriented架构设计。高级工程师（level 5）则需要涉及到到可扩展性、高可用、跨数据中心等问题。Staff engineer (level 6) 在level 5的基础上还要从多方面考虑系统的operations。比如可测试性，安全性和可调试性等。对于系统中每个组成部分都要有比高级工程师更深刻的理解。&lt;/p&gt;
&lt;p&gt;对于不同的岗位，系统面试的侧重点也应该不同。比如前端工程师的系统面试形式和后端就不太一样。做infra的和做产品的也不一样。做ML的和大家都不一样。大部分公司做产品的工程师不需要知道公司下半年要多买多少台机器来支撑服务，而infra的题目不需要有太多的API设计。产品工程师要考虑用户的交互，而这些并不是infra面试的重点。在选择面试题目和难度的时候都要根据工程师的级别和岗位做出相应的调整。否则既无法做出正确的判断，候选人的面试体验也很差。&lt;/p&gt;
&lt;h2 id=&#34;建立系统设计面官试培训流程&#34;&gt;建立系统设计面官试培训流程&lt;/h2&gt;
&lt;p&gt;系统设计面试的难点在于问题通常没有统一的答案和标准。根据候选人申请的岗位、级别、背景不同，面试官要对题目进行调整。因此设立面试培训流程就很有帮助。流程一般包括三步：培训，实例分析和面试观摩。培训的内容可以以本文做参考。实例分析中，经验丰富的面试官找一到两道真题，掰开揉碎讲给学习者听，通过例子告诉学习者哪里是考点、哪里是难点、如何给提示、如何评级等。面试观摩一般分为两步：观摩和反向观摩。观摩一般是去旁听有经验的面试官面试。反向观摩则是有经验的面试官去旁听学习者的面试，提出改进意见并且打分。不合格的需要反复进行这个部分。对于新的题目，在拿来面试之前，一定要找同事来模拟，以保证题目的质量和评判标准的准确性。&lt;/p&gt;
&lt;h2 id=&#34;写在最后-1&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;和其它形式的面试一样，面试官培训中要包含其它的一些主题，比如如何写好面试反馈，如何决定是不是发offer，面试官在面试过程中要注意什么等。&lt;/p&gt;
&lt;p&gt;俗话说：文无第一，武无第二。如果说编程题目是比武的话，系统设计就是比文了。系统设计没有正确的答案，只有适合不适合。面试官不应心存完美的设计，并要求面试者给出一模一样的答案。对于同一道题目，每个候选人都会给出不一样的设计。我们要分析每一个设计的利弊。&lt;/p&gt;
&lt;h1 id=&#34;把握面试的节奏&#34;&gt;把握面试的节奏&lt;/h1&gt;
&lt;p&gt;如果面试是一个小时的话，我一般这么分割时间：&lt;/p&gt;
&lt;p&gt;0-5分钟：问面试者是不是需要喝水上卫生间。简单的互相介绍一下，问问面试者之前的经历。从面试者熟悉的内容开始有助于缓解面试者紧张的情绪。&lt;/p&gt;
&lt;p&gt;5-55分钟：做题。这个时候你需要尽量收集面试者的信息。所以如果面试者卡在某个具体的问题上太久，你可以适当地给一些提示，如果在一个问题上花费太多的时间，他就没有机会把系统设计完整了。&lt;/p&gt;
&lt;p&gt;55-60分钟：回答面试者的问题。这个环节有两个事情需要做：第一是推销你的公司，第二是从其他方面了解面试者。面试者的问题很多时候都反映出了工作中哪些地方对他/她重要。比如公司文化，工作时长等。&lt;/p&gt;
&lt;p&gt;如果面试是45分钟，我会把中间做题的时间缩短，前后的十分钟保持不变。因为两部分对面试十分重要，不能缩的更短了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>陪你骑车好吗 - 驻马店浮沉</title>
      <link>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E9%A9%BB%E9%A9%AC%E5%BA%97%E6%B5%AE%E6%B2%89/</link>
      <pubDate>Sat, 17 Jul 2021 09:08:27 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E9%A9%BB%E9%A9%AC%E5%BA%97%E6%B5%AE%E6%B2%89/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;自从上次拿P车跑完不限车的比赛，我和孩子妈都陷入了沉思。我觉得孩子已经很努力跑得不错了，但是出于装备的碾压，成绩并不是很理想。&lt;/p&gt;
&lt;p&gt;而且训练的时候，教练也跟我们说，感觉孩子跑的还可以，基本体能，骑车动作都不错，注意力也很集中。但是P车的车架太大了，孩子身长臂展暂时还不够，所以姿势总有些怪异，影响她的速度。于是我萌生了换S车的想法。&lt;/p&gt;
&lt;p&gt;细节不表，总之我换车了O(∩_∩)O&lt;/p&gt;
&lt;p&gt;换车以后，我跟孩子妈一致商量，觉得孩子目前还是有很多细节问题需要改进，而且刚换车，也需要熟悉熟悉车子。所以我们开始了一个半月的蛰伏。&lt;/p&gt;
&lt;p&gt;在这一个半月里，我就是带着孩子上课，跑赛道，纠问题，继续上课，跑赛道，找问题；不断的重复重复再重复。&lt;/p&gt;
&lt;p&gt;俱乐部的赛道卡是一个半小时的时长，当时我就给孩子制定了一个训练积分制。&lt;/p&gt;
&lt;p&gt;每次训练，赛道计时一圈，70秒以上不计分，60-70计一分，55-60计两分，50-55计三分，必须至少跑三圈才能休息喝水上厕所，累积三十分就下课。&lt;/p&gt;
&lt;p&gt;最开始的时候，她基本上一场下来要跑二十六七圈，而且跑不到三分。但是几个星期以后，她就基本二十圈左右就能解决战斗了，而且状态好的话，有冲击三分的可能。可喜可贺^o^/&lt;/p&gt;
&lt;p&gt;当然，针对训练强度问题，我在这段训练期间，跟教练讨论，以及到各大学术周刊上查阅了一些医学相关资料。&lt;/p&gt;
&lt;p&gt;有很多家长总是抱怨，哎呀，这么小就让孩子跑这么长时间，累死了；这么小就练专项体育，不利于骨骼发育，影响以后生长。&lt;/p&gt;
&lt;p&gt;可是你想想，你把孩子丢到游乐场里，她上串下跳的可以玩一下午，就运动量而言，不比我骑车更大？也没见你让他在游乐场里坐着，不许到处乱跑呀！&lt;/p&gt;
&lt;p&gt;所以我觉得，这不是一个能不能，而是一个你如何让他能的故事。&lt;/p&gt;
&lt;p&gt;我觉得只要运动前做好热身，运动后及时拉伸，运动量不超过她这个年龄所能承受的一个合理范围，循序渐进的训练，就没有任何问题。&lt;/p&gt;
&lt;p&gt;闲话不多扯，回归主题。&lt;/p&gt;
&lt;p&gt;练了一个半月了，我和孩子妈都觉得自己练得还算可以，看起来像那么回事了。正好驻马店的一个俱乐部邀请我们俱乐部组团去比赛，我们也想检验一下训练的水平，也就蹭着蹭着一起去了。&lt;/p&gt;
&lt;p&gt;比赛过程，好像也没有太多好言语的。&lt;/p&gt;
&lt;p&gt;赛前，我和孩子妈还一直很担心我们实力到底怎么样，一个多月就光埋头练，也不知道自己是不是真的准备好了。&lt;/p&gt;
&lt;p&gt;比赛过程倒是很轻松加愉快，基本就是两个弯以后，一骑绝尘，别人连车尾灯都看不到了。&lt;/p&gt;
&lt;p&gt;如愿拿到了冠军，奖励了她一根雪糕，然后坐在赛场边开始为俱乐部其他的小伙伴们加油o(&lt;em&gt;^▽^&lt;/em&gt;)┛&lt;/p&gt;
&lt;p&gt;我作为家长也挺开心的，四周恭维之声不绝于耳，让我似乎有些飘飘然了，也为日后滑铁卢埋下伏笔。&lt;/p&gt;
&lt;p&gt;因为这一次在驻马店跑得挺好的，而且也没遇到太多的阻碍。所以当过了几周赛事群里传来驻马店另一个俱乐部要办赛的消息后，我就悄么么的报了比赛，想着过去随便跑跑，再为自己的职业生涯添加一金。&lt;/p&gt;
&lt;p&gt;在我自己心里，我已经幻想着我们过去，随便跑两把，随便拿个奖杯，随便领个奖品，就可以打道回府了。结果现实无情的给了我一巴掌。&lt;/p&gt;
&lt;p&gt;赛前，主办方公布的信息里，我就隐约感觉有些不对劲。&lt;/p&gt;
&lt;p&gt;主办方说是室内场，但是我看到的是水泥地篮球场地面，室内篮球场怎么着都不会是水泥地吧；而且主办方说六条长直道，我查过，标准篮球场长28米宽15米，怎么看都不像能摆下六条“长”直道的样子。&lt;/p&gt;
&lt;p&gt;到了现场，就全明白了。&lt;/p&gt;
&lt;p&gt;所谓室内场，还真是室内；无顶棚的露天室内运动馆，正好比赛当天还在下小雨，篮球场上还有少量渍水呢，这室内和我所理解的室内还真是千差万别，主办方抠字眼功夫可真了的。&lt;/p&gt;
&lt;p&gt;赛道也的确是六条直道，但是绝对不长，每条直道不到30米。而且特别窄，我估计最宽不超过3米，并排四个小朋友骑得几乎要肩碰肩了。毕竟就一个篮球场那么大，你还想摆六条直道，呵呵。&lt;/p&gt;
&lt;p&gt;最关键的是，在一周前的车聚上，当时的车聚场地也是跟这一摸一样的一个喷漆水泥篮球场。车聚前也是下了场小雨，地面湿滑，车聚时，孩子摔了七八次，已经摔怕了。&lt;/p&gt;
&lt;p&gt;在开始比赛前，我心里已经特别的七上八下了，赛前主办方的宣传又是室内，又是大直道跑尽兴的，结果这完全的货不对板。而且我肉眼都看得出来孩子对这个地面的恐惧之情。&lt;/p&gt;
&lt;p&gt;我只能一遍遍的拿脚在地上磨给孩子看，打肿脸充胖子的跟孩子说：你看，虽然看起来地跟上次那个地是一样的，但是其实不一样哦，这个地一点都不滑哟，放心大胆地跑！&lt;/p&gt;
&lt;p&gt;然后我还在赛场边做了一个奔跑加急停的示范；嗯，不出意外的，我差点滑摔了。这TM赛没法比了！！！&lt;/p&gt;
&lt;p&gt;比赛结果，不用多说，惨不忍睹。&lt;/p&gt;
&lt;p&gt;我看着我的孩子，在前两个直道遥遥领先，然后每次要过弯的时候，孩子就减速，车子都快刹停了，再散步般的溜达过去，然后进入直线再加速冲起来。这么玩是没前途的，三四个弯以后，就会被反超，然后遗憾落败。&lt;/p&gt;
&lt;p&gt;初赛，复活赛，回家。回家路上，巨大的心理落差，让我实在忍不住吼了孩子，吼得她哇哇的哭。&lt;/p&gt;
&lt;p&gt;但是吼完她，我还是很后悔的。我觉得吧，从主观上来说，这场比赛办赛水平太低，体验极差，这个破场地限制了我们的发挥。从客观上来讲，上次的轻松获胜让我盲目自大，最近放松了对她的训练，逆水行舟不进则退。&lt;/p&gt;
&lt;p&gt;孩子的硬实力，是一回事。但是临场还是要看发挥，看孩子的状态。所以练车不能光练上限，还得练下限；包括临场如何调节孩子的状态，这都是门学问。&lt;/p&gt;
&lt;p&gt;虽然这场比赛不如意了，但是我从中看到了自己的不足和待提升的地方，也不算全无收获吧，哈哈。&lt;/p&gt;
&lt;p&gt;比如说，我现在就坚定了一点，小赛不要报，太稀烂；钱也花了，还玩得一脸不爽，真是花钱买罪受。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>陪你骑车好吗 - 买了辆PUKY</title>
      <link>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E4%B9%B0%E4%BA%86%E8%BE%86puky/</link>
      <pubDate>Sun, 06 Jun 2021 19:28:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E4%B9%B0%E4%BA%86%E8%BE%86puky/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;平衡车，又叫滑步车，英文名有Balance Bike、Running Bike、Walking Bike，Pushbike,外观就像是卸掉了脚踏板和车链的自行车，因为完全靠宝宝用脚自行滑行前进，非常适合18个月到6岁的孩子。最早起源于德国，后在欧洲迅速风行。&lt;/p&gt;
&lt;p&gt;一、平衡车提供孩子一个肢体肌肉发展的机会。欧洲专家证实经由训练发展幼儿的平衡感, 给与孩子的腿部肌肉足够的刺激，对于将来在社交能力上以及在课室里的专注力的训练都有正面的效果。经由自己学习驾驭驰乘他们的第一部两轮车所能带来的成就感以及培养独力性能在他们自信心上的建立有很大的成效。&lt;/p&gt;
&lt;p&gt;二、平衡车是一项高质量的儿童运动。可促进前庭系统发育，锻炼核心平衡力，提高身体协调性。增加大量户外运动，增强宝宝体质和抵抗力，提高对电视、游戏的免疫力。&lt;/p&gt;
&lt;p&gt;三、平衡车是一个媒介。能够吸引宝宝结伴而行，相约骑行玩乐，在团队活动中，增强孩子的社交能力。&lt;/p&gt;
&lt;p&gt;四、平衡车是一个工具。能培养孩子自信心、胆量以及独立精神，特别是在挑战过程中锻炼孩子的专注力，提高心理素质，提高抗挫折能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最开始接触平衡车还是楼下的小哥哥推荐的。&lt;/p&gt;
&lt;p&gt;楼下小哥哥的妈妈，当初在孩子一岁半的时候，看了日本的小车神国立和玖的视频，果断给儿子入了一款迪卡侬的平衡车，希望自己的孩子有朝一日能像他一样飞翔。&lt;/p&gt;
&lt;p&gt;他们在家瞎玩了一年以后，终于在两岁半的时候找到了组织。寻觅得一家小区附近的平衡车训练俱乐部，于是果断地邀请了他儿子的好朋友，也就是我们与她同去上体验课。&lt;/p&gt;
&lt;p&gt;体验课是在一家室内羽毛球场用塑料锥桶和围栏搭建了一条赛道，赛道内有一些波浪板和坡道。&lt;/p&gt;
&lt;p&gt;腆着脸找主办方要了两台Puky试车，孩子们骑上去了就不下来了呀。而且走的时候，孩子玩得不想走！！！没办法，既然孩子想玩，那就考虑买起吧。&lt;/p&gt;
&lt;p&gt;在买车这个问题上，其实我们有两大纠结点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;玩玩还是系统学习&lt;/li&gt;
&lt;li&gt;买什么车&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们最先考虑的问题，还是你想玩成什么样？你是想让孩子骑着车随便玩玩，当个玩具。还是系统的学习平衡车的技巧，有针对的有计划的当成一项体育运动，就像篮球，足球一样。&lt;/p&gt;
&lt;p&gt;我选择了后者，我不希望孩子认为这是个玩具；我希望她把这个东西当作一项正式的项目，学习正确的骑车姿势和技巧，也避免在骑车过程中的一些不正确姿势导致一些危险。&lt;/p&gt;
&lt;p&gt;街边随便买个车，还是在主办方那里买PUKY呢？在这点上我还是相当的犹豫。一个儿童自行车，两千多的价格，总觉得心理上很别扭。小孩子这兴趣总是一阵一阵的，万一孩子玩两次厌烦了怎么办？这钱就全打水漂了？&lt;/p&gt;
&lt;p&gt;带着这颗纠结的心，我在下一个周末迎来了又一次的体验课。&lt;/p&gt;
&lt;p&gt;这一次的体验课就有点不同了，不知道是主办方故意的，还是因为PUKY RIDE太紧俏了。当时孩子出门耽搁了一下，到现场晚了一点，主办方表示RIDE的试骑车已经没有了，于是给了我一台PUKY LR M。&lt;/p&gt;
&lt;p&gt;LR的轮圈是八寸的比RIDE的十二寸轮圈小了一大圈，而且车架也偏小。当时我看着我的孩子很努力的想追上其他的孩子，但是装备的差距实在很明显，人家大轮子滚一圈，顶她小轮子跑三四圈。人家轻轻松松的绝尘而去，而你只能吃灰。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;PUKY RIDE&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;PUKY LR M&#34;&gt;&lt;/p&gt;
&lt;p&gt;我当时很看不下去，走上前去拉住孩子说我们不骑了，回家去吧！！！可是孩子并不乐意。&lt;/p&gt;
&lt;p&gt;我说你车子不好骑，没人家车子大，骑不过人家，这一点也不好玩。我们不玩了吧，回家玩玩具去，孩子也并不乐意。&lt;/p&gt;
&lt;p&gt;她孩子很坚定告诉我，车子能骑，你看我骑得很快，还往使劲的往前蹬了两下。&lt;/p&gt;
&lt;p&gt;从这里，我感受到了孩子坚毅的一面，所以我也狠下心割肉，课后直接提了台PUKY回去。&lt;/p&gt;
&lt;p&gt;没想到，就这小小的一台车，一项简单的儿童运动，从此以后伴随了我们三年！\&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>前端工程师简史</title>
      <link>http://coder.amazingdemo.top/post/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88%E7%AE%80%E5%8F%B2/</link>
      <pubDate>Fri, 04 Jun 2021 17:33:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88%E7%AE%80%E5%8F%B2/</guid>
      
        <description>&lt;p&gt;互联网的飞速发展，催生了很多高薪职业，程序员便是其中佼佼者。&lt;/p&gt;
&lt;p&gt;在程序员群体中，前端岗位以其门槛低（相对其他技术岗位）、工资高（相对非互联网行业）、发展好（薪酬涨幅高）受到广大有为打工人的青睐。&lt;/p&gt;
&lt;p&gt;这个行业是做什么的？他是如何发展起来的？未来还会高速增长么？&lt;/p&gt;
&lt;p&gt;这一切还得从互联网上古时期的一场战争谈起。&lt;/p&gt;
&lt;h1 id=&#34;上古时期&#34;&gt;上古时期&lt;/h1&gt;
&lt;p&gt;1993年，美国伊利诺伊大学NCSA开发了Mosaic浏览器。该浏览器一经推出，就成为当时最流行的浏览器。一年之后，Mosaic的核心开发者马克·安德森在加州创立Mosaic Communication公司，并于同年推出了大受好评的Mosaic Netscape v0.9。&lt;/p&gt;
&lt;p&gt;由于伊利诺伊大学拥有Mosaic的商标权，并已将相关技术转让给Spyglass Entertainment公司。为了避免可能产生的商标权纠纷，Mosaic Communication公司更名为Netscape Communications，也就是网景。&lt;/p&gt;
&lt;p&gt;在浏览器这一新兴市场一举拔得头筹后，网景开始尝试开发一套基于浏览器的网络操作系统。这一举动使PC操作系统领域的霸主微软嗅到了危险的味道。&lt;/p&gt;
&lt;p&gt;1995年，微软从头Spyglass Entertainment手中买下Mosaic的授权，以此为基础开发了Internet Explorer，大名鼎鼎的IE浏览器就此迈上了历史舞台。&lt;/p&gt;
&lt;p&gt;最终，IE赢得了与网景的斗争。经过这场战争，浏览器逐渐普及开，互联网进入以门户网站主导的web1.0时代。&lt;/p&gt;
&lt;p&gt;在此之后的十年间，又发生了IE与火狐浏览器、IE与谷歌浏览器的斗争。&lt;/p&gt;
&lt;p&gt;我们故事的主人公 —— 前端工程师还未登上历史舞台。别急，让我们先来了解些行业知识。&lt;/p&gt;
&lt;h1 id=&#34;什么是前端工程师&#34;&gt;什么是前端工程师&lt;/h1&gt;
&lt;p&gt;我们需要了解“前”和“端”是什么。&lt;/p&gt;
&lt;h2 id=&#34;前&#34;&gt;前&lt;/h2&gt;
&lt;p&gt;前的概念是相对于后的。&lt;/p&gt;
&lt;p&gt;当我们在百度搜索“你好”。&lt;/p&gt;
&lt;p&gt;后端负责提供“你好”相关的数据，前端负责在浏览器中将数据排版并展示。&lt;/p&gt;
&lt;p&gt;如果前端工程师是个画家，后端工程师就是站在他背后默默告诉他需要画什么东西的人。正在看本文的你，就是欣赏这幅画的人。&lt;/p&gt;
&lt;h2 id=&#34;端&#34;&gt;端&lt;/h2&gt;
&lt;p&gt;我们刚才以浏览器举例。浏览器就是一个端。&lt;/p&gt;
&lt;p&gt;屏幕前的你可能是在微信中看的这篇文章，微信也是一个端。同理，IOS、Android、windows等都是端。&lt;/p&gt;
&lt;p&gt;广义上讲，所有负责在端布局、排版、展示画面的工程师，都可以称为前端工程师。&lt;/p&gt;
&lt;p&gt;不同的端使用不同编程语言，就像不同画家属于不同画派，印象派画家当然不会画古典画喽。&lt;/p&gt;
&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在浏览器端，使用Javascript语言开发&lt;/li&gt;
&lt;li&gt;在IOS端，使用Swift或Objective-C语言开发&lt;/li&gt;
&lt;li&gt;在Android端，使用Java语言开发&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于浏览器是历史最久远、最出名的端，所以狭义上，我们将：&lt;/p&gt;
&lt;p&gt;使用Javascript语言，在浏览器端负责展示页面的工程师，称为前端工程师&lt;/p&gt;
&lt;p&gt;后文所指的前端，都是狭义的概念。&lt;/p&gt;
&lt;h1 id=&#34;无规矩不成方圆&#34;&gt;无规矩不成方圆&lt;/h1&gt;
&lt;p&gt;如果说前端工程师是画家，浏览器是画板，那么还需要约定画家能够使用的画笔的规格，这样不同的画家在不同画板上才能以一致的标准画画。&lt;/p&gt;
&lt;p&gt;为了推动通用标准的建立，W3C小组成立。&lt;/p&gt;
&lt;p&gt;处于商业上的考量，微软的IE浏览器并没有完全按照W3C标准实现API。当微软做下这个决定那一刻，未来前端工程师为了浏览器兼容性问题爆肝到黎明的宿命已成定局。&lt;/p&gt;
&lt;p&gt;设想这样一幅景象：&lt;/p&gt;
&lt;p&gt;一位画家要画一片云，W3C标准规定各大画板必须提供笔刷这一工具。&lt;/p&gt;
&lt;p&gt;其他画板都实现了绘画效果一致的笔刷，IE说：“我不，你打我？我只提供毛笔。”&lt;/p&gt;
&lt;p&gt;所以画家在IE上只能用毛笔穷尽奇技淫巧，模仿笔刷的效果画云。&lt;/p&gt;
&lt;p&gt;这种心情，你细品。&lt;/p&gt;
&lt;h1 id=&#34;文艺复兴时期&#34;&gt;文艺复兴时期&lt;/h1&gt;
&lt;p&gt;时间来到2005年，中国进入以“博客”为代表的Web2.0时代，百度也在同年赴美挂牌上市。&lt;/p&gt;
&lt;p&gt;前端进入文艺复兴时期，浏览器兼容性仍是亘古不变的话题。&lt;/p&gt;
&lt;p&gt;在长久的斗争中，工程师中的佼佼者、伟大的毛笔画云老画家、前端人的卡丽熙Dean Edwrad于2006年创造了jQuery。&lt;/p&gt;
&lt;p&gt;jQuery就像一套绘画工具包，画家使用jQuery提供的工具绘画。jQuery内部再针对不同画板使用画板提供的工具绘制。通过这种方式抹平了不同浏览器兼容性之间的差异。&lt;/p&gt;
&lt;p&gt;由于此时前端领域的工作仅仅是绘制简单静态页面，所以还没有出现专职的前端工程师。&lt;/p&gt;
&lt;p&gt;在前后端高度分化的今日，仍有一些后端工程师对前端工程师的工作报以轻视的态度，因为他们认为自己用jQuery也能完成前端工作。&lt;/p&gt;
&lt;h1 id=&#34;第一次工业革命&#34;&gt;第一次工业革命&lt;/h1&gt;
&lt;p&gt;根据第30次《中国互联网络发展状况统计报告》，截止12年6月，我国互联网普及率达到39.9% 。&lt;/p&gt;
&lt;p&gt;从05年到12年，伴随着互联网基础设施建设的发展，家庭电脑性能的提高，浏览器也在不断进化，画家有了更丰富的画笔。&lt;/p&gt;
&lt;p&gt;交互更复杂的网页应运而生。&lt;/p&gt;
&lt;p&gt;2005年12月校内网成立，多少中年人青春岁月的见证者&lt;/p&gt;
&lt;p&gt;在以静态的门户网站为代表的web1.0时代，jQuery可以出色的完成任务。但是面对交互更复杂的网页就显得力不从心了。&lt;/p&gt;
&lt;p&gt;同时，繁荣的互联网生态也对工程师的开发效率提出了更高要求。&lt;/p&gt;
&lt;p&gt;以史为鉴，珍妮纺纱机的出现提高了纺织生产力，预示着第一次工业革命的开启。前端领域急需自己的“纺纱机”。&lt;/p&gt;
&lt;p&gt;一批有悟性的工程师尝试将后端行之有效的工作范式迁移到前端。终于，第一代前端框架诞生。&lt;/p&gt;
&lt;p&gt;其中的佼佼者，便是Backbone.js。&lt;/p&gt;
&lt;p&gt;前文提到过，jQuery是一套可以抹平浏览器兼容性的绘画工具包。&lt;/p&gt;
&lt;p&gt;即使使用了工具包，画家也必须亲自使用工具包内的工具一笔一画绘制。效率低，面对复杂画面时还容易出错。&lt;/p&gt;
&lt;p&gt;作为前端界的珍妮纺纱机，框架就像一部绘画机器，画家对机器描述这幅画的样子，机器使用工具包内的工具完成绘制。&lt;/p&gt;
&lt;p&gt;资本迅速捕捉到这次变革蕴含的利益，紧随Backbone.js，大公司纷纷开发了各自的前端框架。谷歌开发了Angular.js，苹果开发了Ember.js，微软开发了Knockout.js。&lt;/p&gt;
&lt;p&gt;框架的出现意味着前端工作复杂性上升。一部分程序员开始专职前端工作。&lt;/p&gt;
&lt;p&gt;于是，前端工程师作为一个独立的岗位被提出。谁也没有想到，这个由变革而生的职业，会在变革的道路上越走越远。&lt;/p&gt;
&lt;h1 id=&#34;第一次世界大战&#34;&gt;第一次世界大战&lt;/h1&gt;
&lt;p&gt;如果审视一个国家的奋斗轨迹，大抵有两个方向：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对内深耕&lt;/li&gt;
&lt;li&gt;对外扩张&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;深耕自身领域，从jQuery为代表的工具集到Backbone.js为代表的第一代框架，前端尝到了奋斗的甜头。&lt;/p&gt;
&lt;p&gt;鲁迅曾经说过：奋斗只有一次或者无数次。&lt;/p&gt;
&lt;p&gt;著名的程序员问答网站Stack Overflow创始人Jeff Atwood在07年的一篇博文提出了被后人称为Atwood定律的观点：&lt;/p&gt;
&lt;p&gt;任何能够用JavaScript实现的应用系统，最终都必将用JavaScript实现。&lt;/p&gt;
&lt;p&gt;作为开疆扩土的宣言，Atwood定律让前端工程师热血沸腾。然而，现实是残酷的。&lt;/p&gt;
&lt;p&gt;前文说过，前端的工作是在浏览器端使用Javascript语言完成的。在当前，Javascript这门语言只能在浏览器中被解析。&lt;/p&gt;
&lt;p&gt;这是因为Javascript语言需要由内置于浏览器中的Javascript引擎解析为电脑能够理解的机器码并执行。&lt;/p&gt;
&lt;p&gt;前端人遇到了次元壁。&lt;/p&gt;
&lt;p&gt;没想到，仅仅两年后，Atwood定律第一次应验，次元壁被打破。破壁人，名叫Ryan Dahl。&lt;/p&gt;
&lt;p&gt;Ryan Dahl将谷歌浏览器的Javascript引擎（V8引擎）从浏览器中取出，并在其之上实现了更多功能，使Javascript拥有了独立于浏览器的运行环境，这就是Node.js。&lt;/p&gt;
&lt;p&gt;理论上，之前需要后端工程师使用传统后端语言（Java、PHP、Python&amp;hellip;）完成的一切工作，前端工程师都可以在Node.js创造的运行环境中使用Javascript完成。&lt;/p&gt;
&lt;p&gt;阻挡前端工程师扩张的，只有他们自己的想象力了。&lt;/p&gt;
&lt;h1 id=&#34;第二次工业革命&#34;&gt;第二次工业革命&lt;/h1&gt;
&lt;p&gt;根据第35次中国互联网络发展状况统计报告，截止2014年，中国手机网民规模达到85.8%，伴随着4G普及，中国正式进入移动互联网时代。&lt;/p&gt;
&lt;p&gt;繁荣的市场催生了更多人才需求。前端工程师作为刚分化出来的新兴岗位，门槛较其他存在已久的互联网技术岗位低，又享受了互联网红利带来的高工资，一时入行者众多。行业本身也在加速进化。&lt;/p&gt;
&lt;p&gt;以“工厂流水线作业”为代表的效能提升，标志着前端第二次“工业革命”的到来。&lt;/p&gt;
&lt;p&gt;就拿亘古不变的兼容性问题举例，前端工程师探索出一条行之有效的方法：&lt;/p&gt;
&lt;p&gt;书写完成的Javascript代码不直接交给浏览器运行，而是借由Node.js处理文件的能力，将其中浏览器未实现的功能通过当前已有功能来实现，并输出转换后的Javascript文件给浏览器运行。&lt;/p&gt;
&lt;p&gt;这个拥有转换功能的Node.js工具，就是Babel。&lt;/p&gt;
&lt;p&gt;除了Babel，前端流水线上还有很多工具，比如对源代码进行压缩混淆的工具uglify，对代码书写规范进行检查的工具eslint。&lt;/p&gt;
&lt;p&gt;这些工具被按需加入流水线的各个环节，最终产出的代码会被打包成浏览器能够识别的代码。&lt;/p&gt;
&lt;p&gt;流水线操作、工程化改革极大的充盈了前端工程师的弹药库。当一切都准备就绪，进击的前端开始第二次扩张。&lt;/p&gt;
&lt;h1 id=&#34;第二次世界大战&#34;&gt;第二次世界大战&lt;/h1&gt;
&lt;p&gt;前端工程师则借助Node.js的能力，开发后端框架，将前端的势力范围扩展到传统由后端工程师把控的领域。&lt;/p&gt;
&lt;p&gt;国内比较有名的包括阿里开源的egg.js和360奇舞团开源的Think.js。&lt;/p&gt;
&lt;p&gt;一些大公司的架构逐渐从&lt;/p&gt;
&lt;p&gt;前端 &amp;ndash;&amp;gt; 后端&lt;/p&gt;
&lt;p&gt;过渡为&lt;/p&gt;
&lt;p&gt;前端 &amp;ndash;&amp;gt; Node.js中间层 &amp;ndash;&amp;gt; 后端&lt;/p&gt;
&lt;p&gt;在其他领域前端也是花式煊技。&lt;/p&gt;
&lt;p&gt;时间来到2016年，第一代前端框架淡出舞台。业界逐渐形成了第二代前端框架三足鼎立的局面，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React.js 由Facebook开发，在三大框架中最接近Javascript&lt;/li&gt;
&lt;li&gt;Vue.js 由华人程序员尤雨溪开发，以上手简单著称&lt;/li&gt;
&lt;li&gt;Angular 由谷歌开发，由第一代框架Angular.js发展而来，以大而全著称&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;移动互联网时代什么岗位最抢手？当然是移动开发。&lt;/p&gt;
&lt;p&gt;前文提到，如果前端框架是绘图机器，前端工程师的职责就是向这台机器描述“要绘制的画面”。&lt;/p&gt;
&lt;p&gt;其中“描述”是与端无关的，只有具体的“绘制行为”才与端有关。&lt;/p&gt;
&lt;p&gt;前端用熟悉的Javascript描述画面，再由框架绘制在IOS、Android端，那不就能抢占移动开发的地盘了么？&lt;/p&gt;
&lt;p&gt;这点子就像妙蛙种子吃着妙脆角到了妙妙屋 —— 妙到家了&lt;/p&gt;
&lt;p&gt;于是，基于三大框架，诞生了可以在其他端（IOS、Android、windows、Mac&amp;hellip;）绘制画面的工具。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Facebook基于React.js开发了React-Native&lt;/li&gt;
&lt;li&gt;Vue.js与阿里合作开发了weex&lt;/li&gt;
&lt;li&gt;基于Angular语法Ionic团队开发了Ionic&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;新世界&#34;&gt;新世界&lt;/h1&gt;
&lt;p&gt;在前端攻城略地如火如荼进行中，外界环境悄然发生了变化。&lt;/p&gt;
&lt;p&gt;根据第40次中国互联网络发展状况统计报告，手机网民规模已经由12年的72%提升到17年的96% ，移动互联网进入存量市场。&lt;/p&gt;
&lt;p&gt;各大互联网巨头早已完成流量入口的把持，当务之急是丰富业务品类，将用户的注意力更多保留在自家产品的阵营中。&lt;/p&gt;
&lt;p&gt;在这个思想驱动下，中国在技术创新上走在了世界的前沿 —— 小程序诞生了。&lt;/p&gt;
&lt;p&gt;从技术上讲，如果说前端在各个领域发展的是拥有各种能力的葫芦娃，那小程序就是葫芦娃结合体 —— 葫芦小金刚。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;借鉴了React.js的虚拟DOM特性&lt;/li&gt;
&lt;li&gt;使用前端打包工具webpack&lt;/li&gt;
&lt;li&gt;使用Javascript内核&lt;/li&gt;
&lt;li&gt;语法参考Vue.js&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以说，作为一个新兴的端，小程序的开发天生就是为前端工程师准备的。&lt;/p&gt;
&lt;p&gt;紧跟微信之后， 百度、阿里、字节跳动、360、小米、华为纷纷推出自己的小程序。&lt;/p&gt;
&lt;h1 id=&#34;活在当下&#34;&gt;活在当下&lt;/h1&gt;
&lt;p&gt;回首最近两年前端领域最火的概念，无一不是践行前端工程师“对内深耕、对外扩张”的传统。&lt;/p&gt;
&lt;p&gt;对内深耕：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typescript：解决Javascript作为动态语言书写太灵活，在大型、复杂应用开发时缺少束缚的问题。&lt;/li&gt;
&lt;li&gt;微前端：借鉴后端微服务的理念，将一个应用拆分为多个完备、可复用的子应用。使多个团队可以在同一个大应用中独立开发各自的模块。提升开发效率。&lt;/li&gt;
&lt;li&gt;Low-Code：低代码平台。将一些特定领域（比如营销活动页、可视化大屏）的开发任务抽象为工具，用鼠标拖拽组件就能完成页面开发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对外扩张：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;flutter：由谷歌开发的移动端框架，目标是写一份代码，就能在多个端（主要是IOS、Android）运行。由于其使用的Dart语言与Javascript类似，受到前端青睐。&lt;/li&gt;
&lt;li&gt;serverless：继Node.js对后端工程师下手后，终于开始对运维工程师下手了。serverless的目标是让开发者不需要关注服务器运维，可以专注于业务逻辑的开发。&lt;/li&gt;
&lt;li&gt;Deno：Node.js之父的新作品&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有很多概念由于当前业务场景有限，或者门槛、投入产出比等方面的原因，并未普及开。比如Web Components、WebAssembly。&lt;/p&gt;
&lt;h1 id=&#34;后记&#34;&gt;后记&lt;/h1&gt;
&lt;p&gt;遥想90年代，在互联网的发源地美国看来，我们就是个落后的第三世界国家。&lt;/p&gt;
&lt;p&gt;95年，中国第一家互联网公司瀛海威成立。中国互联网人也曾发出这样的疑问：中国人离信息高速公路还有多远？&lt;/p&gt;
&lt;p&gt;四分之一个世纪过去了，从最初复刻国外成熟互联网公司经营模式，到发展中国特色的互联网文化，到如今为全世界树立现代化生活的典范。&lt;/p&gt;
&lt;p&gt;一代代中国互联网人以其朴素的东方哲学，为这个国家的发展贡献力量：越过信用卡时代直接进入移动支付时代、开启了共享经济大潮、将互联网融入衣食住行的每个角落。&lt;/p&gt;
&lt;p&gt;时代的一粒灰落在个人头上就是一座山，在这个变革的行业，知识以“月”为单位进化。996，007早已不是新闻。无数人被这艘巨轮甩下，无数人前仆后继地在追赶他。&lt;/p&gt;
&lt;p&gt;有怀揣理想的默默努力，有背负责任的负重前行。更多的，是认清了生活的真相后仍然热爱它。&lt;/p&gt;
&lt;p&gt;代码不止，奋斗不息。加油，前端人。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>陪你骑车好吗 - 再也不想去宜昌了</title>
      <link>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E5%86%8D%E4%B9%9F%E4%B8%8D%E6%83%B3%E5%8E%BB%E5%AE%9C%E6%98%8C%E4%BA%86/</link>
      <pubDate>Fri, 04 Jun 2021 10:08:32 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E5%86%8D%E4%B9%9F%E4%B8%8D%E6%83%B3%E5%8E%BB%E5%AE%9C%E6%98%8C%E4%BA%86/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在我漫长的平衡车生涯中，我仅仅只去过宜昌一次。一来是觉得挺远的，动车时间都赶上出省了，二来也的确被恶心到了，每每想起总觉得很膈应。&lt;/p&gt;
&lt;p&gt;算起来，2018年08月这场PUKY宜昌站的比赛，应该是我开始比赛生涯以后的第二场比赛。&lt;/p&gt;
&lt;p&gt;那个时候，孩子还是2B的小选手，跑的也是不到100米的超简赛道。基本上就是出发一路冲出去，一个回头弯加几个小弯，比赛就结束了。而且2岁组都不分男女，混战哟！&lt;/p&gt;
&lt;p&gt;我记得比赛是一个周六，而且两岁组是最先开始比赛的队伍。&lt;/p&gt;
&lt;p&gt;我周五晚上下了班，马不停蹄的打的赶往火车站，坐高铁去宜昌，然后在赛场边找酒店住下，折腾到晚上十点多。&lt;/p&gt;
&lt;p&gt;第二天早上八点多又起来。先折腾孩子的起床气，然后安排她吃早餐，吃完早餐下楼去赛场热身，准备上场。&lt;/p&gt;
&lt;p&gt;初赛跑得相当的不错，不愧是训练有素的孩子，嘻嘻。&lt;/p&gt;
&lt;p&gt;两岁组是有人领跑的，就是比赛开始以后，前面是有个工作人员挥舞着旗子在前面跑，孩子们只要追就行了。&lt;/p&gt;
&lt;p&gt;参赛前我就不断地叮嘱孩子，盯住旗子，脚要一刻不停地追。我还特地买了个棒棒糖给她看，跟她说只要追上前面那个拿旗子的大哥哥，跑完了下来吃棒棒糖。&lt;/p&gt;
&lt;p&gt;其实说白了，两岁的孩子你想能练出个什么东西来呢，比谁更有劲？比谁更会控车？想多了吧！&lt;/p&gt;
&lt;p&gt;比赛一开始，场边有家长大喊大叫，有现场音乐咚咚锵锵，有其他小选手神游太虚。&lt;/p&gt;
&lt;p&gt;其实比的就是那份专注力，比的就是看谁能在短短的半分钟的比赛时间内，持续保持住这份追人的专注力。能保持专注，基本上就成功了一大半了。&lt;/p&gt;
&lt;p&gt;半决依然没啥悬念，虽然不是小组第一晋级，但也没啥悬念，成功进入决赛。&lt;/p&gt;
&lt;p&gt;决赛幺蛾子就来了，从孩子们上出发板，我就觉得似乎有那么一丝蹊跷。&lt;/p&gt;
&lt;p&gt;决赛八个人，四个是本地俱乐部的，比赛穿的都是本地俱乐部的队服。决赛时，这四个人就占据了最内侧的几个车道。我就在想啊，四个人全部抽签到最内侧道的概率有多大呢？请数学概率论学得好的网友们帮我算一下。&lt;/p&gt;
&lt;p&gt;我一直在第一条直道的回头弯处守着拍视频，当时看到孩子们上了出发板，却一直没开始比赛，有个家长站在出发板后一直跟主持人争论着什么。&lt;/p&gt;
&lt;p&gt;后来就远远的就看到，过来两个工作人员把最外侧车道的几个孩子相互之间挤了挤，挤出一个空档，插了一个孩子进去，然后比赛很快就开始了。&lt;/p&gt;
&lt;p&gt;可能是我慈父光环作祟，我总觉得，就因为你挤了挤出发板，所以导致出发空间变小了，我家出发肯定受到了一些影响。而且你只让外侧非你俱乐部的的选手相互挤一挤，内侧几个你自己俱乐部的孩子都没动过，这本身就很不公平了。况且冠亚军就出现在内侧的几个选手中间。&lt;/p&gt;
&lt;p&gt;你这就是黄泥巴掉入裤裆，不是屎都说不过去了吧。&lt;/p&gt;
&lt;p&gt;我赶紧去找工作人员理论：为什么八人决赛变九人了，为什么要挤出发板。&lt;/p&gt;
&lt;p&gt;工作人员跟我说：原因是半决的时候搞错了名额，有个孩子本来进了半决，但是终点处交到起点的手写半决名单7和1没写清楚，导致本来被淘汰的孩子跑了半决，本来进半决的孩子却没跑。决赛的时候，那家孩子的家长发现了问题，所以与工作人员产生了一些争执，于是工作人员决定临时增加一个名额，让她比赛。&lt;/p&gt;
&lt;p&gt;于是我据理力争，那八人的出发板站九个人出发，很不公平呀。我要求重赛，刚才决赛第九名就不用上板了，这样也可以保证八人比赛。&lt;/p&gt;
&lt;p&gt;当时我还是顾及双方颜面，并没有说透你们挤人的时候，可是很照顾你们自己俱乐部的孩子呢。&lt;/p&gt;
&lt;p&gt;但是工作人员表示，比赛比完了，重赛是不可能了，你的感受我能理解，下次再办赛，我们会小心处理的。反正就是片汤话，来来回回的说。&lt;/p&gt;
&lt;p&gt;后来我逼急了，工作人员就是说，你要能把这其他七家家长都找齐，他们都同意重赛，我就重赛。&lt;/p&gt;
&lt;p&gt;呵呵哒，你办赛出纰漏，还要我兜底不成？这逻辑说不通啊。&lt;/p&gt;
&lt;p&gt;来来回回，折腾了十几分钟也没个结果。而我还定了高铁票一个小时以后车都要开了。&lt;/p&gt;
&lt;p&gt;最后我也就只能服软了：“你要我找人不现实，你们办赛的问题，凭什么我售后。既然不重赛，那我又觉得不公平，你说怎么办吧？”&lt;/p&gt;
&lt;p&gt;对方工作人员说，要不你加我个微信，今天比赛结束以后，我寄你个小礼品吧？&lt;/p&gt;
&lt;p&gt;我觉得，也只能这样了。场面已经绷住了，我还得赶车。&lt;/p&gt;
&lt;p&gt;于是互加了微信，就撤退赶车去了。没曾想，在动车上，却还有另一个惊天大秘密等待着我。&lt;/p&gt;
&lt;p&gt;在回家的动车上，跟孩子玩小游戏，忘了是怎么起了个头，孩子无意中跟我说到说今天抽赛道的时候扑克牌抽不动，她就换了一张。&lt;/p&gt;
&lt;p&gt;这里需要描述一下抽赛道的规则：工作人员拿着A到8的扑克牌，拿在手上摊开，要求孩子自己去抽牌，家长不能代劳。&lt;/p&gt;
&lt;p&gt;我不知道是不是两岁的孩子力气太小了，所以扑克牌里有些牌太重了，抽不动？只有轻一些的牌才抽的动？可能你们自己俱乐部的四个孩子都是天生神力吧，我们非本地人力气都太小了。&lt;/p&gt;
&lt;p&gt;事情发展到这一步，我已然很气愤了。&lt;/p&gt;
&lt;p&gt;于是回到家，第二天就催促工作人员把我的礼品赶紧寄出。&lt;/p&gt;
&lt;p&gt;但是我还在心里打小算盘，要是你敢寄到付，我就去找PUKY官方微博呀，或者公众号之类的地方投诉你。&lt;/p&gt;
&lt;p&gt;谁曾想，我还是想多了。催了三天，人家每天都跟我说寄了。我说我还没收到，她说那我再催催。我说你把单号给我，我也催催呗。她说不劳您费心，这点小事她来催就好了。&lt;/p&gt;
&lt;p&gt;最后的最后，这事就不了了之了。至今三年了，我也没收到她寄的快递。不知道是哪家快递效率这么低，三年了同省快递都还没寄到。而且这事过了大概一个多月，她就把我拉黑了。&lt;/p&gt;
&lt;p&gt;这件事，最后给了我一点警示：有异议一定要马上申诉，尤其是一定不能让比赛开始，比赛一开始，就全完了，说什么都没用了，绝对不可能再比一次的。&lt;/p&gt;
&lt;p&gt;天道好轮回，苍天饶过谁；没想到这点经验居然还真在不久的将来用上了。当然这是后话了，待续吧……&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43; Boost中的signals2的使用</title>
      <link>http://coder.amazingdemo.top/post/cpp_boost_signals2%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
      <pubDate>Wed, 26 May 2021 15:33:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_boost_signals2%E7%9A%84%E4%BD%BF%E7%94%A8/</guid>
      
        <description>&lt;p&gt;signals2基于Boost的另一个库signals，实现了线程安全的观察者模式。在signals2库中，观察者模式被称为信号/插槽(signals and slots)，他是一种函数回调机制，一个信号关联了多个插槽，当信号发出时，所有关联它的插槽都会被调用。&lt;/p&gt;
&lt;p&gt;许多成熟的软件系统都用到了这种信号/插槽机制(另一个常用的名称是事件处理机制：event/event handler)，它可以很好地解耦一组互相协作的类，有的语言设置直接内建了对它的支持(如c#)，signals2以库的形式为c++增加了这个重要的功能。&lt;/p&gt;
&lt;h1 id=&#34;操作函数&#34;&gt;操作函数&lt;/h1&gt;
&lt;p&gt;signal最重要的操作函数是插槽管理connect()函数，它把插槽连接到信号上，相当于为信号(事件)增加了一个处理的handler。&lt;/p&gt;
&lt;p&gt;插槽可以是任意的可调用对象，包括函数指针、函数对象、以及它们的bind表达式和function对象，signal内部使用function作为容器来保存这些可调用对象。连接时可以指定组号也可以不指定组号，当信号发生时将依据组号的排序准则依次调用插槽函数。&lt;/p&gt;
&lt;p&gt;如果连接成功connect()将返回一个connection，表示了信号与插槽之间的连接关系，它是一个轻量级的对象，可以处理两者间的连接，如断开、重连接、或者测试连接状态。&lt;/p&gt;
&lt;p&gt;成员函数disconnect()可以断开插槽与信号的连接，它有两种形式：传递组号将断开该组的所有插槽，传递一个插槽对象将仅断开该插槽。函数disconnect_all_slots()可以一次性断开信号的所有插槽连接。&lt;/p&gt;
&lt;h1 id=&#34;插槽的连接于使用&#34;&gt;插槽的连接于使用&lt;/h1&gt;
&lt;p&gt;signal就像一个增强的function对象，它可以容纳(使用connect())多个符合模板参数中函数签名类型的函数(插槽)，形成一个插槽链表，然后在信号发生时一起调用：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

void slots1()
{
    cout &amp;lt;&amp;lt; &amp;#34;slots1 call&amp;#34; &amp;lt;&amp;lt; endl;
}

void slots2()
{
    cout &amp;lt;&amp;lt; &amp;#34;slots2 call&amp;#34; &amp;lt;&amp;lt; endl;
}

struct Hello
{
    void operator()()const
    {
        std::cout &amp;lt;&amp;lt; &amp;#34;Hello&amp;#34;;
    }
}; 

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;void()&amp;gt; sig;
    sig.connect(&amp;amp;slots1);
    sig.connect(&amp;amp;slots2);
    sig();
    boost::signals2::signal&amp;lt;void ()&amp;gt; sig1;
    sig1.connect(Hello());
    sig1();
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;注：编译这个程序的时候，确保已经在stdafx.h中加入#define _SCL_SECURE_NO_WARNINGS或者，在C/C++&amp;mdash;-预处理器&amp;mdash;-预处理器定义中加上了_SCL_SECURE_NO_WARNINGS，否则会引发错误(或不能正确输出)，下同。&lt;/p&gt;
&lt;p&gt;在连接插槽时省了了connect()的第二个参数connect_position，它的缺省值是at_back，表示插槽将插入到信号插槽链表的尾部，因此slot2将在slot1之后被调用。&lt;/p&gt;
&lt;p&gt;下面是使用和组号的情况：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    void operator()()
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N value is : &amp;#34; &amp;lt;&amp;lt; N &amp;lt;&amp;lt; endl;
    }
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;void()&amp;gt; sig;
    sig.connect(Slot&amp;lt;1&amp;gt;(), boost::signals2::at_back);	 // 最后一个被调用
    sig.connect(Slot&amp;lt;99&amp;gt;(), boost::signals2::at_front);  // 第一个被调用
    sig.connect(5, Slot&amp;lt;55&amp;gt;(), boost::signals2::at_back); // 组号5的最后一个
    sig.connect(5, Slot&amp;lt;57&amp;gt;(), boost::signals2::at_front);// 组号5的第一个
    sig.connect(10, Slot&amp;lt;100&amp;gt;());// 组号10该组只有一个
    sig();
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;信号的返回值&#34;&gt;信号的返回值&lt;/h1&gt;
&lt;p&gt;signal如function一样，不仅可以把输入参数转发给所以插槽，也可以传回插槽的返回值。默认情况下signal使用合并器optional_last_value，它将使用optional对象返回最后被调用的插槽的返回值。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    int operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N * x value is : &amp;#34; &amp;lt;&amp;lt; endl;
        return (N * x);
    }
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;int(int)&amp;gt; sig;
    sig.connect(Slot&amp;lt;10&amp;gt;());
    sig.connect(Slot&amp;lt;100&amp;gt;());
    cout &amp;lt;&amp;lt; *sig(2) &amp;lt;&amp;lt; endl;;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;signal的operator()调用这时需要传入一个整数参数，这个参数会被signal存储一个拷贝，然后转发给各个插槽。最后signal将返回插槽链表末尾slots&amp;lt;100&amp;gt;()的计算结果，它是一个optional对象，必须用接引用操作符*来获得值(但你可以发现Slotcurrent N * x value is是输出了两次的)。&lt;/p&gt;
&lt;h1 id=&#34;合并器&#34;&gt;合并器&lt;/h1&gt;
&lt;p&gt;大多数时候，插槽的返回值都是有意义的，需要以某种方式处理多个插槽的返回值。&lt;/p&gt;
&lt;p&gt;signal允许用户自定义合并器来处理插槽的返回值，把多个插槽的返回值合并为一个结果返回给用户。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;numeric&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    int operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N * x value is : &amp;#34; &amp;lt;&amp;lt; endl;
        return (N * x);
    }
};

template&amp;lt;typename T&amp;gt;
class combiner
{
public:
    typedef pair result_type;
    combiner(T t = T()) : v(t)
    {
    }
    template&amp;lt;typename InputIterator&amp;gt;
    result_type operator()(InputIterator begin, InputIterator end)const
    {
        if (begin == end)
        {
            return result_type();
        }
        vector vec(begin, end);
        T sum = accumulate(vec.begin(), vec.end(), v);
        T max = *max_element(vec.begin(), vec.end());
        return result_type(sum, max);
    }
private:
    T v;
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;int(int), combiner&amp;lt;int&amp;gt; &amp;gt; sig;
    sig.connect(Slot&amp;lt;10&amp;gt;());
    sig.connect(Slot&amp;lt;20&amp;gt;());
    sig.connect(Slot&amp;lt;3&amp;gt;());
    BOOST_AUTO(x, sig(2));
    cout &amp;lt;&amp;lt; x.first &amp;lt;&amp;lt; &amp;#34;, &amp;#34; &amp;lt;&amp;lt; x.second &amp;lt;&amp;lt; endl;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;combiner类的调用操作符operator()的返回值类型可以是任意类型，完全由用户指定，不一定必须是optional或者是插槽的返回值类型。operator()的模板参数InputIterator是插槽链表的返回值迭代器，可以使用它来遍历所有插槽的返回值，进行所需的处理。&lt;/p&gt;
&lt;p&gt;当信号被调用时，signal会自动把引用操作转换为插槽调用，将调用给定的合并器的operator()逐个处理插槽的返回值，并最终返回合并器operator()的结果。&lt;/p&gt;
&lt;p&gt;如果我们不适用signal的缺省构造函数，而是在构造signal时传入一个合并器的实例，那么signal将使用逐个合并器(的拷贝)处理返回值。例如，下面的代码使用了一个有初值的合并器对象，累加值从100开始：&lt;/p&gt;
&lt;p&gt;signal &amp;gt; sig(combiner(100));&lt;/p&gt;
&lt;h1 id=&#34;管理信号的连接&#34;&gt;管理信号的连接&lt;/h1&gt;
&lt;p&gt;信号与插槽的链接并不要求是永久的，当信号调用玩插槽后，有可能需要把插槽从信号中断开，再连接到其他的信号上去。&lt;/p&gt;
&lt;p&gt;signal可以用成员函数disconnect()断开一个或一组插槽，或者使用disconnect_all_slots()断开所有插槽连接，函数empty()和num_slots()用来检测信号当前插槽的连接状态。&lt;/p&gt;
&lt;p&gt;要断开一个插槽，插槽必须能够进行登记等价比较，对于函数对象来说就是重载一个等价语义的operator==。下面对slots增加一个等价比较：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;numeric&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    int operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N * x value is : &amp;#34; &amp;lt;&amp;lt; endl;
        return (N * x);
    }
};

template&amp;lt;int N&amp;gt;
bool operator == (const Slot &amp;amp;a, const Slot &amp;amp;b)
{
    returntrue;
}

template&amp;lt;typename T&amp;gt;
class combiner
{
public:
    typedef pair result_type;
    combiner(T t = T()) : v(t)
    {
    }
    
    template&amp;lt;typename InputIterator&amp;gt;
    result_type operator()(InputIterator begin, InputIterator end)const
    {
        if (begin == end)
        {
            return result_type();
        }
        vector vec(begin, end);
        T sum = accumulate(vec.begin(), vec.end(), v);
        T max = *max_element(vec.begin(), vec.end());
        return result_type(sum, max);
    }
private:
    T v;
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;int(int)&amp;gt; sig;
    //	assert(sig.empty());
    sig.connect(0, Slot&amp;lt;10&amp;gt;());
    sig.connect(Slot&amp;lt;20&amp;gt;());
    sig.connect(1, Slot&amp;lt;30&amp;gt;());
    assert(sig.num_slots() == 3);
    sig.disconnect(0);
    //	assert(sig.num_slots() == 1);
    sig.disconnect(Slot&amp;lt;30&amp;gt;());
    //	assert(sig.empty());
    sig(2);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;更灵活的管理信号连接&#34;&gt;更灵活的管理信号连接&lt;/h1&gt;
&lt;p&gt;signals2库提供另外一种较为灵活的连接管理方式：使用connection对象。&lt;/p&gt;
&lt;p&gt;每当signal使用connect()连接插槽时，他就会返回一个connection对象。connection对象像是信号与插槽连接关系的一个句柄(handle)，可以管理链接：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;numeric&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    int operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N * x value is : &amp;#34; &amp;lt;&amp;lt; endl;
        return (N * x);
    }
};

template&amp;lt;int N&amp;gt;
bool operator == (const Slot &amp;amp;a, const Slot &amp;amp;b)
{
    return true;
}

template&amp;lt;typename T&amp;gt;
class combiner
{
public:
    typedef pair result_type;
    combiner(T t = T()) : v(t)
    {
    }
    
    template&amp;lt;typename InputIterator&amp;gt;
    result_type operator()(InputIterator begin, InputIterator end)const
    {
        if (begin == end)
        {
            return result_type();
        }
        vector vec(begin, end);
        T sum = accumulate(vec.begin(), vec.end(), v);
        T max = *max_element(vec.begin(), vec.end());
        return result_type(sum, max);
    }
private:
    T v;
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;int(int)&amp;gt; sig;
    boost::signals2::connection c1 = sig.connect(0, Slot&amp;lt;10&amp;gt;());
    boost::signals2::connection c2 = sig.connect(0, Slot&amp;lt;10&amp;gt;());
    boost::signals2::connection c3 = sig.connect(0, Slot&amp;lt;10&amp;gt;());
    c1.disconnect();
    assert(sig.num_slots() == 2);
    assert(!c1.connected());
    assert(c2.connected());
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;另外一种连接管理对象是scoped_connection，它是connection的种类，提供类似scoped_ptr的RAII功能：插槽与信号的连接仅在作用域内生效，当离开作用域时连接就会自动断开。当需要临时连接信号时scoped_connection会非常有用：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;numeric&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    int operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N * x value is : &amp;#34; &amp;lt;&amp;lt; endl;
        return (N * x);
    }
};

template&amp;lt;int N&amp;gt;
bool operator == (const Slot &amp;amp;a, const Slot &amp;amp;b)
{
    return true;
}

template&amp;lt;typename T&amp;gt;
class combiner
{
public:
    typedef pair result_type;
    combiner(T t = T()) : v(t)
    {
    }
    
    template&amp;lt;typename InputIterator&amp;gt;
    result_type operator()(InputIterator begin, InputIterator end)const
    {
        if (begin == end)
        {
            return result_type();
        }
        vector vec(begin, end);
        T sum = accumulate(vec.begin(), vec.end(), v);
        T max = *max_element(vec.begin(), vec.end());
        return result_type(sum, max);
    }
private:
    T v;
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;int(int)&amp;gt; sig;
    sig.connect(0, Slot&amp;lt;10&amp;gt;());
    assert(sig.num_slots() == 1);
    {
        boost::signals2::scoped_connection sc = sig.connect(0, Slot&amp;lt;20&amp;gt;());
        assert(sig.num_slots() == 2);
    }
    assert(sig.num_slots() == 1);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;插槽与信号的连接一旦断开就不能再连接起来，connection不提供reconnet()这样的函数。但可以暂时地阻塞插槽与信号的连接，当信号发生时被阻塞的插槽将不会被调用，connection对象的blocked()函数可以检查插槽是否被阻塞。但被阻塞的插槽并没有断开与信号的链接，在需要的时候可以随时解除阻塞。&lt;/p&gt;
&lt;p&gt;connection对象自身没有阻塞的功能，他需要一个辅助类shared_connection_block，它将阻塞connection对象，知道它被析构或者显式调用unblock()函数：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;#34;stdafx.h&amp;#34;
#include&amp;#34;boost/utility/result_of.hpp&amp;#34;
#include&amp;#34;boost/typeof/typeof.hpp&amp;#34;
#include&amp;#34;boost/assign.hpp&amp;#34;
#include&amp;#34;boost/ref.hpp&amp;#34;
#include&amp;#34;boost/bind.hpp&amp;#34;
#include&amp;#34;boost/function.hpp&amp;#34;
#include&amp;#34;boost/signals2.hpp&amp;#34;
#include&amp;#34;numeric&amp;#34;
#include&amp;#34;iostream&amp;#34;

using namespace std;

template&amp;lt;int N&amp;gt;
struct Slot
{
    void operator()(int x)
    {
        cout &amp;lt;&amp;lt; &amp;#34;Slot current N is : &amp;#34; &amp;lt;&amp;lt; N &amp;lt;&amp;lt; endl;
    }
};

template&amp;lt;int N&amp;gt;
bool operator == (const Slot &amp;amp;a, const Slot &amp;amp;b)
{
    return true;
}

template&amp;lt;typename T&amp;gt;
class combiner
{
public:
    typedef pair result_type;
    combiner(T t = T()) : v(t)
    {
    }
    
    template&amp;lt;typename InputIterator&amp;gt;
    result_type operator()(InputIterator begin, InputIterator end)const
    {
        if (begin == end)
        {
            return result_type();
        }
        vector vec(begin, end);
        T sum = accumulate(vec.begin(), vec.end(), v);
        T max = *max_element(vec.begin(), vec.end());
        return result_type(sum, max);
    }
private:
    T v;
};

int _tmain(int argc, _TCHAR *argv[])
{
    boost::signals2::signal&amp;lt;void(int)&amp;gt; sig;
    boost::signals2::connection c1 = sig.connect(0, Slot&amp;lt;10&amp;gt;());
    boost::signals2::connection c2 = sig.connect(0, Slot&amp;lt;20&amp;gt;());
    assert(sig.num_slots() == 2);
    sig(2);
    cout &amp;lt;&amp;lt; &amp;#34;begin blocking...&amp;#34; &amp;lt;&amp;lt; endl;
    {
        boost::signals2::shared_connection_block block(c1);
        assert(sig.num_slots() == 2);
        assert(c1.blocked());
        sig(2);
    }
    cout &amp;lt;&amp;lt; &amp;#34;end blocking..&amp;#34; &amp;lt;&amp;lt; endl;
    assert(!c1.blocked());
    sig(2);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>陪你骑车好吗 - 成都KTC的遗憾</title>
      <link>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_ktc%E7%9A%84%E9%81%97%E6%86%BE/</link>
      <pubDate>Sat, 22 May 2021 09:08:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_ktc%E7%9A%84%E9%81%97%E6%86%BE/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/&#34;&gt;返回目录&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;彼时Iris已经三岁半了，我们换Strider车也有好几个月了；终于迎来了一次大考，2019年09月08日KTC成都站。&lt;/p&gt;
&lt;p&gt;当初决定报这场比赛，只是因为一个偶然的机会，看到Bike8的公众号推文。赛道摆成熊猫状，挺有意思的，而且我们也想带孩子出去见见世面，旅游 and 见识外省高手，一举两得。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;成都KTC赛道&#34;&gt;&lt;/p&gt;
&lt;p&gt;虽然现在看来，当时可能由于地理位置偏僻了一点，来的各省高手并不算很多；但是对于当时只在武汉周边比赛的我们而言，已经是一个比以前更广阔的舞台了；我们也是纠结再三，最后决定出去见见高山，体验一下“宫家六十四手”。&lt;/p&gt;
&lt;p&gt;但是，在报完KTC之后出了个幺蛾子。因为我们同时也在跑PUKY赛，PUKY的年终总决赛我们还是想去一下的。&lt;/p&gt;
&lt;p&gt;可是截止到九月，我们在PUKY中南战区的积分不算很高，如果2019年09月07日的武汉站PUKY我们不参加，有积分比我们更低的孩子拿奖的话。那么有一定概率，我们年底积分总榜的排名会在八名开外，从而丢失参加年终赛的资格。&lt;/p&gt;
&lt;p&gt;这就导致了，我需要先在武汉比完下午的PUKY赛，然后连夜赶去成都比第二天的KTC。得亏抢票软件给力，最终赶在PUKY赛前一天抢到了晚上十点汉口站出发去往成都的硬卧。&lt;/p&gt;
&lt;p&gt;顺便说一下，第一天的PUKY赛我们表现还是表现不错的，后来居上，半程反超拿到了3GB组的冠军！&lt;/p&gt;
&lt;p&gt;然后我们就马不停蹄的打的前往汉口站，准备赶往成都；列车预计第二天早上七点半到达成都。&lt;/p&gt;
&lt;p&gt;时间安排如此紧凑，大人小孩连轴转赶路，也许这趟行程从一开始，就预示了因为经验的不足以及安排的不合理，我们将举步维艰吧。&lt;/p&gt;
&lt;p&gt;因为去成都大包小包的，尤其是需要提个车，车子又是个异形件，怎么提着都不顺手；又是两场连比，需要持续赶路。所以我们并没有准备太多行李，尤其是没有带吃的，想着钱遭点罪，现吃的买算了。&lt;/p&gt;
&lt;p&gt;7号傍晚，在汉口站简单吃了顿晚饭，就登上了去往成都的火车。也没有在汉口站买点零食备着，感觉火车站周边也没什么商超，可供挑选的种类不多。火车上睡一夜又不用吃夜宵；第二天七点半到成都正好去酒店吃早餐，再采购也来得及。&lt;/p&gt;
&lt;p&gt;在火车上，孩子和妈妈挤在一张硬卧上，结果空间太小，两个人都没怎么睡好。&lt;/p&gt;
&lt;p&gt;后来妈妈考虑到孩子第二天上午就要比赛，这么睡下去不是个办法。于是早上四五点爬起来坐在走道的凳子上，一直坐到下火车，勉强让孩子补了几个小时觉。&lt;/p&gt;
&lt;p&gt;在火车上，我为了摆放携带方便一点，就把平衡车把横拆掉了。结果早上六点多，我爬起来准备装起来的时候，却发现我装不回去了。当时那个吊芯，我拧松一点就卡不住前叉，前叉摇摇晃晃；我拧紧一点前叉就锁死了，把横无法转向。&lt;/p&gt;
&lt;p&gt;我之前比赛也拆装过好几次把横了，从未遇到过这种情况。我拆了装，装了拆，搞了半个多小时也没搞定，急得头上直冒汗。我只好一边把零件重新拆掉都装好，一边紧急联系教练和其他家长寻求解决之道。&lt;/p&gt;
&lt;p&gt;并且本来预计七点半到站的火车，七点一刻了，还没有要到站的迹象。按以往的经验，列车员早该来通知大家准备下车了。要知道，我们可是并没有准备火车上的早餐！&lt;/p&gt;
&lt;p&gt;火车晃晃悠悠一直晚点到八点半才徐徐进站。而KTC比赛预计是十一点半开赛，我们还要去酒店放行李。&lt;/p&gt;
&lt;p&gt;在手机上查导航，火车站到酒店，打车需要将近一小时；酒店到到赛场，打车将近半小时；加起来就要一个半小时！留给我们的时间不多了，车还没处理好。真的是屋漏偏逢连夜雨，船迟又遇打头风。&lt;/p&gt;
&lt;p&gt;下了火车，赶紧一路狂奔到站外打了一个滴滴，九点半才刚刚到酒店。赶紧办理了入住以后，咨询前台得知，酒店的早餐时间已过，服务员看我们带了孩子，临时送给了我们一个鸡蛋。我们赶紧就喂给孩子吃了。&lt;/p&gt;
&lt;p&gt;然后妈妈开始整理行李，分装护具，骑行服等物品，准备去赛场签到。&lt;/p&gt;
&lt;p&gt;因为车子一直没弄好，我的心就一直悬着，我视频接受了教练的远程指导，但是依旧调不好。&lt;/p&gt;
&lt;p&gt;于是紧急联系了KTC的客服，客服说现场有当地协办的俱乐部教练可以帮忙维修，如果是小问题则免费维修，如果需要更换零件再议。&lt;/p&gt;
&lt;p&gt;心急如焚的我马上跟妈妈提议，现在就先不吃了，赶紧去现场，先把车搞好才是正事。也不知道修车需要多久，不要弄得吃完了再去，万一时间来不及，赛都比不了了，那就白跑这一趟了。&lt;/p&gt;
&lt;p&gt;当时成都KTC的地址写的是熊猫基地停车场，根据以往的参赛经验，一般儿童平衡车比赛都是在一个商圈的广场上。商圈嘛，周边都会有很多的配套。所以当时我觉得做这个决定没有丝毫的问题，当时满脑子就是想着修车，修车，赶紧修车。&lt;/p&gt;
&lt;p&gt;下楼赶紧打了一辆滴滴，终点定在了熊猫基地。上车后，我跟师傅说你把车开到停车场门口吧，你不进去我们自己走进去就可以了。&lt;/p&gt;
&lt;p&gt;但是师傅对我们的话表现得很诧异，说我们定位不对。扯了几分钟也没扯清楚，于是最终商议，还是去熊猫基地再说。我都到基地门口了，停车场还能上天不成？&lt;/p&gt;
&lt;p&gt;一下车，我突然就感觉不太对劲。熊猫基地大门在一个主干道旁边，四下望去，并没有我想象中的那种开阔地。&lt;/p&gt;
&lt;p&gt;四处问路人，大部分路人都是来旅游的，也搞不清楚停车场在哪。最后终于在保安处问得停车场的一个大致的方向。需要顺着一个斜坡走下去，据说很有点远。&lt;/p&gt;
&lt;p&gt;我顺着保安指的方向走过去，终于远远的看到KTC的旗子。嗯，真棒，停车场果然上天了。真远，目测得有三四里地吧。&lt;/p&gt;
&lt;p&gt;一手牵着孩子，一手提着车包，呼唤老婆兴奋的跑过去。&lt;/p&gt;
&lt;p&gt;在签到处，拿完赛事包，直奔修理台。老师傅果然给力，三下五除二，几分钟就把车把修好了。看来我还是个半瓢水的葫芦，还要回炉再造才行啊。&lt;/p&gt;
&lt;p&gt;这时候，我心里暗暗松了一口气，环顾四周，突然发现一个重大的问题。这地方看起来很像荒郊野外，周边根本就没有半点商圈的痕迹。唯一的一个厕所离赛场都有四五百米远。&lt;/p&gt;
&lt;p&gt;而且，熊猫基地门口的商铺我来的时候看了，不知道是不是因为旅游淡季的原因，商铺大部分都在装修，压根就没营业。&lt;/p&gt;
&lt;p&gt;翻遍随身携带的包裹，只有几个旺旺雪饼和一瓶赛事包里的矿泉水，真的是弹尽粮绝啊。&lt;/p&gt;
&lt;p&gt;拿出手机翻，附近连个美团外卖都没有，外卖商铺全部都超区了。这时已经十点多了，我们这组的赛事预计十一点半开始。&lt;/p&gt;
&lt;p&gt;这时候再离开赛场去吃饭已经不现实了，外卖也叫不到，真的是骑虎难下啊。&lt;/p&gt;
&lt;p&gt;与老婆讨论了一下，觉得还是忍一忍，十一点半比嘛，来之前吃了一个鸡蛋，还有几块饼干，应该还是可以顶一顶的；我们大人都不喝水不吃饭了，全部留给孩子。十一点半比，比完了十二点，忍一忍还是可以的。&lt;/p&gt;
&lt;p&gt;于是我们就开始正常的热身起来。跑跑跳跳热了半个小时身，感觉有些不对劲，似乎当前广播里的赛程与预计的安排时间并不怎么相符。去签到处打听了一下，赛程已经比预计晚点了半个小时。&lt;/p&gt;
&lt;p&gt;经过一轮热身，孩子似乎也有些扛不住了，频繁的跟我喊饿。&lt;/p&gt;
&lt;p&gt;我又跑去签到处问工作人员附近有没有什么卖东西的，或者饭馆之类的，工作人员说没有。&lt;/p&gt;
&lt;p&gt;我又问他们，那你们中午吃什么；工作人员说有主办方的盒饭。&lt;/p&gt;
&lt;p&gt;我说能不能帮我订一盒，或者你看你们谁能不吃，我两倍价格买你一盒？工作人员也不让。&lt;/p&gt;
&lt;p&gt;时间就在一分一秒的过去，却一直找不到一顿吃的。旺旺雪饼也早已吃完了，然而并没有什么卵用。&lt;/p&gt;
&lt;p&gt;时间节点来到十二点半，晚点一个小时，终于轮到我们上场了。孩子一上午，就吃了一个鸡蛋，几包旺旺雪饼，喝了一肚子水；就这么被拉上去与人较量了。&lt;/p&gt;
&lt;p&gt;初赛跑得还不错，一场直接稳进半决，不需要去跑复活，节约了一轮体力。&lt;/p&gt;
&lt;p&gt;KTC的赛道与我们平日里报的小赛不同，平日里小赛赛道基本上就200米左右。KTC的轮胎赛道号称380米，有些假弯，拉直了跑，估计也有300米左右吧。&lt;/p&gt;
&lt;p&gt;初赛一轮下来，就看着孩子跑得有点发懵，精神有些恍惚。&lt;/p&gt;
&lt;p&gt;果然半决就不行了，一出发，第一个弯道被人挤着，没有切进去。孩子当场就哭了，边哭边跑。我在赛道外看得急死了，只能在围着赛场轮胎追着她跑，边跑边喊：“不要哭，顶出去，追上去，追上去。”&lt;/p&gt;
&lt;p&gt;还好孩子哭了半程，后半程发力，卡在半决第五压线进决。&lt;/p&gt;
&lt;p&gt;在终点处，我接到孩子以后，她一直在哭。我把她抱到旁边的空地上不停的拍她的背，安抚她。&lt;/p&gt;
&lt;p&gt;她哭完了，很委屈的告诉我：“我觉得前面的跑得都没我快，但是我肚子好饿，脚也没力气了，挤也挤不动。”&lt;/p&gt;
&lt;p&gt;看来，果然赛道比平时训练的要长，加上又没吃饭，一轮初赛下来就耗尽了她所有的体能。&lt;/p&gt;
&lt;p&gt;听到她说这些，我真的在那一瞬间有一种热泪盈眶的感觉。就是因为我的赛前安排工作没做好，结果孩子遭这么大个罪。&lt;/p&gt;
&lt;p&gt;这场比赛是我们第一次出省比赛，无论孩子还是家长，我们都一直是很期待的，也为此做了不少准备。在家里也苦炼了小半个月了，结果临场就这么功亏一篑了。哎……&lt;/p&gt;
&lt;p&gt;决赛就不用说了，出发第一个弯又没挤过，然后瞬间孩子就又哭了；这回可就没有后半程发力了，虽然我全程跟跑，卖力嘶吼，期望她能重振旗鼓；但是依旧从出发一直哭到终点，555555……全程吊车尾，结束比赛。&lt;/p&gt;
&lt;p&gt;好歹进了个决，虽然名次不咋地，但也不算白来一场，高山也见识了，也涨了不少比赛知识，挺有收获的。&lt;/p&gt;
&lt;p&gt;本想着，再追一次KTC挽回遗憾，没想竟成KTC绝响，留与后面细说。&lt;/p&gt;
&lt;p&gt;从这以后，无论去哪比赛，无论赛场周边有多少配套。我包里都一定要随身带面包，士力架，矿泉水；保证无论任何情况下，我的孩子绝对有吃有喝。而且一定会在事先查看百度街景地图，观察周边环境（虽然地图数据不一定准确）。&lt;/p&gt;
&lt;p&gt;比完赛都一点多了，我们赶紧离开赛场去找吃的。找到一家牛肉面馆，给她买了一碗馄饨。她一个人三下五除二的全部吃完了。问我们还有没有？&lt;/p&gt;
&lt;p&gt;看来她真的是饿了啊！&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;#ZgotmplZ&#34; alt=&#34;map&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>陪你骑车好吗 - 目录</title>
      <link>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/</link>
      <pubDate>Fri, 21 May 2021 16:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E7%9B%AE%E5%BD%95/</guid>
      
        <description>&lt;ol&gt;
&lt;li&gt;写在前面的话&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E4%B9%B0%E4%BA%86%E8%BE%86PUKY/&#34;&gt;买了辆PUKY&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E5%86%8D%E4%B9%9F%E4%B8%8D%E6%83%B3%E5%8E%BB%E5%AE%9C%E6%98%8C%E4%BA%86/&#34;&gt;再也不想去宜昌了&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_%E9%A9%BB%E9%A9%AC%E5%BA%97%E6%B5%AE%E6%B2%89/&#34;&gt;驻马店浮沉&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../%E9%99%AA%E4%BD%A0%E9%AA%91%E8%BD%A6%E5%A5%BD%E5%90%97_KTC%E7%9A%84%E9%81%97%E6%86%BE/&#34;&gt;成都KTC的遗憾&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>Arduino之HC-SR505红外传感器模块</title>
      <link>http://coder.amazingdemo.top/post/arduino_sr505/</link>
      <pubDate>Fri, 23 Apr 2021 17:23:47 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/arduino_sr505/</guid>
      
        <description>&lt;h1 id=&#34;hc-sr505&#34;&gt;HC-SR505&lt;/h1&gt;
&lt;p&gt;PIR（Pyroelectric Infra Red）传感器可测量检测区域中的物体发出的红外光。 HC-SR505就是这样的传感器。它非常易于使用，如果检测区域中有物体在移动，该传感器会产生HIGH高电平信号。该高电平信号持续约8秒钟，有时被视为不利条件。不幸的是，这个缺点不能在软件上解决，即在源代码中添加替代方法不起作用。该模块的工作电压在4.5V至20V之间。根据数据手册，传感器可检测到长达3m的移动。&lt;/p&gt;
&lt;p&gt;要将SR505连接到Arduino，只需要三根线。 SR505有三个引脚，分别是：“-”、“ OUT”和“ +”。首先，确保为SR505提供足够的电压：Arduino的GND引脚连接到SR505的“-”引脚。接下来，必须将Arduino的5V引脚连接到SR505的“ +”引脚。OUT引脚将根据检测到的内容返回LOW或HIGH。该引脚必须连接到Arduino的其中一个数字引脚。&lt;/p&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//连线方法
//MPU-UNO
//+ -5V
//- -GND
//OUT-D2

#define SIGNAL_PIN 2

void setup()
{
  Serial.begin(9600);
  pinMode(SIGNAL_PIN, INPUT);
}
void loop() {
  if(digitalRead(SIGNAL_PIN)==HIGH) {
    Serial.println(&amp;#34;Movement detected.&amp;#34;);
  } else {
    Serial.println(&amp;#34;Did not detect movement.&amp;#34;);
  }
  delay(1000);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>Arduino之GY-BMP280-3.3高精度大气压强传感器模块</title>
      <link>http://coder.amazingdemo.top/post/arduino_bmp280/</link>
      <pubDate>Fri, 23 Apr 2021 16:23:47 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/arduino_bmp280/</guid>
      
        <description>&lt;h1 id=&#34;gy-bmp280-33&#34;&gt;GY-BMP280-3.3&lt;/h1&gt;
&lt;p&gt;GY-BMP280-3.3 高精度大气压强传感器模块是个低功耗数字复合传感器，它可以测量环境温度和大气压强。气压敏感元件是一个低噪高精度高分辨率绝对大气压力压电式感应元件；温度感测元件具有低噪高分辨率特性，温度值可以对气压进行温度补偿自校正。通过配置采样率寄存器，可以设置敏感元件的采样率。非常适合空间有限的移动设备，如智能是手机、平板电脑、智能手表和可穿戴设备、天气预报、垂直速度指示、飞控设备、室内室外导航、智能家居装置。&lt;/p&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;需要下载两个库：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adafruit_BMP280_Library，用于BMP280传感器的Arduino库&lt;/li&gt;
&lt;li&gt;Adafruit_Sensor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意千万不要接5V的接口，会烧芯片&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/*
VCC 3.3v
GND gnd
SCL 13
SDA 11
CSB 10
SDO 12
备注：不要使用5V，可能会损坏BMP280传感器
*/

#include &amp;lt;Wire.h&amp;gt;

#include &amp;lt;SPI.h&amp;gt;

#include &amp;lt;Adafruit_Sensor.h&amp;gt;  

#include &amp;lt;Adafruit_BMP280.h&amp;gt;

 

#define BMP_SCK 13  

#define BMP_MISO 12

#define BMP_MOSI 11 

#define BMP_CS 10

 

Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO,  BMP_SCK);  

 

void setup() {

  Serial.begin(9600);

  Serial.println(F(&amp;#34;BMP280 测试&amp;#34;));

  

  if (!bmp.begin()) {  

    Serial.println(F(&amp;#34;找不到对应的传感器&amp;#34;));

    while (1);

  }

}

 

void loop() {

    Serial.print(F(&amp;#34;温度 = &amp;#34;));

    Serial.print(bmp.readTemperature());

    Serial.println(&amp;#34; *C&amp;#34;);

    

    Serial.print(F(&amp;#34;气压 = &amp;#34;));

    Serial.print(bmp.readPressure());

    Serial.println(&amp;#34; Pa&amp;#34;);

 

    Serial.print(F(&amp;#34;海拔 = &amp;#34;));

    Serial.print(bmp.readAltitude(1013.25));

    Serial.println(&amp;#34; m&amp;#34;);

    

    Serial.println();

    delay(2000);

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>Arduino之US015高分辨超声波测距模块</title>
      <link>http://coder.amazingdemo.top/post/arduino_us015/</link>
      <pubDate>Fri, 23 Apr 2021 15:29:47 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/arduino_us015/</guid>
      
        <description>&lt;h1 id=&#34;超声波测距原理&#34;&gt;超声波测距原理&lt;/h1&gt;
&lt;p&gt;超声波测距原理是在超声波发射装置发出超声波，它的根据是接收器接到超声波时的时间差，与雷达测距原理相似。 超声波发射器向某一方向发射超声波，在发射时刻的同时开始计时，超声波在空气中传播，途中碰到障碍物就立即返回来，超声波接收器收到反射波就立即停止计时。（超声波在空气中的传播速度为340m/s，根据计时器记录的时间t（秒），就可以计算出发射点距障碍物的距离(s)，即：s=340t/2）&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E8%B6%85%E5%A3%B0%E6%B3%A2%E6%B5%8B%E8%B7%9D%E5%8E%9F%E7%90%86.jpg&#34; alt=&#34;超声波测距原理&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;us-015-超声波测距模块&#34;&gt;US-015 超声波测距模块&lt;/h1&gt;
&lt;p&gt;US-015是目前市场上分辨率最高，重复测量一致性最好的超声波测距模块；US-015的分辨率高于1mm，可达0.5mm，测距精度高；重复测量一致性好，测距稳定可靠。US-015超声波测距模块可实现2cm~4m的非接触测距功能，供电电压为5V，工作电流为2.2mA，支持GPIO通信模式，工作稳定可靠。&lt;/p&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/US015_1.JPG&#34; alt=&#34;US015_1&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/US015_2.JPG&#34; alt=&#34;US015_2&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/US015_3.JPG&#34; alt=&#34;US015_3&#34;&gt;&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/*
项目：测试距离（单位mm）
原理：声波在空气中传播速度为340m/s，根据计时器记录时间t，即可算出发射点距离障碍物的距离S，即S=340m/s*t/2，这就是所谓的时间差测距法。

//连线方法
//MPU-UNO
//VCC-5V
//GND-GND
//Echo-D2
//Trig-D3
*/

unsigned int EchoPin = 3;          
unsigned int TrigPin = 2;          
unsigned long Time_Echo_us = 0;
unsigned long Len_mm  = 0;

void setup()
{  //Initialize
    Serial.begin(9600);        //Serial: output result to Serial monitor
    pinMode(EchoPin, INPUT);   //Set EchoPin as input, to receive measure result from US-015
    pinMode(TrigPin, OUTPUT);  //Set TrigPin as output, used to send high pusle to trig measurement (&amp;gt;10us)
}

void loop()
{
    digitalWrite(TrigPin, HIGH);    //begin to send a high pulse, then US-015 begin to measure the distance
    delayMicroseconds(20);          //set this high pulse width as 20us (&amp;gt;10us)
    digitalWrite(TrigPin, LOW);     //end this high pulse
   
    Time_Echo_us = pulseIn(EchoPin, HIGH);               //calculate the pulse width at EchoPin,
    if((Time_Echo_us &amp;lt; 60000) &amp;amp;&amp;amp; (Time_Echo_us &amp;gt; 1))     //a valid pulse width should be between (1, 60000).
    {
      Len_mm = (Time_Echo_us*34/100)/2;      //calculate the distance by pulse width, Len_mm = (Time_Echo_us * 0.34mm/us) / 2 (mm)
      Serial.print(&amp;#34;Present Distance is: &amp;#34;);  //output result to Serial monitor
      Serial.print(Len_mm, DEC);            //output result to Serial monitor
      Serial.println(&amp;#34;mm&amp;#34;);                 //output result to Serial monitor
    }
    delay(1000);                            //take a measurement every second (1000ms)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>Arduino之GY-30光照传感器</title>
      <link>http://coder.amazingdemo.top/post/arduino_gy30/</link>
      <pubDate>Fri, 23 Apr 2021 15:23:47 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/arduino_gy30/</guid>
      
        <description>&lt;h1 id=&#34;gy-30-光照传感器&#34;&gt;GY-30 光照传感器&lt;/h1&gt;
&lt;p&gt;数字光强度检测模块:GY-30&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;采用ROHM原装BH1750FVI芯片&lt;/li&gt;
&lt;li&gt;供电电源  ：3-5v&lt;/li&gt;
&lt;li&gt;光照度范围：0-65535 lx&lt;/li&gt;
&lt;li&gt;传感器内置16bitAD转换器&lt;/li&gt;
&lt;li&gt;直接数字输出，省略复杂的计算，省略标定&lt;/li&gt;
&lt;li&gt;不区分环境光源&lt;/li&gt;
&lt;li&gt;接近于视觉灵敏度的分光特性&lt;/li&gt;
&lt;li&gt;可对广泛的亮度进行1勒克斯的高精度测定&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;采用IIC通讯方式，GY-30VCC接+5V，GND接地。GY-30 SDA 、SCL分别接A4、 A5&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
#include &amp;lt;Wire.h&amp;gt;
// GY-30
// BH1750FVI
// in ADDR &amp;#39;L&amp;#39; mode 7bit addr
#define ADDR 0b0100011
// addr &amp;#39;H&amp;#39; mode
// #define ADDR 0b1011100
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Wire.begin();
  Wire.beginTransmission(ADDR);
  Wire.write(0b00000001);
  Wire.endTransmission();
}
void loop() {
  // put your main code here, to run repeatedly:
  int val = 0;
  // reset
  Wire.beginTransmission(ADDR);
  Wire.write(0b00000111);
  Wire.endTransmission();
 
  Wire.beginTransmission(ADDR);
  Wire.write(0b00100000);
  Wire.endTransmission();
  // typical read delay 120ms
  delay(120);
  Wire.requestFrom(ADDR, 2); // 2byte every time
  for (val = 0; Wire.available() &amp;gt;= 1; ) {
    char c = Wire.read();
    //Serial.println(c, HEX);
    val = (val &amp;lt;&amp;lt; 8) + (c &amp;amp; 0xFF);
  }
  val = val / 1.2;
  Serial.print(&amp;#34;lx: &amp;#34;);
  Serial.println(val);
  Serial.println(&amp;#34;OK&amp;#34;);
  delay(100);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>Arduino之DHT11温湿度传感器模块</title>
      <link>http://coder.amazingdemo.top/post/arduino_dht11/</link>
      <pubDate>Fri, 23 Apr 2021 14:22:47 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/arduino_dht11/</guid>
      
        <description>&lt;h1 id=&#34;dht11-温湿度传感器&#34;&gt;DHT11 温湿度传感器&lt;/h1&gt;
&lt;p&gt;DHT11 是一款温湿度传感器，可以用来测试环境温湿度，该传感器的的温度测量范围为 0 ~ 50°C,误差为 2 °C；湿度测量范围为在 0°C时为30%-90%RH，环境温度为 25°C时，湿度测量范围为20%-90%RH，在50°C时，测量范围是20%-80%。&lt;/p&gt;
&lt;p&gt;一般从网上买的 Arduino 传感器套件版本的 DHT11，会有一个带 LED 的焊好的板，上面有三个接线引脚，分别是 VCC, GND, DATA，接线时，只要将 VCC 接到 Arduino 5V上，GND 接到 Arduino GND 上，DATA 引脚接 Arduino Pin8上即可。&lt;/p&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/DHT11.png&#34; alt=&#34;DHT11&#34;&gt;&lt;/p&gt;
&lt;p&gt;DHT11 Arduino 的测试程序也很简单，主要分几个部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引入 dht11 库&lt;/li&gt;
&lt;li&gt;设置端口&lt;/li&gt;
&lt;li&gt;检查 DHT11 是否正确连接&lt;/li&gt;
&lt;li&gt;获取 DHT11 测量数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DHT11库&lt;a href=&#34;http://coder.amazingdemo.top/userfile/Dht11.zip&#34;&gt;下载链接&lt;/a&gt;，将压缩文件解压到arduino安装文件夹的libraryies中。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//连线方法
//MPU-UNO
//VCC-5V
//GND-GND
//DATA-D2

double Fahrenheit(double celsius) 
{
        return 1.8 * celsius + 32;
}    //摄氏温度度转化为华氏温度

double Kelvin(double celsius)
{
        return celsius + 273.15;
}     //摄氏温度转化为开氏温度

// 露点（点在此温度时，空气饱和并产生露珠）
// 参考: http://wahiduddin.net/calc/density_algorithms.htm 
double dewPoint(double celsius, double humidity)
{
        double A0= 373.15/(273.15 + celsius);
        double SUM = -7.90298 * (A0-1);
        SUM += 5.02808 * log10(A0);
        SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
        SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
        SUM += log10(1013.246);
        double VP = pow(10, SUM-3) * humidity;
        double T = log(VP/0.61078);   // temp var
        return (241.88 * T) / (17.558-T);
}

// 快速计算露点，速度是5倍dewPoint()
// 参考: http://en.wikipedia.org/wiki/Dew_point
double dewPointFast(double celsius, double humidity)
{
        double a = 17.271;
        double b = 237.7;
        double temp = (a * celsius) / (b + celsius) + log(humidity/100);
        double Td = (b * temp) / (a - temp);
        return Td;
}

#include &amp;lt;dht11.h&amp;gt;

dht11 DHT11;

#define DHT11PIN 2

void setup()
{
  Serial.begin(9600);
  Serial.println(&amp;#34;DHT11 TEST PROGRAM &amp;#34;);
  Serial.print(&amp;#34;LIBRARY VERSION: &amp;#34;);
  Serial.println(DHT11LIB_VERSION);
  Serial.println();
}

void loop()
{
  Serial.println(&amp;#34;\n&amp;#34;);

  int chk = DHT11.read(DHT11PIN);

  Serial.print(&amp;#34;Read sensor: &amp;#34;);
  switch (chk)
  {
    case DHTLIB_OK: 
                Serial.println(&amp;#34;OK&amp;#34;); 
                break;
    case DHTLIB_ERROR_CHECKSUM: 
                Serial.println(&amp;#34;Checksum error&amp;#34;); 
                break;
    case DHTLIB_ERROR_TIMEOUT: 
                Serial.println(&amp;#34;Time out error&amp;#34;); 
                break;
    default: 
                Serial.println(&amp;#34;Unknown error&amp;#34;); 
                break;
  }

  Serial.print(&amp;#34;Humidity (%): &amp;#34;);
  Serial.println((float)DHT11.humidity, 2);

  Serial.print(&amp;#34;Temperature (oC): &amp;#34;);
  Serial.println((float)DHT11.temperature, 2);

  Serial.print(&amp;#34;Temperature (oF): &amp;#34;);
  Serial.println(Fahrenheit(DHT11.temperature), 2);

  Serial.print(&amp;#34;Temperature (K): &amp;#34;);
  Serial.println(Kelvin(DHT11.temperature), 2);

  Serial.print(&amp;#34;Dew Point (oC): &amp;#34;);
  Serial.println(dewPoint(DHT11.temperature, DHT11.humidity));

  Serial.print(&amp;#34;Dew PointFast (oC): &amp;#34;);
  Serial.println(dewPointFast(DHT11.temperature, DHT11.humidity));

  delay(2000);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;烧录程序后，通过Arduino的串口监视器查看，的确有数据出来。但是不断地报checksum error，不知道咋解决。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>算数独</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E8%87%AA%E5%8A%A8%E7%AE%97%E6%95%B0%E7%8B%AC/</link>
      <pubDate>Tue, 20 Apr 2021 19:11:53 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E8%87%AA%E5%8A%A8%E7%AE%97%E6%95%B0%E7%8B%AC/</guid>
      
        <description>&lt;p&gt;闲暇的时候写的小程序，从map输入数独的题目后自动计算。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;#34;stdafx.h&amp;#34;

#include &amp;lt;vector&amp;gt;
#include &amp;lt;map&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;assert.h&amp;gt;

// 获取行index
std::vector&amp;lt;int&amp;gt; askRow(int index)
{
    std::vector&amp;lt;int&amp;gt; rownum;

    auto rowindex = index / 10;
    for (int i = 0; i &amp;lt; 9; i++)
    {
        rownum.push_back(rowindex * 10 + i);
    }
    return rownum;
}

// 获取列index
std::vector&amp;lt;int&amp;gt; askCol(int index)
{
    std::vector&amp;lt;int&amp;gt; colnum;

    auto colindex = index % 10;
    for (int i = 0; i &amp;lt; 9; i++)
    {
        colnum.push_back(i * 10 + colindex);
    }
    return colnum;
}

// 获取宫index
std::vector&amp;lt;int&amp;gt; askBlock(int index)
{
    std::vector&amp;lt;std::vector&amp;lt;int&amp;gt;&amp;gt; blocknum = {
        {0,1,2,10,11,12,20,21,22},
        {3,4,5,13,14,15,23,24,25},
        {6,7,8,16,17,18,26,27,28},
        {30,31,32,40,41,42,50,51,52},
        {33,34,35,43,44,45,53,54,55},
        {36,37,38,46,47,48,56,57,58},
        {60,61,62,70,71,72,80,81,82},
        {63,64,65,73,74,75,83,84,85},
        {66,67,68,76,77,78,86,87,88},
    };

    for (const std::vector&amp;lt;int&amp;gt;&amp;amp; item : blocknum)
    {
        if (item.end() != std::find(item.begin(), item.end(), index))
            return item;
    }
    assert(false);
    return std::vector&amp;lt;int&amp;gt;();
}

// 检查冲突(入参为格子的值,而非index)
bool check(const std::vector&amp;lt;char&amp;gt;&amp;amp; block)
{
    char checknum[9] = { 0 };
    for (auto item : block)
    {
        if (item == 0)
            continue;

        // 同一位置已经有值了
        if (checknum[item - 1] == 1)
            return false;

        // 占位
        checknum[item - 1] = 1;
    }
    return true;
}

// 获取一个检查数据
std::vector&amp;lt;char&amp;gt; getBlock(const std::vector&amp;lt;char&amp;gt;&amp;amp; table, const std::vector&amp;lt;int&amp;gt;&amp;amp; block)
{
    assert(block.size() == 9);

    std::vector&amp;lt;char&amp;gt; blockData;
    for (size_t i = 0; i &amp;lt; block.size(); ++i)
    {
        blockData.push_back(table.at(block.at(i)));
    }
    return blockData;
}

bool calcBackup(std::vector&amp;lt;char&amp;gt;&amp;amp; table, std::vector&amp;lt;std::pair&amp;lt;char, std::vector&amp;lt;char&amp;gt;&amp;gt;&amp;gt;&amp;amp; backupmap)
{
    // 侯选数
    backupmap.clear();
    for (int index = 0; index &amp;lt; 90; ++index)
    {
        if (index % 10 == 9)
            continue;

        if (table.at(index) &amp;gt; 0)
            continue;

        // 获取三种类型的数据集
        auto blockRow = askRow(index);
        auto blcokDataRow = getBlock(table, blockRow);
        auto blockCol = askCol(index);
        auto blcokDataCol = getBlock(table, blockCol);
        auto block = askBlock(index);
        auto blcokData = getBlock(table, block);

        // 获取每个格子的候选数
        std::vector&amp;lt;char&amp;gt; vec;
        for (int i = 1; i &amp;lt; 10; i++)
        {
            if (blcokDataRow.end() == std::find(blcokDataRow.begin(), blcokDataRow.end(), i) &amp;amp;&amp;amp;
                blcokDataCol.end() == std::find(blcokDataCol.begin(), blcokDataCol.end(), i) &amp;amp;&amp;amp;
                blcokData.end() == std::find(blcokData.begin(), blcokData.end(), i))
                vec.push_back(i);
        }

        // 如果只有一个候选数,则重算
        assert(vec.size() &amp;gt; 0);
        if (vec.size() == 1)
        {
            table.at(index) = vec[0];
            return true;
        }
        backupmap.push_back(std::make_pair(index, vec));
    }
    return false;
}

// 递归,一个个的试
bool calc(std::vector&amp;lt;char&amp;gt;&amp;amp; table, const std::vector&amp;lt;std::pair&amp;lt;char, std::vector&amp;lt;char&amp;gt;&amp;gt;&amp;gt;&amp;amp; backupmap, int num, int index)
{
    int currentIndex = backupmap.at(num).first;
    table.at(currentIndex) = backupmap.at(num).second.at(index);

    auto blockRow = askRow(currentIndex);
    auto blockCol = askCol(currentIndex);
    auto block = askBlock(currentIndex);
    if (check(getBlock(table, block)) &amp;amp;&amp;amp; check(getBlock(table, blockRow)) &amp;amp;&amp;amp; check(getBlock(table, blockCol)))
    {
        if (num &amp;gt;= backupmap.size() - 1)
        {
            // 计算到最后一格,出现全部备选数据相匹配的情况
            return true;
        }
        else
        {
            // 当前格子匹配正常,继续计算下一个
            for (int index = 0; index &amp;lt; backupmap.at(num + 1).second.size(); ++index)
            {
                if (calc(table, backupmap, num + 1, index))
                {
                    return true;
                }
            }
        }
    }
    table.at(currentIndex) = 0;
    return false;
}

void prinfTable(const std::vector&amp;lt;char&amp;gt;&amp;amp; table)
{
    for (int row = 0; row &amp;lt; 9; ++row)
    {
        if (row % 3 == 0)
        {
            printf(&amp;#34;------------------------------------------ \n&amp;#34;);
        }

        printf(&amp;#34;%4d%4d%4d | %4d%4d%4d | %4d%4d%4d \n&amp;#34;, table[row * 10], table[row * 10 + 1], table[row * 10 + 2],
            table[row * 10 + 3], table[row * 10 + 4], table[row * 10 + 5],
            table[row * 10 + 6], table[row * 10 + 7], table[row * 10 + 8]);
    }
}

bool fill(const std::map&amp;lt;char, char&amp;gt;&amp;amp; origin, std::vector&amp;lt;char&amp;gt;&amp;amp; table)
{
    for (auto item : origin)
    {
        table.at(item.first) = item.second;
    }

    int i = 0;
    std::vector&amp;lt;std::pair&amp;lt;char, std::vector&amp;lt;char&amp;gt;&amp;gt;&amp;gt; backupmap;
    while (calcBackup(table, backupmap))
    {
        if (i++ &amp;gt; 90)
            break;
    }

    if (backupmap.size() &amp;gt; 0)
    {
        for (int index = 0; index &amp;lt; backupmap.front().second.size(); ++index)
        {
            if (calc(table, backupmap, 0, index))
            {
                return true;
            }
        }
        return false;
    }
    return true;
}

int main()
{
    std::map&amp;lt;char, char&amp;gt; origin;
    origin.insert(std::make_pair(12, 1));
    origin.insert(std::make_pair(15, 9));
    origin.insert(std::make_pair(18, 7));
    origin.insert(std::make_pair(20, 7));
    origin.insert(std::make_pair(22, 9));
    origin.insert(std::make_pair(23, 4));
    origin.insert(std::make_pair(27, 2));
    origin.insert(std::make_pair(28, 3));
    origin.insert(std::make_pair(32, 4));
    origin.insert(std::make_pair(33, 7));
    origin.insert(std::make_pair(36, 6));
    origin.insert(std::make_pair(43, 1));
    origin.insert(std::make_pair(44, 5));
    origin.insert(std::make_pair(47, 8));
    origin.insert(std::make_pair(55, 4));
    origin.insert(std::make_pair(61, 2));
    origin.insert(std::make_pair(66, 7));
    origin.insert(std::make_pair(67, 3));
    origin.insert(std::make_pair(70, 5));
    origin.insert(std::make_pair(71, 8));
    origin.insert(std::make_pair(74, 1));
    origin.insert(std::make_pair(78, 2));
    origin.insert(std::make_pair(84, 4));
    origin.insert(std::make_pair(85, 3));

    std::vector&amp;lt;char&amp;gt; table(90, 0);
    if (fill(origin, table))
    {
        std::cout &amp;lt;&amp;lt; &amp;#34;happy end&amp;#34; &amp;lt;&amp;lt; std::endl;
        prinfTable(table);
    }
    else
    {
        std::cout &amp;lt;&amp;lt; &amp;#34;sorry&amp;#34; &amp;lt;&amp;lt; std::endl;
    }

    getchar();
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>前端矩阵 (matrix) 封装</title>
      <link>http://coder.amazingdemo.top/post/js_%E5%89%8D%E7%AB%AF%E7%9F%A9%E9%98%B5%E5%B0%81%E8%A3%85/</link>
      <pubDate>Sat, 20 Feb 2021 15:12:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/js_%E5%89%8D%E7%AB%AF%E7%9F%A9%E9%98%B5%E5%B0%81%E8%A3%85/</guid>
      
        <description>&lt;p&gt;使用 canvas 画布来操控图形或者图片的二维变化的方式一般是平移、缩放、和旋转。canvas 已相应提供了相关的 api。如：translate(), scale() 和 rotate()，但这些功能无法统一处理并且无法描述当前画布的处于某种状态。为了实现这种统一连贯性的状态描述，canvas 给我们提供了另外一种思路及方法，即使用：transform() / setTransform()；因为画布上的每个对象都拥有一个当前的 3 x 3 变换矩阵，都可以使用一个 3 x 3 矩阵来描述当前状态，其实是 2 x 3 矩阵，但为了便于计算，人为添加第三行 0 0 1 变成 3 x 3 矩阵。费话不多话，下面直接看矩阵变换的已封装好的功能，为了便于读者理解，每一步都有详细解释及介绍。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;function createMatrix()
{
    /**
     * 初始一个矩阵，开始时，a, d 为 1，其它为 0
     * a  c  e
     * b  d  f
     * 0  0  1
     *
     * a	水平缩放绘图
     * b	水平倾斜绘图
     * c	垂直倾斜绘图
     * d	垂直缩放绘图
     * e	水平移动绘图
     * f	垂直移动绘图
     *
     * 1  0  0
     * 0  1  0
     * 0  0  1
     *
     * */
    let m = []
    m[0] = 1
        m[1] = 0
        m[2] = 0
        m[3] = 1
        m[4] = 0
        m[5] = 0
        return m
}
function multiply(m1, m2)
{
    // 左乘
    /**
     * 已知矩阵 A 和矩阵 B，求 A 和 B 的乘积 C= AB
     * 常规方法：矩阵 C 中每一个元素 Cij = A 的第 i 行 乘以（点乘） B 的第 j 列
     *
     * m1[0]  m1[2]  m1[4]     m2[0]  m2[2]  m2[4]     m[0]  m[2]  m[4]
     * m1[1]  m1[3]  m1[5]  X  m2[1]  m2[3]  m2[5]  =  m[1]  m[3]  m[5]
     *  0      0      1          0      0      1        0      0     1
     *
     *
     * */
    let m = []
    m[0] = m1[0] * m2[0] + m1[2] * m2[1] + m1[4] * 0
        m[2] = m1[0] * m2[2] + m1[2] * m2[3] + m1[4] * 0
        m[4] = m1[0] * m2[4] + m1[2] * m2[5] + m1[4] * 1
        m[1] = m1[1] * m2[0] + m1[3] * m2[1] + m1[5] * 0
        m[3] = m1[1] * m2[2] + m1[3] * m2[3] + m1[5] * 0
        m[5] = m1[1] * m2[4] + m1[3] * m2[5] + m1[5] * 1
        return m
}
function translate(m, v)
{ // v 是个二维向量，即一个数组 [x, y]，向量 v 是用于表示 x y 轴方向移动的距离，及其可表示移动的方向
    let arr = [...m]
    arr[4] = arr[4] + v[0]
        arr[5] = arr[5] + v[1]
        return arr
}
function rotate(m, rad)
{
    /**
     * 假如 A（X, Y） 点初始角度为 a，绕圆点旋转 θ 度，在坐标系上可得 B 点的坐标（X&amp;#39;, Y&amp;#39;），算出半径为 r = √(X2 + Y2)
     * X&amp;#39; = cos(a + θ) * r
     * Y&amp;#39; = sin(a + θ) * r
     * 根据三角函数公式：cos(α + β) = cosαcosβ - sinαsinβ
     * 可得 X&amp;#39; = r * cosa * cosθ – r * sina * sinθ =  X * cosθ – Y * sinθ
     * 根据三角函数公式：sin(α + β) = sinαcosβ + cosαsinβ
     * 可得 Y&amp;#39; = sinacosθ * r + cosasinθ * r =  X * sinθ + Y * cosθ
     * X’       cosθ    -sinθ   0       X
     * Y’   =   sinθ    cosθ    0   X   Y
     * 1         0       0      1       1
     * 根据结合律结果应为：
     *
     * cosθ    -sinθ   0       m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]
     * sinθ    cosθ    0   X   m[1]  m[3]  m[5]  =   arr[1]  arr[3]  arr[5]
     *  0       0      1       0      0      1         0       0        1
     *
     */
    return multiply(
        [
            Math.cos(rad),
            Math.sin(rad),
            -1 * Math.sin(rad),
            Math.cos(rad),
            0,
            0
        ],
        m)
}
function scale(m, v)
{ // v 是个二维向量，即一个数组 [scaleX, scaleY]，向量 v 是用于表示 x y 方向的伸缩程度
    /**
     * 假如 X，Y 分别缩放（a, b）倍
     * X&amp;#39; = X * a
     * Y&amp;#39; = Y * b
     *
     * a    0    0       m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]
     * 0    b    0   X   m[1]  m[3]  m[5]  =   arr[1]  arr[3]  arr[5]
     * 0    0    1       0      0      1         0       0        1
     *
     */
    return multiply([v[0], 0, 0, v[1], 0, 0], m)
}
function invert(m)
{
    // 矩阵求逆
    /**
     * 待定系数法求逆
     * X&amp;#39; = X * a
     * Y&amp;#39; = Y * b
     *
     * m[0]  m[2]  m[4]      arr[0]  arr[2]  arr[4]       1   0   0
     * m[1]  m[3]  m[5]  *   arr[1]  arr[3]  arr[5]   =   0   1   0
     * 0      0      1         0       0        1         0   0   1
     *
     * m[0] * arr[0] + m[2] * arr[1] + m[4] * 0 = 1    =&amp;gt; arr[0] = (1 - m[2] * arr[1]) / m[0]
     * m[0] * arr[2] + m[2] * arr[3] + m[4] * 0 = 0    =&amp;gt; arr[2] = (0 - m[2] * arr[3]) / m[0]
     * m[0] * arr[4] + m[2] * arr[5] + m[4] * 1 = 0    =&amp;gt; arr[4] = (0 - m[2] * arr[5] - m[4]) / m[0]
     * m[1] * arr[0] + m[3] * arr[1] + m[5] * 0 = 0    =&amp;gt; arr[0] = (0 - m[3] * arr[1]) / m[1]
     * m[1] * arr[2] + m[3] * arr[3] + m[5] * 0 = 1    =&amp;gt; arr[2] = (1 - m[3] * arr[3]) / m[1]
     * m[1] * arr[4] + m[3] * arr[5] + m[5] * 1 = 0    =&amp;gt; arr[4] = (0 - m[3] * arr[5] - m[5]) / m[1]
     */
    let arr = []
    arr[1] = 1 / m[0] / (m[2] / m[0] - m[3] / m[1])
        arr[0] = 0 - (m[3] * arr[1]) / m[1]
        arr[3] = 1 / m[1] / (m[3] / m[1] - m[2] / m[0])
        arr[2] = 0 - (m[2] * arr[3]) / m[0]
        arr[5] = (m[4] / m[0] - m[5] / m[1]) / (m[3] / m[1] - m[2] / m[0])
        arr[4] = 0 - m[3] * arr[5] - m[5] / m[1]
        return arr
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;使用上面的矩阵函数功能来操控画布画图&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var screenWidth = $(window).width() &amp;gt; 750 ? 750 : $(window).width()
var screenHeight = $(window).height()
var c = document.getElementById(&amp;#39;myCanvas_1&amp;#39;)
c.setAttribute(&amp;#39;width&amp;#39;, screenWidth)
c.setAttribute(&amp;#39;height&amp;#39;, screenHeight)
var ctx = c.getContext(&amp;#39;2d&amp;#39;)
var m = createMatrix()
ctx.fillStyle = &amp;#39;red&amp;#39;
ctx.fillRect(100, 100, 250, 100)
// translate
m = translate(m, [30, 10]) // 平移 30 10
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = &amp;#39;orange&amp;#39;
ctx.fillRect(100, 100, 250, 100)
// rotate
m = rotate(m, (30 * Math.PI) / 180) // 旋转 30 度
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = &amp;#39;yellow&amp;#39;
ctx.fillRect(100, 100, 250, 100)
// scale
m = scale(m, [0.7, 0.5]) // 缩放 0.7, 0.5
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = &amp;#39;green&amp;#39;
ctx.fillRect(100, 100, 250, 100)
// invert 求逆
m = invert(m)
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5])
ctx.fillStyle = &amp;#39;blue&amp;#39;
ctx.fillRect(100, 100, 250, 100)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>boost b2 参数说明</title>
      <link>http://coder.amazingdemo.top/post/boost_b2%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E/</link>
      <pubDate>Tue, 26 Jan 2021 13:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/boost_b2%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E/</guid>
      
        <description>&lt;h1 id=&#34;命令参数&#34;&gt;命令参数&lt;/h1&gt;
&lt;p&gt;b2 命令的功能强大，用起来也比较复杂，因此在使用之前，最好先查看一下该命令的帮助：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;b2.exe --help&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;stageinstall&#34;&gt;stage/install&lt;/h2&gt;
&lt;p&gt;stage 表示只生成库（dll 和 lib），install 还会生成包含头文件的 include 目录。推荐使用 stage，因为 install 生成的 include 目录实际就是源码包下的 boost 目录，需要 include 的时候可以直接使用，不需要再次生成，这样可以节省大量的编译时间。&lt;/p&gt;
&lt;h2 id=&#34;toolset&#34;&gt;toolset&lt;/h2&gt;
&lt;p&gt;指定编译器，可选的如 borland、gcc、msvc-14.0（VS2015）等。如果不指定，会自动搜索本地可用的编译器（可查看 ./project-config.jam 文件以确认）。&lt;/p&gt;
&lt;h2 id=&#34;withoutwith&#34;&gt;without/with&lt;/h2&gt;
&lt;p&gt;选择不编译/编译哪些库（类似于黑名单/白名单）。&amp;ndash;with-python 的含义是仅编译 python，其他的都不编译。反过来，如果用 &amp;ndash;without-python，意思就是除了 python， 其他的都编译。with/without 参数可以多次出现，以限定多个库。如果不设置 with/without 参数，默认全部编译，可能需要几个小时的时间！&lt;/p&gt;
&lt;p&gt;需要注意，编译 Boost.python 需要确保本地安装了 Python，并且 python 命令已加入环境变量。&lt;/p&gt;
&lt;p&gt;要查看 Boost 包含的所有库，可使用以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;b2.exe --show-libraries&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;stagedirprefix&#34;&gt;stagedir/prefix&lt;/h2&gt;
&lt;p&gt;stage 时使用 stagedir，install 时使用 prefix，表示编译生成文件的路径。推荐给不同的编译环境指定不同的目录，如 Visual Studio 2015 的 x86 应用对应的是 bin/lib32-msvc-14.0，x64 应用对应的是 bin/lib64-msvc-14.0。如果都生成到一个目录下，将没有任何益处，徒增管理难度。如果使用了 install 参数，那么还将在上述指定的目录下生成 include 目录，用于保存头文件。&lt;/p&gt;
&lt;h2 id=&#34;build-dir&#34;&gt;build-dir&lt;/h2&gt;
&lt;p&gt;编译生成的中间文件的路径，默认是 Boost 根目录下的 bin.v2 目录，一般无需设置。&lt;/p&gt;
&lt;h2 id=&#34;link&#34;&gt;link&lt;/h2&gt;
&lt;p&gt;指定生成动态链接库还是静态链接库，取值为 static|shared。生成静态链接库使用 static，生成动态链接库需使用 shared。如不指定，默认使用 static。静态库的缺点是占用空间比较大，优点是程序发布的时候无需附带 Boost 库的 dll，比较整洁。推荐使用静态库的方式编译 Boost.python，这样发布程序的时候就不用 Boost 的 dll 了，并且也多占用不了太多空间。&lt;/p&gt;
&lt;h2 id=&#34;runtime-link&#34;&gt;runtime-link&lt;/h2&gt;
&lt;p&gt;指定运行时是动态还是静态链接其他库。同样有 shared 和 static 两种方式。如果不指定，默认是 shared，一般无需设置。&lt;/p&gt;
&lt;p&gt;static生成的是MTd和MT，MD的话runtime-link=shared。&lt;/p&gt;
&lt;h2 id=&#34;threading&#34;&gt;threading&lt;/h2&gt;
&lt;p&gt;要编译的库是单线程还是多线程，可取值 single|multi。如果不指定，默认是 multi，一般无需设置。&lt;/p&gt;
&lt;h2 id=&#34;variant&#34;&gt;variant&lt;/h2&gt;
&lt;p&gt;debug|release，编译 debug 版本还是 release 版本。一般与最终发布的程序是 debug 还是 release 版相对应。如果不指定，默认两个都编译，一般无需设置。&lt;/p&gt;
&lt;h2 id=&#34;address-model&#34;&gt;address-model&lt;/h2&gt;
&lt;p&gt;编译成 32 位版本还是 64 位版本，可取值 32|64。如果不指定，默认两个版本都编译。如果是编译 Boost.python，该参数就要与本地安装的 Python 位数相对应，否则编译会出错，因此最好设置一下。&lt;/p&gt;
&lt;h1 id=&#34;boost-静态库动态库的命名规则&#34;&gt;Boost 静态库/动态库的命名规则&lt;/h1&gt;
&lt;p&gt;以 Boost.python 为例，如果编译的是静态库（link=static），将会生成单个 .lib 文件：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;libboost_python36-vc140-mt-gd-x32-1_68.lib&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;而如果编译的是动态库（link=shared），将会生成两个文件（.lib 和 .dll）：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;boost_python36-vc140-mt-gd-x32-1_68.lib
boost_python36-vc140-mt-gd-x32-1_68.dll&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;动态库虽然也生成 .lib 文件，但它与静态库的 .lib 文件差别很大。动态库的 .lib 更像是对 .dll 的声明，二者的关系类似于 .h 与 .cpp 的关系。因此，动态库中的 .lib 文件要比静态库的 .lib 文件小得多。&lt;/p&gt;
&lt;p&gt;下面以静态库的命名规则为例进行分析：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;libboost_python36-vc140-mt-sgd-x32-1_68.lib
| ||   | |      | |   | || ||| | | |  |
 -  ---   ------   ---  -- - -  -   --
 1   2       3      4    5 6 7  8    9&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;静态库以 lib 开头，动态库开头没有 lib。&lt;/li&gt;
&lt;li&gt;所有的库都含有 boost 前缀。&lt;/li&gt;
&lt;li&gt;Boost 库名称，本例中为 python36。&lt;/li&gt;
&lt;li&gt;编译器名称及其版本，vc140 指的是 msvc-14.0，对应 Visual Studio 2015。&lt;/li&gt;
&lt;li&gt;有 mt 代表 threading=multi，没有则代表 threading=single。&lt;/li&gt;
&lt;li&gt;有 s 代表 runtime-link=static，没有则代表 runtime-link=shared。&lt;/li&gt;
&lt;li&gt;有 gd 代表 debug 版本，没有则代表 release 版本。&lt;/li&gt;
&lt;li&gt;目标位数，x32 代表 32 位，x64 代表 64 位。&lt;/li&gt;
&lt;li&gt;Boost 库的版本号，1_68 代表 Boost 1.68 版本。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>合并两个 Git 仓库，并保留源仓库的所有提交记录</title>
      <link>http://coder.amazingdemo.top/post/%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AAgit%E4%BB%93%E5%BA%93/</link>
      <pubDate>Sat, 09 Jan 2021 17:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AAgit%E4%BB%93%E5%BA%93/</guid>
      
        <description>&lt;h1 id=&#34;需求&#34;&gt;需求&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;有2个 git 仓库：repo1、repo2；&lt;/li&gt;
&lt;li&gt;想将 repo1 中的文件移入 repo2；&lt;/li&gt;
&lt;li&gt;repo1 的提交记录要保留；&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;实现&#34;&gt;实现&lt;/h1&gt;
&lt;p&gt;将 repo1 作为远程仓库，添加到 repo2 中，并设置别名为 other：&lt;/p&gt;
&lt;p&gt;切换至 repo2 仓库，并执行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git remote add other git@github.com:xxx.git&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;从 repo1 仓库中抓取数据到本仓库：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git fetch other&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;将 repo1 仓库抓取的 master 分支作为新分支 checkout 到本地，新分支名设定为 repo1&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git checkout -b repo1 other/master&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;切换至 repo2 的 master 分支&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git checkout master&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;将 repo1 合并入 master 分支&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git merge repo1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这里会报错 fatal: refusing to merge unrelated histories，这个问题是因为两个根本不相干的 git 库， 一个是本地库，一个是远端库，然后本地要去推送到远端，远端觉得这个本地库跟自己不相干，所以告知无法合并；需要加上 &amp;ndash;allow-unrelated-histories 把两段不相干的分支进行强行合并；&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;git merge repo1 --allow-unrelated-histories&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;大功告成~&lt;/p&gt;
&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;大致思路是伪造远程的 repo1 仓库为 repo2 的一个分支，然后合并进来；&lt;/li&gt;
&lt;li&gt;若是文件有冲突、或要建立子目录，建议在 repo1 中先解决，再进行如上操作；&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>如何从 GitHub 上下载单个文件夹？</title>
      <link>http://coder.amazingdemo.top/post/%E4%BB%8Egithub%E4%B8%8A%E4%B8%8B%E8%BD%BD%E5%8D%95%E4%B8%AA%E6%96%87%E4%BB%B6%E5%A4%B9/</link>
      <pubDate>Thu, 31 Dec 2020 16:44:22 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%BB%8Egithub%E4%B8%8A%E4%B8%8B%E8%BD%BD%E5%8D%95%E4%B8%AA%E6%96%87%E4%BB%B6%E5%A4%B9/</guid>
      
        <description>&lt;p&gt;今天要从GitHub上下载东西，结果突然发现GitHub只提供整项目下载，而不支持选择某一个子文件夹下载。&lt;/p&gt;
&lt;p&gt;搜索答案，找到了一种方法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先在GitHub上打开需要下载的子文件夹，记下浏览器里的路径&lt;/li&gt;
&lt;li&gt;然后，将路径里面的 /tree/master/ 替换为 /trunk/&lt;/li&gt;
&lt;li&gt;最后到SVN里面，选择检出这个修改后的url&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果不是GIT的master分支，则将 /tree/master/ 替换为 /branches/branchname/ 其中 branchname 为分支名&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>如何设计高效的C&#43;&#43;类</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E8%AE%BE%E8%AE%A1%E9%AB%98%E6%95%88%E7%9A%84%E7%B1%BB/</link>
      <pubDate>Sat, 07 Nov 2020 13:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E8%AE%BE%E8%AE%A1%E9%AB%98%E6%95%88%E7%9A%84%E7%B1%BB/</guid>
      
        <description>&lt;p&gt;一直以来，使用C++面向对象机制，主要是为了其封装和多态特性。往往设计类时，只是为了功能的堆砌，没有考虑的更加深入。&lt;/p&gt;
&lt;p&gt;之前也阅读过《Effective C++》，只是那时是在学生时代。如今工作了，重新阅读，有不少新的感悟。最关键的是，能从更高的视角去设计程序，之前杂乱无序的点与点，逐渐连接成一条条线。希望后续能够成面、成立体。&lt;/p&gt;
&lt;p&gt;回到正题：如何设计C++类？当然，更合理的表述应该是：如何设计高效、优雅的C++类？（菜鸟的思考）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：我们并不是要设计一个程序，或者一个大的模块，我们考虑的更加具体，具体到其中的一点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;我们需要实现哪些功能&#34;&gt;我们需要实现哪些功能？&lt;/h1&gt;
&lt;p&gt;“先谋后动”，在设计具体类时，必须要清楚下面几点：&lt;/p&gt;
&lt;p&gt;1、设计该类的目的&lt;/p&gt;
&lt;p&gt;清晰的概念远比模棱两口的理解，更能帮助我们深入分析问题。有时候我们觉得我就是需要实现这个功能罢了，只要能用就好了。然而，多和产品经理或技术经理沟通，有可能出现意想不到的结果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;该功能没有我们想到那么复杂，并不需要自己设计；&lt;/li&gt;
&lt;li&gt;该功能比我们想到的更加复杂，我们需要考虑更多正确性、高效性、扩展性、维护性等方面的额问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2、使用该类的场景&lt;/p&gt;
&lt;p&gt;不同的使用场景，相同功能类的设计需求是不一样的。譬如，设计一个视频解码类，如果使用场景为视频播放器，那我们设计的类必须要考虑不同的编码格式；但如果使用场景为视频会议，我们设计的类就不需要考虑太多编码格式的问题，反而需要针对某种格式进行效率优化。&lt;/p&gt;
&lt;p&gt;3、潜在的扩展方向&lt;/p&gt;
&lt;p&gt;程序不是一成不变的，外界事物不停的变化，催生不同的需求。如果我们的程序不可扩展，那每次需求变更，之前的工作都白费了。类的设计一定要考虑到，未来潜在的扩展方向。如果我们无法确定潜在的扩展方向，至少留下可扩展的接口，不要把一切行为、属性都写死。&lt;/p&gt;
&lt;h1 id=&#34;如何构造析构如何拷贝赋值&#34;&gt;如何构造析构、如何拷贝赋值？&lt;/h1&gt;
&lt;p&gt;C++类的构造析构和拷贝赋值是设计C++类时最基本的要点，有不少细节部分需要考虑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构造函数：合成的默认构造函数、默认构造函数、default关键词、explicit关键字、类型转换、延迟初始化、单例模式&lt;/li&gt;
&lt;li&gt;析构函数：默认析构函数、虚析构函数&lt;/li&gt;
&lt;li&gt;拷贝构造函数：深拷贝/浅拷贝、禁止拷贝&lt;/li&gt;
&lt;li&gt;赋值构造函数：深拷贝/浅拷贝、禁止拷贝&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了上面的细节部分，我们需要明确几个准则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;除非默认操作非你所需，否则请用=default来定义构造析构函数；&lt;/li&gt;
&lt;li&gt;除非编译器合成为你所需，否则请用=delete来定义赋值拷贝函数；&lt;/li&gt;
&lt;li&gt;除非类不可能成为基类，否则请将析构函数定义为virtual；&lt;/li&gt;
&lt;li&gt;构造与析构过程中，不调用virtual函数、析构函数不能抛出异常。&lt;/li&gt;
&lt;li&gt;拷贝构造复制需要处理深拷贝和浅拷贝，赋值操作需要额外考虑自我赋值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;显式初始化和类型转换&#34;&gt;显式初始化和类型转换&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;首先表明我的立场：建议单参数构造函数都声明为explicit，拒绝隐式类型转换，除非你有意要隐式转换。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;显式初始化主要针对于单参数构造函数，用explicit关键字来声明构造函数——拒绝隐式类型转换。在C++中，单参数的构造函数主要承担两个功能：对象构造、类型转换。&lt;/p&gt;
&lt;p&gt;这儿强调单参数，是由于多个实参的构造函数不能用于执行隐式转换，所以无须将这些构造函数声明为explicit，但你若是非要声明也是可以的。同时，我们只能在类内声明explicit关键词，不能再类外部重复声明。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class String{
public:
    explicit String(int n);
};
explicit String::String(int n) { } /* error */
String str = 3; /* error */
String str(3);  /*  ok   */&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;使用隐式类型转换需要注意的是编译器只会自动执行一步类型转换，从《C++ Primer》中摘取一个例子来看看：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class SalesData {
public:
    SalesData(const std::string &amp;amp;s);
    SalesData&amp;amp; combine(SalesData);
}

SalesData data;
data.combine(&amp;#34;99-99-99-99&amp;#34;); /* error: cannot transfer &amp;#34;99-99-99-99&amp;#34; to std::string to SalesData at one time*/
data.combine(std::string(&amp;#34;99-99-99-99&amp;#34;)); /* ok: transfer std::string to SalesData */&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;很多时候，隐式的类型转换逻辑上是合理的，我们设计上需要实现一个隐式转换，是可以接受的。很多编译器会自动将类型转换操作转换成对象构造操作。譬如将整形转换成年龄：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Age {
public:
    Age(int age);    
};
Age age = 12; /* ok, same as Age age(12) */&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;然而大多数隐式类型转换常常会带来逻辑上的错误，而且这种错误一旦发生很难查询，所以并不推荐使用隐式转换——除非你是有意为之，否则都使用显式构造。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;explicit之所以被导入到C++，是为了提供程序员一种方法，使他们能够制止”单一参数的constructor”被当作一个conversion运算符。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;《C++ Primer》课后习题中有一题很好的阐述了——何时需要隐式转换、何时需要避免隐式转换：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;std::vector将其单参数构造函数定义成explicit的，而std::string则不是，你觉得原因何在？
std::string接受的单参数是const char*类型，如果我们得到了一个常量指针，则把它看做std::string对象是自然而然的过程，编译器自动把参数类型转换成类类型也非常符合逻辑，因此我们无须指定为explicit。与std::string相反，std::vector接受的单参数是int类型，这个参数的原意是指定std::vector的容量。如果我们在本来需要std::vector的地方提供一个int值并且希望这个int值自动转换成std::vector，则这个过程显得比较牵强，因此把std::vector的单参数构造函数定义成explicit的更加合理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;C++的标准模板库是学习C++理论实践的好地方，总结下标准库中用explicit修饰的构造函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接受一个容量参数的std::vector构造函数是explicit的。&lt;/li&gt;
&lt;li&gt;接受一个指针参数的std::shared_ptr/std::auto_ptr构造函数是explicit的。&lt;/li&gt;
&lt;li&gt;接受一个std::string参数的std::sstream构造函数是explicit的&lt;/li&gt;
&lt;li&gt;接受一个std::string或C-String参数的std::fstream构造函数是explicit的&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;虚函数表&#34;&gt;虚函数表&lt;/h1&gt;
&lt;p&gt;虚函数表在构造函数中建立，在析构函数中销毁，因此构造函数不可能为虚函数，而析构函数可以为虚函数。同时，调用派生类构造函数，首先会出发基类的构造函数，如果基类的构造函数调用了一个虚函数，该虚函数此时并不具备多态特性。也就是——绝不要在构造和析构过程中调用虚函数。&lt;/p&gt;
&lt;h1 id=&#34;虚析构函数&#34;&gt;虚析构函数&lt;/h1&gt;
&lt;p&gt;带有多态性质的基类应该声明一个virtual析构函数，如果class带有任何virtual函数，其就应该拥有一个virtual析构函数。默认情况下，类的析构函数是非虚函数，如果基类的析构函数是虚函数，则派生类的析构函数也都是虚函数。对于多态性质的基类，如果不声明虚析构函数，则会导致内存泄漏，看看下面代码：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Ball {
public:
    Ball();
    ~Ball()
private:
    uint32_t _circle;
};
class BasketBall : public Ball {
}
Ball *ball = new BasketBall();
delete ball; /* 由于Ball的析构函数非虚，ball中属于Ball类部分的成员不会被析构 */&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;当然，如果class一定不会用作基类使用，或者即使是作为基类使用，也不会定义virtual函数，那么是可以不用价格基类声明为virtual的。&lt;/p&gt;
&lt;h1 id=&#34;构造析构与异常处理&#34;&gt;构造析构与异常处理&lt;/h1&gt;
&lt;p&gt;C++中的构造函数是没有返回值的，C++中通知对象构造失败的唯一方法，就是在构造函数中抛出异常。当对象发生部分构造时，已经构造完毕的子对象将会逆序地被析构，此时该对象的析构函数不会被执行。&lt;/p&gt;
&lt;p&gt;如果此时我们使用new操作，分配了内存，将会导致该块内存被泄漏。所以并不建议构造函数抛出异常。除非能够保证不存在内存泄漏。建议的方法是：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class T {
public:
    T() { }
    void init() { /* do init here */ }
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;对于析构函数，绝不能抛出异常。&lt;/p&gt;
&lt;h1 id=&#34;单例模式&#34;&gt;单例模式&lt;/h1&gt;
&lt;p&gt;如果程序中某个类只允许有一个实例，这个时候就需要使用到单例模式。单例模式Wrapper有很多实现，譬如Boost中的singleton，Poco中的SingletonHold。我们来看看Poco中对单例Wrapper的实现：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template &amp;lt;class S&amp;gt;
class SingletonHolder
    /// This is a helper template class for managing
    /// singleton objects allocated on the heap.
    /// The class ensures proper deletion (including
    /// calling of the destructor) of singleton objects
    /// when the application that created them terminates.
{
public:
    SingletonHolder():
        _pS(0)
        /// Creates the SingletonHolder.
    {
    }

    ~SingletonHolder()
        /// Destroys the SingletonHolder and the singleton
        /// object that it holds.
    {
        delete _pS;
    }

    S* get()
        /// Returns a pointer to the singleton object
        /// hold by the SingletonHolder. The first call
        /// to get will create the singleton.
    {
        FastMutex::ScopedLock lock(_m);
        if (!_pS) _pS = new S;
        return _pS;
    }

private:
    S* _pS;
    FastMutex _m;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;看看Poco::SingletonHolder的代码，S类型的实例仅仅创建一次。如果我们需要将单例嵌入到自己的代码逻辑中，可以按照下面代码实现：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Singleton
{
public:
    static Singleton* getInstance()
    {
        return _s;
    }   
private:
    Singleton();
    ~Singleton();

    class Release {
    public:
        ~Release()
        {
            delete _s;
        }
    };
    static Singleton *_s;
    static Release r;
};

Singleton* Singleton::_s = new Singleton();
Singleton::Release Singleton::r;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;下文中延迟初始化那一节也是一种单例模式的实现。&lt;/p&gt;
&lt;h1 id=&#34;深拷贝浅拷贝&#34;&gt;深拷贝、浅拷贝&lt;/h1&gt;
&lt;p&gt;如果一个类拥有资源，当这个类的对象发生复制过程的时候，资源重新分配，这个过程就是深拷贝，反之，没有重新分配资源，就是浅拷贝。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Test {
public:
    Test() : p(new std::string(&amp;#34;Test&amp;#34;)) { }
    ~Test() { delete p; }
    Test(const Test&amp;amp; t) {
        p = new std::string(*t.p); /* 如果这儿不进行深拷贝，析构时则会有多个对象重复删除p */
    }
    Test&amp;amp; operator= (const Test t) {
        swap(t);
        return *this;
    }
private:
    std::string *p;    
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;禁止拷贝&#34;&gt;禁止拷贝&lt;/h1&gt;
&lt;p&gt;如果我们不需要编译器为我们实现默认拷贝构造函数，我们需要主动地驳回编译器这一默认的行为。通常的做法有三种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将相应的成员函数（这里是拷贝构造函数/赋值构造函数）声明为private，并不予实现；&lt;/li&gt;
&lt;li&gt;将函数声明为delete；&lt;/li&gt;
&lt;li&gt;继承一个不允许该操作的基类。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class UnCopyable {
protected:
    UnCopyable();
    ~UnCopyable();
private:
    UnCopyable(const UnCopyable&amp;amp;);
    UnCopyable&amp;amp; operator=(const UnCopyable&amp;amp;)；
};

class Impl : public UnCopyable { };&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;延迟初始化&#34;&gt;延迟初始化&lt;/h1&gt;
&lt;p&gt;延迟初始化是一个很有用的概念。一个对象的延迟初始化（也称延迟实例化）意味着该对象的创建将会延迟至第一次使用该对象时。延迟初始化主要用于提高性能，避免浪费计算，并减少程序内存要求。&lt;/p&gt;
&lt;p&gt;尽量将对象的定义延迟到第一次使用，甚至是直到能够给它初值实参为止。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class LazyInstance {
};

LazyInstance&amp;amp; getInstance(int param) {
    static LazyInstance instance(param);
    return instance;
}
``

上述代码中，instance实例对象直到getInstance()函数被第一次调用时，才会进行初始化。对比下列代码：&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;class LazyInstance {
};&lt;/p&gt;
&lt;p&gt;static LazyInstance instance;
``&lt;/p&gt;
&lt;p&gt;instance实例对象是静态成员，会在程序一开始调用默认构造函数初始化。&lt;/p&gt;
&lt;p&gt;延迟初始化同时也解决了跨编译单元中非局部静态成员的初始化次序不定的问题——函数内局部静态对象会在第一次调用时被初始化。我们通过下列例子来说明该问题（摘自Effective C++）：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/* FileSystem.cpp */
class FileSystem {
public:
    std::size_t numDisks() const;
};
extern FileSystem tfs;

/* Directory.cpp */
class Directory {
public:
    Directory(params);
};
Directory::Directory(params) {
    std::size_t disks = tfs.numDisks();
}

Director dir(tfs);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;由于FileSystem.cpp和Directory.cpp是两个编译单元，其初始化次序在C++标准中并没有被定义，我们无法确定tsf是否一定在dir初始化之前初始化。解决办法就是采用延迟初始化：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class FileSystem {};
FileSystem&amp;amp; tfs() {
    static FileSystem fs;
    return fs;
}

class Directory {};
Directory::Directory(params) {
    std::size_t disks = tfs().numDisks();
}

Directory&amp;amp; dir() {
    static Directory td;
    return td;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;接口设计需要考虑哪些问题&#34;&gt;接口设计需要考虑哪些问题？&lt;/h1&gt;
&lt;p&gt;接口设计的原则就是易用。为了达到易用的目的，我们需要遵循下面准则，同时也要考虑下面问题：&lt;/p&gt;
&lt;h2 id=&#34;接口一致准则&#34;&gt;接口一致准则&lt;/h2&gt;
&lt;p&gt;除非有好理由，否则应该尽量令你的类的行为与内置类型一致。&lt;/p&gt;
&lt;p&gt;接口的一致性，主要体现在两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;相同操作，自定义类型的行为应该与内置类型一致；&lt;/li&gt;
&lt;li&gt;相同接口名，在同一自定义类型中，其功能、使用方式也应该一样；&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;误操作防御准则&#34;&gt;误操作防御准则&lt;/h2&gt;
&lt;p&gt;考虑客户在使用该接口时，可能出现哪些错误，进行针对性防御设计。（如果防御编程消耗过大，就要仔细权衡了。）&lt;/p&gt;
&lt;p&gt;参数限定，譬如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果参数存在上下限、固定值集合（月份值），最好进行限定（新建月份类型）；&lt;/li&gt;
&lt;li&gt;如果是值传递，最好是改成常引用传递（内置类型、STL迭代器、函数对象除外）；&lt;/li&gt;
&lt;li&gt;如果不想改变参数值，最好声明常参数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;返回值限定，譬如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;const限定（防止试图修改返回值）；&lt;/li&gt;
&lt;li&gt;智能指针限定（防止内存泄漏）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;私有还是公有&#34;&gt;私有还是公有？&lt;/h2&gt;
&lt;p&gt;为什么接口可以是私有呢？因为从广义来说，类的设计者也是类中成员的客户，私有的接口是面向类的设计者的。&lt;/p&gt;
&lt;p&gt;对于成员函数，主要分为三种：私有、继承、公有，其安排主要是根据这三者的区别来定，没有什么特殊的地方。&lt;/p&gt;
&lt;p&gt;对于数据成员，建议将所有数据成员声明为私有（考虑封装）。不管是类对象访问，还是派生类对象访问，都可以通过对应的getter/setter函数来操作。需要注意的是：protected并不比public更具封装性。&lt;/p&gt;
&lt;h2 id=&#34;成员函数还是非成员函数&#34;&gt;成员函数还是非成员函数？&lt;/h2&gt;
&lt;p&gt;如果接口必须要操作类私有成员，需要将接口声明为成员函数或友元函数。&lt;/p&gt;
&lt;p&gt;如果接口可以通过访问成员函数来进行所需操作，建议用非成员、非友元函数来替换成员函数。&lt;/p&gt;
&lt;p&gt;因为这样做可以增加封装性、包括弹性和机能扩充性。这种非成员、非友元函数被称为便利函数，往往一个类会存在一系列不同的便利函数，功能相关的便利函数会被定义在同一个头文件内。&lt;/p&gt;
&lt;p&gt;如果接口的所有参数都需要进行类型转换，那么这个函数必须是非成员函数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Rational {
public:
    const Rational operator* (const Rational &amp;amp;rhs) const;    
};

Rational oneEight(1, 8), oneHalf(1, 2);
Rational result = oneHalf * oneEight; /* ok */
result = oneHalf * 2; /* ok */
result = 2 * oneHalf; /* error */

/* non-member */
const Rational operator*(const Rational &amp;amp;lhs, const Rational &amp;amp;rhs);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;是否需要为inline函数&#34;&gt;是否需要为inline函数？&lt;/h1&gt;
&lt;p&gt;需要明确的是：inline只是对编译器的一个申请，并不是强制命令。将函数定义于class定义式内是隐喻inline的，定义于class定义式外需要显式声明为inline。通常inline函数被放置在头文件内。&lt;/p&gt;
&lt;p&gt;如果编译器无法将你要求的函数inline化，其通常会给你一个警告信息。下列函数往往是非inline的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;函数内部包含循环体；&lt;/li&gt;
&lt;li&gt;构造函数/析构函数/虚函数；&lt;/li&gt;
&lt;li&gt;存在被改变的可能：内联函数被改变，所有用到内联函数的程序都必须重新编译，而非内联函数只需要连接就行了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于是否需要为inline函数的建议是：将大多数inline函数限制在小型、被频繁调用的函数身上，同时不要因为函数模板出现在头文件，就将其声明为inline。&lt;/p&gt;
&lt;h1 id=&#34;接口与实现分离&#34;&gt;接口与实现分离&lt;/h1&gt;
&lt;p&gt;接口与实现分离能够将编译依存性最小化，采用Handles Classes和Interface Classes能够很好地实现——相依于声明，不要相依于定义式。程序库头文件应该以完全且仅有声明式的形式存在，这种做法不论是否涉及template都适用。&lt;/p&gt;
&lt;p&gt;如果我们按照一般的C++教材上的方式现实代码，编译依存关系会导致很严重的重新编译和连接：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/* Person.h*/
#include &amp;lt;string&amp;gt;
#include &amp;#34;Data.h&amp;#34;
#include &amp;#34;Address.h&amp;#34;

class Person {
public:
    Person(const std::string&amp;amp; name, const Data&amp;amp; birthday, const Address&amp;amp; addr);
    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
private:
    std::string _name;
    Data        _birth;
    Address     _addr;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;上面代码，如果Data.h中的内容或其依赖的内容有任何改变，所有包含Person.h头文件的源文件都需要重新编译和连接。&lt;/p&gt;
&lt;p&gt;Handles Classes利用pimpl idiom（pointer to implementation）思想，利用该思想来重写上面代码：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/* Person.h */
#include &amp;lt;string&amp;gt;
#include &amp;lt;memory&amp;gt;

class PersonImpl;
class Data;
class Address;

class Person {
public:
    Person(const std::string&amp;amp; name, const Data&amp;amp; birthday, const Address&amp;amp; addr);
    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
private:
    std::shared_ptr&amp;lt;PersonImpl&amp;gt; pImpl;
};

/* PersonImpl.h */
#include &amp;#34;Data.h&amp;#34;
#include &amp;#34;Address.h&amp;#34;

class PersonImpl {
public:
    Person(const std::string&amp;amp; name, const Data&amp;amp; birthday, const Address&amp;amp; addr);
    std::string name() const;
    std::string birthDate() const;
    std::string address() const;
private:
    std::string _name;
    Data        _birth;
    Address     _addr;
};

/* Person.cxx */
#include &amp;#34;Person.h&amp;#34;
#include &amp;#34;PersonImpl.h&amp;#34;

Person::Person(const std::string&amp;amp; name, const Data&amp;amp; birthday, const Address&amp;amp; addr)
    : pImpl(new PersonImpl(name, birthday, addr)) { }
std::string Person::name() const
{
    retur pImpl-&amp;gt;name();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Interface Classes利用抽象基类和多态的特性，将接口定义在接口类中，而具体实现使用具体类，改写Person代码如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/* PersonI.h */
#include &amp;lt;string&amp;gt;
#include &amp;lt;memory&amp;gt;

class Data;
class Address;

class PersonI {
public:
    virtual ~PersonI();
    static std::shared_ptr&amp;lt;PersonI&amp;gt; create(const std::string &amp;amp;name, const Data &amp;amp;birthday, const Address &amp;amp;addr);
    virtual std::string name() const = 0;
    virtual std::string birthDate() const = 0;
    virtual std::string address() const = 0;
};

/* PersonImpl.h */
#include &amp;#34;PersonI.h&amp;#34;
#include &amp;#34;Data.h&amp;#34;
#include &amp;#34;Address.h&amp;#34;

class PersonImpl : public PersonI {
public:
    Person(const std::string&amp;amp; name, const Data&amp;amp; birthday, const Address&amp;amp; addr);
    virtual std::string name() const override;
    virtual std::string birthDate() const override;
    virtual std::string address() const override;
private:
    std::string _name;
    Data        _birth;
    Address     _addr;
};

/* PersonImpl.cxx */
#include &amp;#34;PersonImpl.h&amp;#34;

std::shared_ptr&amp;lt;PersonI&amp;gt; create(const std::string &amp;amp;name, const Data &amp;amp;birthday, const Address &amp;amp;addr)
{
    return std::shared_ptr&amp;lt;PersonI&amp;gt;(new PersonImpl(name, birthday, addr));
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;两种改写方法都存在一定的代价，一般程序库头文件推荐使用Interface Classes。&lt;/p&gt;
&lt;h1 id=&#34;接口实现需要考虑哪些问题&#34;&gt;接口实现需要考虑哪些问题？&lt;/h1&gt;
&lt;h2 id=&#34;返回值&#34;&gt;返回值&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;绝不要返回指向一个函数局部自动对象的指针或引用。&lt;/li&gt;
&lt;li&gt;绝不要返回指向一个函数内部堆对象的引用，除非你能完全杜绝内存泄漏问题。&lt;/li&gt;
&lt;li&gt;绝不要返回指向一个函数局部静态对象的指针或引用，如果有可能同时需要多个这样的对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int&amp;amp; getStatic(int param) {
    static int test;
    test += param;
    return test;
}
if(getStatic(param1) == getStatic(param2)) { ... } // 总是相等&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;注意：第三种情况，并不是要我们完全不要返回，譬如我们需要某类型的唯一实例，返回函数局部静态变量是可行的：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Person&amp;amp; getPerson()
{
    static Person p;
    return p;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;避免返回对象内部成分（私有成员：数据和函数）的句柄（指针或引用），例外：operator[]。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class Rect {
public:
    Point&amp;amp; getPoint() const { return _point; }
private:
    Point _point;
};
Point t;
{
    const Rect r;
    t = r.getPoint();
    t.setX(100); // 我们更改了常对象内部的值
}
int x = t.getX(); // r已被析构，t现在为空悬指针了，wops...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;类型转换&#34;&gt;类型转换&lt;/h1&gt;
&lt;p&gt;优良的C++代码很少使用转型，但是也无法完全摆脱转型。考虑使用转型时，需要遵守下列准则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;尝试使用无需转型操作的设计替代转型动作，譬如：使用类的多态特性取代dynamic_cast转型；&lt;/li&gt;
&lt;li&gt;将转型操作隐藏在某个函数背后，如果需要转型时，调用该函数，而不是直接将转型操作放进代码内；&lt;/li&gt;
&lt;li&gt;使用C++新式转型操作取代旧式转型操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;转型操作有时很难避免，因此我们需要清楚的掌握四种转型操作（static_cast、const_cast、reinterpret_cast、dynamic_cast）的差异与缺陷：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dynamic_cast：影响程序效率，同时也是程序异常的潜在因素，尽量拒绝使用dynamic_cast转型操作；&lt;/li&gt;
&lt;li&gt;const_cast：去除常量性，如果需要获取常量性，需要使用static_cast；&lt;/li&gt;
&lt;li&gt;reinterpret_cast：不可移植，在指针转换的时候可以使用，但是要慎用；&lt;/li&gt;
&lt;li&gt;static_cast：上面三种转型之外的转型操作都可以由static_cast来负责。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;转型操作切记试图揣摩编译器中对象的布局，因为对象的布局是随着不同的编译器而不同的。&lt;/p&gt;
&lt;h1 id=&#34;资源释放&#34;&gt;资源释放&lt;/h1&gt;
&lt;p&gt;通过良好的接口实现，接口使用者，无须考虑资源释放等问题。资源释放问题可以通过智能指针很好的解决。使用std::shared_ptr和删除器，我们能很好的解决资源释放问题。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;std::shared_ptr&amp;lt;Resource&amp;gt; res(getResource(), releaseResource);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;异常安全&#34;&gt;异常安全&lt;/h1&gt;
&lt;p&gt;当异常被抛出后，异常安全的函数不会泄露任何资源，也不允许数据败坏。异常安全函数能够提供下列三种保证之一：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本承诺：如果异常被抛出，程序内的任何事物都保持在有效状态下（并不保证是原来的状态）。&lt;/li&gt;
&lt;li&gt;强烈保证：如果异常被抛出，如果函数成功，就会完全成功，如果函数失败，就会恢复到调用函数之前的状态。&lt;/li&gt;
&lt;li&gt;不抛保证：绝不抛出异常，程序总能完成它们原先承诺的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们推荐：对于C++函数，需要提供强烈保证，尽量不抛保证。&lt;/p&gt;
&lt;p&gt;为了完成异常安全保证，我们需要知道哪些操作不抛异常，哪些操作会抛出异常，哪些操作有助于异常安全保证：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;作用于内置类型身上的所有操作都是nothrow保证的；&lt;/li&gt;
&lt;li&gt;动态内存如果无法找到足够内存以满足需求会抛出bad_alloc异常；&lt;/li&gt;
&lt;li&gt;swap函数提供不抛保证，利用copy-and-swap特性能够很轻松的完成强烈异常保证（当然也存在例外）；&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;struct PMImpl {
    std::shared_ptr&amp;lt;Image&amp;gt; _image;
    int imageChanges;
};
class Menu {
private:
    Mutex _mutex;
    std::shared_ptr&amp;lt;PMImpl&amp;gt; _pImpl;
};
void Menu::change(std::istream&amp;amp; src) {
    Lock m(&amp;amp;_mutex);
    std::shared_ptr&amp;lt;PMImpl&amp;gt; p(new PMImpl(*_pImpl);
    p-&amp;gt;_image.reset(new Image(src));
    ++p-&amp;gt;imageChanges;
    std::swap(_pImpl, p);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;有时候我们是做不到强烈异常保证的，譬如消耗过大、程序结构复杂、历史代码等，这个时候就需要退而取其次——基本保证。函数提供的异常安全性保证通常最高只等于其所调用的各个函数的异常安全保证中的最弱者。所以，即使我们努力的保证了自己写的代码是强烈异常安全的，我们还是可能调用非此保证的代码。&lt;/p&gt;
&lt;p&gt;标准库版本的swap函数时不抛出异常的，如果我们需要自己实现swap函数，也务必要保证其不抛出异常——因为swap的一个最好的应用是帮助classes提供强烈的异常安全性保证。&lt;/p&gt;
&lt;p&gt;我们还需要注意一定：析构函数一定不能抛出异常，否则会导致资源泄漏&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>使用.NET Core创建Windows服务（三） - 使用.NET Core工作器方式</title>
      <link>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A13%E4%BD%BF%E7%94%A8.net-core%E5%B7%A5%E4%BD%9C%E5%99%A8%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Sat, 26 Sep 2020 14:38:59 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A13%E4%BD%BF%E7%94%A8.net-core%E5%B7%A5%E4%BD%9C%E5%99%A8%E6%96%B9%E5%BC%8F/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../NETCore_%E4%BD%BF%E7%94%A8NETCore%E5%88%9B%E5%BB%BAWindows%E6%9C%8D%E5%8A%A11%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8E%A8%E8%8D%90%E6%96%B9%E5%BC%8F/&#34;&gt;使用微软推荐方式&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;../NETCore_%E4%BD%BF%E7%94%A8NETCore%E5%88%9B%E5%BB%BAWindows%E6%9C%8D%E5%8A%A12%E4%BD%BF%E7%94%A8Topshelf%E6%96%B9%E5%BC%8F/&#34;&gt;使用Topshelf方式&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;安装步骤&#34;&gt;安装步骤&lt;/h1&gt;
&lt;p&gt;这里首先你要确保你已经安装了.NET Core 3.0或以上版本。在我编写这篇文章的时候， .NET Core 3.1刚刚发布，Visual Studio应该会提示你升级到最新版本。但是如果你想要在.NET Core 2.x项目中使用这个方式，应该是行不通的。&lt;/p&gt;
&lt;p&gt;如果你喜欢使用命令行创建项目，你就需要使用工作器(worker)类型创建项目：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dotnet new worker&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;如果你是一个和我一样喜欢使用Visual Studio的开发人员，那么你可以在Visual Studio中使用项目模板完成相同的功能。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/netcore%E6%9C%8D%E5%8A%A131.png&#34; alt=&#34;VS项目模板&#34;&gt;&lt;/p&gt;
&lt;p&gt;这样做将创建出一个包含两个文件的项目。其中Program.cs文件是应用的启动“引导程序”。另外一个文件是worker.cs文件，在这个文件中，你可以编写你的服务逻辑。&lt;/p&gt;
&lt;p&gt;这看起来应该是相当的容易，但是为这个程序添加额外的并行后台服务，你还需要添加一个类，并让它继承BackgroundService类:&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public class MyNewBackgroundWorker : BackgroundService
{    
	protected override Task ExecuteAsync(CancellationToken stoppingToken)    
	{        
		//Do something.     
	}
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;然后在Program.cs中，我们要做的只是把当前的Worker注册到服务集合(Service Collection)中即可。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.ConfigureServices((hostContext, services) =&amp;gt;
{
    services.AddHostedService&amp;lt;Worker&amp;gt;();
    services.AddHostedService&amp;lt;MyNewBackgroundWorker&amp;gt;();
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;实际上作为“后台服务”任务的运行程序，AddHostedService方法已经在框架中存在了很长时间了。在之前我们已经完成的一篇关于ASP.NET Core托管服务的文章， 但是在当时场景中，我们托管是是整个应用，而非一个在你应用程序幕后运行的东西。&lt;/p&gt;
&lt;h1 id=&#34;运行调试我们的应用&#34;&gt;运行/调试我们的应用&lt;/h1&gt;
&lt;p&gt;在默认的工作器(worker)模板中，已经包含了一个后台服务，这个服务可以将当前时间输出到控制台窗口。下面让我们点击F5来运行程序，看看我们能得到什么。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;info: CoreWorkerService.Worker[0]      
	  Worker running at: 12/07/2019 08:20:30 +13:00
info: Microsoft.Hosting.Lifetime[0]      
	  Application started. Press Ctrl+C to shut down.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在我们启动程序之后，程序立刻就运行了！我们可以保持控制台的打开状态来调试应用，或者直接关闭窗口退出。相较于使用&amp;quot;Microsoft&amp;quot;方式来调试一个Windows服务，这简直就是天堂。&lt;/p&gt;
&lt;p&gt;这里我们需要注意的另外一件事情是编写控制台程序的平台。在最后，我们不仅在控制台窗口输出了时间，还通过依赖注入创建了一个托管worker. 我们也可以使用依赖注入容器来注入仓储，配置环境变量，获取配置等。&lt;/p&gt;
&lt;p&gt;但这里我们还没有做的事情是，将这个应用转换为Windows服务。。&lt;/p&gt;
&lt;h1 id=&#34;将我们的应用转换成window服务&#34;&gt;将我们的应用转换成Window服务&lt;/h1&gt;
&lt;p&gt;为了将应用转换成Windows服务，我们需要使用如下命令引入一个包。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Install-Package Microsoft.Extensions.Hosting.WindowsServices&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;下一步，我们需要修改Program.cs文件，添加UseWindowsService()方法的调用。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public static IHostBuilder CreateHostBuilder(string[] args) =&amp;gt; 
	Host.CreateDefaultBuilder(args)    
		.ConfigureServices((hostContext, services) =&amp;gt;    
		{        
			services.AddHostedService&amp;lt;Worker&amp;gt;();   
         })
         .UseWindowsService();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;以上就是所有需要变更的代码。&lt;/p&gt;
&lt;p&gt;运行我们的程序，你会发现和之前的效果完全样。但是这里最大的区别是，我们可以将当前应用以Windows服务的形式安装了。&lt;/p&gt;
&lt;p&gt;为了实现这一目的，我们需要发布当前项目。在当前项目目录中，我们可以运行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dotnet publish -r win-x64 -c Release&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;然后我们就可以借助标准的Windows服务安装器来安装当前服务了。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sc create TestService BinPath=C:\full\path\to\publish\dir\WindowsServiceExample.exe&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;当前，你也可以使用Windows服务安装器的其他命令。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sc start TestService
sc stop TestService
sc delete TestService&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;最后检查一下我们的服务面板。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/netcore%E6%9C%8D%E5%8A%A132.png&#34; alt=&#34;服务面板&#34;&gt;&lt;/p&gt;
&lt;p&gt;服务已经正常工作了。&lt;/p&gt;
&lt;h1 id=&#34;在linux中运行服务&#34;&gt;在Linux中运行服务&lt;/h1&gt;
&lt;p&gt;老实说，我没有太多的Linux经验，但是终归是需要了解一下&amp;hellip;&lt;/p&gt;
&lt;p&gt;在Linux系统中, 如果你希望我们编写的“Windows”服务在Linux系统中作为服务运行，你需要做以下2步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用Microsoft.Extensions.Hosting.Systemd替换之前的Microsoft.Extensions.Hosting.WindowsServices。&lt;/li&gt;
&lt;li&gt;使用UseSystemd()替换UseWindowsService()。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;microsoft-vs-topshelf-vs-net-core-workers&#34;&gt;Microsoft vs Topshelf vs .NET Core Workers&lt;/h1&gt;
&lt;p&gt;到现在为止，我们已经介绍了借助3种不同的方式来创建Windows服务。&lt;/p&gt;
&lt;p&gt;你可能会问“好吧，那我到底应该选择哪一种？”&lt;/p&gt;
&lt;p&gt;这里呢，我们可以首先把&amp;quot;Microsoft&amp;quot;这种老派学院式的方式抛弃。以为它的调试实在是太麻烦了，而且没有什么实际的用处。&lt;/p&gt;
&lt;p&gt;然后剩下的就是Topshelf和.NET Core工作器两种方式了。在我看来，.NET Core工作器，已经很好的融入.NET Core生态系统，如果你正在开发ASP.NET Core应用，那么使用.NET Core工作器就很有意义。最重要的是，当你创建一个后台服务的时候，你可以让它在一个ASP.NET Core网站中的任意位置运行，这非常的方便。但是缺点是安装。你必须使用SC命令来安装服务。这一部分Topshelf可能更胜一筹。&lt;/p&gt;
&lt;p&gt;Topshelf总体上将非常的友好，并且具有最好的安装方式，但是使用额外的库，也增加了学习的成本。&lt;/p&gt;
&lt;p&gt;所以Topshelf和.NET Core工作器，大家可以自行选择，都是不错的方案。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>使用.NET Core创建Windows服务（二） - 使用Topshelf方式</title>
      <link>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A12%E4%BD%BF%E7%94%A8topshelf%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Sat, 26 Sep 2020 14:38:57 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A12%E4%BD%BF%E7%94%A8topshelf%E6%96%B9%E5%BC%8F/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;../NETCore_%E4%BD%BF%E7%94%A8NETCore%E5%88%9B%E5%BB%BAWindows%E6%9C%8D%E5%8A%A11%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8E%A8%E8%8D%90%E6%96%B9%E5%BC%8F/&#34;&gt;使用微软推荐方式&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在前一篇文章中，我给大家介绍了，如何基于微软推荐方式使用.NET Core创建Windows服务。我们发现使用这种方式，我们很容易就可以搭建和运行一个Windows服务，但是问题是使用这种方式，代码调试将非常困难。&lt;/p&gt;
&lt;p&gt;那么现在就是Topshelf出场的时候了。Topshelf是一个.NET Standard库，它消除了在.NET Framework和.NET Core中创建Windows服务的那些麻烦。&lt;/p&gt;
&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;
&lt;p&gt;与微软推荐方式类似，这里Visual Studio并没有提供一个基于Topshelf创建Windows服务的模板，所以我们依然需要通过创建普通控制台程序的方式，来创建一个Windows服务。&lt;/p&gt;
&lt;p&gt;然后，我们需要通过Package Manager Console, 运行以下命令，安装Topshelf类库。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Install-Package Topshelf&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;下面我们就来使用Topshelf重构之前的服务代码。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public class LoggingService : ServiceControl
{
    private const string _logFileLocation = @&amp;#34;C:\temp\servicelog.txt&amp;#34;;
 
    private void Log(string logMessage)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation));
        File.AppendAllText(_logFileLocation, 
        	DateTime.UtcNow.ToString() + &amp;#34; : &amp;#34; + logMessage + Environment.NewLine);
    }
 
    public bool Start(HostControl hostControl)
    {
        Log(&amp;#34;Starting&amp;#34;);
        return true;
    }
 
    public bool Stop(HostControl hostControl)
    {
        Log(&amp;#34;Stopping&amp;#34;);
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;代码看起来是不是很简单？&lt;/p&gt;
&lt;p&gt;这里我们的服务类继承了ServiceControl类（实际上并不需要，但是这可以为我们的工作打下良好的基础）。我们必须实现服务开始和服务结束两个方法，并且像以前一样记录日志。&lt;/p&gt;
&lt;p&gt;在Program.cs文件的Main方法中，我们要写的代码也非常的简单。我们可以直接使用HostFactory.Run方法来启动服务。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static void Main(string[] args)
{
    HostFactory.Run(x =&amp;gt; x.Service&amp;lt;LoggingService&amp;gt;());
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这看起来真是太简单了。但这并不是HostFactory类的唯一功能。这里我们还可以设置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务的名称&lt;/li&gt;
&lt;li&gt;服务是否自动启动&lt;/li&gt;
&lt;li&gt;服务崩溃之后的重启时间&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static void Main(string[] args)
{
    HostFactory.Run(x =&amp;gt;
        {
            x.Service&amp;lt;LoggingService&amp;gt;();
            x.EnableServiceRecovery(r =&amp;gt; r.RestartService(TimeSpan.FromSeconds(10)));
            x.SetServiceName(&amp;#34;TestService&amp;#34;);
            x.StartAutomatically();
         }
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这里其实能说的东西很多，但是我建议你还是自己去看看Topshelf的文档，学习一下其他的配置选项。基本上你能使用Windows命令行完成的所有操作，都可以使用代码来设置： &lt;a href=&#34;https://topshelf.readthedocs.io/en/latest/configuration/config_api.html&#34;&gt;https://topshelf.readthedocs.io/en/latest/configuration/config_api.html&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;部署服务&#34;&gt;部署服务&lt;/h1&gt;
&lt;p&gt;和之前一样，我们需要针对不同的Windows环境发布我们的服务。在Windows命令提示符下，我们可以在项目目录中执行以下命令：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dotnet publish -r win-x64 -c Release&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;现在我们就可以查看一下bin\Release\netcoreappX.X\win-x64\publish目录，我们会发现一个编译好的exe，下面我们就会使用这个文件来安装服务。&lt;/p&gt;
&lt;p&gt;在上一篇文章中，我们是使用SC命令来安装Windows服务的。使用Topshelf我们就不需要这么做了，Topshelf提供了自己的命令行参数来安装服务。基本上使用代码能完成的配置，都可以使用命令行来完成。&lt;/p&gt;
&lt;p&gt;你可以查看相关的文档：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://docs.topshelf-project.com/en/latest/overview/commandline.html&#34;&gt;http://docs.topshelf-project.com/en/latest/overview/commandline.html&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;WindowsServiceExample.exe install&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这里WindowsServiceExample.exe是我发布之后的exe文件。运行以上命令之后，服务应该就正常安装了！这里有一个小问题，我经常发现，即使配置了服务自动启动，但是服务安装之后，并不会触发启动操作。所有在服务安装之后，我们还需要通过以下命令来启动服务。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;WindowsServiceExample.exe start&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在生产环境部署的时候，我的经验是在安装服务之后，等待10秒钟，再启动服务。&lt;/p&gt;
&lt;h1 id=&#34;调试服务&#34;&gt;调试服务&lt;/h1&gt;
&lt;p&gt;当我们是使用微软推荐方式的时候，我们会遇到了调试困难的问题。大多数情况下，无论是否在服务内部运行，我们都不得不使用命令行标志、#IF DEBUG指令或者配置值来实现调试。然后使用Hack的方式在控制台程序中模拟服务。&lt;/p&gt;
&lt;p&gt;因此，这就是为什么我们要使用Topshelf。&lt;/p&gt;
&lt;p&gt;如果我们的服务代码已经在Visual Studio中打开了，我们就可以直接启动调试。Topshelf会模拟在控制台中启动服务。我们应该能在控制台中看到以下的消息。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;The TestService service is now running, press Control+C to exit.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这确实符合了我们的需求。它启动了我们的服务，并像真正的Windows服务一样在后台运行。我们可以像往常一样设置断点，基本上它遵循的流程和正常安装的服务一样。&lt;/p&gt;
&lt;p&gt;我们可以通过ctrl+c, 来关闭我们的应用，但是在运行服务执行Stop方法之前，它是不能被关闭的，这使我们可以调试服务的关闭流程。与调试指令和配置标志相比，这要容易的多。&lt;/p&gt;
&lt;p&gt;这里需要注意一个问题。如果你收到的以下内容的消息：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;The TestService service is running and must be stopped before running via the console&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这意味着你尝试调试的服务实际上已经作为Windows服务被安装在系统中了，你需要停止（不需要卸载）这个正在运行的服务，才可以正常调试。&lt;/p&gt;
&lt;h1 id=&#34;后续&#34;&gt;后续&lt;/h1&gt;
&lt;p&gt;.NET Core中实际上已经提供了一种完全不同的方式运行Windows服务。它的实质是利用了ASP.NET Core中引入的“托管服务”模型，并允许它们作为Windows服务来运行，这真的是非常的棒。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>使用.NET Core创建Windows服务（一） - 使用官方推荐方式</title>
      <link>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A11%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8E%A8%E8%8D%90%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Sat, 26 Sep 2020 14:38:55 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/netcore_%E4%BD%BF%E7%94%A8netcore%E5%88%9B%E5%BB%BAwindows%E6%9C%8D%E5%8A%A11%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8E%A8%E8%8D%90%E6%96%B9%E5%BC%8F/</guid>
      
        <description>&lt;p&gt;创建Windows服务来运行批处理任务或者运行后台任务，是一种非常常见的模式，但是由于云服务（Amazon Lambda, Azure WebJobs以及Azure Functions）的激增，你可能不会经常使用Windows服务了。个人而言，我非常喜欢使用Azure WebJobs, 因为我可以直接编写一个控制台程序，而不需要考虑如何云中运行它，一个批处理文件可以将其装换成一个自动化任务，并且可以保证7*24小时的运行。&lt;/p&gt;
&lt;p&gt;但是也许你还没有使用云服务，或者你有一堆要作为Windows服务运行的旧版应用程序需要转换为.NET Core, 但是不能完全将他们转换为“无服务器”(serverless)应用。 那么这边文章就是适合你的。&lt;/p&gt;
&lt;p&gt;在许多方面，.NET Core中的Windows服务和.NET Framework中的Windows服务完全相同。但是，在编写服务的时候，你可能会遇到一些小问题。此外，本文中，我们仅介绍“Microsoft”方式的Windows服务创建，在后续，我会继续介绍如何使用第三方库TopShelf来简化这该过程。&lt;/p&gt;
&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;
&lt;p&gt;由于Visual Studio没有提供创建Windows服务的模板，所以我们需要通过创建控制台程序的方式来创建一个Windows服务。&lt;/p&gt;
&lt;p&gt;创建完成之后，我们需要安装一个Nuget程序包，这个程序包会将一些Windows特定的API添加到.NET Core中，这些API实际上已经在完整框架中提供了，但是其中许多是Windows特有的，例如Windows服务。因此, 它们并没有包含在.NET Core的基础库中，但是可以通过将Nuget程序包的方式引入到.NET Core中。&lt;/p&gt;
&lt;p&gt;下面我们就可以在Package Manager Console中输入以下命令。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Install-Package Microsoft.Windows.Compatibility&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;代码&#34;&gt;代码&lt;/h1&gt;
&lt;p&gt;以上引入的Nuget程序包中，最让我们感兴趣的是ServiceBase类。这是一个用于编写Windows服务的基类，它提供了一系列的事件钩子，包含服务启动、结束、暂停等。&lt;/p&gt;
&lt;p&gt;下面呢，我们将在代码中创建一个类，这个类负责将一些简单的日志输出到一个临时文件中。我们将使用这个例子来了解其中的原理。我们的代码如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class LoggingService : ServiceBase
{
    private const string _logFileLocation = @&amp;#34;C:\temp\servicelog.txt&amp;#34;;
 
    private void Log(string logMessage)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation));
        File.AppendAllText(_logFileLocation, DateTime.UtcNow.ToString() + &amp;#34; : &amp;#34; + logMessage + Environment.NewLine);
    }
 
    protected override void OnStart(string[] args)
    {
        Log(&amp;#34;Starting&amp;#34;);
        base.OnStart(args);
    }
 
    protected override void OnStop()
    {
        Log(&amp;#34;Stopping&amp;#34;);
        base.OnStop();
    }
 
    protected override void OnPause()
    {
        Log(&amp;#34;Pausing&amp;#34;);
        base.OnPause();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;所以这里你会注意到，我们的类是继承了ServiceBase类，并且我们重写了几个事件方法，输出了一些日志。在服务启动时，会触发OnStart事件，在服务终止的时候，会触发OnStop事件。这里我们不应该将过于繁重的任务放置在OnStart事件中来处理。&lt;/p&gt;
&lt;p&gt;如果我们想从Main方式中启动这个服务，代码非常的简单。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static void Main(string[] args)
{
    ServiceBase.Run(new LoggingService());
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;以上就是全部代码。&lt;/p&gt;
&lt;h1 id=&#34;服务部署&#34;&gt;服务部署&lt;/h1&gt;
&lt;p&gt;在发布服务的时候，我们不可能仅依靠Visual Studio来构建我们所需要的服务，我们还需要专门针对Windows运行时进行构建。为此，我们需要在项目根目录的命令提示符下运行以下命令。注意，这里我们传入了一个-r标记来告诉它要构建那个平台。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dotnet publish -r win-x64 -c Release&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;命令运行完毕之后，我们可以检查以下/bin/release/netcoreappX.X/publish目录，我们可以找到所有的发布代码，但是最重要的是，这里我们可以得到一个可执行的exe文件。如果我们不指定运行时，我们只会获得一个.NET Core的dll程序集，使用这个程序集，我们是没有办法创建Windows服务的。&lt;/p&gt;
&lt;p&gt;现在我们可以将这个发布目录移动带其他的任何地方，但是现在我们就暂时使用当前的发布目录。&lt;/p&gt;
&lt;p&gt;下一步，我们需要使用管理员角色打开一个命令提示符，然后输入一下命令。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sc create TestService BinPath=C:\full\path\to\publish\dir\WindowsServiceExample.exe&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;SC命令是一个标准的Windows命令（与.NET Core无关），它可以用来安装Windows服务。这里我们将我们的测试服务命名为TestService，更重要的是，我们通过BinPath参数指定了可执行exe文件。&lt;/p&gt;
&lt;p&gt;运行之后，我们应该会得到以下结果。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[SC] CreateService SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;然后我们要做的就是启动服务。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sc start TestService&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;现在我们可以查看一下我们的日志文件，查看服务的运行情况。&lt;/p&gt;
&lt;p&gt;如果想要停止并删除服务，我们可以使用一下命令。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sc stop TestService
sc delete TestService&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;服务调试&#34;&gt;服务调试&lt;/h1&gt;
&lt;p&gt;在这里，我真的认为，使用&amp;quot;Microsoft&amp;quot;的方式注定会失败。因为调试服务实在是太繁琐了。&lt;/p&gt;
&lt;p&gt;首先，我们将ServiceBase中重写的方法设置为受保护，这意味着我们无法在类之外访问它们，这使得调试它们变得更加困难。这里我发现最好的方法是为每个事件提供一个public方法, 并在受保护方法中调用这些public方法来完成功能，这虽然有点混乱，&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public void OnStartPublic(string[] args)
{
    Log(&amp;#34;Starting&amp;#34;);
}
 
protected override void OnStart(string[] args)
{
    OnStartPublic(args);
    base.OnStart(args);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;但是至少我们可以做如下了事情了。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static void Main(string[] args)
{
    var loggingService = new LoggingService();
    if (true)   //Some check to see if we are in debug mode (Either #IF Debug etc or an app setting)
    {
        loggingService.OnStartPublic(new string[0]);
        while(true)
        {
            //Just spin wait here. 
            Thread.Sleep(1000);
        }
        //Call stop here etc. 
    }
    else
    {
        ServiceBase.Run(new LoggingService());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;你的另一个选择是，在调试模式下进行项目发布，安装服务，然后附加调试器。实际上，这是Microsoft建议你使用的方式，但是我认为这简直一团糟。&lt;/p&gt;
&lt;h1 id=&#34;后续&#34;&gt;后续&lt;/h1&gt;
&lt;p&gt;实际上，我们可以在这里做一些其他非常有用的事情， 比如我们可以通过创建一个install.bat批处理文件来为我们运行SC Create命令。但我认为，上面我们看到的调试问题，已经让我不再想使用这种方式了。 幸运的是，有一个名为Topshelf的库可以帮助我们减轻很多麻烦，在本系列的下一部分中，我们将研究如何它。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>PostgresSql开放局域网访问</title>
      <link>http://coder.amazingdemo.top/post/postgressql%E5%BC%80%E6%94%BE%E5%B1%80%E5%9F%9F%E7%BD%91%E8%AE%BF%E9%97%AE/</link>
      <pubDate>Wed, 05 Aug 2020 19:12:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/postgressql%E5%BC%80%E6%94%BE%E5%B1%80%E5%9F%9F%E7%BD%91%E8%AE%BF%E9%97%AE/</guid>
      
        <description>&lt;p&gt;安装完postgres后，默认只能本机访问数据库，下面通过配置实现局域网内访问postgres数据库。&lt;/p&gt;
&lt;p&gt;1、修改配置文件&lt;/p&gt;
&lt;p&gt;在安装目录的/data文件夹下，打开pg_hba.conf文件，定位到如下文本。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# IPv4 local connections:
host all all 127.0.0.1/32 md5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;添加文本（host all all 0.0.0.0/0 md5），0.0.0.0/0中“/”前的0表示任意IP，“/”后的0表示任意子网掩码，如此设置后局域网内即可访问数据库。若需要限制，可以根据实际情况设置。&lt;/p&gt;
&lt;p&gt;修改后的文本如下。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# IPv4 local connections:
host all all 127.0.0.1/32 md5
host all all 0.0.0.0/0 md5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;2、修改防火墙&lt;/p&gt;
&lt;p&gt;在修改配置文件后，还需要修改防火墙设置。有2中方式：&lt;/p&gt;
&lt;p&gt;a)关闭防火墙（不建议）&lt;/p&gt;
&lt;p&gt;b)设置防火墙规则&lt;/p&gt;
&lt;p&gt;在控制面板中搜索防火墙，打开windows defender防火墙，找到“高级设置”打开。&lt;/p&gt;
&lt;p&gt;在“入站规则”中新建一条规则，规则类型选择“端口”，特定本地端口端口填写安装时设置的端口号，如默认的“5432”，其他默认即可。先新建一条TCP的规则，再新建一条UDP的规则。&lt;/p&gt;
&lt;p&gt;设置好防火墙后，重新启动PostgreSQL服务，局域网内即可访问postgresql数据库了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>DLL Hell 浅谈</title>
      <link>http://coder.amazingdemo.top/post/dllhell%E6%B5%85%E8%B0%88/</link>
      <pubDate>Sun, 22 Mar 2020 13:55:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/dllhell%E6%B5%85%E8%B0%88/</guid>
      
        <description>&lt;p&gt;DLL动态链接库是程序复用的重要方式，DLL可以导出函数，使函数被多个程序复用，DLL中的函数实现可以被修改而无需重新编译和连接使用该DLL的应用程序。作为一名面向对象的程序员，希望DLL可以导出类，以便在类的层次上实现复用。所幸的是，DLL确实也可以导出类。&lt;/p&gt;
&lt;p&gt;然而事实却没这么简单，导出类的DLL在维护和修改时有很多地方必需很小心，增加成员变量、修改导出类的基类等操作都可能导致意想不到的后果，也许用户更新了最新版本的DLL库后，应用程序就再也不能工作了。这就是著名的DLLHell（DLL地狱）问题。&lt;/p&gt;
&lt;p&gt;DLL地狱问题是怎么产生的呢？看下面的例子，假设DLL有一个导出类Class D1：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class ClassD
{
public:
  int GetInt();

private:
  int m_i;
};

int ClassD::GetInt()
{
  return m_i;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;应用程序使用现在的代码来使用这个类：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ClassD d;
printf(“%d”,d.GetInt());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;程序进行正正常，没有什么问题。后来DLL需要升级，对ClassD进行了修改，增加了一个成员变量，如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class ClassD //修改后
{
public:
  int GetInt();

private:
  int m_i2;
  int m_i;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;把新的DLL编译连接完成后，复制到应用程序目录，这个倒霉的应用程序调用GetInt方法恐怕再也无法得正确的值了。事实上它还算幸运的，如果GetInt的实现改成如下这样，那么它马上就要出错退出了。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int ClassD::GetInt() // 修改后
{
  return m_i++;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这样的事情，称它是个地狱（Hell）一点也不夸张。为什么会出错呢？我们要先从类实例的创建开始，看看使用一个类的工作过程。&lt;/p&gt;
&lt;p&gt;首先，程序语句“ClassD d；”为这个类申请一块内存。这块内存保存该类的所有成员变量，以及虚函数表。内存的大小由类的声明决定，在应用程序编译时就已经确定。&lt;/p&gt;
&lt;p&gt;然后，当调用“d.GetInt()”时，把申请的这一块内存做为this指针传给GetInt函数，GetInt函数从this指向的位置开始，加上m_i应有的偏移量，计算m_i所在的内存位置，并从该位置取数据返回。m_i相对this的偏移量是由m_i在类中定义的位置决定的，定义在前的成员变量在内存中也更靠前。这个偏移量在DLL编译时确定。&lt;/p&gt;
&lt;p&gt;当ClassD的定义改为修改后的状态时，有些东西变了。&lt;/p&gt;
&lt;p&gt;第一个变的是内存的大小。因为修改后的ClassD多了一个成员变量，所以内存也变大了。然而这一点应用程序并不知道。&lt;/p&gt;
&lt;p&gt;第二个变的是m_i的偏移地址。因为在m_i之前定义了一个m_i2，m_i的实现偏移地址实际已经靠后了。所以d.GetInt()访问的将是原来m_i后面的那个位置，而这个位置已经超出原来那块内存的后部范围了。&lt;/p&gt;
&lt;p&gt;很显然，在更换了DLL后，应用程序还按原来的大小申请了一块内存，而它调用的方法却访问了比这块内存更大的区域，出错再在所难免。&lt;/p&gt;
&lt;p&gt;同样的情形还会发生在以下这些种情况中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;应用程序直接访问类的公有变量，而该公有变量在新DLL中定义的位置发生了变化；
2 .应用程序调用类的一个虚函数，而新的类中，该虚函数的前面又增加了一个虚函数；&lt;/li&gt;
&lt;li&gt;新类的后面增加了成员变量，并且新类的成员函数将访问、修改这些变量；&lt;/li&gt;
&lt;li&gt;修改了新类的基类，基类的大小发生了变化；&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;等等，总言而之，一不小心，你的程序就会掉进地狱。通过对这些引起出错的情况进行分析，会发现其实只有三点变化会引起出错，因为这三点是使用这个DLL的应用程序在编译时就需要确定的内容，它们分别是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;类的大小；&lt;/li&gt;
&lt;li&gt;类成员的偏移地址；&lt;/li&gt;
&lt;li&gt;虚函数的顺序。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;要想做一个可升级的DLL，必需避免以上三个问题。所以以下三点用来使DLL远离地狱。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不直接生成类的实例。对于类的大小，当我们定义一个类的实例，或使用new语句生成一个实例时，内存的大小是在编译时决定的。要使应用程序不依赖于类的大小，只有一个办法：应用程序不生成类的实例，使用DLL中的函数来生成。把导出类的构造函数定义为私有的(privated)，在导出类中提供静态(static)成员函数(如NewInstance())用来生成类的实例。因为NewInstance()函数在新的DLL中会被重新编译，所以总能返回大小正确的实例内存。&lt;/li&gt;
&lt;li&gt;不直接访问成员变量。应用程序直接访问类的成员变量时会用到该变量的偏移地址。所以避免偏移地址依赖的办法就是不要直接访问成员变量。把所有的成员变量的访问控制都定义为保护型(protected)以上的级别，并为需要访问的成员变量定义Get或Set方法。Get或Set方法在编译新DLL时会被重新编译，所以总能访问到正确的变量位置。&lt;/li&gt;
&lt;li&gt;忘了虚函数吧，就算有也不要让应用程序直接访问它。因为类的构造函数已经是私有(privated)的了，所以应用程序也不会去继承这个类，也不会实现自己的多态。如果导出类的父类中有虚函数，或设计需要（如类工场之类的框架），一定要把这些函数声明为保护的(protected)以上的级别，并为应用程序重新设计调用该虑函数的成员函数。这一点也类似于对成员变量的处理。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果导出的类能遵循以上三点，那么以后对DLL的升级将可以认为是安全的。&lt;/p&gt;
&lt;p&gt;如果对一个已经存在的导出类的DLL进行维护，同样也要注意：不要改动所有的成员变量，包括导出类的父类，无论定义的顺序还是数量；不要动所有的虚函数，无论顺序还是数量。&lt;/p&gt;
&lt;p&gt;总结起来，其实是一句话：导出类的DLL不要导出除了函数以外的任何内容。听起来是不是有点可笑呢！&lt;/p&gt;
&lt;p&gt;事实上，建议你在发布导出类的DLL的时候，重新定义一个类的声明，这个声明可以不管原来的类里的成员变量之类的，只把接口函数列在类的声明里，如下面的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class ClassInterface
{
privated:
  ClassInterface();
             
public:
  static ClassInterface * NewInstance();
  int GetXXX();
  void SetXXX();
  void Function();
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;使用该DLL的应用程序用上面的定义作为ClassInterface的头文件，便不会有任何可能导致的安全问题。&lt;/p&gt;
&lt;p&gt;DLL地狱问是归根结底是因为DLL当初是作为函数级共享库设计的，并不能真正提供一个类所必需的信息。类层上的程序复用只有Java和C#生成的类文件才能做到。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>在Word中排出漂亮的代码</title>
      <link>http://coder.amazingdemo.top/post/%E5%9C%A8word%E4%B8%AD%E6%8E%92%E5%87%BA%E6%BC%82%E4%BA%AE%E7%9A%84%E4%BB%A3%E7%A0%81/</link>
      <pubDate>Sun, 15 Mar 2020 17:24:54 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%9C%A8word%E4%B8%AD%E6%8E%92%E5%87%BA%E6%BC%82%E4%BA%AE%E7%9A%84%E4%BB%A3%E7%A0%81/</guid>
      
        <description>&lt;p&gt;在Word中展示漂亮的代码，其实本质就是制作一份html文件，复制到Word时会保留其源格式。很多代码编辑器或者IDE都有提供制作html文件的功能，比如CodeBlocks（File -&amp;gt; Export -&amp;gt; As HTML&amp;hellip;）。后来我发现Notepad++才是最方便的工具。&lt;/p&gt;
&lt;h1 id=&#34;notepad操作&#34;&gt;Notepad++操作&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;用Notepad++直接编辑代码文件，注意文件后缀，比如.cpp是C++程序，.m是Matlab，写对后缀表示的文件类型，才有对应的语法高亮效果。&lt;/li&gt;
&lt;li&gt;选中需要的代码块（或者不选，默认对全文操作），使用“插件 -&amp;gt; NppExport”，选择“Copy HTML to clipboard”。&lt;/li&gt;
&lt;li&gt;然后直接粘贴到Word就行了。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;“Copy HTML to clipboard”是把当前文本以html格式复制到剪切板，所以再把剪切板的内容复制到Word，就不同于以往的纯文本复制，而是相当于在网页上把一段带有格式的文本复制到Word。这也正是我所说的Notepad++是最方便的工具的原因——Notepad++提供工具，省略了很多中间操作。有兴趣的读者可以试试里面的&amp;quot;Export to HTML&amp;quot;，就更能明白我要表达的含义了。&lt;/p&gt;
&lt;h1 id=&#34;更上一层楼-行号与灰色背景&#34;&gt;更上一层楼: 行号与灰色背景&lt;/h1&gt;
&lt;p&gt;很多读者或许并不能满足这种效果。我们看计算机书籍经常会看到代码都有灰色背景，有时候还需要标明行号。&lt;/p&gt;
&lt;p&gt;为了以后从Word把代码复制出来方便，行号和代码在内容上应该分开。故我的处理方法是插入一个一行两列的表格。第一列写行号，第二列放代码。自己进行一些行号右对齐等的微调后即可。&lt;/p&gt;
&lt;p&gt;那灰色背景呢？这个东西专业排版术语叫“底纹”，读者自己百度学习下就会了，很简单。不过注意底纹也有分很多类：文字底纹、段落底纹、单元格底纹、表格底纹。所以往往有人修改底纹，发现没用改不掉，然后就满口骂Word，Word就冤枉的又躺枪了。其实原因很可能是你改了段落底纹，可是那个东西本来是文字底纹。。。&lt;/p&gt;
&lt;p&gt;Tips:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将行号的数字颜色改成背景色就能“隐藏行号”了，需要的时候再调回黑色。&lt;/li&gt;
&lt;li&gt;注意表格线框是可以隐藏的。&lt;/li&gt;
&lt;li&gt;有时候代码排版的比较紧凑，行间距较小会导致下划线&amp;quot;_&amp;ldquo;没掉了，这时可以试试用“Consolas”字体。&lt;/li&gt;
&lt;li&gt;如果以后需要从Word复制代码，放到表格第二列的左上角，鼠标变成一个黑色箭头时，就能单击全选代码文本进行复制了。行号对源代码不会有任何的干扰。&lt;/li&gt;
&lt;li&gt;注意代码不要过长，长度超过一行时，可能会带来一些不便。&lt;/li&gt;
&lt;li&gt;如果发现空格并没有与字母等宽（如下图所示），则是半全角字符的bug作祟。在“文件 -&amp;gt; 选项 -&amp;gt; 高级”的“布局选项”去掉&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;更加智能与自动化&#34;&gt;更加智能与自动化&lt;/h1&gt;
&lt;p&gt;从此终于能排出漂亮的代码了。不过&amp;hellip;&amp;hellip;这个操作也太麻烦了吧！我写一个200行的代码还要自己输行号？！&lt;/p&gt;
&lt;p&gt;哈哈，不用担心，你忘了我们的职业是“程序猿”了吗？office套件提供了VBA，让用户可以写程序、宏来辅助完成办公操作。VBA使用最广泛的当属Excel了。这里，我也写两个宏来自动化Word的一些操作。不了解VBA的读者可以自己百度自学下。&lt;/p&gt;
&lt;p&gt;Alt + F11可以进入VBA，然后在“Normal -&amp;gt; 模块”，右键选择“插入模块”就行了，然后把我下面的代码复制到“模块1”并保存。（Normal是Word打开时会自动载入的一个模板文件，把宏放在这里，可以保证在任意一个Word文档中都能调用。）&lt;/p&gt;
&lt;p&gt;这里我写了两个宏，一个是输入连续数字的宏，读者调用一下玩玩就知道什么功能了。另一个是对代码表格进行一些处理的宏（可以解决底纹等问题），选中那个一行两列的表格，读者跑跑我的宏就知道效果了。（这两个宏的配置会比较个性化，读者可以根据自己的喜好来开发。）&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Sub 设置代码表格()
&amp;#39; author: code4101
&amp;#39; 设置代码表格 宏
&amp;#39;
&amp;#39;
    &amp;#39; 背景色为morning的配色方案，RGB为(229,229,229)
    With Selection.Tables(1)
        With .Shading
            .Texture = wdTextureNone
            .ForegroundPatternColor = wdColorAutomatic
            .BackgroundPatternColor = 15066597
        End With
        .Borders(wdBorderLeft).LineStyle = wdLineStyleNone
        .Borders(wdBorderRight).LineStyle = wdLineStyleNone
        .Borders(wdBorderTop).LineStyle = wdLineStyleNone
        .Borders(wdBorderBottom).LineStyle = wdLineStyleNone
        .Borders(wdBorderVertical).LineStyle = wdLineStyleNone
        .Borders(wdBorderDiagonalDown).LineStyle = wdLineStyleNone
        .Borders(wdBorderDiagonalUp).LineStyle = wdLineStyleNone
        .Borders.Shadow = False
        .AutoFitBehavior (wdAutoFitContent)  &amp;#39;自动调整大小
    End With
    With Options
        .DefaultBorderLineStyle = wdLineStyleSingle
        .DefaultBorderLineWidth = wdLineWidth050pt
        .DefaultBorderColor = wdColorAutomatic
    End With
    
    &amp;#39; 段落无首行缩进，行间距为固定值12磅
    With Selection.ParagraphFormat
        .LeftIndent = CentimetersToPoints(0)
        .RightIndent = CentimetersToPoints(0)
        .SpaceBefore = 0
        .SpaceBeforeAuto = False
        .SpaceAfter = 0
        .SpaceAfterAuto = False
        .LineSpacingRule = wdLineSpaceExactly
        .LineSpacing = 12
        .KeepWithNext = False
        .KeepTogether = False
        .PageBreakBefore = False
        .NoLineNumber = False
        .Hyphenation = True
        .FirstLineIndent = CentimetersToPoints(0)
        .OutlineLevel = wdOutlineLevelBodyText
        .CharacterUnitLeftIndent = 0
        .CharacterUnitRightIndent = 0
        .CharacterUnitFirstLineIndent = 0
        .LineUnitBefore = 0
        .LineUnitAfter = 0
        .MirrorIndents = False
        .TextboxTightWrap = wdTightNone
        .AutoAdjustRightIndent = True
        .DisableLineHeightGrid = False
        .FarEastLineBreakControl = True
        .WordWrap = True
        .HangingPunctuation = True
        .HalfWidthPunctuationOnTopOfLine = False
        .AddSpaceBetweenFarEastAndAlpha = True
        .AddSpaceBetweenFarEastAndDigit = True
        .BaseLineAlignment = wdBaselineAlignAuto
    End With
    &amp;#39; 清除原有的段落底纹
    Selection.ParagraphFormat.Shading.BackgroundPatternColor = wdColorAutomatic
End Sub
 
Sub 输入连续数字()
&amp;#39; author: code4101
    行数 = InputBox(&amp;#34;请输入代码终止行数&amp;#34;, &amp;#34;输入行数&amp;#34;, &amp;#34;50&amp;#34;)
    For i = 1 To 行数 - 1
        Selection.TypeText Text:=i
        Selection.TypeParagraph
    Next
    Selection.TypeText Text:=行数
End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>Word中标题编号变为竖线处理</title>
      <link>http://coder.amazingdemo.top/post/word%E4%B8%AD%E6%A0%87%E9%A2%98%E7%BC%96%E5%8F%B7%E5%8F%98%E4%B8%BA%E7%AB%96%E7%BA%BF%E5%A4%84%E7%90%86/</link>
      <pubDate>Sat, 14 Mar 2020 19:22:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/word%E4%B8%AD%E6%A0%87%E9%A2%98%E7%BC%96%E5%8F%B7%E5%8F%98%E4%B8%BA%E7%AB%96%E7%BA%BF%E5%A4%84%E7%90%86/</guid>
      
        <description>&lt;p&gt;排版标题和目录后发现标题变为了竖线，很无奈，看怎么解决（只要标题编号变成非数字编号，利用该方法都能解决）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将光标放在竖线（方块）后，利用方向键（键盘上四个小箭头）左移键，将竖线（方块）变成灰色。&lt;/li&gt;
&lt;li&gt;“ctrl+shift+S”，弹出该应用样式的对话框，点击重新应用数字编号出现。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种方法解决得不彻底，过一段时间可能问题还会复现。&lt;/p&gt;
&lt;p&gt;发生问题的原因：是因为从邪恶的乱文件中粘贴把文档里的样式弄乱了。&lt;/p&gt;
&lt;p&gt;解决办法：管理样式-&amp;gt;导入/导出—&amp;gt;打开一份使用同样模版的正常文档，用该文档中的对应样式替换被损坏的样式。&lt;/p&gt;
&lt;p&gt;有人给出了微软官网的解决方案：通过VBA宏重置文档模版中编号列表的字体设置。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 临时创建一个VBA宏，输入以下代码并进行：
// 如果没有“开发工具”选择，请在“选项”→“自定义功能区”进行启用

Sub 列表的编号不显示()

For Each templ In ActiveDocument.ListTemplates

For Each lev In templ.ListLevels

lev.Font.Reset

Next lev

Next templ

End Sub&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;那么编号消失是字体宽度被改了？如何避免？不要将任何来自格式混乱的文档中的文字直接粘贴到自己的文档里，即使选择“仅粘贴文本”也有可能弄坏本地样式。有需要时，通过将正文粘贴至txt再复制的方式中转。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Word标题自动编号设置</title>
      <link>http://coder.amazingdemo.top/post/word%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%BC%96%E5%8F%B7%E8%AE%BE%E7%BD%AE/</link>
      <pubDate>Wed, 04 Mar 2020 13:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/word%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%BC%96%E5%8F%B7%E8%AE%BE%E7%BD%AE/</guid>
      
        <description>&lt;p&gt;进入word后，点击&amp;quot;多级列表&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/Word%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%BC%96%E5%8F%B7%E8%AE%BE%E7%BD%AE1.jpg&#34; alt=&#34;多级列表&#34;&gt;&lt;/p&gt;
&lt;p&gt;选择&amp;quot;定义新的多级列表&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/Word%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%BC%96%E5%8F%B7%E8%AE%BE%E7%BD%AE2.png&#34; alt=&#34;定义新的多级列表&#34;&gt;&lt;/p&gt;
&lt;p&gt;多级列表设置&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/Word%E6%A0%87%E9%A2%98%E8%87%AA%E5%8A%A8%E7%BC%96%E5%8F%B7%E8%AE%BE%E7%BD%AE3.jpg&#34; alt=&#34;多级列表设置&#34;&gt;&lt;/p&gt;
&lt;p&gt;如果框不是这样的话，应该要点击左下角的&amp;quot;更多&amp;quot;。&lt;/p&gt;
&lt;p&gt;步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选择最左侧的“单击要修改的级别”，选择1&lt;/li&gt;
&lt;li&gt;“将更改应用于“选择 整个列表&lt;/li&gt;
&lt;li&gt;“将级别链接到样式”选择标题1（1步的级别和标题要对应）&lt;/li&gt;
&lt;li&gt;“输入编号的格式“的信息来自“包含的级别编号来自”和“此级别的编号样式“，“包含的级别编号”管理子标题的编号是否跟上级标题是否对应，从1级向下逐级设置（设置过程中会看到“输入编号的格式”的变化），“此级别的编号样式”将管理末位编号，&lt;/li&gt;
&lt;li&gt;“起始编号”采用默认即可。&lt;/li&gt;
&lt;li&gt;设置完成之后，不要关闭窗口，继续设置标题2，步骤为从1到5。所有标题标题设置完成之后，点击确定，完成。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;重点：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“输入编号的格式”内容为灰色，是自动内容，否则为静态内容。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“包含的级别编号来自”要从从1级向下逐级设置，否则出现效果错觉。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一次性设置完多级列表，不要标题1 设置一次，关闭这个窗口，然后标题2 又设置一次&lt;/strong&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C# 跨线程更新UI</title>
      <link>http://coder.amazingdemo.top/post/dotnet_%E8%B7%A8%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0ui/</link>
      <pubDate>Wed, 25 Dec 2019 13:38:53 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/dotnet_%E8%B7%A8%E7%BA%BF%E7%A8%8B%E6%9B%B4%E6%96%B0ui/</guid>
      
        <description>&lt;h1 id=&#34;消息机制&#34;&gt;消息机制&lt;/h1&gt;
&lt;h2 id=&#34;windows程序消息机制&#34;&gt;windows程序消息机制&lt;/h2&gt;
&lt;p&gt;Windows GUI程序是基于消息机制的，有个主线程维护着一个消息泵。这个消息泵让windows程序生生不息。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/WindowsGUI%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%B6%88%E6%81%AF%E5%BE%AA%E7%8E%AF.jpg&#34; alt=&#34;Windows GUI程序的消息循环&#34;&gt;&lt;/p&gt;
&lt;p&gt;Windows程序有个消息队列，窗体上的所有消息是这个队列里面消息的最主要来源。这里的while循环使用了GetMessage（）这个方法，这是个阻塞方法，也就是队列为空时方法就会被阻塞，从而这个while循环停止运动，这避免了一个程序把cpu无缘无故地耗尽，让其它程序难以得到响应。当然在某些需要cpu最大限度运动的程序里面就可以使用另外的方法，例如某些3d游戏或者及时战略游戏中，一般会使用PeekMessage（）这个方法，它不会被windows阻塞，从而保证整个游戏的流畅和比较高的帧速。&lt;/p&gt;
&lt;p&gt;这个主线程维护着整个窗体以及上面的子控件。当它得到一个消息，就会调用DispatchMessage方法派遣消息，这会引起对窗体上的窗口过程的调用。窗口过程里面当然是程序员提供的窗体数据更新代码和其它代码。&lt;/p&gt;
&lt;h2 id=&#34;dotnet里面的消息循环&#34;&gt;dotnet里面的消息循环&lt;/h2&gt;
&lt;p&gt;Dotnet窗体程序封装了上述的while循环，这个循环就是通过Application.Run方法启动的。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public static void Main(string[] args)
{
   Form f = new Form();
   Application.Run(f);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;线程外操作gui控件的问题&#34;&gt;线程外操作GUI控件的问题&lt;/h2&gt;
&lt;p&gt;如果从另外一个线程操作windows窗体上的控件，就会和主线程产生竞争，造成不可预料的结果，甚至死锁。因此windows GUI编程有一个规则，就是只能通过创建控件的线程来操作控件的数据，否则就可能产生不可预料的结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;因此，dotnet里面，为了方便地解决这些问题，Control类实现了ISynchronizeInvoke接口，提供了Invoke和BeginInvoke方法来提供让其它线程更新GUI界面控件的机制。&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public interface ISynchronizeInvoke
{
    [HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
    IAsyncResult BeginInvoke(Delegate method, object[] args);
    object EndInvoke(IAsyncResult result);
    object Invoke(Delegate method, object[] args);
    bool InvokeRequired { get; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;如果从线程外操作windows窗体控件，那么就需要使用Invoke或者BeginInvoke方法，通过一个委托把调用封送到控件所属的线程上执行。&lt;/p&gt;
&lt;h1 id=&#34;消息机制线程间和进程间通信机制&#34;&gt;消息机制&amp;mdash;线程间和进程间通信机制&lt;/h1&gt;
&lt;h2 id=&#34;window消息发送&#34;&gt;window消息发送&lt;/h2&gt;
&lt;p&gt;Windows消息机制是windows平台上的线程或者进程间通信机制之一。Windows消息值其实就是定义的一个数据结构，最重要的是消息的类型，它就是一个整数；然后就是消息的参数。消息的参数可以表示很多东西。&lt;/p&gt;
&lt;p&gt;Windows提供了一些api用来向一个线程的消息队列发送消息。因此，一个线程可以向另一个线程的消息队列发送消息从而告诉对方做什么，这样就完成了线程间的通信。有些api发送消息需要一个窗口句柄，这种函数可以把消息发送到指定窗口的主线程消息队列；而有些则可以直接通过线程句柄，把消息发送到该线程消息队列中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6%E9%80%9A%E4%BF%A1.jpg&#34; alt=&#34;消息机制通信&#34;&gt;&lt;/p&gt;
&lt;p&gt;SendMessage是windows api，用来把一个消息发送到一个窗口的消息队列。这个方法是个阻塞方法，也就是操作系统会确保消息的确发送到目的消息队列，并且该消息被处理完毕以后，该函数才返回。返回之前，调用者将会被暂时阻塞。&lt;/p&gt;
&lt;p&gt;PostMessage也是一个用来发送消息到窗口消息队列的api函数，但这个方法是非阻塞的。也就是它会马上返回，而不管消息是否真的发送到目的地，也就是调用者不会被阻塞。&lt;/p&gt;
&lt;h2 id=&#34;invoke-and-begininvoke&#34;&gt;Invoke and BeginInvoke&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/InvokeOrBeginInvoke.jpg&#34; alt=&#34;Invoke Or BeginInvoke&#34;&gt;&lt;/p&gt;
&lt;p&gt;Invoke或者BeginInvoke方法都需要一个委托对象作为参数。委托类似于回调函数的地址，因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了更改控件状态的代码，那么由于最终执行这个方法的是界面线程，从而避免了竞争条件，避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件，那么将会产生竞争条件，造成不可预料的结果。&lt;/p&gt;
&lt;p&gt;使用Invoke完成一个委托方法的封送，就类似于使用SendMessage方法来给界面线程发送消息，是一个同步方法。也就是说在Invoke封送的方法被执行完毕前，Invoke方法不会返回，从而调用者线程将被阻塞。&lt;/p&gt;
&lt;p&gt;使用BeginInvoke方法封送一个委托方法，类似于使用PostMessage进行通信，这是一个异步方法。也就是该方法封送完毕后马上返回，不会等待委托方法的执行结束，调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。&lt;/p&gt;
&lt;p&gt;但是在内部实现上，Invoke和BeginInvoke都是用了PostMessage方法，从而避免了SendMessage带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。&lt;/p&gt;
&lt;h2 id=&#34;使用场合问题&#34;&gt;使用场合问题&lt;/h2&gt;
&lt;p&gt;如果你的后台线程在更新一个UI控件的状态后不需要等待，而是要继续往下处理，那么你就应该使用BeginInvoke来进行异步处理。&lt;/p&gt;
&lt;p&gt;如果你的后台线程需要操作UI控件，并且需要等到该操作执行完毕才能继续执行，那么你就应该使用Invoke。否则，在后台线程和主截面线程共享某些状态数据的情况下，如果不同步调用，而是各自继续执行的话，可能会造成执行序列上的问题，虽然不发生死锁，但是会出现不可预料的显示结果或者数据处理错误。&lt;/p&gt;
&lt;p&gt;可以看到ISynchronizeInvoke有一个属性，InvokeRequired。这个属性就是用来在编程的时候确定，一个对象访问UI控件的时候是否需要使用Invoke或者BeginInvoke来进行封送。如果不需要那么就可以直接更新。在调用者对象和UI对象同属一个线程的时候这个属性返回false。在后面的代码分析中我们可以看到，Control类对这一属性的实现就是在判断调用者和控件是否属于同一个线程的。&lt;/p&gt;
&lt;h1 id=&#34;delegate_begininvoke&#34;&gt;Delegate_BeginInvoke&lt;/h1&gt;
&lt;p&gt;通过一个委托来进行同步方法的异步调用，也是.net提供的异步调用机制之一。但是Delegate.BeginInvoke方法是从ThreadPool取出一个线程来执行这个方法，以获得异步执行效果的。也就是说，如果采用这种方式提交多个异步委托，那么这些调用的顺序无法得到保证。而且由于是使用线程池里面的线程来完成任务，使用频繁，会对系统的性能造成影响。&lt;/p&gt;
&lt;p&gt;Delegate.BeginInvoke也是讲一个委托方法封送到其它线程，从而通过异步机制执行一个方法。调用者线程则可以在完成封送以后去继续它的工作。但是这个方法封送到的最终执行线程是运行库从ThreadPool里面选取的一个线程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这里需要纠正一个误区，那就是Control类上的异步调用BeginInvoke并没有开辟新的线程完成委托任务，而是让界面控件的所属线程完成委托任务的。看来异步操作就是开辟新线程的说法不一定准确。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;解决方案&#34;&gt;解决方案&lt;/h1&gt;
&lt;p&gt;InvokeRequired属性，获取一个值，该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法，因为调用方位于创建控件所在的线程以外的线程中。如果控件的 Handle 是在与调用线程不同的线程上创建的（说明您必须通过 Invoke 方法对控件进行调用），则为 true；否则为 false。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public void function()
{
    // 委托代码
}

private void InvokeIfRequired(MethodInvoker function)
{
    if (控件.InvokeRequired)
        控件.BeginInvoke(function);
    else
        function();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</description>
      
    </item>
    
    <item>
      <title>定时任务cron表达式详解</title>
      <link>http://coder.amazingdemo.top/post/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1cron%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%A6%E8%A7%A3/</link>
      <pubDate>Tue, 12 Nov 2019 14:12:25 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1cron%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%A6%E8%A7%A3/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/cron.png&#34; alt=&#34;CRON表达式&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;示例&#34;&gt;示例&lt;/h1&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;表达式&lt;/th&gt;
          &lt;th&gt;含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;0 * * * * ?&lt;/td&gt;
          &lt;td&gt;每1分钟触发一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 0 * * * ?&lt;/td&gt;
          &lt;td&gt;每天每1小时触发一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 0 10 * * ?&lt;/td&gt;
          &lt;td&gt;每天10点触发一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 * 14 * * ?&lt;/td&gt;
          &lt;td&gt;在每天下午2点到下午2:59期间的每1分钟触发&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 30 9 1 * ?&lt;/td&gt;
          &lt;td&gt;每月1号上午9点半&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 15 10 15 * ?&lt;/td&gt;
          &lt;td&gt;每月15日上午10:15触发&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;*/5 * * * * ?&lt;/td&gt;
          &lt;td&gt;每隔5秒执行一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 */1 * * * ?&lt;/td&gt;
          &lt;td&gt;每隔1分钟执行一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 0 5-15 * * ?&lt;/td&gt;
          &lt;td&gt;每天5-15点整点触发&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 0/3 * * * ?&lt;/td&gt;
          &lt;td&gt;每三分钟触发一次&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;0 0 0 1 * ?&lt;/td&gt;
          &lt;td&gt;每月1号凌晨执行一次&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
      
    </item>
    
    <item>
      <title>稳定器三种模式的区别</title>
      <link>http://coder.amazingdemo.top/post/%E7%A8%B3%E5%AE%9A%E5%99%A8%E4%B8%89%E7%A7%8D%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%8C%BA%E5%88%AB/</link>
      <pubDate>Tue, 29 Oct 2019 17:52:51 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%A8%B3%E5%AE%9A%E5%99%A8%E4%B8%89%E7%A7%8D%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%8C%BA%E5%88%AB/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E6%A8%AA%E6%BB%9A.gif&#34; alt=&#34;横滚&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E4%BF%AF%E4%BB%B0.gif&#34; alt=&#34;俯仰&#34;&gt;&lt;/p&gt;
&lt;p&gt;开机默认跟随模式，或者叫左右跟随模式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E5%B7%A6%E5%8F%B3%E6%A8%A1%E5%BC%8F.gif&#34; alt=&#34;左右跟随&#34;&gt;&lt;/p&gt;
&lt;p&gt;跟随模式横滚、俯仰锁定，水平朝向随握把方向平滑转动。也就意味着，操作者水平转动握杆，相机会缓慢的转动至握把朝向的方向拍摄；操作者俯拍或者仰拍，相机仍然朝最初的俯仰方向拍摄；操作者左右摆动稳定器，相机仍然保持水平方向拍摄。在这个模式下，UP/DOWN键，改变的是相机的俯仰角度。&lt;/p&gt;
&lt;p&gt;该模式比较适合拍摄“环视一周”的感觉，也很适合以自己为轴心的自拍。只要将握杆朝向需要拍摄的对象，就能平缓的跟随这个对象进行拍摄。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F.gif&#34; alt=&#34;全锁定&#34;&gt;&lt;/p&gt;
&lt;p&gt;锁定模式的意思是水平朝向、横滚、俯仰都锁定。也就意味着，操作者水平转动握杆，相机仍然朝原来的方向拍摄；操作者俯拍或者仰拍，相机仍然朝最初的俯仰方向拍摄；操作者左右摆动稳定器，相机仍然保持水平方向拍摄。反正怎么转都是对着最初的点，仔细看上图的手柄转动就明白了。在这个模式下，UP/DOWN键，改变的是相机的俯仰角度。&lt;/p&gt;
&lt;p&gt;这个模式的话，拍摄延特定方向运动的物体比较方便，比如：跟拍100米赛跑的运动员、滑滑板的人……拍摄者可以自由的活动手腕，不用担心相机朝向出错。但是，这个模式想要自拍就悲剧了，因为它根本不会跟着你的脸走。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E5%85%A8%E8%B7%9F%E9%9A%8F.gif&#34; alt=&#34;全跟随&#34;&gt;&lt;/p&gt;
&lt;p&gt;全跟随模式的横滚锁定，俯仰、水平朝向跟随握把方向、倾角平滑转动。也就意味着，操作者水平转动握杆，相机会缓慢的转动至握把朝向的方向拍摄；操作者俯拍或者仰拍，相机会缓慢的跟随至握把倾斜的方向拍摄；操作者左右摆动稳定器，相机仍然保持水平方向拍摄。在这个模式下，UP/DOWN键改变的是相机的横滚角度。&lt;/p&gt;
&lt;p&gt;这个模式比较适合自由的拍摄，无论你看哪里，相机都会稳定的跟随，就像是我们在玩角色类的电脑游戏，可以上下左右到处看，拍摄的视频会有一种身临其境的感觉。&lt;/p&gt;
&lt;p&gt;总结一下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我认为是最常用、最标准的模式是跟随模式，适合大部分的拍摄情况。它非常接近人在正常步行状态下眼睛的运动模式：多数情况平视前方，转弯时会转头，几乎不会大范围的往天上或地上看。所以我感觉这个模式是比较标准而常用的一个拍摄模式。&lt;/li&gt;
&lt;li&gt;在跟随模式和锁定模式下，非暴力操作都不会使手机发生水平偏移，全跟随模式下不按键也不会发生水平偏移，换言之：手机基本上随时都是“端平”了的。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>Windows 配置 Aria 及 AriaNg</title>
      <link>http://coder.amazingdemo.top/post/windows%E9%85%8D%E7%BD%AEaria%E5%8F%8Aariang/</link>
      <pubDate>Tue, 22 Oct 2019 12:02:29 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/windows%E9%85%8D%E7%BD%AEaria%E5%8F%8Aariang/</guid>
      
        <description>&lt;h1 id=&#34;aria是什么&#34;&gt;Aria是什么&lt;/h1&gt;
&lt;p&gt;Aria2 是一个多平台轻量级，支持 HTTP、FTP、BitTorrent 等多协议、多来源的命令行下载工具。Aria2 可以从多个来源、多个协议下载资源，最大的程度上利用了你的带宽。Aria2 有着非常小的资源占用，在关闭磁盘缓存的情况下，物理内存占用通常为 4M（正常 HTTP/FTP 下载的情况下），BitTorrent 下载每秒2.8M/S的情况下，CPU 占有率约为 6%。Aria2 支持 JSON-RPC 和 XML-RPC 接口远程调用。&lt;/p&gt;
&lt;p&gt;很多人在 Windows 可能用过 Internet Download Manager，是很好用的多线程下载工具。Aria2 跟 IDM 类似，不仅可以多线程下载，还可以通过多来源进行下载，简单的说就是从多个镜像服务器同时下载一个文件，Aria2 还支持 BT 协议，弥补了 IDM 只支持 HTTP 和 FTP 的痛点。&lt;/p&gt;
&lt;h1 id=&#34;下载-aria2-及-web-管理面板&#34;&gt;下载 Aria2 及 Web 管理面板&lt;/h1&gt;
&lt;p&gt;Aria2 是一个命令行下载工具，所以使用的时候要敲命令，可是每下载一个文件敲一条命令，太麻烦了，那咋办？Aria2 支持远程接口调用，只需要配置一个 Web 管理面板就可以在浏览器管理 Aira2 了&lt;/p&gt;
&lt;h2 id=&#34;下载并解压-aria2-主程序&#34;&gt;下载并解压 Aria2 主程序&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/aria2/aria2/releases&#34;&gt;下载&lt;/a&gt; Aria2 主程序&lt;/p&gt;
&lt;p&gt;这里有不同平台的压缩包，Windows下载 win-32bit 或者 win-64bit，下载后解压，解压到剩余空间比较大的地方，不要解压在桌面。&lt;/p&gt;
&lt;h2 id=&#34;下载并解压-ariang-管理面板&#34;&gt;下载并解压 AriaNg 管理面板&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/mayswind/AriaNg/releases&#34;&gt;下载&lt;/a&gt; AriaNg 管理面板，下载后解压。&lt;/p&gt;
&lt;h1 id=&#34;安装配置-aria2-及-web-管理面板&#34;&gt;安装/配置 Aria2 及 Web 管理面板&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;http://coder.amazingdemo.top/userfile/aria2_conf.zip&#34;&gt;下载&lt;/a&gt; Aria2 配置文件，解压至 Aria2 目录里。&lt;/p&gt;
&lt;p&gt;默认已经配置好了，如果需要更改配置，用记事本等文本编辑器，打开 aria2.conf ，里面有对应的注释，这里说几个比较重要的参数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dir=Download
文件保存目录 ，默认下载到 Aria2 目录的 Download 文件夹。

disk-cache=32M
硬盘缓存，默认 32M，作用是将数据缓存到内存中。

file-allocation=none
文件预分配方式，配置文件有速度比较，具体看你的硬盘，机械硬盘用默认的none不进行预分配就好，固态硬盘可以选择falloc。

enable-rpc=true
是否启用 RPC，RPC 是远程调用接口，开启：true，关闭：false。

#rpc-secret=mivm.cn
RPC 授权令牌，如果启用授权令牌，远程管理会要求输入令牌，去掉 # 即可启用，默认授权令牌：mivm.cn.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;剩下的参数配置文件有注释，更多参数可以查阅官方文档（英文）。&lt;/p&gt;
&lt;p&gt;修改完记得重启 Aria2&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://coder.amazingdemo.top/userfile/aria2_bat.zip&#34;&gt;下载&lt;/a&gt; Aria2 控制脚本，解压至 Aria2 目录里。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Start.bat
带命令行窗口输出启动 Aria2

Start.vbs
不带命令行窗口启动 Aria2

Stop.bat
停止 Aria2

Status.bat
查看 Aria2 进程状态

Restart.bat
重启 Aria2

Boot.bat
开启或关闭 Aria2 开机启动&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;运行 Start.vbs 或 Start.bat 启动 Aria2&lt;/p&gt;
&lt;p&gt;首次运行会出现防火墙警告，允许即可。&lt;/p&gt;
&lt;p&gt;自行配置IIS后，浏览器访问 http://localhost/ 就可以访问 Web 管理面板。&lt;/p&gt;
&lt;h1 id=&#34;aria2-web-管理面板&#34;&gt;Aria2 Web 管理面板&lt;/h1&gt;
&lt;p&gt;设置中文方法：点击 AriaNg Settings → Language 选择 简体中文&lt;/p&gt;
&lt;p&gt;Aria2 状态 如果显示 已连接，恭喜你，Aria2 搭建成功，如果显示 未连接，请检查 Aria2 是否正常开启，或者重启 Aria2。&lt;/p&gt;
&lt;h1 id=&#34;小贴士&#34;&gt;小贴士&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Web 管理面板删除下载任务后，Aria2 并不会删除下载文件或缓存，需要去下载文件夹删除掉。&lt;/li&gt;
&lt;li&gt;同一个局域网内，其他设备输入当前设备内网 IP 地址，就可以访问 Web 管理界面 。&lt;/li&gt;
&lt;li&gt;浏览器关闭 Web 管理面板也不影响 Aria2 下载。&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>服务启动时报错The service did not respond to the start or control request in a timely fashion</title>
      <link>http://coder.amazingdemo.top/post/%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E6%97%B6%E6%8A%A5%E9%94%99/</link>
      <pubDate>Sat, 19 Oct 2019 12:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%E6%97%B6%E6%8A%A5%E9%94%99/</guid>
      
        <description>&lt;h1 id=&#34;案例环境&#34;&gt;案例环境&lt;/h1&gt;
&lt;p&gt;启动一台数据库服务器（Windows Server 2003）的Reporting Service（SQL Server 2008 R2）服务时，出现如下错误信息：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;System.InvalidOperationException: Cannot start service ReportServer on computer &amp;lsquo;xxx&amp;rsquo;. &amp;mdash;&amp;gt; System.ComponentModel.Win32Exception: The service did not respond to the start or control request in a timely fashion
&amp;mdash; End of inner exception stack trace &amp;mdash;
at System.ServiceProcess.ServiceController.Start(String[] args)
at System.ServiceProcess.ServiceController.Start()
at ReportServicesConfigUI.Panels.ServerInformationPanel.StartStopServiceTask(Boolean start)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从网上搜索了一些资料后发现：出现这个错误是因为安装了&lt;a href=&#34;http://support.microsoft.com/kb/2716429&#34;&gt;MS12-070 security patch&lt;/a&gt; 导致：因为安装了这个安全补丁后，当.NET应用程序启动时， .NET Framework就会尝试去验证Microsoft程序集是否正确并且没有被修改过。.NET Framework是通过连接到公布在互联网的服务器来验证的。当.NET Framework框架试图连接到互联网上的服务器去验证时超时或时间比较长，这就会使得其他超时设置比较短的.NET 应用程序（如 SSIS 或 SSRS）启动失败，出现下面错误信息。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;The request failed or the service did not respond in a timely fashion.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;英文原文如下所：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When a .NET application startup the .NET Framework it tries to check if the Microsoft assemblies are right and have not been modified, this is done connecting to a server that has a revocation list in internet. When the framework tries to connect to the server to check if it has access to internet with an elevated timeout, this makes that any other .NET application that has less timeout setting fails (as SSIS or SSRS) with the error message you are getting.&lt;/p&gt;
&lt;p&gt;The error message you are getting when you try to start SSRS:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The request failed or the service did not respond in a timely fashion.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Means that the SSRS timeout is lower than the timeout of the connection to the revoke list server. Some recent windows updates have introduced this issue after you installed SP2 of SSRS 2008 R2. We are  aware of this problem and we have already provided several workarounds.&lt;/p&gt;
&lt;p&gt;This issue affects to all applications that runs on .NET framework 2.0 and there are several workarounds for this problem&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;解决方法&#34;&gt;解决方法&lt;/h1&gt;
&lt;h2 id=&#34;第一种解决方案&#34;&gt;第一种解决方案&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;点击开始（Start）菜单，点击运行（Run），输入regedit命令&lt;/li&gt;
&lt;li&gt;找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control.&lt;/li&gt;
&lt;li&gt;找到ServicesPipeTimeout选项，单击右键选项“编辑”选项，选择“Decimal&amp;quot;选项，输入60000. 有可能你找不到ServicesPipeTimeout选项，这时你就需要新建该选项。然后如上所述设置其值。&lt;/li&gt;
&lt;li&gt;设置完成后，需要重启服务器才能生效。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我按上面方案就讲问题解决了，博客Reporting Services service doesn&amp;rsquo;t start after the installation of MS12-070 security patch中介绍，如果第一种方案没有解决问题，可以通过下面第二种方案、第三种方案解决问题&lt;/p&gt;
&lt;h2 id=&#34;第二种解决方案&#34;&gt;第二种解决方案&lt;/h2&gt;
&lt;p&gt;如果第一种方案没有解决问题，你可以通过通过设置ReportingServicesService.exe.config配置文件禁用认证检查。ReportingServicesService.exe.config通常位于C:\Program Files\Microsoft SQL Server&amp;lt;RS Instance&amp;gt;\Reporting Services\ReportServer\bin 。其中我这台服务器的路径为C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER\Reporting Services\ReportServer\bin ，在ReportingServicesService.exe.config 添加&amp;quot;&lt;generatePublisherEvidence enabled=&#34;false&#34;/&gt;&amp;quot;&lt;/p&gt;
&lt;p&gt;英文原文如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2nd workaround:&lt;/p&gt;
&lt;p&gt;If the first action plan didn&amp;rsquo;t work, try disabled the certificate checking in the ReportingServicesService.exe.config file:&lt;/p&gt;
&lt;p&gt;Edit the ReportingServicesService.exe.config file usually located in this folder: C:\Program Files\Microsoft SQL Server&amp;lt;RS Instance&amp;gt;\Reporting Services\ReportServer\bin&lt;/p&gt;
&lt;p&gt;Add the &amp;ldquo;&lt;generatePublisherEvidence enabled=&#34;false&#34;/&gt;&amp;rdquo; within the &lt;Runtime&gt; tag&lt;/p&gt;
&lt;p&gt;Patch the server with the KB.&lt;/p&gt;
&lt;p&gt;If you want to distribute this workaround to many users you can créate a script in PowerShell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;第三种解决方案&#34;&gt;第三种解决方案&lt;/h2&gt;
&lt;p&gt;在控制面板里面找到”Internet Option“，在高级设置里面找到”Check for publisher&amp;rsquo;s certificate revocation&amp;quot; 选项，去掉勾选即可。如果 SSRS服务是以Network Service账号启动，那么修改“Internet Option”选项并不会使之生效。如果是这样，你可以为SSRS服务账号修改下面一些配置，修改注册表&lt;/p&gt;
&lt;p&gt;HKEY_USERS&amp;lt;SID&amp;gt;\Software\Microsoft\Windows\CurrentVersion\WinTrust\TrustProviders\Software 下State值&lt;/p&gt;
&lt;p&gt;从原先的0x00023c00改为0x00023e00&lt;/p&gt;
&lt;p&gt;英文原文如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;3rd workaround:&lt;/p&gt;
&lt;p&gt;Another option would be disabling the global checking of the certificated in the machine following the steps below:&lt;/p&gt;
&lt;p&gt;o   Start -&amp;gt; Control panel
o   Internet Options
o   Advanced
o   Uncheck &amp;ldquo;Check for publisher’s certificate revocation&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If the services are running under the Network Service account the change to the revocation in internet options is not working for them. In that case, you can perform the following changes for the SSRS service account:&lt;/p&gt;
&lt;p&gt;Modify the following setting in the registry:&lt;/p&gt;
&lt;p&gt;HKEY_USERS&amp;lt;SID&amp;gt;\Software\Microsoft\Windows\CurrentVersion\WinTrust\TrustProviders\Software&lt;/p&gt;
&lt;p&gt;Where the default value for State key is 0x00023c00&lt;/p&gt;
&lt;p&gt;And you will need to change it to 0x00023e00&lt;/p&gt;
&lt;p&gt;(More information in: &lt;a href=&#34;http://support.microsoft.com/kb/2715304&#34;&gt;http://support.microsoft.com/kb/2715304&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;In order to get the Windows SID for this user and be able to find out the previous registry key, please follow the below instructions.&lt;/p&gt;
&lt;p&gt;Download SPGETSID tool from &lt;a href=&#34;http://technet.microsoft.com/en-us/sysinternals/bb897417.aspx&#34;&gt;http://technet.microsoft.com/en-us/sysinternals/bb897417.aspx&lt;/a&gt;
Once it is installed and save to a local file, open a cmd window (with administrator privileges ) and navigate through the cmd to the folder where this spgetsid.exe is located.
Then type the following command line:    spgetsid domain\user                       (where the domain\user is your SSRS service account)
Once get the SID please go to the previous registry key for this user and apply the change.
S-1-5-20 is the SID for Network Service, and for other SIDs: &lt;a href=&#34;http://support.microsoft.com/kb/243330&#34;&gt;http://support.microsoft.com/kb/243330&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SID: S-1-5-18&lt;/p&gt;
&lt;p&gt;Name: Local System&lt;/p&gt;
&lt;p&gt;Description: A service account that is used by the operating system.&lt;/p&gt;
&lt;p&gt;SID: S-1-5-19&lt;/p&gt;
&lt;p&gt;Name: NT Authority&lt;/p&gt;
&lt;p&gt;Description: Local Service&lt;/p&gt;
&lt;p&gt;SID: S-1-5-20&lt;/p&gt;
&lt;p&gt;Name: NT Authority&lt;/p&gt;
&lt;p&gt;Description: Network Service&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      
    </item>
    
    <item>
      <title>Quartz.net 设置任务中同时最多运行一个实例 [DisallowConcurrentExecution]</title>
      <link>http://coder.amazingdemo.top/post/quartz%E8%AE%BE%E7%BD%AE%E4%BB%BB%E5%8A%A1%E4%B8%AD%E5%90%8C%E6%97%B6%E6%9C%80%E5%A4%9A%E8%BF%90%E8%A1%8C%E4%B8%80%E4%B8%AA%E5%AE%9E%E4%BE%8B/</link>
      <pubDate>Thu, 10 Oct 2019 13:35:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/quartz%E8%AE%BE%E7%BD%AE%E4%BB%BB%E5%8A%A1%E4%B8%AD%E5%90%8C%E6%97%B6%E6%9C%80%E5%A4%9A%E8%BF%90%E8%A1%8C%E4%B8%80%E4%B8%AA%E5%AE%9E%E4%BE%8B/</guid>
      
        <description>&lt;p&gt;Quartz定时任务默认都是并发执行的，不会等待上一次任务执行完毕，只要间隔时间到就会执行, 如果定时任执行太长，会长时间占用资源，导致其它任务堵塞。&lt;/p&gt;
&lt;p&gt;比如Job设置1分钟跑一次，每次获取50条短信发送，发送完成后状态设置为已发送。但是可能Job1执行的时间超过1分钟50条短信还没有跑完，这时候Job2 启动了，就会获取全部未发送的记录继续发送。造成重复执行短信发送的问题。&lt;/p&gt;
&lt;p&gt;DisallowConcurrentExecution：此标记用在实现Job的类上面,意思是不允许并发执行,按照我之前的理解是 不允许调度框架在同一时刻调用Job类，后来经过测试发现并不是这样，而是Job(任务)的执行时间[比如需要10秒]大于任务的时间间隔[Interval（5秒)],那么默认情况下,调度框架为了能让 任务按照我们预定的时间间隔执行,会马上启用新的线程执行任务。否则的话会等待任务执行完毕以后 再重新执行！（这样会导致任务的执行不是按照我们预先定义的时间间隔执行）&lt;/p&gt;
&lt;p&gt;以C#为例可以很清楚的看出区别.&lt;/p&gt;
&lt;p&gt;设置Job每1秒执行一次&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
IJobDetail syncFansJob = JobBuilder.Create&amp;lt;CustomerUserJob&amp;gt;()
     .Build();
ITrigger syncFansTrigger = TriggerBuilder.Create()
      .StartNow()
      .WithSimpleSchedule(x =&amp;gt; x.WithIntervalInSeconds(1).RepeatForever())
      .Build();
scheduler.ScheduleJob(syncFansJob, syncFansTrigger);
scheduler.Start();
CustomerUserJob&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;没有设置[DisallowConcurrentExecution]属性，Job每一秒执行一次&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    public class CustomerUserJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            ApiCommon.WriteLocalLog(&amp;#34;begin job..&amp;#34;);
            Thread.Sleep(5*1000);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/quart1.jpg&#34; alt=&#34;一秒执行一次&#34;&gt;&lt;/p&gt;
&lt;p&gt;设置[DisallowConcurrentExecution]属性&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    [DisallowConcurrentExecution]
    public class CustomerUserJob : IJob
    {
        public void Execute(IJobExecutionContext context)
        {
            ApiCommon.WriteLocalLog(&amp;#34;begin job..&amp;#34;);
            Thread.Sleep(5*1000);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/quart5.jpg&#34; alt=&#34;五秒执行一次&#34;&gt;&lt;/p&gt;
&lt;p&gt;可以看到Job在每5秒执行一次.&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43;11 智能指针与垃圾回收</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/</link>
      <pubDate>Wed, 09 Oct 2019 14:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/</guid>
      
        <description>&lt;p&gt;程序员在处理现实生活中的C/C++程序的时候，常会遇到诸如程序运行时突然退出，或占用的内存越来越多，最后不得不定期重启的一些典型症状。这些问题的源头可以追溯到C/C++中的显式堆内存管理上。通常情况下，这些症状都是由于程序没有正确处理堆内存的分配与释放造成的，从语言层面来讲，我们可以将其归纳为以下的一些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;野指针：一些内存单元已被释放，之前指向它的指针却还在被使用。这些内存有可能被运行时系统重新分配给程序使用，从而导致无法预测的错误。&lt;/li&gt;
&lt;li&gt;重复释放：程序试图去释放已经被释放过的内存单元，或者释放已经被重新分配过的内存单元，就会导致重复释放错误。通常重复释放内存会导致C/C++运行时系统打印出大量错误及诊断信息。&lt;/li&gt;
&lt;li&gt;内存泄漏：不再需要使用的内存单元如果没有释放就会导致内存泄漏。如果程序不断地重复进行这类操作，将会导致内存占用剧增。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;虽然显式的管理内存在性能上有一定的优势，但也被广泛地认为是容易出错的。随着多线程程序的出现和广泛使用，内存管理不佳的情况还可能会变得更加严重。因此，很多程序员也认为编程语言应该提供更好的机制，让程序员拜托内存管理的细节。在C++中，一个这样的机制就是标准库中的智能指针。在C++11新标准中，智能指针被进行了改进，以更加适应实际的应用需求。而进一步的，标准库还提供了所谓“最小垃圾回收”的支持。&lt;/p&gt;
&lt;h1 id=&#34;c11的智能指针&#34;&gt;C++11的智能指针&lt;/h1&gt;
&lt;p&gt;在C++98中，智能指针通过一个模版类型“auto_ptr”来实现。auto_ptr以对象的方式管理堆分配的内存，并在适当的时间（比如析构），释放所获得的堆内存。这种堆内存管理的方式只需要程序员将new操作返回的指针作为auto_ptr的初始值即可，程序员不用再显式的调用delete。比如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auto_ptr(new int);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这在一定程度上避免了堆内存忘记释放而造成的问题。不过auto_ptr有一些缺点（拷贝时返回一个左值，不能调用delete[]等），所以在C++11标准中被废弃了。C++11标准中改用unique_ptr、shared_ptr及weak_ptr等智能指针来自动回收堆分配的对象。&lt;/p&gt;
&lt;p&gt;我们来看一个C++11中使用新的智能指针的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;

int main()
{
	unique_ptr&amp;lt;int&amp;gt; up1(new int(11));   //无法复制的unique_ptr
//	unique_ptr&amp;lt;int&amp;gt; up2 = up1;   //不能通过编译
	cout&amp;lt;&amp;lt;*up1&amp;lt;&amp;lt;endl;   //11
	
	unique_ptr&amp;lt;int&amp;gt; up3 = move(up1);  //现在up3是数据唯一的unique_ptr智能指针

	cout&amp;lt;&amp;lt;*up3&amp;lt;&amp;lt;endl;   //11
//	cout&amp;lt;&amp;lt;*up1&amp;lt;&amp;lt;endl;   //运行时错误

	up3.reset();    //显式释放内存
	up1.reset();    //不会导致运行时错误
//	cout&amp;lt;&amp;lt;*up3&amp;lt;&amp;lt;endl;    //运行时错误


	//不能写成shared_ptr&amp;lt;int&amp;gt; sp1 = new int(22); 
	//否则会报错：error C2440: “初始化”: 无法从“int *”转换为“std::tr1::shared_ptr&amp;lt;_Ty&amp;gt;”
	shared_ptr&amp;lt;int&amp;gt; sp1(new int(22)); 
	shared_ptr&amp;lt;int&amp;gt; sp2 = sp1;

	cout&amp;lt;&amp;lt;*sp1&amp;lt;&amp;lt;endl;   //22
	cout&amp;lt;&amp;lt;*sp2&amp;lt;&amp;lt;endl;   //22

	sp1.reset();
	cout&amp;lt;&amp;lt;*sp2&amp;lt;&amp;lt;endl;   //22
	
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在上面的代码中，使用了两种不同的智能指针 unique_ptr 及 shared_ptr 来自动的释放堆对象的内存。由于每个智能指针都重载了*运算符，用户可以使用 *up1 这样的方式来访问所分配的堆内存。而在该指针析构或者调用 reset 成员的时候，智能指针都可能释放其拥有的堆内存。从作用上来讲，unique_ptr 和 shared_ptr 还是和以前的 auto_ptr 保持了一致。&lt;/p&gt;
&lt;p&gt;不过从上述代码还是可以看到，unique_ptr 和 shared_ptr 在对所占内存的共享上还是有一定的区别的。&lt;/p&gt;
&lt;p&gt;直观来看，unique_ptr 形如其名地，与所指对象的内存绑定紧密，不能与其他 unique_ptr 类型的指针对象共享所指对象的内存。比如，上例中的unique_ptr&lt;int&gt; up2 = up1; 不能通过编译，是因为每个 unique_ptr 都是唯一地“拥有”所指向的对象内存，由于 up1 唯一地占有 new 分配的堆内存，所以 up2 无法共享其“所有权”。事实上，这种“所有权”仅能够通过标准库的 move函数 来转移。我们可以看到代码中 up3 的定义，unique_ptr&lt;int&gt; up3 = move(up1); 一旦“所有权”转移成功了，原来的 unique_ptr 指针就失去了对象内存的所有权。此时再使用已经“失势”的 unique_ptr，就会导致运行时错误。上例中的后段使用 *up1 就是很好的例子。&lt;/p&gt;
&lt;p&gt;而从实现上讲，unique_ptr 则是一个删除了拷贝构造函数、保留了移动构造函数的指针封装类型。程序员仅可以使用右值对 unique_ptr 对象进行构造，而且一旦构造成功，右值对象中的指针即被“窃取”，因此该右值对象即刻失去了对指针的“所有权”。&lt;/p&gt;
&lt;p&gt;而 shared_ptr 同样形如其名，允许多个该智能指针共享的“拥有”同一堆分配对象的内存。与 unique_ptr 不同的是，由于在实现上采用了引用计数，所以一旦一个 shared_ptr 指针放弃了“所有权”（失效），其他的 shared_ptr 对对象内存的引用并不会受到影响。上述代码中，智能指针 sp2 就很好的说明了这种状况。虽然 sp1 调用了 reset 成员函数，但由于 sp1 和 sp2 共享了 new 分配的堆内存，所以 sp1 调用 reset 成员函数只会导致引用计数的降低，而不会导致堆内存的释放。只有在引用计数归零的时候，shared_ptr 才会真正释放所占有的堆内存的空间。&lt;/p&gt;
&lt;p&gt;在C++11标准中，除了unique_ptr 和 shared_ptr，智能指针还包括了 weak_ptr 这个类模板。weak_ptr 的使用更为复杂一点，它可以指向 shared_ptr 指针指向的对象内存，却并不拥有该内存。而使用 weak_ptr 成员 lock，则可返回其指向内存的一个 shared_ptr 对象，且在所指对象内存已经无效时，返回指针空值（nullptr）。这在验证shared_ptr智能指针的有效性上会很有作用，看下例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;

void Check(weak_ptr&amp;lt;int&amp;gt; &amp;amp;wp)
{
	shared_ptr&amp;lt;int&amp;gt; sp = wp.lock();   //转换为shared_ptr&amp;lt;int&amp;gt;
	if (sp != nullptr)
	{
		cout&amp;lt;&amp;lt;&amp;#34;still &amp;#34; &amp;lt;&amp;lt; *sp &amp;lt;&amp;lt;endl;
	}else
		cout&amp;lt;&amp;lt; &amp;#34;pointer is invalid.&amp;#34;&amp;lt;&amp;lt;endl;
}

int main()
{
	shared_ptr&amp;lt;int&amp;gt; sp1(new int(22));
	shared_ptr&amp;lt;int&amp;gt; sp2 = sp1;   //指向shared_ptr&amp;lt;int&amp;gt; 所指对象
	weak_ptr&amp;lt;int&amp;gt; wp = sp1;    //指向shared_ptr&amp;lt;int&amp;gt;所指对象

	cout&amp;lt;&amp;lt;*sp1&amp;lt;&amp;lt;endl;   //22
	cout&amp;lt;&amp;lt;*sp2&amp;lt;&amp;lt;endl;   //22
	Check(wp);   //still 22

	sp1.reset();
	cout&amp;lt;&amp;lt;*sp2&amp;lt;&amp;lt;endl;   //22
	Check(wp);   //still 22

	sp2.reset();
	Check(wp);     //pointer is invalid

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在此代码中，我们定义了一个共享对象内存的两个 shared_ptr ——sp1 及 sp2。而 weak_ptr wp 同样指向该对象内存。可以看到，在 sp1 及 sp2 都有效的时候，我们调用 wp 的 lock函数，将返回一个有效的 shared_ptr 对象供试用，于是 Check函数 会输出以下内容：still 22 此后我们分别调用了 sp1 及 sp2 的 reset函数，这会导致对唯一的堆内存对象的引用计数降至0。而一旦引用计数归0，shared_ptr&lt;int&gt;就会释放堆内存空间，使之失效。此时我们再调用 weak_ptr 的 lock 函数时，则返回一个指针空值nullptr。这是 Check函数 则会打印出：pointer is invalid这样的语句了。在整个过程中，只有 shared_ptr 参与了引用计数，而 weak_ptr 没有影响其指向的内存的引用计数。因此可以验证 shared_ptr 指针的有效性。&lt;/p&gt;
&lt;p&gt;简单情况下，程序员用 unique_ptr 代替以前使用 auto_ptr 的代码就可以使用 C++11 中的智能指针了。而 shared_ptr 及 weak_ptr 则可用在用户需要引用计数的地方。&lt;/p&gt;
&lt;p&gt;总的来说，虽然智能指针能帮助用户进行有效的堆内存管理，但是它还是需要程序员显式地声明智能指针，而完全不需要考虑回收指针类型的内存管理方案可能会更讨人喜欢。当然，这种方案早已有了，就是垃圾回收机制。&lt;/p&gt;
&lt;h1 id=&#34;垃圾回收的分类&#34;&gt;垃圾回收的分类&lt;/h1&gt;
&lt;h2 id=&#34;什么是垃圾回收&#34;&gt;什么是垃圾回收？&lt;/h2&gt;
&lt;p&gt;我们把之前使用过，现在不再使用或者没有任何指针再指向的内存空间就称为“垃圾”。而将这些“垃圾”收集起来以便再次利用的机制，就被称为“垃圾回收”（Garbage Collection）。&lt;/p&gt;
&lt;p&gt;垃圾回收的方式虽然很多，但主要可以分为两大类：&lt;/p&gt;
&lt;h2 id=&#34;基于引用计数reference-counting-garbage-collector的垃圾回收器&#34;&gt;基于引用计数（reference counting garbage collector）的垃圾回收器&lt;/h2&gt;
&lt;p&gt;简单地说，引用计数主要是使用系统记录对象被引用（引用、指针）的次数。当对象被引用的次数变为0时，该对象即可被视作“垃圾”而回收。使用引用计数做垃圾回收的算法的一个优点是实现很简单，与其他垃圾回收算法相比，该方法不会造成程序暂停，因为计数的增减与对象的使用是紧密结合的。此外，引用计数也不会对系统的缓存或者交换空间造成冲击，因此被认为“副作用”较小。但是这种方法比较难处理“环形引用”问题，此外由于计数带来的额外开销也并不小，所以在实用上也有一定的限制。&lt;/p&gt;
&lt;h2 id=&#34;基于跟踪处理tracing-garbage-collector的垃圾回收器&#34;&gt;基于跟踪处理（tracing garbage collector）的垃圾回收器&lt;/h2&gt;
&lt;p&gt;相比于引用计数，跟踪处理的垃圾回收机制被更为广泛的应用。其基本方法是产生跟踪对象的关系图，然后进行垃圾回收。使用跟踪方式的垃圾回收算法主要有以下几种：&lt;/p&gt;
&lt;p&gt;（1）标记 – 清除（Mark – Sweep）&lt;/p&gt;
&lt;p&gt;顾名思义，这个算法可以分为两个过程。首先该算法将程序中正在使用的对象视为“根对象”，从根对象开始查找它们所引用的堆空间，并在这些堆空间上做标记。当标记结束后，所有被标记的对象就是可达目标（Reachable Object）或活对象（Live Object），而没有被标记的对象就被认为是垃圾，在第二步的清扫（Sweep）阶段会被回收掉。&lt;/p&gt;
&lt;p&gt;这种方法的特点是活的对象不会被移动，但是其存在会出现大量的内存碎片的问题。&lt;/p&gt;
&lt;p&gt;（2）标记 – 整理（Mark – Compact）&lt;/p&gt;
&lt;p&gt;这个算法标记的方法和 标记-清除 的方法一样，但是标记完之后，不再遍历所有对象清扫垃圾了，而是将活的对象向“左”靠齐，这就解决了内存碎片的问题。&lt;/p&gt;
&lt;p&gt;标记 – 整理 的方法有个特点就是移动活的对象，因此相应的，程序中所有对堆内存的引用都必须更新。&lt;/p&gt;
&lt;p&gt;（3）标记 – 拷贝（Mark – Copy）&lt;/p&gt;
&lt;p&gt;这种算法将堆空间分为两个部分：From 和 To。刚开始系统只从 From  的堆空间里面分配内存，当 From 分配满的时候系统就开始垃圾回收：从From 堆空间找出所有的活对象，拷贝到To的堆空间里。这样一来，From 的堆空间里面就全剩下垃圾了。而对象被拷贝到 To 里之后，在 To 里是紧凑排列的。接下来是需要将 From 和 To 交换一下角色，接着从新的 From 里面开始分配。&lt;/p&gt;
&lt;p&gt;标记 – 拷贝 算法的一个问题是堆的利用率只有一半，而且也需要移动活的对象。此外，从某种意义上讲，这种算法其实是 标记 – 整理 算法的另一种实现而已。&lt;/p&gt;
&lt;p&gt;虽然历来 C++ 都没有公开的支持过垃圾回收机制，但垃圾回收并非某些语言的专利。事实上，C++11标准也开始对垃圾回收做了一定的支持，虽然支持的程度还非常有限，但我们已经看到了C++语言变得更为强大的端倪。&lt;/p&gt;
&lt;h1 id=&#34;c与垃圾回收&#34;&gt;C++与垃圾回收&lt;/h1&gt;
&lt;p&gt;如我们提到的，在C++11中，智能指针等可以支持引用计数。不过由于引用计数并不能有效解决形如“环形引用”等问题，其使用会受到一些限制。而且基于一些其他的原因，比如因多线程程序等而引入的内存管理上的困难，程序员可能也会需要垃圾回收。&lt;/p&gt;
&lt;p&gt;一些第三方的C/C++库已经支持 标记 – 清除 方法的垃圾回收，比如一个比较著名的 C/C++ 垃圾回收库——Boehm。该垃圾回收器需要程序员使用库中的堆内存分配函数显式的替代 malloc，继而将堆内存的管理交给垃圾回收器来完成垃圾回收。不过由于C/C++中指针类型的使用非常灵活，这样的库在实际使用中会有一些限制，可移植性也不好。&lt;/p&gt;
&lt;p&gt;为了解决垃圾回收中的安全性和可移植性问题，在2007年，惠普的 Hans-J.Boehm（Boehm的作者）和赛门铁克的Mike Spertus 共同向 C++ 委员会递交了一个关于 C++ 中垃圾回收的提案。该提案通过添加 gc_forbidden、gc_relaxed、gc_required、gc_safe、gc_strict等关键字来支持 C++ 语言中的垃圾回收。该提案甚至可以让程序员显式的要求垃圾回收。刚开始这得到了大多数委员的支持，后来却在标准的初稿中删除了，原因是该特性过于复杂，并且还存在一些问题（比如与显式调用析构函数的现有的库的兼容问题等。）。所以，Boehm 和 Spertus 对初稿进行了简化，仅仅保留了支持垃圾回收的最基本的部分，即通过对语言的约束，来保证安全的垃圾回收。这是我们现在看到的 C++11标准 中的“最小垃圾回收支持”的历史来由。&lt;/p&gt;
&lt;p&gt;而要保证安全的垃圾回收，首先必须知道C/C++语言中什么样的行为可能导致垃圾回收中出现“不安全”的状况。简单地说，不安全源自于C/C++语言对指针的“放纵”，即允许过分灵活的使用。&lt;/p&gt;
&lt;p&gt;我们可以看下面这个例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main()
{
	int* p = new int;
	p += 10;  //移动指针，可能导致垃圾回收器
	p -= 10;  //回收原来指向的内存
	*p = 10;  //再次使用原本相同的指针则可能无效
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在代码中，我们对 指针p 做了自加和自减操作。这在C/C++中被认为是合理的，因为指针有所指向的类型，自加或者自减能够使程序员轻松地找到“下一个”同样的对象（实际是一个迭代器的概念）。不过对于垃圾回收来说，一旦p指向了别的地址，则可认为p曾指向的内存不再使用，垃圾回收器可以据此对其进行回收。这对之后p的使用（*p = 10）带来的后果将是灾难性的。&lt;/p&gt;
&lt;p&gt;再看一个例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main()
{
	int *p = new int;
	int *q = (int*)(reinterpret_cast&amp;lt;long long&amp;gt;(p)^2012);   //q隐藏了p

	//做一些其他工作，垃圾回收器可能已经回收了p指向的对象
	q = (int*)(reinterpret_cast&amp;lt;long long&amp;gt;(q)^2012);   //这里的 q == p
	*q = 10;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在代码中，我们用指针 q 隐藏了指针 p。而之后又用可逆的异或运算将 p “恢复”了出来。在main函数中，p实际所指向的内存都是有效的，但由于该指针被隐藏了，垃圾回收器可以早早的将p指向的对象回收掉。同样，语句 *q = 10 的后果也是灾难性的。&lt;/p&gt;
&lt;p&gt;指针的灵活使用可能是C/C++中的一大优势，而对于垃圾回收来说，却会带来很大的困扰。被隐藏的指针会导致编译器在分析指针的可达性（生命周期）时出错。而即使编译器开发出了隐藏指针分析手段，其带来的编译开销也不会让程序员对编译时间的显著增长视而不见。历史上，解决这种问题的方法通常是新接口。C++11 和 垃圾回收的解决方案也不例外，就是让程序员利用这样的接口来提示编译器代码中存在指针不安全的区域。&lt;/p&gt;
&lt;h1 id=&#34;c11-与-最小垃圾回收支持&#34;&gt;C++11 与 最小垃圾回收支持&lt;/h1&gt;
&lt;p&gt;C++11 新标准为了做到最小的垃圾回收支持，首先对“安全”的指针进行了定义，或者使用C++11 中的术语说，安全派生（safely derived）的指针。安全派生的指针是指向有 new 分配的对象或其子对象的指针。安全派生指针的操作包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在解引用基础上的引用，比如：&amp;amp;*p。&lt;/li&gt;
&lt;li&gt;定义明确的指针操作，比如：p+1。&lt;/li&gt;
&lt;li&gt;定义明确的指针转换，比如：static_cast&amp;lt;void*&amp;gt;(p)。&lt;/li&gt;
&lt;li&gt;指针和整型之间的reinterpret_cast，比如：reinterpret_cast&amp;lt;intptr_t&amp;gt;(p)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意：intptr_t是C++11中一个可选择实现的类型，其长度等于平台上指针的长度（通过decltype声明）。&lt;/p&gt;
&lt;p&gt;我们可以再看看这段代码：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main()
{
	int *p = new int;
	int *q = (int*)(reinterpret_cast&amp;lt;long long&amp;gt;(p)^2012);   //q隐藏了p

	//做一些其他工作，垃圾回收器可能已经回收了p指向的对象
	q = (int*)(reinterpret_cast&amp;lt;long long&amp;gt;(q)^2012);   //这里的 q == p
	*q = 10;

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;reinterpret_cast&lt;long long&gt;(p)是合法的安全派生操作，而转换后的指针再进行异或操作：reinterpret_cast&lt;long long&gt;(p)^2012之后，指针就不再是安全派生的了，这是因为异或操作不是一个安全派生操作。同理，reinterpret_cast&lt;long long&gt;(q)^2012也不是安全派生指针。因此，根据定义，在使用内存回收器的情况下，*q = 10 的行为是不确定的，如果程序在此处发生错误也是合理的。&lt;/p&gt;
&lt;p&gt;在C++11 的规则中，最小垃圾回收支持是基于安全派生指针这个概念的。程序员可以通过 get_pointer_safety 函数查询来确认编译器是否支持这个特性。get_pointer_safety 的原型如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pointer_safety get_pointer_safety() noexcept&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;其返回一个 pointer_safety 类型的值。如果该值为 pointer_safety::strict，则表明编译器支持最小垃圾回收及安全派生指针等相关概念，如果该值为 pointer_safety::relax 或是 pointer_safety::preferred，则表明编译器并不支持，基本上跟没有垃圾回收的C 和C++98 一样。不过按照一些解释，pointer_safety::preferred 和 pointer_safety::relax 也略有不同，前者垃圾回收器可能被用作一些辅助功能，如内存泄漏检测或检测对象是否被一个错误的指针解引用。&lt;/p&gt;
&lt;p&gt;此外，如果程序员代码中出现了指针不安全使用的状况，C++11 允许程序员通过一些API 来通知垃圾回收器不得回收该内存。C++11 的最小垃圾回收支持使用了垃圾回收的术语，即需声明该内存为“可到达”的。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void declare_reachable(void* p);
template &amp;lt;class T&amp;gt; T *undeclare_reachable(T *p) noexcept;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;declare_reachable() 显式地通知垃圾回收器某一个对象应被认为可达的，即使它的所有指针都对回收器不可见。undeclare_reachable()则可以取消这种可达声明。如下例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
using namespace std;

int main()
{
	int *p = new int;
	declare_reachable(p);   //在p被隐藏之前声明为可达的
	int *q = (int*)((long long)p^2012);
	//解除可达声明
	q = undeclare_reachable&amp;lt;int&amp;gt;((int*)((long long)q^2012));
	*q = 10;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这可能是一个能够运行的例子。这里，我们在p指针被不安全派生（隐藏）之前使用 declare_reachable  声明其是可达的。这样一来，它会被垃圾回收器忽略而不会被回收。而在我们通过可逆的异或运算使得q指针指向p所指对象时，我们则使用了 undeclare_reachable 来取消可达声明。注意 undeclare_reachable 不是通知垃圾回收器p所指对象已经可以回收了。实际上，declare_reachable 和 undeclare_reachable 只是确立了一个代码范围，即在两者之间的代码运行中，p所指对象不会被垃圾回收器所回收。&lt;/p&gt;
&lt;p&gt;这里可能有的读者会注意到一个细节，declare_reachable 只需要传入一个简单的 void* 指针，但 undeclare_reachable 却被设计为一个函数模版。这是一个极不对称的设计。但事实上 undeclare_reachable 使用模版的主要目的是为了返回合适类型以供程序使用。而垃圾回收器本来就知道指针所指向的内存的大小，因此 declare_reachable 传入 void* 指针就已经足够了。&lt;/p&gt;
&lt;p&gt;有的时候程序员会选择在一大片连续的堆内存上进行指针式操作，为了让垃圾回收器不关心该区域，也可以使用 declare_no_pointers 及 undeclare_no_pointers 函数来告诉垃圾回收器该内存区域不存在有效的指针。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void declare_no_pointers(char *p, size_t n) noexcept;
void undeclare_no_pointers(char *p, size_t n) noexcept;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;其使用方式与 declare_reachable 及 undeclare_reachable 类似，不过指定的是从p开始的连续n的内存。&lt;/p&gt;
&lt;h1 id=&#34;垃圾回收的兼容性&#34;&gt;垃圾回收的兼容性&lt;/h1&gt;
&lt;p&gt;尽管在设计 C++11标准 时想尽可能保证向后兼容，但是对于垃圾回收来说，破坏向后兼容是不可避免的。通常情况下，如果我们想要程序使用垃圾回收，或者可靠的内存泄漏检测，我们就必须做出必要的假设来保证垃圾回收能工作。而为此，我们必须限制指针的使用或者使用declare_reachable/undeclare_reachable、declare_no_pointer/undeclare_no_pointer来让一些不安全的指针使用免于垃圾回收器的检查。因此想让老的代码毫不费力的使用垃圾回收，现实情况下对大多数代码还是不可能的。&lt;/p&gt;
&lt;p&gt;此外，C++11标准中对指针的垃圾回收支持仅限于系统提供的 new 操作符分配的内存，而malloc分配的内存则会被认为总是可达的，即无论何时垃圾回收器都不予回收。因此使用malloc等的较老代码的堆内存还是必须有程序员自己控制。&lt;/p&gt;
&lt;p&gt;不知道现在有哪些编译添加了对垃圾回收特性的支持，即使是所谓的“最小垃圾回收”。标准的发展以及垃圾回收在C/C++中的实现可能还需要一定的时间。不过有了最小支持，用户可能在新代码中会注意指针的使用，并对形如指针隐藏的状况使用合适的函数来对被隐藏指针的堆对象进行保护。按照C++的设计，显式的delete使用与垃圾回收并不会形成冲突。如果程序员选择这么做的话，就应该能够保证最大的代码向前兼容性。在未来某个时刻C++垃圾回收支持完成的时候，代码可以直接享受其带来的益处。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43;11 智能指针学习</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%AD%A6%E4%B9%A0/</link>
      <pubDate>Wed, 09 Oct 2019 13:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E5%AD%A6%E4%B9%A0/</guid>
      
        <description>&lt;p&gt;C#、Java、python和go等语言中都有垃圾自动回收机制，在对象失去引用的时候自动回收，而且基本上没有指针的概念，而C++语言不一样，C++充分信任程序员，让程序员自己去分配和管理堆内存，如果管理的不好，就会很容易的发生内存泄漏问题，而C++11增加了智能指针(Smart Pointer)。主要分为shared_ptr、unique_ptr和weak_ptr三种，使用时需要引用头文件&lt;memory&gt;。c++98中还有auto_ptr，基本被淘汰了，不推荐使用。而c++11中shared_ptr和weak_ptr都是参考的boost库中实现的。&lt;/p&gt;
&lt;h1 id=&#34;原始指针&#34;&gt;原始指针&lt;/h1&gt;
&lt;p&gt;c语言中最常使用的是malloc()函数分配内存，free()函数释放内存，而c++中对应的是new、delete关键字。malloc()只是分配了内存，而new则更进一步，不仅分配了内存，还调用了构造函数进行初始化。使用示例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main()
{
    // malloc返回值是 void*
    int* argC = (int*)malloc(sizeof(int));
    free(argC);

    char*c = (char*)malloc(100);
    free(c);

    char *age = new int(25); //做了两件事情 1.分配内存 2.初始化
    int* height = new int(160);

    delete height;
    delete age;
    
    char* arr = new int[100];
    delete[] arr;
    /*delete数组需要使用delete[]，事实上，c++原始支持的数据结构组成的
    数组不需要[]也可以，但 自定义的数据类型组成的数组必须使用delete[]*/
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;new和delete必须成对出现，有时候是不小心忘记了delete，有时候则是很难判断在这个地方自己是不是该delete，这个和资源的生命周期有关，这个资源是属于我这个类管理的还是由另外一个类管理的，如果是我管理的，就由我来delete，由别人管理的就由别人delete，我就算析构了也不影响该资源的生命周期。例如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 情况1： 需要自己delete
const char* getName() {
    char *valueGroup = new char[1000];
    // do something
    return valueGroup;
}

// 情况2： 不需要自己delete
const char* getName2() {
    static char valueGroup[1000];
    // do something
    return valueGroup;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;只通过函数签名来看，这两个函数没有什么区别，但是由于实现的不同，有时候需要自己管理内存，有时候不需要，这个时候就需要看文档说明了。这就是使用一个&amp;quot;裸&amp;quot;指针不好的地方。&lt;/p&gt;
&lt;p&gt;一点改进是，如果需要自己管理内存的话，最好显示的将自己的资源传递进去，这样的话，就能知道是该资源确实应该由自己来管理。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;char *getName(char* v, size_t bufferSize) {
    //do something
    return v;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;上面还是小问题，自己小心一点，再仔细看看文档，还是有机会避免这些情况的。但是在c++引入异常的概念之后，程序的控制流就发生了根本性的改变，在写了delete的时候还是有可能发生内存泄漏。如下例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void badThing(){
    throw 1;// 抛出一个异常
}

void test() {
    char* a = new char[1000];

    badThing();
    // do something
    delete[] a;
}

int main() {
    try {
        test();
    }
    catch (int i){
        cout &amp;lt;&amp;lt; &amp;#34;error happened &amp;#34; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; endl;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;上面的new和delete是成对出现的，但是程序在中间的时候抛出了异常，由于没有立即捕获，程序从这里退出了，并没有执行到delete，内存泄漏还是发生了。&lt;/p&gt;
&lt;p&gt;C++中的构造函数和析构函数十分强大，可以使用构造和析构解决这种问题，比如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class SafeIntPointer {
public:
    explicit SafeIntPointer(int v) : m_value(new int(v)) { }
    ~SafeIntPointer() {
        delete m_value;
        cout &amp;lt;&amp;lt; &amp;#34;~SafeIntPointer&amp;#34; &amp;lt;&amp;lt; endl;
    }
    int get() { return *m_value; }
private:
    int* m_value;
};

void badThing(){
    throw 1;// 抛出一个异常
}

void test() {
    SafeIntPointer a(5);

    badThing();
}

int main() {
    try {
        test();
    }
    catch (int i){
        cout &amp;lt;&amp;lt; &amp;#34;error happened &amp;#34; &amp;lt;&amp;lt; i &amp;lt;&amp;lt; endl;
    }
}

// 结果
// ~SafeIntPointer
// error happened 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;可以看到，就算发生了异常，也能够保证析构函数成功执行！这里的例子是这个资源只有一个人使用，我不用了就将它释放掉。但还有种情况，一份资源被很多人共同使用，要等到所有人都不再使用的时候才能释放掉，对于这种问题，就需要对上面的SafeIntPointer增加一个引用计数，如下：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class SafeIntPointer {
public:
    explicit SafeIntPointer(int v) : m_value(new int(v)), m_used(new int(1)) { }
    ~SafeIntPointer() {
        cout &amp;lt;&amp;lt; &amp;#34;~SafeIntPointer&amp;#34; &amp;lt;&amp;lt; endl;
        (*m_used) --; //引用计数减1
        if(*m_used &amp;lt;= 0){
            delete m_used;
            delete m_value;
            cout &amp;lt;&amp;lt; &amp;#34;real delete resources&amp;#34; &amp;lt;&amp;lt; endl;
        }

    }
    SafeIntPointer(const SafeIntPointer&amp;amp; other) {
        m_used = other.m_used;
        m_value = other.m_value;
        (*m_used)++; //引用计数加1
    }
    SafeIntPointer&amp;amp; operator= (const SafeIntPointer&amp;amp; other) {
        if (this == &amp;amp;other) // 避免自我赋值!!
           return *this;

        m_used = other.m_used;
        m_value = other.m_value;
        (*m_used)++; //引用计数加1
        return *this;
    }

    int get() { return *m_value; }
    int getRefCount() {
        return *m_used;
    }

private:
    int* m_used;
    int* m_value;
};

int main() {
    SafeIntPointer a(5);
    cout &amp;lt;&amp;lt; &amp;#34;ref count = &amp;#34; &amp;lt;&amp;lt; a.getRefCount() &amp;lt;&amp;lt; endl;
    SafeIntPointer b = a;
    cout &amp;lt;&amp;lt; &amp;#34;ref count = &amp;#34; &amp;lt;&amp;lt; a.getRefCount() &amp;lt;&amp;lt; endl;
    SafeIntPointer c = b;
    cout &amp;lt;&amp;lt; &amp;#34;ref count = &amp;#34; &amp;lt;&amp;lt; a.getRefCount() &amp;lt;&amp;lt; endl;
}

/*
ref count = 1
ref count = 2
ref count = 3
~SafeIntPointer
~SafeIntPointer
~SafeIntPointer
real delete resources
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;可以看到每一次赋值，引用计数都加一，最后每次析构一次后引用计数减一，知道引用计数为0，才真正释放资源。要写出一个正确的管理资源的包装类还是蛮难的，比如上面那个上面例子就不是线程安全的，只能属于一个玩具，在实际工程中简直没法用。而到了C++11，终于提供了一个共享的智能指针解决这个问题。&lt;/p&gt;
&lt;h1 id=&#34;shared_ptr共享的智能指针&#34;&gt;shared_ptr共享的智能指针&lt;/h1&gt;
&lt;h2 id=&#34;shared_ptr的基本使用&#34;&gt;shared_ptr的基本使用&lt;/h2&gt;
&lt;p&gt;shared_ptr的基本使用很简单，看几个例子就明白了：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye&amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 取个别名 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;

void print(Object* obj) {
    std::cout &amp;lt;&amp;lt; &amp;#34;in print(Object* obj) :  &amp;#34; &amp;lt;&amp;lt; std::endl;
//    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl;
}

void print(Object obj) {
    std::cout &amp;lt;&amp;lt; &amp;#34;in print(Object obj) :  &amp;#34; &amp;lt;&amp;lt; std::endl;
//    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl;
}

void print(ObjectPtr obj) {
    std::cout &amp;lt;&amp;lt; &amp;#34;in print(ObjectPtr obj) :  &amp;#34;;
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl;
}

void printRef(const ObjectPtr&amp;amp; obj) {
    std::cout &amp;lt;&amp;lt; &amp;#34;in print(const ObjectPtr&amp;amp; obj) :  &amp;#34;;
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl;
}

int main() {
    // 创建一个智能指针，管理的资源就是 new出来的Object(1)
    //ObjectPtr obj=new Object(1); //不能将一个原始指针直接赋值给一个智能指针
    ObjectPtr obj(new Object(1)); // 正确
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 1
    ObjectPtr obj2(obj);
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 2
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj2.use_count() &amp;lt;&amp;lt; std::endl; // 2 obj和obj2管理的资源是一样的
    ObjectPtr obj3 = obj2;
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 3

    obj2.reset(); // obj2不再管理之前的资源，资源的引用计数会减1
    // 或者可以写成 obj2 = nullptr;
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 2

    ObjectPtr obj4; //obj4 开始没有管理资源
    // 将管理的资源 相互交换
    // 交换后 obj3没有再管理资源， obj4管理obj3之前管理的资源
    // 或者写成 std::swap(obj3, obj4);
    obj3.swap(obj4);
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 还是 2

    // 还可以从智能指针中获取原始指针
    auto p = obj.get(); // auto = Object*
    // 需要判断这个obj是否确实管理着资源， 可能为 nullptr
    if( p ) {
        std::cout &amp;lt;&amp;lt; &amp;#34;p-&amp;gt;id() is &amp;#34; &amp;lt;&amp;lt; p-&amp;gt;id() &amp;lt;&amp;lt; std::endl;
        std::cout &amp;lt;&amp;lt; &amp;#34;(*p).id() is &amp;#34; &amp;lt;&amp;lt; (*p).id() &amp;lt;&amp;lt; std::endl;
    }
    // 智能指针也可以像普通指针一样使用
    // 重载了 operator bool
    if( obj ) {
        std::cout &amp;lt;&amp;lt; &amp;#34;obj-&amp;gt;id() is &amp;#34; &amp;lt;&amp;lt; obj-&amp;gt;id() &amp;lt;&amp;lt; std::endl; // 重载了operator -&amp;gt;
        std::cout &amp;lt;&amp;lt; &amp;#34;(*obj).id() is &amp;#34; &amp;lt;&amp;lt; (*obj).id() &amp;lt;&amp;lt; std::endl;// 重载了operator *
    }

    // obj.use_count()可以判断当前有多少智能指针在管理资源
    // 如果判断是不是只有一个人在管理这个资源， 用unique()函数更加高效
    // unique() 等价于 obj.use_count() == 1
    obj4 = nullptr; // obj4不在管理，这个时候的引用计数变成了 1
    std::cout &amp;lt;&amp;lt; &amp;#34;ref count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 1

    if( obj.unique() )
        std::cout &amp;lt;&amp;lt; &amp;#34;only one hold ptr &amp;#34;&amp;lt;&amp;lt;  std::endl;
    else
        std::cout &amp;lt;&amp;lt; &amp;#34;not noly one hold ptr&amp;#34; &amp;lt;&amp;lt; std::endl; //其实也有可能没有人再管理

    // 将智能指针当作参数传递给函数时
    // 如果是值传递， 智能指针发生一次拷贝，
    // 在函数内部时智能指针的引用计数会 + 1
    // 离开函数作用域时， 智能指针会析构， 引用计数会 - 1
    print(obj);
    // 如果传递的是引用， 对引用计数没影响 而且工作量比较小(没有拷贝)
    // 推荐使用引用方式传递， 传值的方式也有用处，比如多线程时
    printRef(obj);

    // 还可以不传递智能指针， 传递原生类型
    print(*obj); //传Object类型的时候，离开函数的时候参数obj会发生一次析构
    print(obj.get());

}
/*
init obj
ref count is 1
ref count is 2
ref count is 2
ref count is 3
ref count is 2
ref count is 2
p-&amp;gt;id() is 1
(*p).id() is 1
obj-&amp;gt;id() is 1
(*obj).id() is 1
ref count is 1
only one hold ptr
in print(ObjectPtr obj) :  ref count is 2
in print(const ObjectPtr&amp;amp; obj) :  ref count is 1
in print(Object obj) :
bye bye1    //这个是在调用print(Object obj)时，局部变量析构时打印的
in print(Object* obj) :
bye bye1 //这个是在资源在没有人引用的时候，执行析构函数产生的
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;给shared_ptr指定删除器&#34;&gt;给shared_ptr指定删除器&lt;/h2&gt;
&lt;p&gt;大部分用法都基本上在上面的例子中体现出来了，当没有人引用这个资源的时候，智能指针的默认行为是调用 delete销毁这个资源，而我们也可以人为指定这个步骤，因为有些资源不一定是new出来的，所以不应该使用默认的delete行为，还有一个情况是，在用智能指针管理动态数组的时候，需要自己指定删除器函数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye&amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;

void deleterOfObject(Object* obj) {
    if ( obj ) {
        std:: cout &amp;lt;&amp;lt; &amp;#34;delete obj &amp;#34; &amp;lt;&amp;lt; obj-&amp;gt;id() &amp;lt;&amp;lt; std::endl;
        delete obj;
    }
}

void useDeleter() {
    //指定删除动作  使用外面定义的函数
    ObjectPtr obj(new Object(2), deleterOfObject);
    ObjectPtr obj2 = obj;
    // obj 和 obj2 会在离开这个函数的时候析构，于是，就调用了 deleterOfObject

    //管理数组  使用匿名函数当作删除函数
    std::shared_ptr&amp;lt;int&amp;gt; p(new int[10], [](int* p){
        std::cout &amp;lt;&amp;lt; &amp;#34;delete[] p&amp;#34; &amp;lt;&amp;lt; std::endl;
        delete[] p; //需要使用delete[]
    });

    // vector&amp;lt;&amp;gt; 没必要使用智能指针， 不用new 和 delete... 内部已经管理了
}

int main() {
    useDeleter();
}

/*
init obj
delete[] p  //注意析构和构造的顺序是相反的
delete obj 2
bye bye2
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;shared_ptr主要就是利用变量出了作用域之后析构函数一定能被调用到，哪怕是出现了异常。&lt;/p&gt;
&lt;h2 id=&#34;不要用一个原始的指针初始化多个shared_ptr&#34;&gt;不要用一个原始的指针初始化多个shared_ptr&lt;/h2&gt;
&lt;p&gt;例如下面的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye &amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;

int main() {
    Object *obj = new Object(2);
    ObjectPtr p1(obj);
    ObjectPtr p2(obj);
    std::cout &amp;lt;&amp;lt; p1.use_count() &amp;lt;&amp;lt; &amp;#34;  &amp;#34; &amp;lt;&amp;lt; p2.use_count() &amp;lt;&amp;lt; std::endl;
    std::cout &amp;lt;&amp;lt; &amp;#34;finished&amp;#34; &amp;lt;&amp;lt; std::endl;
}

/*
init obj
1  1
finished
bye bye 2
bye bye 203200  //m_id成为了随机数
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;可以发现，虽然是用的同一个指针初始化了两个shared_ptr，但是这两个shared_ptr却没有关联，它们的引用计数都是1，然后问题就发生了，p2先析构，于是引用计数变为了0，就开始删除它管理的资源obj，于是obj就被析构了，这是还算正常，接着析构p1，引用计算也变成了0，它也开始删除自己管理的资源obj，相当于多次delete了同一个对象，m_id成为了随机数，这还算好的情况，如果Object内部还有指针，或者obj的地址被其他变量占据了，delete掉这块内存就会发生严重的错误！而且不好发现原因。&lt;/p&gt;
&lt;h2 id=&#34;将this指针正确的传递给shared_ptr&#34;&gt;将this指针正确的传递给shared_ptr&lt;/h2&gt;
&lt;p&gt;其实就是由于上面的原因，我们不可能传递this指针给shared_ptr，因为用同一个指针初始化两个shared_ptr，它们之间并没有关联，如下面的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Y
{
public:
    std::shared_ptr&amp;lt;Y&amp;gt; f(){
        return std::shared_ptr&amp;lt;Y&amp;gt;(this);
    }
};

int main()
{
    std::shared_ptr&amp;lt;Y&amp;gt; p1(new Y());
    std::shared_ptr&amp;lt;Y&amp;gt; p2 = p1-&amp;gt;f(); // p2是由this构造共享智能指针
    std::cout &amp;lt;&amp;lt; p1.use_count() &amp;lt;&amp;lt; &amp;#34;  &amp;#34; &amp;lt;&amp;lt; p2.use_count() &amp;lt;&amp;lt; std::endl; // 1  1
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;从上面的例子可以看出，返回由this构造的shared_ptr并没有用，返回还可能造成严重错误(由于可能多次delete)！解决办法是继承std::enable_shared_from_this&lt;Y&gt;，然后使用shared_from_this()构造shared_ptr。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Y : public std::enable_shared_from_this&amp;lt;Y&amp;gt;
{
public:
    std::shared_ptr&amp;lt;Y&amp;gt; f(){
        return shared_from_this();
    }
};

int main()
{
    std::shared_ptr&amp;lt;Y&amp;gt; p1(new Y());
    std::shared_ptr&amp;lt;Y&amp;gt; p2 = p1-&amp;gt;f(); // p2是由p1的thiss构造共享智能指针
    std::cout &amp;lt;&amp;lt; p1.use_count() &amp;lt;&amp;lt; &amp;#34; &amp;#34; &amp;lt;&amp;lt; p2.use_count() &amp;lt;&amp;lt; std::endl; // 2  2

    std::shared_ptr&amp;lt;Y&amp;gt; p3(new Y());
    std::shared_ptr&amp;lt;Y&amp;gt; p4 = p3-&amp;gt;f(); // p4是由p3的this构造的构造共享智能指针
    std::cout &amp;lt;&amp;lt; p1.use_count() &amp;lt;&amp;lt; &amp;#34; &amp;#34; &amp;lt;&amp;lt; p2.use_count() &amp;lt;&amp;lt; &amp;#34; &amp;#34;
              &amp;lt;&amp;lt; p3.use_count() &amp;lt;&amp;lt; &amp;#34; &amp;#34; &amp;lt;&amp;lt; p4.use_count() &amp;lt;&amp;lt; std::endl; // 2 2 2 2
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;可以发现引用计数确实增加了。并且由p1得到的shared_from_this()增加的就是p1的引用计数，p3得到的shared_from_this()增加的就是p3的引用计数，这和this的含义是一样的。所以我们在类内部需要传递this指针给shared_ptr时，需要继承自std::enable_shared_from_this&lt;T&gt;，并且使用shared_from_this()替代this。而shared_from_this()就是借助了weak_ptr。原理在后面再讲。&lt;/p&gt;
&lt;h2 id=&#34;shared_ptr的正确构造方式&#34;&gt;shared_ptr的正确构造方式&lt;/h2&gt;
&lt;p&gt;其实上面使用的智能指针构造方式有一点点问题，ObjectPtr obj(new Object(1));这一个语句其实调用了两次new，一次是new Object(1)，另一次是构造内部的引用计数变量的时候，那有没有办法只掉用一次new呢，答案就是使用make_shared&lt;T&gt;()模板函数，它将资源和引用计数变量一起new出来，例如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;cassert&amp;gt;
class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye &amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;

int main() {
    // 和 ObjectPtr obj(new Object(2)); 一样
    // 但是只调用了一次new
    ObjectPtr obj = std::make_shared&amp;lt;Object&amp;gt;(2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;然而，这个函数也有失效的时候，如果管理的资源对象的构造函数是私有的他就没有办法了。&lt;/p&gt;
&lt;h1 id=&#34;weak_ptr弱引用的智能指针&#34;&gt;weak_ptr弱引用的智能指针&lt;/h1&gt;
&lt;h2 id=&#34;循环引用问题的引出&#34;&gt;循环引用问题的引出&lt;/h2&gt;
&lt;p&gt;在有些情况下，shared_ptr也会遇见很尴尬、不能处理的情况，那就是循环引用，看下面的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Parent;  //Parent类的前置声明

typedef std::shared_ptr&amp;lt;Parent&amp;gt; ParentPtr;

class Child {
public:
    ParentPtr father;
    ~Child() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye child&amp;#34; &amp;lt;&amp;lt; std::endl;
    }
};

typedef std::shared_ptr&amp;lt;Child&amp;gt; ChildPtr;

class Parent {
public:
    ChildPtr son;
    ~Parent() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye parent&amp;#34; &amp;lt;&amp;lt; std::endl;
    }
};

void testParentAndChild() {
    ParentPtr p(new Parent());  // 1  资源A
    ChildPtr c(new Child());  // 2   资源B
    p-&amp;gt;son = c;     // 3      c.use_count() == 2 and p.use_count() == 1 
    c-&amp;gt;father = p;    //  4   c.use_count() == 2 and p.use_count() == 2
}

int main() {
    testParentAndChild();
    std::cout &amp;lt;&amp;lt; &amp;#34;finished&amp;#34; &amp;lt;&amp;lt; std::endl;
}

/*
// 没有调用Parent 和 Child 的析构函数
finished
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;很惊讶的发现，用了shared_ptr管理资源，资源最后还是没有释放！内存泄漏还是发生了。&lt;/p&gt;
&lt;p&gt;分析：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行编号1的语句时，构造了一个共享智能指针p，称呼它管理的资源叫做资源A（new Parent()产生的对象）吧， 语句2构造了一个共享智能指针c，管理资源B(new Child()产生的对象)，此时资源A和B的引用计数都是1，因为只有1个智能指针管理它们，执行到了语句3的时候，是一个智能指针的赋值操作，资源B的引用计数变为了2，同理，执行完语句4，资源A的引用计数也变成了2。&lt;/li&gt;
&lt;li&gt;出了函数作用域时，由于析构和构造的顺序是相反的，会先析构共享智能指针c，资源B的引用计数就变成了1；接下来继续析构共享智能指针p，资源A的引用计数也变成了1。由于资源A和B的引用计数都不为1，说明还有共享智能指针在使用着它们，所以不会调用资源的析构函数！&lt;/li&gt;
&lt;li&gt;这种情况就是个死循环，如果资源A的引用计数想变成0，则必须资源B先析构掉(从而析构掉内部管理资源A的共享智能指针)，资源B的引用计数想变为0，又得依赖资源A的析构，这样就陷入了一个死循环。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要想解决这个问题，只能引入新的智能指针weak_ptr，顾名思义，弱引用，也就是不增加引用计数，它不管理shared_ptr内部管理的指针，他只是起一个监视的作用。它监视的不是shared_ptr本身，而是shared_ptr管理的资源！！！weak_ptr没有重载操作符*和-&amp;gt;，它不能直接操作资源，但是它可以获取所监视的shared_ptr(如果资源还没有被析构的话)。&lt;/p&gt;
&lt;h2 id=&#34;weak_ptr的基本用法&#34;&gt;weak_ptr的基本用法&lt;/h2&gt;
&lt;p&gt;weak_ptr使用示例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye&amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 取个别名 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;

void sharedPtrWithWeakPtr() {
    ObjectPtr obj(new Object(1));
    typedef std::weak_ptr&amp;lt;Object&amp;gt; WeakObjectPtr;
    WeakObjectPtr weakObj(obj); //使用共享指针 初始化 弱引用指针
    //weakObj 仅仅是一个监听者，不会增加引用计数
    std::cout &amp;lt;&amp;lt; &amp;#34;obj use count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 1
    {
        // lock() 方法返回一个 它对应的共享指针
        // 下面这句话的结果是 2, 而不是1，
        // 说明weakObj.lock() 内部也得到了一个新的共享指针，所以引用计数+1
        // 在执行完这句话后就析构掉了，引用计数-1
        std::cout &amp;lt;&amp;lt; &amp;#34;weakObj.lock().use_count() is &amp;#34; &amp;lt;&amp;lt; weakObj.lock().use_count() &amp;lt;&amp;lt; std::endl; // 2
        // 由于发生了一次 赋值 ，所以 引用次次数 +1
        // auto === ObjectPtr
        auto p = weakObj.lock(); //如果weakObj监视的资源存在， p就存在
        std::cout &amp;lt;&amp;lt; &amp;#34;obj use count is &amp;#34; &amp;lt;&amp;lt; obj.use_count() &amp;lt;&amp;lt; std::endl; // 2
        if ( p ) {
            // do what you want to do
        } else {

        }
    }

    // 共享指针不再管理任何资源的时候，weakObj的行为
    // 注意：如果在obj.reset前，还存在共享指针管理它的资源
    // 如 ：ObjectPtr obj1(obj); weakObj.lock();还是有效的
    obj.reset();
    {
        auto p = weakObj.lock();
        if( p ) {
            //不应该到这里来
            std::cout &amp;lt;&amp;lt; &amp;#34;weakObj is not null 1&amp;#34; &amp;lt;&amp;lt; std::endl;
        } else {
            std::cout &amp;lt;&amp;lt; &amp;#34;weakObj is null 1&amp;#34; &amp;lt;&amp;lt; std::endl;
        }
    }

    // 共享指针管理其他资源的时候，weakObj的行为
    // 注意：weak_ptr.lock()
    // 只有在 存在某一个shared_ptr管理的资源和该weak_ptr一样 的时候才有效果！
    obj.reset(new Object(2));
    {
        auto p = weakObj.lock();
        if( p ) {
            //不应该到这里来
            std::cout &amp;lt;&amp;lt; &amp;#34;weakObj is not null 2&amp;#34; &amp;lt;&amp;lt; std::endl;
        } else {
            std::cout &amp;lt;&amp;lt; &amp;#34;weakObj is null 2&amp;#34; &amp;lt;&amp;lt; std::endl;
        }
    }

    weakObj = obj; // 重新监视  obj
    // 用weakObj 判断管理的资源是否过期
    if(weakObj.expired()) {

    } else {

    }
}

int main() {
    sharedPtrWithWeakPtr();
    std::cout &amp;lt;&amp;lt; &amp;#34;finished&amp;#34; &amp;lt;&amp;lt; std::endl;
}
/*
init obj
obj use count is 1
weakObj.lock().use_count() is 2
obj use count is 2
bye bye1
weakObj is null 1
init obj
weakObj is null 2
bye bye2
finished
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;由上面的例子可以看出，weak_ptr和初始化它的share_ptr没有关系，而是和share_ptr管理的资源有关系。假如WeakObjectPtr weakObj(obj);，如果obj.reset()，weakObj.lock()的返回值就是空，如果obj.reset(new Object(2));，替换了管理对象，则一起的资源就被析构了，weakObj.lock()的返回值同样为空，同样可以推断，如果除了obj以外还有其他共享智能指针一起管理资源，也就是说obj.reset()的时候资源不会被析构，weakObj.lock();的返回值就不会为空了。不明白的话自己写个简单的测试用例就知道了，如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void sharedPtrWithWeakPtr() {
    ObjectPtr obj(new Object(1));
    typedef std::weak_ptr&amp;lt;Object&amp;gt; WeakObjectPtr;
    WeakObjectPtr weakObj(obj); //使用共享指针 初始化 弱引用指针
    ObjectPtr obj1 = obj; //注释掉这句话打印的就是error， 加上这句话打印的就是ok
    obj.reset();
    auto p = weakObj.lock();
    if( p ) {
        std::cout &amp;lt;&amp;lt; &amp;#34;ok&amp;#34; &amp;lt;&amp;lt; std::endl;
    } else {
        std::cout &amp;lt;&amp;lt; &amp;#34;error&amp;#34; &amp;lt;&amp;lt; std::endl;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;weak_ptr解决循环引用&#34;&gt;weak_ptr解决循环引用&lt;/h2&gt;
&lt;p&gt;用weak_ptr可以解决上面的循环引用问题，将Child内部的parent指针换成weak_ptr管理：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Parent;  //Parent类的前置声明

typedef std::shared_ptr&amp;lt;Parent&amp;gt; ParentPtr;
typedef std::weak_ptr&amp;lt;Parent&amp;gt; WeakParentPtr;
class Child {
public:
    WeakParentPtr father;
    ~Child() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye child&amp;#34; &amp;lt;&amp;lt; std::endl;
    }
};

typedef std::shared_ptr&amp;lt;Child&amp;gt; ChildPtr;
//typedef std::weak_ptr&amp;lt;Child&amp;gt; WeakChildPtr;
class Parent {
public:
    //WeakChildPtr son;
    ChildPtr son;
    ~Parent() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye parent&amp;#34; &amp;lt;&amp;lt; std::endl;
    }
};

void testParentAndChild() {
    ParentPtr p(new Parent());  // 1  资源A
    ChildPtr c(new Child());  // 2   资源B
    p-&amp;gt;son = c;     // 3      c.use_count() == 2 and p.use_count() == 1
    c-&amp;gt;father = p;    //  4   c.use_count() == 2 and p.use_count() == 1
}

int main() {
    testParentAndChild();
    std::cout &amp;lt;&amp;lt; &amp;#34;finished&amp;#34; &amp;lt;&amp;lt; std::endl;
}
/*
bye parent //成功调用析构函数
bye child
finished
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;修改为弱引用后，成功的释放了资源，只要将任意一个shared_ptr换成weak_ptr，就可以解决问题。当然，也可以两个都换成weak_ptr，至于这三种方案谁更好，就暂时不清楚了。&lt;/p&gt;
&lt;h2 id=&#34;shared_from_this实现原理&#34;&gt;shared_from_this()实现原理&lt;/h2&gt;
&lt;p&gt;std::enable_shared_from_this&lt;T&gt;模板类中有一个weak_ptr，这个weak_ptr用来观测this智能指针，调用shared_from_this()函数的时候，会在内部调用weak_ptr的lock()方法，将所观测的shared_ptr返回。这个设计要依赖于当前对象已经有了一个相应的控制块。为此，必须已经存在一个指向当前对象的shared_ptr（比如在调用过shared_from_this()成员函数之外已经有了一个）。假如没有这样shared_ptr存在，那么shared_from_this()会抛异常。 那么这个weak_ptr在什么时候赋值的呢？答案就是在外部第一次构造shared_ptr的时候(如之前的std::shared_ptr&lt;Y&gt; p1(new Y());)，对std::enable_shared_from_this&lt;T&gt;进行了赋值（具体实现有点复杂，还不太懂。。），这也就为什么在调用shared_from_this()时，必须存在一个指向当前对象的shared_ptr的原因了。由于这个原因，不要在构造函数中调用shared_from_this()，如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;

class Y : public std::enable_shared_from_this&amp;lt;Y&amp;gt;
{
public:
    Y() {
        std::shared_ptr&amp;lt;Y&amp;gt; p = shared_from_this();
    }

    std::shared_ptr&amp;lt;Y&amp;gt; f(){
        return shared_from_this();
    }
};

int main()
{
    std::shared_ptr&amp;lt;Y&amp;gt; p1(new Y());

}
// 会抛出异常！
/*
terminate called after throwing an instance of &amp;#39;std::bad_weak_ptr&amp;#39;
  what():  bad_weak_ptr
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;unique_ptr独占的智能指针&#34;&gt;unique_ptr独占的智能指针&lt;/h1&gt;
&lt;p&gt;unique_ptr相对于其他两个智能指针更加简单，它和shared_ptr使用差不多，但是功能更为单一，它是一个独占型的智能指针，不允许其他的智能指针共享其内部的指针，更像原生的指针（但更为安全，能够自己释放内存）。不允许赋值和拷贝操作，只能够移动。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;cassert&amp;gt;
class Object {
public:
    Object(int id) : m_id(id) {
        std::cout &amp;lt;&amp;lt; &amp;#34;init obj &amp;#34; &amp;lt;&amp;lt; std::endl;
    }
    ~Object() {
        std::cout &amp;lt;&amp;lt; &amp;#34;bye bye &amp;#34; &amp;lt;&amp;lt; m_id &amp;lt;&amp;lt; std::endl;
    }
    int id() const {
        return m_id;
    }
private:
      int m_id;
};

// 让写起来更方便
typedef std::shared_ptr&amp;lt;Object&amp;gt; ObjectPtr;
typedef std::unique_ptr&amp;lt;Object&amp;gt; UniqueObjectPtr;

// 只能传递引用 不能传值
void print(const UniqueObjectPtr&amp;amp; obj) {
    std::cout &amp;lt;&amp;lt; obj-&amp;gt;id() &amp;lt;&amp;lt; std::endl;
}

int main() {
    UniqueObjectPtr obj(new Object(1));
//    UniqueObjectPtr obj1 = obj; // 编译错误，不允许赋值
    // 获取原生指针
    auto p = obj.get();
    if( p ) {

    } else {

    }

    // better 重载了 operator bool
    if(obj) {

    } else {

    }
    // 重载了 operator -&amp;gt; 和 operator *
    std::cout &amp;lt;&amp;lt; obj-&amp;gt;id() &amp;lt;&amp;lt; &amp;#34;  &amp;#34; &amp;lt;&amp;lt; (*obj).id() &amp;lt;&amp;lt; std::endl;
    print(obj);
    // 释放管理的指针，由其他东西处理
    p = obj.release();
//    delete p; 自己负责处理
    obj.reset(p); //析构之前负责管理的对象，重新管理 p指针
    obj.reset(); // 析构之前负责管理的对象， 不再管理任何资源
    // 允许 移动操作
    UniqueObjectPtr obj1(new Object(1));
    // obj1已经部管理任何资源 obj2开始管理obj1之前的资源
    UniqueObjectPtr obj2 = std::move(obj1);
    assert(obj1 == nullptr);
    std::cout &amp;lt;&amp;lt; obj2-&amp;gt;id() &amp;lt;&amp;lt; std::endl;

    // 将unique_ptr管理的内容给 shared_ptr
    ObjectPtr obj3(std::move(obj2));
    assert(obj2 == nullptr);

}

/*
init obj
1  1
1
bye bye 1
init obj
1
bye bye 1
*/&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;unique_ptr管理数组资源不需要指定删除器：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;std::shared_ptr&amp;lt;int&amp;gt; p(new int[10], [](int* p){
            std::cout &amp;lt;&amp;lt; &amp;#34;delete[] p&amp;#34; &amp;lt;&amp;lt; std::endl;
            delete[] p; //需要使用delete[]
        });
std::unique_ptr&amp;lt;int&amp;gt; p2(new int[10]); //不需要指定删除器&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;性能与安全的权衡&#34;&gt;性能与安全的权衡&lt;/h1&gt;
&lt;p&gt;使用智能指针虽然能够解决内存泄漏问题，但是也付出了一定的代价。以shared_ptr举例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;shared_ptr的大小是原始指针的两倍，因为它的内部有一个原始指针指向资源，同时有个指针指向引用计数。&lt;/li&gt;
&lt;li&gt;引用计数的内存必须动态分配。虽然一点可以使用make_shared()来避免，但也存在一些情况下不能够使用make_shared()。&lt;/li&gt;
&lt;li&gt;增加和减小引用计数必须是原子操作，因为可能会有读写操作在不同的线程中同时发生。比如在一个线程里有一个指向一块资源的shared_ptr可能调用了析构（因此所指向的资源的引用计数减一），同时，在另一线程里，指向相同对象的一个shared_ptr可能执行了拷贝操作（因此，引用计数加一）。原子操作一般会比非原子操作慢。但是为了线程安全，又不得不这么做，这就给单线程使用环境带来了不必要的困扰。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;智能指针主要是使用构造和析构来管理资源的。&lt;/li&gt;
&lt;li&gt;shared_ptr很好用也很难用，有两种构造方式，使用引用计数实现多人同时管理一份资源。使用this的时候要格外注意。&lt;/li&gt;
&lt;li&gt;weak_ptr可以解决shared_ptr的循环引用问题。&lt;/li&gt;
&lt;li&gt;unique_ptr最像裸指针，但更为安全，保证资源的释放，不能复制只能移动。&lt;/li&gt;
&lt;li&gt;智能指针带来了性能问题，在不同场合可以选择不同的解决方案。优先使用类的实例(如果内存足够)，其次unique_ptr，最后才是shared_ptr。&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43;11 容器相关特性</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E5%AE%B9%E5%99%A8%E7%9B%B8%E5%85%B3%E7%89%B9%E6%80%A7/</link>
      <pubDate>Tue, 08 Oct 2019 15:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E5%AE%B9%E5%99%A8%E7%9B%B8%E5%85%B3%E7%89%B9%E6%80%A7/</guid>
      
        <description>&lt;h1 id=&#34;cbegin和cend&#34;&gt;cbegin和cend&lt;/h1&gt;
&lt;p&gt;原来的begin和end返回的iterator是否是常量取决于对应的容器类型，但是有时，即使容器不是常量类型，我们也希望获得一个const_iterator，以避免不必要的修改行为。C++11新标准中提供了cbegin和cend函数，无论容器类型，都固定返回const_iterator。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; c1 = {0, 1, 2, 3, 4};
auto it1_1 = c1.begin();
auto it1_2 = c1.cbegin();
*it1_1 = 4;
//*it1_2 = 5; // wrong, const iterator&amp;#39;s value can&amp;#39;t be changed via this iterator.
cout&amp;lt;&amp;lt;*it1_1&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;*it1_2&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;标准库的begin和end&#34;&gt;标准库的begin和end&lt;/h1&gt;
&lt;p&gt;C++11新标准提供了begin和end函数，可以对普通数组使用，获得头指针和尾指针。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int c2[] = {0, 1, 2, 3, 4};
auto it2_1 = begin(c2);
auto it2_2 = end(c2);
while(it2_1 != it2_2)
{
	cout&amp;lt;&amp;lt;*it2_1&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
	it2_1++;
}
cout&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;新的赋值方式&#34;&gt;新的赋值方式&lt;/h1&gt;
&lt;p&gt;C++11允许使用一个{}包围的初始值列表来进行复制。如果等号左侧是个容器，那么怎么赋值由容器决定。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; c3;
c3 = {0, 1, 2, 3, 4};
for(auto it3_1 = c3.begin(); it3_1 != c3.end(); it3_1++)
	cout&amp;lt;&amp;lt;*it3_1&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
cout&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;initializer_list&#34;&gt;initializer_list&lt;/h1&gt;
&lt;p&gt;C++11新标准中新增了initializer_list类型，其实在之前介绍初始化的那篇文章中，使用vector v = {0, 1, 2, 3, 4}这种初始化形式时，就隐式的使用了initializer_list：每当在程序中出现一段以{}包围的字面量时，就会自动构造一个initializer_list对象。&lt;/p&gt;
&lt;p&gt;另外，initializer_list的另一个作用就在于作为函数的形参，这样的函数可以方便的传入以{}包围的不定长列表：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void print_list(initializer_list&amp;lt;int&amp;gt; il)
{
	for(auto it = il.begin(); it != il.end(); it++)
	{
		cout&amp;lt;&amp;lt;*it&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
		//*it = 100; // wrong. initializer_list element is read-only.
	}
	cout&amp;lt;&amp;lt;endl;
}

print_list({0, 1, 2, 3, 4});
print_list({0, 1, 2, 3, 4, 5});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;但是，需要注意的是，initializer_list中的元素是只读的。&lt;/p&gt;
&lt;h1 id=&#34;array&#34;&gt;array&lt;/h1&gt;
&lt;p&gt;C++11标准中提供了定长数组容器array，相比于普通数组更安全、更易使用。array是定长数组，所以不支持诸如插入、删除等改变容器大小的操作，但是可以对元素进行赋值改变其值。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;array&amp;lt;int, 5&amp;gt; c4 = {0, 1, 2, 3, 4};
c4[3] = 100; // can&amp;#39;t insert since the array size is fixed.
for(auto it4_1 = c4.begin(); it4_1 != c4.end(); it4_1++)
{
	cout&amp;lt;&amp;lt;*it4_1&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
}
cout&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;forward_list&#34;&gt;forward_list&lt;/h1&gt;
&lt;p&gt;C++11标准中增加了新的容器forward_list，提供了一个快速的、安全的单向链表实现。因为是单向链表，所以也就没有rbegin、rend一类的函数支持了。&lt;/p&gt;
&lt;p&gt;同样是因为单向链表的缘故，无法访问到给定元素的前驱，所以没有提供insert函数，而对应提供了一个insert_after函数，用于在给定元素之后插入节点。erase_after、emplace_after同理。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;forward_list&amp;lt;int&amp;gt; c5 = {3, 4};
c5.push_front(2);
c5.push_front(1);
auto it5_1 = c5.before_begin();
c5.insert_after(it5_1, 0);
for(auto it5_2 = c5.begin(); it5_2 != c5.end(); it5_2++)
{
	cout&amp;lt;&amp;lt;*it5_2&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
}
cout&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;swap&#34;&gt;swap&lt;/h1&gt;
&lt;p&gt;新标准中提供了非成员版本的swap操作，此操作对array容器，会交换元素的值；对其他容器，则只交换容器的内部结构，并不进行元素值的拷贝操作，所以在这种情况下是非常迅速的。&lt;/p&gt;
&lt;p&gt;正因如此，当swap array后，原来array上的迭代器还依然指向原有元素，只是元素的值变了；&lt;/p&gt;
&lt;p&gt;而swap非array容器之后，原来容器上的迭代器将指向对方容器上的元素，而指向的元素的值却保持不变。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; c6 = {0, 1, 2, 3, 4};
vector&amp;lt;int&amp;gt; c7 = {5, 6, 7, 8, 9};
auto it6_1 = c6.begin();
auto it7_1 = c7.begin();
swap(c6, c7);
for(auto it6_2 = c6.begin(); it6_2 != c6.end(); it6_2++)
	cout&amp;lt;&amp;lt;*it6_2&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
cout&amp;lt;&amp;lt;endl;
	
for(auto it7_2 = c7.begin(); it7_2 != c7.end(); it7_2++)
	cout&amp;lt;&amp;lt;*it7_2&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
cout&amp;lt;&amp;lt;endl;

cout&amp;lt;&amp;lt;(it6_1 == c7.begin())&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;(it7_1 == c6.begin())&amp;lt;&amp;lt;endl;

array&amp;lt;int, 5&amp;gt; c8 = {0, 1, 2, 3, 4};
array&amp;lt;int, 5&amp;gt; c9 = {5, 6, 7, 8, 9};
auto it8_1 = c8.begin();
auto it9_1 = c9.begin();
swap(c8, c9);
cout&amp;lt;&amp;lt;(it8_1 == c8.begin())&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;(it9_1 == c9.begin())&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;emplace&#34;&gt;emplace&lt;/h1&gt;
&lt;p&gt;emplace操作将使用接受的参数构造一个对应容器中的元素，并插入容器中。这一点，使用普通的insert、push操作是做不到的。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;class TestData
{
	public:
		TestData(string name, int age, double salary): name(name), age(age), salary(salary)
		{}
	private:
		string name;
		int age;
		double salary;
};

vector&amp;lt;TestData&amp;gt; c10;
c10.emplace_back(&amp;#34;yubo&amp;#34;, 26, 100000000000.0);
//c10.push_back(&amp;#34;laowang&amp;#34;, 56, 10.5); // wrong. no 3 params push_back
c10.push_back(TestData(&amp;#34;laowang&amp;#34;, 56, 10.5));
cout&amp;lt;&amp;lt;c10.size()&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;shrink_to_fit&#34;&gt;shrink_to_fit&lt;/h1&gt;
&lt;p&gt;一般可变长容器会预先多分配一部分内存出来，以备在后续增加元素时，不用每次都申请内存。所以有size和capacity之分。size是当前容器中存有元素的个数，而capacity则是在不重新申请内存的情况下，当前可存放元素的最大数目。而shrink_to_fit就表示将capacity中的多余部分退回，使其回到size大小。但是，这个函数的具体效果要依赖于编译器的实现……&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; c11;
for(int i = 0; i &amp;lt; 24; i++)
	c11.push_back(i);
cout&amp;lt;&amp;lt;c11.size()&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;c11.capacity()&amp;lt;&amp;lt;endl;
c11.shrink_to_fit();
cout&amp;lt;&amp;lt;c11.size()&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;c11.capacity()&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;无序关联容器&#34;&gt;无序关联容器&lt;/h1&gt;
&lt;p&gt;C++11新标准中引入了对map、set等关联容器的无序版本，叫做unorderer_map/unordered_set。&lt;/p&gt;
&lt;p&gt;无序关联容器不使用键值的比较操作来组织元素顺序，而是使用哈希。这样在某些元素顺序不重要的情况下，效率更高。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;unordered_map&amp;lt;string, int&amp;gt; c12;
map&amp;lt;string, int&amp;gt; c13;
string string_keys[5] = {&amp;#34;aaa&amp;#34;, &amp;#34;bbb&amp;#34;, &amp;#34;ccc&amp;#34;, &amp;#34;ddd&amp;#34;, &amp;#34;eee&amp;#34;};
for(int i = 0; i &amp;lt; 5; i++)
{
	c12[string_keys[i]] = i;
	c13[string_keys[i]] = i;
}
cout&amp;lt;&amp;lt;&amp;#34;normal map:\n&amp;#34;;
for(auto it13 = c13.begin(); it13 != c13.end(); it13++)
	cout&amp;lt;&amp;lt;it13-&amp;gt;first&amp;lt;&amp;lt;&amp;#39;:&amp;#39;&amp;lt;&amp;lt;it13-&amp;gt;second&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
cout&amp;lt;&amp;lt;endl;
cout&amp;lt;&amp;lt;&amp;#34;unordered map:\n&amp;#34;;
for(auto it12 = c12.begin(); it12 != c12.end(); it12++)
	cout&amp;lt;&amp;lt;it12-&amp;gt;first&amp;lt;&amp;lt;&amp;#39;:&amp;#39;&amp;lt;&amp;lt;it12-&amp;gt;second&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;;
cout&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;tuple&#34;&gt;tuple&lt;/h1&gt;
&lt;p&gt;熟悉python的程序员应该对tuple都不陌生，C++11中也引入了这一数据结构，用于方便的将不同类型的值组合起来。&lt;/p&gt;
&lt;p&gt;可以通过如下方式，获取tuple中的元素、tuple的长度等：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//tuple&amp;lt;int, string, vector&amp;lt;int&amp;gt;&amp;gt; c14 = {1, &amp;#34;tuple&amp;#34;, {0, 1, 2, 3, 4}}; // wrong. must explicit initialize
tuple&amp;lt;int, string, vector&amp;lt;int&amp;gt;&amp;gt; c14{1, &amp;#34;tuple&amp;#34;, {0, 1, 2, 3, 4}};
get&amp;lt;0&amp;gt;(c14) = 2;
typedef decltype(c14) ctype;
size_t sz = tuple_size&amp;lt;ctype&amp;gt;::value;
cout&amp;lt;&amp;lt;get&amp;lt;0&amp;gt;(c14)&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;get&amp;lt;1&amp;gt;(c14)&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;get&amp;lt;2&amp;gt;(c14)[0]&amp;lt;&amp;lt;&amp;#39;\t&amp;#39;&amp;lt;&amp;lt;sz&amp;lt;&amp;lt;endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;cbegin和cend提供了固定获取const_iterator的方式。&lt;/li&gt;
&lt;li&gt;begin和end用于普通数组获得首尾指针。&lt;/li&gt;
&lt;li&gt;可以使用{}包围的初始值列表进行赋值。&lt;/li&gt;
&lt;li&gt;增加initializer_list类型用于方便的使用{}包围的不定长列表。&lt;/li&gt;
&lt;li&gt;增加新的定长数组容器array 单向链表容器forward_list。&lt;/li&gt;
&lt;li&gt;增加非成员函数版本的swap操作。对array swap只交换元素值，而容器的结构不变；对其他容器则只改变容器数据结构，而元素值不变。&lt;/li&gt;
&lt;li&gt;增加emplace操作用于将参数传递给构造函数构造元素并插入容器。&lt;/li&gt;
&lt;li&gt;增加shrink_to_fit函数用于退回多余的空间。&lt;/li&gt;
&lt;li&gt;增加无序关联容器。&lt;/li&gt;
&lt;li&gt;增加tuple容器。&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>C&#43;&#43;11 时空性能方面的改进</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E6%97%B6%E7%A9%BA%E6%80%A7%E8%83%BD%E6%96%B9%E9%9D%A2%E7%9A%84%E6%94%B9%E8%BF%9B/</link>
      <pubDate>Tue, 08 Oct 2019 08:30:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E6%97%B6%E7%A9%BA%E6%80%A7%E8%83%BD%E6%96%B9%E9%9D%A2%E7%9A%84%E6%94%B9%E8%BF%9B/</guid>
      
        <description>&lt;h1 id=&#34;大小固定容器-array&#34;&gt;大小固定容器 array&lt;/h1&gt;
&lt;p&gt;std::array是一个支持随机访问且大小（size）固定的容器，它是c++11中新增的容器。它有如下特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不预留多余空间，只分配必须空间（译注：size() == capacity()）。&lt;/li&gt;
&lt;li&gt;可以使用初始化表（initializer list）的方式进行初始化。&lt;/li&gt;
&lt;li&gt;保存了自己的size信息。&lt;/li&gt;
&lt;li&gt;不支持隐式指针类型转换。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以认为它是一个很不错的内建数组类型。示例：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;array&amp;lt;int,6&amp;gt; a = { 1, 2, 3 };
a[3]=4;
int x = a[5];    
// array的默认数据元素为0，所以x的值变成0
int* p1 = a; 
// 错误: std::array不能隐式地转换为指针
int* p2 = a.data(); 
// 正确，data()得到指向第一个元素的指针&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;可以认为array是一个紧缩版的vector，它比vector高效（没有自动空间分配），但缺少了push_back这样的神器，使得它的使用场景一般是用来替换c++内建的数组类型，而不是vector；&lt;/p&gt;
&lt;h1 id=&#34;前向列表-forward_list&#34;&gt;前向列表 forward_list&lt;/h1&gt;
&lt;p&gt;c++11新增的容器：前向列表 forward_list&lt;/p&gt;
&lt;p&gt;前向列表是一个能够在任意位置快速插入和删除的容器（列表都这特性，前向列表当然也具有这特性），但不支持快速随机存取。&lt;/p&gt;
&lt;p&gt;它是用单向链表实现的，相比较于它的C实现而言没有什么额外开销。相较于std::list而言，此容器耗费的空间更少，因为它是单向的，不是双向的。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;std::forward_list&amp;lt;int&amp;gt; mylist (3,5);   
// 3 ints with value 5
for (int&amp;amp; x : mylist ) std::cout &amp;lt;&amp;lt; &amp;#34; &amp;#34; &amp;lt;&amp;lt; x;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;哈希表无序容器-unordered-containers&#34;&gt;哈希表[无序容器] unordered containers&lt;/h1&gt;
&lt;p&gt;hash容器在很多之前的编译器中就包含进来了；比如gcc 较早的版本中，它存在于tr1命名空间中；&lt;/p&gt;
&lt;p&gt;以unordered_map为例，unordered_map基于散列表实现，元素之间无序存储；&lt;/p&gt;
&lt;p&gt;而map基于红黑树实现，元素之间有序（通过operator&amp;lt; 进行比较）；&lt;/p&gt;
&lt;p&gt;hash版本的查找时间复杂度为O(1),在数据量很大时，比红黑树的版本效率高很多；&lt;/p&gt;
&lt;p&gt;对比在C++11中和之前使用上的区别：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// c++0x中：
#include &amp;lt;tr1/unordered_map&amp;gt;
std::tr1:: unordered_map&amp;lt; char,int &amp;gt;  map1;
map1.insert(std::pair&amp;lt;char,int&amp;gt;(&amp;#39;a&amp;#39;,100) );
 
// C++11中：
#include &amp;lt;unordered_map&amp;gt;
std::unordered_map&amp;lt; char,int &amp;gt;  map1;
map1.insert(std::pair&amp;lt;char,int&amp;gt;(&amp;#39;a&amp;#39;,100) );&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;常量表达式-constexpr&#34;&gt;常量表达式 constexpr&lt;/h1&gt;
&lt;p&gt;编译期计算（Compile-time evaluation）：常量表达式&lt;/p&gt;
&lt;p&gt;在程序中，有些计算是与运行时无关的；每次执行都是相同的结果；&lt;/p&gt;
&lt;p&gt;常量表达式允许让这些计算发生在编译时，而不是在运行时；&lt;/p&gt;
&lt;p&gt;这样利用编译时的计算能力，将显著提升程序执行时的效果；&lt;/p&gt;
&lt;p&gt;eg:对函数申明为constexpr&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;constexpr int multiply (int x, int y)
{
    return x * y;
}
 
// 将在编译时计算
const int val = multiply( 10, 10 );
cin &amp;gt;&amp;gt; x;
// 由于输入参数x只有在运行时确定，所以以下这个不会在编译时计算，但执行没问题
const int val2 = mutliply（x,x);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;静态断言-static_assert&#34;&gt;静态断言 static_assert&lt;/h1&gt;
&lt;p&gt;static_assert提供一个编译时的断言检查。如果断言为真，什么也不会发生。如果断言为假，编译器会打印一个特殊的错误信息。由于是在编译期间执行的，所以它不会影响运行时的性能；&lt;/p&gt;
&lt;p&gt;expression在编译期进行求值，当结果为false（即：断言失败）时，将string作为错误消息输出。例如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;static_assert(sizeof(long) &amp;gt;= 8,
   “64-bit code generation required for this library.”);
struct S { X m1; Y m2; };
static_assert(sizeof(S)==sizeof(X)+sizeof(Y), ”unexpected padding in S”);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;static_assert在判断代码的编译环境方面十分有用，比如判断当前编译环境是否64位。但需要注意的是，由于static_assert在编译期进行求值，它不能对那些依赖于运行期计算的值的进行检验。例如：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int f(int* p, int n)
{
      
//错误：表达式“p == 0”不是一个常量表达式
      static_assert(p == 0,
          “p is not null”);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;正确的做法是在运行期进行判断，假如条件不成立则抛出异常；&lt;/p&gt;
&lt;p&gt;下面这段代码原本期望只做用于整数类型。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template &amp;lt;typename T1, typename T2&amp;gt;
auto add(T1 t1, T2 t2) -&amp;gt; decltype(t1 + t2)
{
return t1 + t2;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;但是如果有人写出如下代码，编译器并不会报错&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;std::cout &amp;lt;&amp;lt; add(1, 3.14) &amp;lt;&amp;lt; std::endl;
std::cout &amp;lt;&amp;lt; add(&amp;#34;one&amp;#34;, 2) &amp;lt;&amp;lt; std::endl;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;程序会打印出4.14和”e”。但是如果我们加上编译时断言，那么以上两行将产生编译错误。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template &amp;lt;typename T1, typename T2&amp;gt;
auto add(T1 t1, T2 t2) -&amp;gt; decltype(t1 + t2)
{
   static_assert(std::is_integral&amp;lt;T1&amp;gt;::value, &amp;#34;Type T1 must be integral&amp;#34;);
   static_assert(std::is_integral&amp;lt;T2&amp;gt;::value, &amp;#34;Type T2 must be integral&amp;#34;);
 
   return t1 + t2;
}
 
error C2338: Type T2 must be integral
see reference to function template instantiation &amp;#39;T2 add&amp;lt;int,double&amp;gt;(T1,T2)&amp;#39; being compiled
   with
   [
      T2=double,
      T1=int
   ]
error C2338: Type T1 must be integral
see reference to function template instantiation &amp;#39;T1 add&amp;lt;const char*,int&amp;gt;(T1,T2)&amp;#39; being compiled
   with
   [
      T1=const char *,
      T2=int
   ]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;move语义和右值引用&#34;&gt;move语义和右值引用&lt;/h1&gt;
&lt;h2 id=&#34;move语义和右值介绍&#34;&gt;move语义和右值介绍&lt;/h2&gt;
&lt;p&gt;左值就是一个有名字的对象，而右值则是一个无名对象（临时对象）。move语义允许修改右值（以前右值被看作是不可修改的，等同于const T&amp;amp;类型）。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;void incr(int&amp;amp; a) { ++a; }
int i = 0;
incr(i);    
// i变为1
//错误：0不是一个左值
incr(0);
// 0不是左值，无法直接绑定到非const引用：int&amp;amp;。
// 假如可行，那么在调用时，将会产生一个值为0的临时变量，
// 用于绑定到int&amp;amp;中，但这个临时变量将在函数返回时被销毁，
// 因而，对于它的任何更改都是没有意义的，
// 所以编译器拒绝将临时变量绑定到非const引用，但对于const的引用，
// 则是可行的
”&amp;amp;&amp;amp;”表示“右值引用”。右值引用可以绑定到右值（但不能绑定到左值）：
 
X a;
X f();
X&amp;amp; r1 = a;        
// 将r1绑定到a(一个左值)
X&amp;amp; r2 = f();    
// 错误：f()的返回值是右值，无法绑定
X&amp;amp;&amp;amp; rr1 = f();    
// OK：将rr1绑定到临时变量
X&amp;amp;&amp;amp; rr2 = a;    
// 错误：不能将右值引用rr2绑定到左值a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;考虑如下函数：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template&amp;lt;class T&amp;gt; swap(T&amp;amp; a, T&amp;amp; b) 
// 老式的swap函数
    {
        T tmp(a);
// 现在有两份&amp;#34;a&amp;#34;
        a = b;      
// 现在有两份&amp;#34;b&amp;#34;
        b = tmp;    
// 现在有两份tmp(值同a)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;如果T是一个拷贝代价相当高昂的类型，例如string和vector，那么上述swap()操作也将煞费气力；我们的初衷其实并不是为了把这些变量拷来拷去，我是仅仅想将变量a,b,tmp的值做一个“移动”（即通过tmp来交换a,b的值）。&lt;/p&gt;
&lt;p&gt;移动赋值操作背后的思想是，“赋值”不一定要通过“拷贝”来做，还可以通过把源对象简单地“偷换”给目标对象来实现。例如对于表达式s1=s2，我们可以不从s2逐字拷贝，而是直接让s1“侵占”s2内部的数据存储；&lt;/p&gt;
&lt;p&gt;我们可以通过move()操作符来实现源对象的“移动”：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template &amp;lt;class T&amp;gt;
void swap(T&amp;amp; a, T&amp;amp; b)  
//“完美swap”（大多数情况下）
{
      T tmp = move(a);  
// 变量a现在失效（译注：内部数据被move到tmp中了）
      a = move(b);      
// 变量b现在失效（译注：内部数据被move到a中了，变量a现在“满血复活”了）
      b = move(tmp);    
// 变量tmp现在失效（译注：内部数据被move到b中了，变量b现在“满血复活”了）
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;move(x) 意味着“你可以把x当做一个右值”，把move()改名为rval()或许会更好，但是事到如今，move()已经使用很多年了。在C++11中，move()模板函数以及右值引用被正式引入。&lt;/p&gt;
&lt;p&gt;将拷贝改进成移动操作，减少创建不必要的对象，节省了对象的空间分配消耗和构造析构的调用；&lt;/p&gt;
&lt;h2 id=&#34;move对算法中的改进&#34;&gt;move对算法中的改进&lt;/h2&gt;
&lt;p&gt;基于move的std::sort()和std::set::insert()要比基于copy的对应版本快15倍以上。不过它对标准库中已有操作的性能改善不多，因为它们的实现中已经使用了类似的方法进行优化了（例如string，vector使用了调优过的swap操作来代替copy了）。当然如果你自己的代码中包含了move操作的话，就能自动从新标准库中获益了。&lt;/p&gt;
&lt;h2 id=&#34;move对容器的改进&#34;&gt;move对容器的改进&lt;/h2&gt;
&lt;p&gt;在C++11的标准库中，所有的容器都提供了移动构造函数和移动赋值操作符，那些插入新元素的操作，如insert()和push_back(), 也都有了可以接受右值引用的版本。最终的结果是，在没有用户干预的情况下，标准容器和算法的性能都提升了，而这些都应归功于拷贝操作的减少。&lt;/p&gt;
&lt;p&gt;以vector为例，定义“移动构造函数(move constructors)”和“移动赋值操作符(move assignments”来“移动”而非复制它们的参数：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;template&amp;lt;class T&amp;gt; class vector {
        
// …
        vector(const vector&amp;amp;);  
// 拷贝构造函数
        vector(vector&amp;amp;&amp;amp;);   
// 移动构造函数
        vector&amp;amp; operator= (const vector&amp;amp;); 
// 拷贝赋值函数
        vector&amp;amp; operator =(vector&amp;amp;&amp;amp;);  
// 移动赋值函数
}; 
//注意：移动构造函数和移动赋值操作符接受
// 非const的右值引用参数，而且通常会对传入的右值引用参数作修改&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;容器新增了move版的构造和赋值函数后，它最重要的内涵就是允许我们高效的从函数中返回一个容器：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;int&amp;gt; make_random(int n)
        {
            vector&amp;lt;int&amp;gt; ref(n);
            
// 产生0-255之间的随机数
                for(auto x&amp;amp; : ref) x = rand_int(0,255);
 
                return ref;
        }
 
        vector&amp;lt;int&amp;gt; v = make_random(10000);
        for (auto x : make_random(1000000)) cout &amp;lt;&amp;lt; x &amp;lt;&amp;lt; &amp;#39;\n&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;上边代码的关键点是vector没有被拷贝操作(vector ref的内存空间不会在函数返回时被stack自动回收了，move assignment通过右值引用精巧的搞定了这个问题)。对比我们现在的两种惯用法：在自由存储区来分配vector的空间，我们得负担上内存管理的问题了；通过参数传进已经分配好空间的vector，我们得要写不太美观的代码了。&lt;/p&gt;
&lt;h1 id=&#34;原地安置操作-emplace-operations&#34;&gt;原地安置操作 Emplace operations&lt;/h1&gt;
&lt;p&gt;在大多数情况下，push_back()使用移动构造函数（而不是拷贝构造函数）来保证它更有效率，不过在极端情况下我们可以走的更远。为何一定要进行拷贝/移动操作？为什么不能在vector中分配好空间，然后直接在这个空间上构造我们需要的对象呢？做这种事儿的操作被叫做”原地安置”（emplace，含义是：putting in place）。&lt;/p&gt;
&lt;p&gt;举一个emplace_back()的例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vector&amp;lt;pair&amp;lt;string,int&amp;gt;&amp;gt; vp;
string s;
int i;
while(cin&amp;gt;&amp;gt;s&amp;gt;&amp;gt;i) vp.emplace_back(s,i);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;emplace_back()接受了可变参数模板变量并通过它来构造所需类型。至于emplace_back()是否比push_back()更有效率，取决于它和可变参数模板的具体实现。如果你认为这是一个重要的问题，那就实际测试一下。否则，就从美感上来选择它们吧。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>平衡车家长等级大排名</title>
      <link>http://coder.amazingdemo.top/post/balancebike_%E5%B9%B3%E8%A1%A1%E8%BD%A6%E5%AE%B6%E9%95%BF%E7%AD%89%E7%BA%A7%E5%A4%A7%E6%8E%92%E5%90%8D/</link>
      <pubDate>Wed, 28 Aug 2019 16:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/balancebike_%E5%B9%B3%E8%A1%A1%E8%BD%A6%E5%AE%B6%E9%95%BF%E7%AD%89%E7%BA%A7%E5%A4%A7%E6%8E%92%E5%90%8D/</guid>
      
        <description>&lt;h1 id=&#34;一级&#34;&gt;一级：&lt;/h1&gt;
&lt;p&gt;儿童体育只停留在日常的思想中。总是觉得应该顺应潮流给孩子报一个体育项目。从小就看着周围的孩子在平衡车上驰骋，但迟迟没有出手，等待中迎来了孩子的五岁生日。&lt;/p&gt;
&lt;p&gt;耗时：五分钟（准确的说是每天思考五分钟）&lt;/p&gt;
&lt;p&gt;难度系数：0&lt;/p&gt;
&lt;h1 id=&#34;二级&#34;&gt;二级：&lt;/h1&gt;
&lt;p&gt;没有在平衡车项目上犹豫不决，但迄今唯一的一次果断出手就是在迪卡侬的柜台前，以为299元的价格购入孩子的第一辆也是唯一一辆平衡车。&lt;/p&gt;
&lt;p&gt;耗时：一天&lt;/p&gt;
&lt;p&gt;难度系数：几乎为0（因为口袋里不可能连299块钱都没有）&lt;/p&gt;
&lt;h1 id=&#34;三级&#34;&gt;三级：&lt;/h1&gt;
&lt;p&gt;开始了解平衡车的品牌。对于几大品牌有了初步的了解。在认知的捆绑下，花费近2000元买下了人生中最贵的一辆自行车。时至今日，任然不能理解一辆2000元平衡车的成本和利润构成。并且始终困惑。&lt;/p&gt;
&lt;p&gt;耗时：一个月&lt;/p&gt;
&lt;p&gt;难度系数：30（毕竟花费了一个中年男人近2个月的零花钱）&lt;/p&gt;
&lt;h1 id=&#34;四级&#34;&gt;四级：&lt;/h1&gt;
&lt;p&gt;欣然乐意购买了高级平衡车，并且在家附近的儿童平衡车俱乐部购买了课程。每周六下午都会拖着睡眼惺忪的孩子，肩抗着平衡车游走在家和俱乐部之间。&lt;/p&gt;
&lt;p&gt;这时的家长发现骑车变得复杂。出发、起跑、弯道、超越、滑行、意识、体能&amp;hellip;这些都是什么鬼？&lt;/p&gt;
&lt;p&gt;耗时：3个月&lt;/p&gt;
&lt;p&gt;难度系数：50&lt;/p&gt;
&lt;h1 id=&#34;五级&#34;&gt;五级：&lt;/h1&gt;
&lt;p&gt;孩子坚持训练，确实体会到平衡车对于身体素质的提高。开始着眼于市内的各个平衡车比赛。偶尔会在周末带着孩子驱车前往比赛现场。&lt;/p&gt;
&lt;p&gt;开始频繁加入到骑游和夜骑的队中中去。&lt;/p&gt;
&lt;p&gt;耗时六个月&lt;/p&gt;
&lt;p&gt;难度系数：60&lt;/p&gt;
&lt;h1 id=&#34;六级&#34;&gt;六级：&lt;/h1&gt;
&lt;p&gt;开始沉迷于参加各个平衡车比赛。每个周末不辞辛苦地奔走在怀柔、密云等远郊区。但是孩子始终无法适应比赛的节奏，频繁的一轮游，使家长痛下升级车辆的决心。&lt;/p&gt;
&lt;p&gt;开始思考孩子在平衡车上的发展方向，竞速？竞技？模棱两可。&lt;/p&gt;
&lt;p&gt;耗时：九个月&lt;/p&gt;
&lt;p&gt;难度系数：70&lt;/p&gt;
&lt;h1 id=&#34;七级&#34;&gt;七级：&lt;/h1&gt;
&lt;p&gt;在俱乐部教练的指引下，第一次更换了轮组和车座。孩子的成绩顺势提高了一些。在一些非著名的比赛上，孩子已经可以跻身决赛甚至登上了第三名的领奖台。&lt;/p&gt;
&lt;p&gt;这时的你已经可以在赛场上“指点江山”，和周围平衡车的同僚仿佛在召开学术研讨会。&lt;/p&gt;
&lt;p&gt;耗时：一年&lt;/p&gt;
&lt;p&gt;难度系数：75&lt;/p&gt;
&lt;h1 id=&#34;八级&#34;&gt;八级：&lt;/h1&gt;
&lt;p&gt;这个级别的家长可以说是对改装如数家珍，对于初级的改装知识更是脱口而出，信手拈来。开始关注并购买了碳编条和碳三刀轮组。车架从铝合金过度到炫彩电镀再到超轻的碳架。对于鱼龙混杂的配件市场有了自己坚定的认知。这时的孩子已经稳定进入到决赛的出发台，并偶有冠军奖杯带回，家里原本放花瓶的柜子挂满了孩子的完赛奖牌。&lt;/p&gt;
&lt;p&gt;这时的家长，俨然一个合格的教练。每天训练完成后，会不辞劳苦地给孩子总结归纳。&lt;/p&gt;
&lt;p&gt;无论寒冬酷暑，早上陪孩子一起跑步；晚上陪孩子一起训练。&lt;/p&gt;
&lt;p&gt;后备箱里平躺着三辆以上的平衡车。头盔护具的数量更是惊人。&lt;/p&gt;
&lt;p&gt;耗时：2年&lt;/p&gt;
&lt;p&gt;难度系数：85&lt;/p&gt;
&lt;h1 id=&#34;九级&#34;&gt;九级：&lt;/h1&gt;
&lt;p&gt;对于把立的长度和下沉角度有了自己独到的见解；对于编条轮组和三刀轮组应对的不同地形了然于胸；执拗于一辆车怎么才能改装到2kg以下；高薪聘请著名教练对孩子的弯道技术和发力动作一遍遍地改进。从裤兜里随手一摸就变换出两把PARKTOOL的扳手。披星戴月、风雨兼程地周游列省，甚至到美国、日本、欧洲去和高手切磋比试。&lt;/p&gt;
&lt;p&gt;对于比赛中遇到的对手情况了如指掌，并且能够制定并迅速地调整战略战术。&lt;/p&gt;
&lt;p&gt;耗时：3年&lt;/p&gt;
&lt;p&gt;难度系数：95&lt;/p&gt;
&lt;h1 id=&#34;十级&#34;&gt;十级：&lt;/h1&gt;
&lt;p&gt;满足以上所有要求。并且在平衡车以外还给孩子报了游泳、击剑、篮球、足球，甚至冰球的课程。运用自己在平衡车上的“成功经验”，陪着孩子钟情于儿童体育的世界里周而复始，并且为之欣慰。&lt;/p&gt;
&lt;p&gt;耗时&amp;hellip;.&lt;/p&gt;
&lt;p&gt;难度系数：99&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>我的猫死了</title>
      <link>http://coder.amazingdemo.top/post/%E7%8C%AB%E6%AD%BB%E4%BA%86/</link>
      <pubDate>Tue, 06 Aug 2019 13:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%8C%AB%E6%AD%BB%E4%BA%86/</guid>
      
        <description>&lt;h1 id=&#34;幸存者内疚&#34;&gt;幸存者内疚&lt;/h1&gt;
&lt;p&gt;当人们遭遇一些创伤事件，一些人幸存下来了，另一些人却没能活下来，于是，幸存下来的人们可能会认为自己做错了什么，而对没能存活下来的人们感到内疚，产生幸存者内疚（Survivor Guilt）或幸存者综合征（Survivor Syndrome）。&lt;/p&gt;
&lt;p&gt;这些幸存者可能是战争、自然灾难、流行病的幸存者，自杀者的家人朋友，急救人员，治疗师，大范围裁员后的员工。&lt;/p&gt;
&lt;p&gt;幸存者内疚不是一个特殊诊断，而是PTSD的一个重要症状。幸存者因为其他人的死亡而责备自己，包括那些在拯救幸存者中去世的人，或幸存者尽力去拯救却没有救成功的人。&lt;/p&gt;
&lt;h1 id=&#34;20190801&#34;&gt;20190801&lt;/h1&gt;
&lt;p&gt;没有人知道这只猫是从哪来的，我只知道那晚猫突然出现在我们的楼道里，在麻麻开门的时候窜入我家，然后就赖着不走了。&lt;/p&gt;
&lt;p&gt;麻麻把我们吃的饭菜挑拣了一部分拿了一个小杯子分给他，发现他特别喜欢吃蒸鸡蛋，所以我们叫他蛋花。&lt;/p&gt;
&lt;p&gt;那晚，蛋花在我们的客厅里渡过了一晚。&lt;/p&gt;
&lt;h1 id=&#34;20190802&#34;&gt;20190802&lt;/h1&gt;
&lt;p&gt;早上的时候，蛋花一直在厕所门口来回走动，还喵喵的叫。麻麻猜测是想上厕所了，于是让我拿了一个纸盒放到卫生间里，果然他马上就跑进去拉了臭臭。&lt;/p&gt;
&lt;p&gt;麻麻说可能之前有人养过，所以熟悉人类养猫的规则。但是从昨晚一直到今早，我们测试了几次，发现如果我们把猫放到门外不理他，他会一直待在门外小范围活动，并不会去寻找他的主人。我们猜测，可能是被遗弃了。&lt;/p&gt;
&lt;p&gt;麻麻带蛋花去了医院检查，医生说猫龄可能五个月左右，没有弓形虫没有猫瘟，只是体内外有寄生虫和虱子，形势还不错。更证实了我们的猜测，可能是一只刚遗弃不久的小猫。&lt;/p&gt;
&lt;p&gt;我们买了驱虫药，自动喂食器，猫粮，猫砂，辛苦的当起了铲屎官。&lt;/p&gt;
&lt;h1 id=&#34;20190803&#34;&gt;20190803&lt;/h1&gt;
&lt;p&gt;孩子要去外地比赛，我们临走前，还特地把网络摄像头也架起来了。预留了足够的水和猫粮。&lt;/p&gt;
&lt;p&gt;在出门的间隙，还通过手机远程视频看他，并通过远程语音呼唤他，跟他互动。&lt;/p&gt;
&lt;p&gt;在比赛的时候，还一直鼓励孩子拿一个大奖杯回去给蛋花看。&lt;/p&gt;
&lt;h1 id=&#34;20190804&#34;&gt;20190804&lt;/h1&gt;
&lt;p&gt;我们比完赛回到家里，蛋花并没有像网上说的那样不理人。他依然听到开门声以后，跑到门口迎接了我们的回家。&lt;/p&gt;
&lt;p&gt;但是当晚，我们也感到了一丝压力。&lt;/p&gt;
&lt;p&gt;医生说猫因为在外面流浪过，所以驱虫大约需要10天，在这10天内，家里需要注意卫生，注意孩子与猫的接触以及大人的食物安全，与环境安全。&lt;/p&gt;
&lt;p&gt;但是孩子太小了，她并不能很好的约束自己的行为。她经常用手去摸猫，摸完了以后不及时洗手又在家里的其他地方到处摸，或者去吃东西喝水。&lt;/p&gt;
&lt;p&gt;因此我们很头疼，这几天也经常吼孩子，打孩子。另外因为担心猫在家到处跑，所以家里的厨房，卧室，书房门一直都是关闭的，空气不流通，现在渐渐的，空气里弥漫着一股腐臭味。家里的生活全被打乱了。&lt;/p&gt;
&lt;p&gt;虽然医生说十天以后，虫子会被打得七七八八，到时候带去宠物店洗个澡，家里全面消毒以后，只要注意不要让猫再跑到外面去流浪的话，一直在家里活动的前提下，之前的注意事项就会宽松很多。但是我们依然很头疼，觉得对于未来需要承担的责任有一种无力感。&lt;/p&gt;
&lt;h1 id=&#34;20190805&#34;&gt;20190805&lt;/h1&gt;
&lt;p&gt;不知道是因为家里腐臭味加剧，还是因为对猫毛过敏，前几天孩子只是鼻子不舒服，今天开始继续恶化，鼻塞流鼻涕了。&lt;/p&gt;
&lt;p&gt;我们觉得，对于养猫这件事，我们之前还是考虑得太简单了，事实是我们可能还是承担不起。&lt;/p&gt;
&lt;p&gt;考虑到之前观察到的，这只猫也不会乱跑。于是决定一方面将猫移至楼道里喂养，一方面在网上积极的寻找领养人。经过反复的跟孩子讲道理，安抚情绪，孩子也同意了这个方案。&lt;/p&gt;
&lt;h1 id=&#34;20190806&#34;&gt;20190806&lt;/h1&gt;
&lt;p&gt;昨晚八点多，我出去加猫粮，猫不在楼道里。我加完猫粮回家的时候，对门门开了，猫在她家餐桌下。对门说猫趁她们开门溜进去了，她们喂了点东西，但是不准备让她在家留宿。我说我们先在楼道养两天，正在寻找领养人，然后就回家了。后来听声音，猫应该被成功推出门了。&lt;/p&gt;
&lt;p&gt;九点左右，我又出去换猫砂却没有看到猫了。当时我想可能到哪里去玩去了吧，也没有太在意。&lt;/p&gt;
&lt;p&gt;后来对面的爷爷奶奶回家，又听到她们大声的喊叫，听起来好像是猫又趁爷爷奶奶开门，跑他们家去了。我也没有太留意，想来过一阵子应该又会被推出来的。&lt;/p&gt;
&lt;p&gt;今早七点多起来换猫砂，发现猫粮没怎么动过，猫砂上也没有使用的痕迹，一直保持着昨晚的样子，我上下楼简单转了一圈也没找到猫在哪。&lt;/p&gt;
&lt;p&gt;后来我下楼去上班的时候，还一边等电梯一边打字跟麻麻说，猫可能嫌弃我们把他扔在楼道，昨晚就已经跑掉去寻找更好的主人去了。&lt;/p&gt;
&lt;p&gt;结果字还没打完，一开单元门就看到这一幕。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/cat1.jpg&#34; alt=&#34;cat1&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/cat2.jpg&#34; alt=&#34;cat2&#34;&gt;&lt;/p&gt;
&lt;p&gt;我刚看到，还以为他嫌楼道热，晚上趁人进进出出，跑下楼乘凉。&lt;/p&gt;
&lt;p&gt;我还边笑着边说，蛋花，你怎么跑出来了，快进去吃饭上厕所。然后就准备用脚把他赶到单元门附近去，拉开门带他上电梯回楼道里。&lt;/p&gt;
&lt;p&gt;结果喊了两声没动静，我用脚点了点，发现肢体已经僵硬了。凑近一看，鼻头上已经结了血痂，苍蝇在血痂上飞飞停停；眼眶里爬了好多蚂蚁；头下面的地上流了一小摊血。&lt;/p&gt;
&lt;p&gt;我猜想，应该是被人从楼上丢下来，头部着地摔死了。不知道他死前经历了怎样的痛苦，有没有想起前几天在我家里跟我们一家三口玩耍的快乐。&lt;/p&gt;
&lt;p&gt;他的猫玩具刚从淘宝买了还在路上，本来准备等找到新主人一起送给新主人的；而且我们也答应孩子，可以在我们的带领下每天在楼道里跟猫一起玩，只要进门以后第一时间洗手，做好清洁。&lt;/p&gt;
&lt;p&gt;一个生命就这样陨落了，悄无声息。有时候我就在想，昨晚我换猫砂的时候没看到他，是不是应该再坚持上下楼找一找，是不是那个时候他还没有遇害，要是我坚持找一找是不是可以从歹徒手上挽救一下呢？会不会是因为我们把他寄养在楼道里，引来了楼上楼下哪位邻居的不满，做出如此极端的行为？&lt;/p&gt;
&lt;p&gt;我不想像福尔摩斯一样去揣测这是谁干的，这也没有很大的意义。就算抓住凶手，我又能怎么样呢？这件事部分责任也在我自己看管不严。&lt;/p&gt;
&lt;p&gt;愿天堂没有伤害，愿你来生能有比我更适合的人照顾你，让你免于被伤害！！！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>平衡车圈扎心扎肺的大实话</title>
      <link>http://coder.amazingdemo.top/post/balancebike_%E5%B9%B3%E8%A1%A1%E8%BD%A6%E5%9C%88%E6%89%8E%E5%BF%83%E6%89%8E%E8%82%BA%E7%9A%84%E5%A4%A7%E5%AE%9E%E8%AF%9D/</link>
      <pubDate>Sun, 28 Jul 2019 16:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/balancebike_%E5%B9%B3%E8%A1%A1%E8%BD%A6%E5%9C%88%E6%89%8E%E5%BF%83%E6%89%8E%E8%82%BA%E7%9A%84%E5%A4%A7%E5%AE%9E%E8%AF%9D/</guid>
      
        <description>&lt;ol&gt;
&lt;li&gt;买车前一定要试骑，网上挑选好的款式，试骑时会发现很多问题。&lt;/li&gt;
&lt;li&gt;让孩子挑选自己喜欢的外观，他们会骑得更久。&lt;/li&gt;
&lt;li&gt;车子轻对孩子很重要，对你更重要，不信邪的人扛车的时候都后悔了。&lt;/li&gt;
&lt;li&gt;平衡车三份质量，七分保养。买车要挑选有本地俱乐部的大品牌，碰到小概率的瑕疵问题和使用中的保养、维修问题时，会有添完瓶盖，发现“再来一瓶”的喜悦。&lt;/li&gt;
&lt;li&gt;买车很简单，半天就搞定，使用却要好几年，你和俱乐部的故事，才刚刚开始。&lt;/li&gt;
&lt;li&gt;头盔真的很重要！不要想着在头盔上省钱！摔一跤就什么都赚回来了！&lt;/li&gt;
&lt;li&gt;平衡车不只有竞速，还有泥地竞速。真的是在泥地上的竞速。&lt;/li&gt;
&lt;li&gt;练好平衡车，要具备力量、速度、耐力、协调、敏捷、柔韧这些身体素质。我们哪里是在练车，我们是在练体育啊。&lt;/li&gt;
&lt;li&gt;并不是训练了，比赛就能赢，毕竟你只是每周训练一次，有人是每天训练一小时&lt;/li&gt;
&lt;li&gt;俗话说，不管实力够不够，都要用装备来凑，不花钱，怎么能安心。&lt;/li&gt;
&lt;li&gt;学平衡车以前，你认为骑车很简单，迈开腿往前跑就行了。慢慢地，你发现骑车好复杂，步频、步幅、动作模式，重心，力点，身体意识，体能练习……这些都是什么鬼？&lt;/li&gt;
&lt;li&gt;离开运动量、运动强度、运动密度来谈运动伤害，就是耍流氓。关键还是要选对俱乐部！&lt;/li&gt;
&lt;li&gt;看上去好像平衡车圈的家长都不用上班，事实上不是所有人家里都有矿，大部分人家里只有矿泉水，只是他们把刷手机的时间用在了带娃锻炼上。&lt;/li&gt;
&lt;li&gt;平衡车圈那么多人，那么多比赛，遇到一个真心为你娃感到骄傲的人，很不容易，可待成闺蜜。&lt;/li&gt;
&lt;li&gt;有人的地方，就有无赖，可以评论的地方，就有杠精。&lt;/li&gt;
&lt;li&gt;让孩子爱上读书的正确打开方式，是自己多读书。让孩子爱上运动的正确打开方式，是自己多运动。 骑车也一样。&lt;/li&gt;
&lt;li&gt;自己没有上过平衡车，永远不要说这个so easy！不信邪的，请参加亲子赛！泵道也欢迎你挑战！&lt;/li&gt;
&lt;li&gt;学平衡车，不是比谁更厉害，是比谁更能坚持。&lt;/li&gt;
&lt;li&gt;天赋，可以让孩子用很少的努力，就获得很棒的车感，并学会必要的技巧，但是这种情况，只会发生在启蒙阶段。人不可能看一眼就学会微积分，看一眼就学会量子力学。再有天赋的孩子，也不可能不努力，就成为冠军收割机。&lt;/li&gt;
&lt;li&gt;平衡车圈的努力有两种，一种是外在压迫下的努力，在外在压迫下的努力过程中，渐渐地孩子越来越有成就感，越来越自信，最后越来越痴迷，就变成了第二种努力—有内驱力的努力。而有内驱力的努力，才是真正的天赋。&lt;/li&gt;
&lt;li&gt;大神是用来膜拜的，不是用来模仿的。不假思索地生搬硬套大神的练习方式，会使我们陷入“按照别人的方法却教不出别人家孩子”的怪圈。&lt;/li&gt;
&lt;li&gt;大神总轻描淡写的说我们没怎么练。却不会告诉你他是怎么从弱鸡成长起来，怎么经历被虐的支离破碎，怎么经历冬练三九夏练三伏，最后练就现在的王之藐视。&lt;/li&gt;
&lt;li&gt;有些车手，看起来很敏感，易退缩，甚至有些“娘”，不要着急把他们推出去，他们不是在“害怕”，而是在“观察”，他们不是“胆小型车手”，而是“谨慎型车手”。对他们来说，旁观很重要，旁观是参与的前奏。&lt;/li&gt;
&lt;li&gt;高手都会扎推比赛。有些是想着，这比赛高手一定都会去，我娃找他们较量，就能发现自己的弱点。有些是想着，这比赛高手一定都不会去，我娃去了就能拿第一，结果都想一块了。&lt;/li&gt;
&lt;li&gt;每次都以温情片开始，以武打片结束的绝对不是只有你一个人&lt;/li&gt;
&lt;li&gt;情绪的崩溃，都是从企图立即纠正孩子的错误动作开始的。这么危险的事，还是教给教练吧。&lt;/li&gt;
&lt;li&gt;虽然我们和大神一样，每到周末，不是在比赛，就是在去比赛的路上，大神总是能赚回路费，我们却只能回答：名次重要么！&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>那种喝了会发出满足的「啊——」声的清爽饮料</title>
      <link>http://coder.amazingdemo.top/post/%E6%B8%AF%E5%BC%8F%E5%86%BB%E6%9F%A0%E8%8C%B6/</link>
      <pubDate>Mon, 22 Jul 2019 17:17:51 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E6%B8%AF%E5%BC%8F%E5%86%BB%E6%9F%A0%E8%8C%B6/</guid>
      
        <description>&lt;p&gt;最拿手的港式冻柠茶，茶餐厅的那种，比起别的品种，港式的更清甜，茶味更重。简单易做，基本能做得和外面一样。&lt;/p&gt;
&lt;p&gt;首先需要的工具材料有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;新鲜柠檬&lt;/li&gt;
&lt;li&gt;红茶（我一般是用车仔，袋装的那种，因为便宜也不差，如果有更好的红茶可以自行试试）&lt;/li&gt;
&lt;li&gt;单晶冰糖&lt;/li&gt;
&lt;li&gt;耐热容器、细长铁勺&lt;/li&gt;
&lt;li&gt;电子秤。（这是量化了的做法。以免每次做出来甜度都不太一样……）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;做法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先放入 34 克冰糖（记得减去容器的质量），挂着两个茶包。&lt;/li&gt;
&lt;li&gt;然后将 377 克开水缓慢淋着茶包倒入容器内。&lt;/li&gt;
&lt;li&gt;然后用铁勺搅拌（最好先拿起茶包），直至冰糖融化。&lt;/li&gt;
&lt;li&gt;然后盖上盖子泡一晚。第二天放入冰箱。&lt;/li&gt;
&lt;li&gt;喝的时候切四五片柠檬先放入杯子，然后放三到四块冰块。然后倒入红茶就可以了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大概就是这种茶餐厅杯子的量能倒两杯（卖相有点丑，追求情调的可以再弄点花样）&lt;/p&gt;
&lt;p&gt;如果想要一次做多点每个数据按比相应乘 2；乘 3……就可以了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>用最简单的方式，在家做麻辣香锅</title>
      <link>http://coder.amazingdemo.top/post/%E5%9C%A8%E5%AE%B6%E5%81%9A%E9%BA%BB%E8%BE%A3%E9%A6%99%E9%94%85/</link>
      <pubDate>Mon, 22 Jul 2019 17:16:51 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E5%9C%A8%E5%AE%B6%E5%81%9A%E9%BA%BB%E8%BE%A3%E9%A6%99%E9%94%85/</guid>
      
        <description>&lt;p&gt;怎样在家里用最简单的方式做麻辣香锅？&lt;/p&gt;
&lt;p&gt;我开过一段时间的麻辣香锅小馆（自己的配料，而非加盟，口碑很不错），还算有一些经验，下面拿出来分享一下。&lt;/p&gt;
&lt;p&gt;先说调料：料油（楼上有说五香油的，一个东西）、猪油、葱油、藤椒油、红油（自己炼的）、郫县豆瓣、火锅底料、葱段、姜片、蒜瓣、麻椒、干红椒、灯笼椒、香芹、香菜、芝麻、香葱。&lt;/p&gt;
&lt;p&gt;再说配料：五花肉片、鲜虾（我的意思是别用虾仁）、丸类调理品（虾丸墨鱼丸撒尿牛丸等等）、鸡翅膀、羊肉段、午餐肉、鸡肉段、青笋（莴苣）、笋尖（罗汉笋）、藕片（这个很重要，提香、提味、提外形）、娃娃菜、油麦菜、魔芋丝、油豆皮、土豆条、木耳、生菜。&lt;/p&gt;
&lt;p&gt;至于选哪些食材配料，根据个人口味自行选择就可以，荤素比例也根据个人口味吧，太荤了也不见得解馋。&lt;/p&gt;
&lt;p&gt;下面说一下准备工作：&lt;/p&gt;
&lt;p&gt;调料方面，料油得自己炼一下，把香料（香叶、八角、桂皮、山奈、良姜、肉蔻、草果、大茴、丁香、麻椒等等）放在一个容器里，烧热油，用滚油激香料，用盖子闷住。&lt;/p&gt;
&lt;p&gt;炼猪油葱油就不用说了（在此严重并深深地鄙视一下，炼点葱油就卖很贵的那些投机商贩，大家想学，我可以教，很简单），藤椒油买瓶装现成的，郫县豆瓣和火锅底料也买现成的，丹丹豆瓣不错、大红袍底料也不错，葱切寸段姜切片备用。&lt;/p&gt;
&lt;p&gt;盐真用不上，火锅料和豆瓣已经提供了足够的咸味。&lt;/p&gt;
&lt;p&gt;鸡精鸡粉什么的，我做厨师从来不用。&lt;/p&gt;
&lt;p&gt;食材方面，五花肉、鸡肉等切好上浆后滑油煸熟，虾提前用油煸熟（可以焯水，不过味道没煸熟的香），丸类调理品放在水里煮熟，直接炒是热不透的。其他蔬菜类也是焯水焯熟，记得放点盐和油，菜会更绿。土豆条提前炸熟味道会更好，生菜不用焯水，直接炒。&lt;/p&gt;
&lt;p&gt;下面开始炒锅：&lt;/p&gt;
&lt;p&gt;料油猪油打底，葱姜蒜麻椒干红辣椒炝锅，香味出来就放调料，把切成块的火锅底料和剁碎的豆瓣放进锅内，再放一点糖提鲜，放一点番茄酱，一点点蚝油，东古酱油。翻炒至火锅料化开，放灯笼椒，放肉类和丸类还有香芹，翻炒至肉类全熟，放蔬菜，觉得颜色浅可以放那么一丢丢的老抽提色，颠锅翻炒均匀，淋上一点葱油提香、淋上藤椒油提麻味、淋上红油提色，再次翻炒均匀，关火，出锅。&lt;/p&gt;
&lt;p&gt;装入盆中，中间放上香菜碎和香葱碎，整体撒上芝麻，麻辣香锅就做好啦。&lt;/p&gt;
&lt;p&gt;至于炼好的猪油，猪油拌饭有人吃过吧，香吧~&lt;/p&gt;
&lt;p&gt;至于炼好的葱油，拌面啊，壮哉我大沙县小吃啊~&lt;/p&gt;
&lt;p&gt;补充一点，鸡翅，各人做法都不一样，我的做法是提前腌渍，然后挂糊炸熟，炒锅的时候跟肉类一起下锅，否则热不透。&lt;/p&gt;
&lt;p&gt;大概就是这样，好久不做了，想起来什么细节我再回来补充。就酱。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>使用boost.filesystem检查文件是否存在的正确方式</title>
      <link>http://coder.amazingdemo.top/post/cpp_%E4%BD%BF%E7%94%A8filesystem%E6%A3%80%E6%9F%A5%E6%96%87%E4%BB%B6%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%E7%9A%84%E6%AD%A3%E7%A1%AE%E6%96%B9%E5%BC%8F/</link>
      <pubDate>Wed, 19 Jun 2019 15:12:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/cpp_%E4%BD%BF%E7%94%A8filesystem%E6%A3%80%E6%9F%A5%E6%96%87%E4%BB%B6%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8%E7%9A%84%E6%AD%A3%E7%A1%AE%E6%96%B9%E5%BC%8F/</guid>
      
        <description>&lt;p&gt;在实际项目中，使用了boost.filesystem进行文件操作。boost.filesystem在发生错误的时候会抛出异常，但是在大部分情况下这些异常是可以忽略的，例如，在检查文件是否存在的时候，发生错误可以等同于文件不存在。虽然boost.filesystem也提供了重载函数，通过输出参数返回错误来代替异常，但是在每个调用点都得定义一个输出参数，稍显麻烦。所以，为了简化客户代码，我们实现了一些包装函数，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;bool IsFileExistent(const boost::filesystem::path&amp;amp; path) {
    boost::system:error_code error;
    return boost::filesystem::is_regular_file(path, error);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;上面的函数用来检查文件是否存在，使用了boost::filesystem::is_regular_file。当path指向一个“常规文件”的时候，认为该文件存在；否则其它任何情况都认为文件不存在。&lt;/p&gt;
&lt;p&gt;这个函数一直都很正常，直到最近有用户反馈，当发送OneDrive同步文件夹中的文件时，会出现文件不存在的提示，而这个文件明明是存在的。经过排查，我发现当OneDrive开启了“按需文件”功能时，boost::filesystem::is_regular_file对于同步文件夹中的文件会返回false。这个功能的设置界面如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/filesystem.png&#34; alt=&#34;filesystem&#34;&gt;&lt;/p&gt;
&lt;p&gt;当开启了“按需文件”功能时，OneDrive中的文件信息会同步到本地文件系统中，但文件内容不会立即同步，只有真正用到文件内容时才会同步下来。这样一来可以节省本地的硬盘空间。并不是所有Windows系统都支持该功能，只有安装了2017年10月份发布的秋季更新的Windows 10系统才可以。&lt;/p&gt;
&lt;p&gt;显然，当OneDrive中的文件还没有将内容同步下来时，它并不算是一个“常规文件”，难怪boost::filesystem::is_regular_file会返回false。事实上，用boost::filesystem::status获取这些文件的类型时，会返回symlink_file，boost.filesystem将它们视为符号链接文件。&lt;/p&gt;
&lt;p&gt;不论是常规文件还是符号链接文件，呈现给用户的都是能够正常使用的文件。所以，不能单纯地用boost::filesystem::is_regular_file来检查文件是否存在了，下面是包装函数的改进版本：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;bool IsFileExistent(const boost::filesystem::path&amp;amp; path) {

    boost::system:error_code error;
    auto file_status = boost::filesystem::status(path, error);
    if (error) {
        return false;
    }

    if (! boost::filesystem::exists(file_status)) {
        return false;
    }

    if (boost::filesystem::is_directory(file_status)) {
        return false;
    }

    return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;首先，通过boost::filesystem::status获取文件的信息，如果发生错误，则认为文件不存在。然后，使用boost::filesystem::exists判断文件是否存在，该函数不区分文件夹和文件，所以最后还要使用boost::filesystem::is_directory判断一下是否文件夹，只要不是文件夹，都认为文件是存在的。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>位图文件大小的精准计算方法</title>
      <link>http://coder.amazingdemo.top/post/%E4%BD%8D%E5%9B%BE%E6%96%87%E4%BB%B6%E5%A4%A7%E5%B0%8F%E7%9A%84%E7%B2%BE%E5%87%86%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95/</link>
      <pubDate>Wed, 19 Jun 2019 12:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%BD%8D%E5%9B%BE%E6%96%87%E4%BB%B6%E5%A4%A7%E5%B0%8F%E7%9A%84%E7%B2%BE%E5%87%86%E8%AE%A1%E7%AE%97%E6%96%B9%E6%B3%95/</guid>
      
        <description>&lt;h1 id=&#34;8位bit位图&#34;&gt;8位（bit）位图&lt;/h1&gt;
&lt;p&gt;彩色版中有2^8=256种颜色，具体哪256种颜色可由调色板灵活规定，因此每个像素点最多有256种情况（颜色），故刚好可用两位十六进制码（16^2=256）表示，占1字节。&lt;/p&gt;
&lt;p&gt;一幅512×512的8位位图大小计算方法：位图文件头（14字节）+ 位图信息头（40字节）+ 调色板（256×彩色表4字节）+ 实际像素点占内存（512×512×1字节）= 263222字节（Byte）。&lt;/p&gt;
&lt;h1 id=&#34;24位位图&#34;&gt;24位位图&lt;/h1&gt;
&lt;p&gt;又名RGB真彩色图，含2^24=16777216=16M色，没有彩色表，原因上文已说明。&lt;/p&gt;
&lt;p&gt;每个像素点由3个字节（十六进制码6位）表示，每个字节负责控制一种颜色，分别为蓝（Blue）、绿（Green）、红（Red）。为什么每种颜色用1个字节控制呢？我们知道，图像中任何颜色都是由蓝、绿、红混合而成，而在24位位图中这三种颜色的跨度（深度）分别为256=2^8，占二进制8bit=1字节，故蓝、绿、红刚好可用1个字节表示。那么每个像素点可能的颜色就有256×256×256=2^24种。&lt;/p&gt;
&lt;p&gt;一幅256×256的24位位图大小计算方法：位图文件头（14字节）+ 位图信息头（40字节）+ 实际像素点占内存（256×256×3字节）=196662字节（Byte）。&lt;/p&gt;
&lt;p&gt;需要注意的是，Windows有“补零”的习惯！即要求位图的每一行像素所占字节数必须被4整除。若不能倍4整除，则在该位图每一行的十六进制码末尾“补”1至3个字节的“00”。例如：一幅宽253×高256的24位位图，微软在生成该图为实际文件时，计算每一行像素所占字节=宽253×3字节=759字节，检验其被4除余1，则在每行的十六进制码末尾加1个字节，补“00”，变为760字节。&lt;/p&gt;
&lt;p&gt;因此我们计算该图大小时应先判断是否“补零”，再得出算法：该图大小 = 位图文件头（14字节）+ 位图信息头（40字节）+ 实际像素点占内存（高256×每行760字节）= 194614字节（Byte）。&lt;/p&gt;
&lt;p&gt;有趣的是，“补零”只针对位图的宽进行检验，一幅宽256×高253的24位位图，其大小 = 位图文件头（14字节）+ 位图信息头（40字节）+ 实际像素点占内存（高253×每行768字节）= 194358字节（Byte）&amp;lt; 196662字节（Byte）。这样，只是把此图像的宽和高颠倒，图像所占内存竟然变小了。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>NextCloud升级下载地址被墙</title>
      <link>http://coder.amazingdemo.top/post/nextcloud%E5%8D%87%E7%BA%A7%E4%B8%8B%E8%BD%BD%E5%9C%B0%E5%9D%80%E8%A2%AB%E5%A2%99/</link>
      <pubDate>Mon, 18 Feb 2019 13:27:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/nextcloud%E5%8D%87%E7%BA%A7%E4%B8%8B%E8%BD%BD%E5%9C%B0%E5%9D%80%E8%A2%AB%E5%A2%99/</guid>
      
        <description>&lt;p&gt;nextcloud下载地址被墙后，国内服务器更新nextcloud时，网页更新时会卡在第5步，下载失败。&lt;/p&gt;
&lt;p&gt;这时的解决方案是，手动下载nextcloud最新的的zip压缩包nextcloud_13.0.x.zip，将它上传到data/updater/downloads文件夹&lt;/p&gt;
&lt;p&gt;这时候登录nextcloud提示维护模式，应该做两个步骤&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一，在/config中把配置文件中的’maintenance’ 改为false&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;‘maintenance’ =&amp;gt; false&lt;/p&gt;
&lt;p&gt;也可以在Nextcloud根目录下执行：&lt;/p&gt;
&lt;p&gt;sudo -u www-data php occ  maintenance:mode &amp;ndash;off&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二 在data/updater中，有一个隐藏文件.step，告诉更新器现在安装到第几步了，手动更改它&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将{“state”:”start”,”step”:5} 更改为 {“state”:”stop”,”step”:6}&lt;/p&gt;
&lt;p&gt;注意，start改为stop，5改成6（即：跳过第五步下载,直接进入第六步）&lt;/p&gt;
&lt;p&gt;再次进入nextcloud，会看到更新器已停止。点击继续运行更新器，这时候更新器就默认完成了下载，从第6步开始了。接下来按照正常的更新步骤即可。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么越是修新路反而更堵了</title>
      <link>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%B6%8A%E6%98%AF%E4%BF%AE%E6%96%B0%E8%B7%AF%E5%8F%8D%E8%80%8C%E6%9B%B4%E5%A0%B5%E4%BA%86/</link>
      <pubDate>Mon, 17 Dec 2018 19:45:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%B6%8A%E6%98%AF%E4%BF%AE%E6%96%B0%E8%B7%AF%E5%8F%8D%E8%80%8C%E6%9B%B4%E5%A0%B5%E4%BA%86/</guid>
      
        <description>&lt;h1 id=&#34;引言&#34;&gt;引言&lt;/h1&gt;
&lt;p&gt;布雷斯悖论来自于一个真实的现象：城市的某个繁华路段特别的堵塞，人们在其边上新修一条道路，期望舒缓整个交通网路的堵塞状况。&lt;/p&gt;
&lt;p&gt;道路刚修好时特别令人兴奋，但很快，新修的道路也被严重堵塞了，而更令人困惑的是：原先堵塞的路网似乎并未从新修道路的分流中得到好处，也比之前更堵了。&lt;/p&gt;
&lt;p&gt;用更学术语言来表达就是：当在一个繁忙交通网上增加一条交通线路，新增线路不但没有减少原有的交通延滞，反而拉低了整个交通网络的服务水准(level ofservice)。这种现象与人类的常识相悖，被称为布雷斯悖论。&lt;/p&gt;
&lt;h1 id=&#34;描述&#34;&gt;描述&lt;/h1&gt;
&lt;p&gt;如果觉得不可思议，不妨看一下这个概念模型。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E5%B8%83%E9%9B%B7%E6%96%AF1.jpg&#34; alt=&#34;布雷斯1&#34;&gt;&lt;/p&gt;
&lt;p&gt;每天都有一批人必须从S点前往E点。&lt;/p&gt;
&lt;p&gt;而想要完成这段路程，共有2条线路，分别为SME或SWE。&lt;/p&gt;
&lt;p&gt;其中SW与ME的路况较好，无论有多少车辆驶过都只需要45分钟。&lt;/p&gt;
&lt;p&gt;但SM段与WE段就不同了，比较容易堵车：随着车流量x的增大，所需时长（x/100）也会增加。&lt;/p&gt;
&lt;p&gt;现在假设每天都会有4000辆小汽车会从起点S前往终点E。&lt;/p&gt;
&lt;p&gt;这时，一场博弈已经展开了，站在分叉路口上的老司机会选择走哪条路呢？&lt;/p&gt;
&lt;p&gt;如果有a人选择走SME路线，那么总时长则为a/100+45，而走另一条路SWE的总时长则为45+（4000-a）/100。&lt;/p&gt;
&lt;p&gt;由此可见，无论哪条路人更多，该路段的通行时间就会越长。&lt;/p&gt;
&lt;p&gt;当然，司机们一开始也无法得知其他人会如何选择路线，便会偏向于随机选择一边。&lt;/p&gt;
&lt;p&gt;司马亿每天上班，也同样要经历这样的抉择。&lt;/p&gt;
&lt;p&gt;但到后来，如果SME所花时间多，大家便会涌向SWE。&lt;/p&gt;
&lt;p&gt;而SWE的人多了所花时间变长了，人们又会自然回到SME。&lt;/p&gt;
&lt;p&gt;最终，这两条路都会平均分摊到2000辆的车流量，通行时间固定为2000/100+45=65分钟。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/%E5%B8%83%E9%9B%B7%E6%96%AF2.jpg&#34; alt=&#34;布雷斯2&#34;&gt;&lt;/p&gt;
&lt;p&gt;以上，便是修路之前的大致状况了。现在我们就来修路吧，看看会发生什么？&lt;/p&gt;
&lt;p&gt;如图所示，在MW之间新增一条快速通道，使得M与W连成一体。&lt;/p&gt;
&lt;p&gt;这条路畅通得“如飞一般的感觉”，所花时间几乎可以忽略不计，设为0。&lt;/p&gt;
&lt;p&gt;这对全体司机来说，无疑是件大好事。&lt;/p&gt;
&lt;p&gt;而且说MW路段是捷径都不为过，他们每一个人都能从这段新路中获得优势。&lt;/p&gt;
&lt;p&gt;在这种情况下，就算SM与WE路段挤满了人（即x=4000），这两段路的耗时也只需4000/100=40分钟。&lt;/p&gt;
&lt;p&gt;所以相比另外两段路（SW与ME）固定耗时45分钟，走SM与WE路段始终耗时更短。&lt;/p&gt;
&lt;p&gt;而我们也注意到，此刻原来两条路线SME与SWE均需要花费85分钟。&lt;/p&gt;
&lt;p&gt;经济学中假设每个司机都是自私自利的，他们必然会选择对自己最有益的路线。&lt;/p&gt;
&lt;p&gt;于是，所有人会毫不犹豫地选择SMWE这条新路。&lt;/p&gt;
&lt;p&gt;用80分钟（4000/100+0+4000/100=80）走完全程。&lt;/p&gt;
&lt;p&gt;但是回过头看看才发现，大家都被坑了。&lt;/p&gt;
&lt;p&gt;原本在不修这条该死的新路前，无论走哪条路都只需要65分钟。&lt;/p&gt;
&lt;p&gt;惊不惊喜，意不意外，多修了一条近路，司机的总通行时长反而增加了。&lt;/p&gt;
&lt;p&gt;我们可以注意到，正是“自私”造成了这种困境。&lt;/p&gt;
&lt;p&gt;事实上，如果所有司机约定好牺牲一下走回原来的老路，大家都会比现状好些，至少每人都能节省个15分钟。&lt;/p&gt;
&lt;p&gt;但在利益面前，人都是趋利的，这也是布雷斯悖论的关键所在。&lt;/p&gt;
&lt;p&gt;在这个4000人的博弈中，你个人的选择已经不太重要了，其他的3999人才是重点。&lt;/p&gt;
&lt;p&gt;于是我们可以看到，大家都竞相选择最有利的路线。&lt;/p&gt;
&lt;p&gt;最后却在无形中之中，使得交通系统崩溃。&lt;/p&gt;
&lt;h1 id=&#34;布雷斯悖论产生的原因&#34;&gt;布雷斯悖论产生的原因&lt;/h1&gt;
&lt;p&gt;人这种动物是趋利的，且作为个体身在网络中是无法窥探全局的，因此，其决策都是基于自身利益考虑。新建了一条快速的捷径，于是纷纷走这条路，结果造成了更大的拥堵。&lt;/p&gt;
&lt;p&gt;这也是纳什均衡中提到的，个体聪明选择的汇总，其实并非最优解。所以加入了新的道路，反而让所有人都陷入了“囚徒困境”式的纳什均衡。而大家都因忌讳损害自身的利益而选择抄近路，则是布雷斯悖论中的纳什均衡点。&lt;/p&gt;
&lt;p&gt;如果我们所有的汽车都是无人驾驶，机器计算，则能迅速计算出最优路线，做出网络效率最大化的决策，避免无谓的拥堵，提高整个城市的生产效率，人类的生产效率又会上升一个台阶。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>IIS下HTTP跳转HTTPS方法</title>
      <link>http://coder.amazingdemo.top/post/iis%E4%B8%8Bhttp%E8%B7%B3%E8%BD%AChttps%E6%96%B9%E6%B3%95/</link>
      <pubDate>Fri, 12 Oct 2018 12:07:49 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/iis%E4%B8%8Bhttp%E8%B7%B3%E8%BD%AChttps%E6%96%B9%E6%B3%95/</guid>
      
        <description>&lt;p&gt;下载URL重写组件，IIS是带有URL重写功能组件的，但是并没有集成到IIS中去，要自行下载安装，安装成功后在网站配置页面就会出现URL重写组件。&lt;/p&gt;
&lt;p&gt;微软下载地址(64位)：www.microsoft.com/zh-cn/download/details.aspx?id=7435&lt;/p&gt;
&lt;p&gt;微软下载地址(32位)：www.microsoft.com/zh-cn/download/details.aspx?id=5747&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https1.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;双击进去URL重写， 在右侧点击添加规则，选择空白规则，确定。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https2.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;设置规则名称，设置匹配URL，注意模式输入：  (.*)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https3.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加条件，先添加一个HTTP_HOST的不匹配条件，注意：是与模式不匹配，模式为：  ^(localhost)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https4.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;添加一个HTTPS的匹配条件，注意：是与模式匹配，模式为：  ^OFF$&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https5.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;最后配置重写操作，按照图上实例选择填写。https://{HTTP_HOST}/{R:1}&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/https6.png&#34; alt=&#34;http跳转Https&#34;&gt;&lt;/p&gt;
&lt;p&gt;点击右上角的应用后，重启IIS服务。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Win2012下部署多个HTTPS网站</title>
      <link>http://coder.amazingdemo.top/post/win2012%E4%B8%8B%E9%83%A8%E7%BD%B2%E5%A4%9A%E4%B8%AAhttps%E7%BD%91%E7%AB%99/</link>
      <pubDate>Fri, 12 Oct 2018 10:03:40 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/win2012%E4%B8%8B%E9%83%A8%E7%BD%B2%E5%A4%9A%E4%B8%AAhttps%E7%BD%91%E7%AB%99/</guid>
      
        <description>&lt;p&gt;当一个https的请求到达IIS服务器时，https请求为加密状态，需要拿到相应的服务器证书解密请求。由于每个站点对应的证书不同，服务器需要通过请求中不同的主机头来判断需要用哪个证书解密，然而主机头作为请求的一部分也被加密。最终IIS只好使用第一个绑定到该IP：PORT的站点证书解密请求，从而有可能造成对于其他站点的请求失败而报错。&lt;/p&gt;
&lt;h1 id=&#34;解决方法&#34;&gt;解决方法&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;将每个https站点绑定到不同的端口。但是这样的话客户端浏览网页时必须手动指定端口，例如 https：//domain.com:444&lt;/li&gt;
&lt;li&gt;为每个站点分配一个独立的ip，这样冲突就解决了，甚至主机头也不用添加了。&lt;/li&gt;
&lt;li&gt;使用通配证书。我们采用通配证书颁发给.domain.com，这样任何访问该domain的请求均可以通过该证书解密，证书匹配错误也就不复存在了。&lt;/li&gt;
&lt;li&gt;升级为IIS8，IIS8中添加的对于SNI(Server Name Indication)的支持，服务器可以通请求中提取出相应的主机头从而找到相应的证书。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;方案分析&#34;&gt;方案分析&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;明显不太现实，我们不能让用户在访问我们的网站时还要指定端口号，一来不太好记，二来用户体验不好；&lt;/li&gt;
&lt;li&gt;虽然可行，但由于我们使用的是阿里云的服务器，是固定IP，没办法再给服务器再分配一个IP，故该方案不适合我们的场景；&lt;/li&gt;
&lt;li&gt;虽然可行，但我们使用的是域名型SSL证书，而且要购买通配型SSL证书的话，价格也比较贵，故该方案不适合我们的场景；&lt;/li&gt;
&lt;li&gt;至少需要使用Windows Server 2012，也就是IIS版本至少要是IIS8，不是IIS8的可以自行升级为IIS8，在IIS8中，因为添加了对SNI的支持（具体概念及原理，读者可自行查阅相关资料，这里就不多说），所以可以很方便的解决我们所面临的问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;操作步骤&#34;&gt;操作步骤&lt;/h1&gt;
&lt;p&gt;安装服务器证书，进入IIS，点击主机名，选择服务器证书，点击右侧导入，选择自己的证书文件进行导入。导入的时候，一定要勾选“允许导出证书”，否则会部署不成功。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/iis3.png&#34; alt=&#34;多HTTPS配置&#34;&gt;&lt;/p&gt;
&lt;p&gt;在第一个网站绑定SSL证书，不要绑定IP地址填写主机名（作为默认的SSL证书，开启SNI需要配置有默认SSL证书），绑定对应域名的证书文件，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/iis2.png&#34; alt=&#34;多HTTPS配置&#34;&gt;&lt;/p&gt;
&lt;p&gt;然后选择第二个网站，绑定对应的SSL证书，这时您就需要填写主机名，同时勾选上“需要服务器名称指示（N）”，选择对应的证书文件，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/iis1.png&#34; alt=&#34;多HTTPS配置&#34;&gt;&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>解决CEFSharp关闭时主程序退出的问题</title>
      <link>http://coder.amazingdemo.top/post/dotnet_%E8%A7%A3%E5%86%B3cefsharp%E5%85%B3%E9%97%AD%E6%97%B6%E4%B8%BB%E7%A8%8B%E5%BA%8F%E9%80%80%E5%87%BA%E7%9A%84%E9%97%AE%E9%A2%98/</link>
      <pubDate>Thu, 30 Aug 2018 17:26:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/dotnet_%E8%A7%A3%E5%86%B3cefsharp%E5%85%B3%E9%97%AD%E6%97%B6%E4%B8%BB%E7%A8%8B%E5%BA%8F%E9%80%80%E5%87%BA%E7%9A%84%E9%97%AE%E9%A2%98/</guid>
      
        <description>&lt;p&gt;昨天在写代码时，碰到了一个问题。&lt;/p&gt;
&lt;p&gt;Form里面有一个TabControl，里面有多个TabPage，每个TabPage里面都放了一个CefSharp.WinForms.ChromiumWebBrowser用来展示网页。但是无论关闭任何一个Tabpage，主程序都会退出，并且在输出页面报 &lt;em&gt;“程序[2508] XXX.vshost.exe 已退出，返回值为 -1073741819 (0xc0000005) &amp;lsquo;Access violation&amp;rsquo;。”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;从这个报错上来看，好像是访问受限，也就是进程在访问已被释放的资源。&lt;/p&gt;
&lt;p&gt;于是着手解决资源释放问题，试了好多种办法都不奏效。只要一关闭任何一个TabPage，主程序都会自动关闭并且抛异常。&lt;/p&gt;
&lt;p&gt;最坑爹的是，官方示例里面的“CefSharp.WinForms.Example”也是错误的，也会在关闭TabPage的时候释放资源，抛异常。&lt;/p&gt;
&lt;p&gt;最终，皇天不负有心人，终于还是找到了解决途径。&lt;/p&gt;
&lt;p&gt;在程序初始化的时候，我们需要初始化浏览器参数，以前我都是这样初始化的：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CefSharp.CefSettings setting = new CefSharp.CefSettings();
CefSharp.Cef.Initialize(setting);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;但是其实应该这样初始化：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;CefSharp.CefSettings setting = new CefSharp.CefSettings();
CefSharp.Cef.Initialize(setting, false, false);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;看接口注释：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;//&lt;/p&gt;
&lt;p&gt;// 摘要:&lt;/p&gt;
&lt;p&gt;//     Initializes CefSharp with user-provided settings.  This function should be&lt;/p&gt;
&lt;p&gt;//     called on the main application thread to initialize the CEF browser process.&lt;/p&gt;
&lt;p&gt;//&lt;/p&gt;
&lt;p&gt;// 参数:&lt;/p&gt;
&lt;p&gt;//   cefSettings:&lt;/p&gt;
&lt;p&gt;//     CefSharp configuration settings.&lt;/p&gt;
&lt;p&gt;//&lt;/p&gt;
&lt;p&gt;//   shutdownOnProcessExit:&lt;/p&gt;
&lt;p&gt;//     When the Current AppDomain (relative to the thread called on) Exits(ProcessExit&lt;/p&gt;
&lt;p&gt;//     event) then Shudown will be called.&lt;/p&gt;
&lt;p&gt;//&lt;/p&gt;
&lt;p&gt;//   performDependencyCheck:&lt;/p&gt;
&lt;p&gt;//     Check that all relevant dependencies avaliable, throws exception if any are&lt;/p&gt;
&lt;p&gt;//     missing&lt;/p&gt;
&lt;p&gt;public static bool Initialize(CefSettings cefSettings, bool shutdownOnProcessExit, bool performDependencyCheck);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第二个参数就是用来设置是否在退出的时候释放资源，默认为true，就会在当前页面退出的时候调用Shudown。然后Shudown就会把整个浏览器的资源释放掉。当浏览器资源被释放了以后，其他页面访问资源就变成了空指针访问，然后就会挂掉。&lt;/p&gt;
&lt;p&gt;所以我们如果是做多页面窗口的话，需要手动将这个参数置为false。这样才能保证一个页面关闭时，其他页面不受影响。&lt;/p&gt;
&lt;p&gt;至于当前被关闭页面的资源释放，我们需要显示的调用ChromiumWebBrowser的父控件的Dispose方法。如下官方示例代码所示：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;private void CloseTabToolStripMenuItemClick(object sender, EventArgs e)
{
    var currentIndex = browserTabControl.SelectedIndex;
    var tabPage = browserTabControl.Controls[currentIndex];

    var control = GetCurrentTabControl();
    if (control != null)
    {
        control.Dispose();
    }

    browserTabControl.Controls.Remove(tabPage);
    tabPage.Dispose();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;最后，在主程序退出时，需要显式的调用CefSharp.Cef.Shutdown()，用来释放资源；比如FormClosing的时候。&lt;/p&gt;
&lt;p&gt;另外，CefSharp.Cef.Initialize需要在和ChromiumWebBrowser使用的地方放在一起，不可跨DLL初始化。因为静态函数，静态变量是无法跨DLL使用的。所以如果初始化和使用的地方不在同一个DLL内，则无效。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>CefSharp屏蔽右键菜单</title>
      <link>http://coder.amazingdemo.top/post/dotnet_cefsharp%E5%B1%8F%E8%94%BD%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95/</link>
      <pubDate>Wed, 29 Aug 2018 15:12:50 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/dotnet_cefsharp%E5%B1%8F%E8%94%BD%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95/</guid>
      
        <description>&lt;p&gt;实现接口：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;internal class MenuHandler : CefSharp.IContextMenuHandler
{
    public bool OnContextMenuCommand(CefSharp.IWebBrowser browserControl, CefSharp.IBrowser browser, CefSharp.IFrame frame, CefSharp.IContextMenuParams parameters, CefSharp.CefMenuCommand commandId, CefSharp.CefEventFlags eventFlags)
    {
        System.Console.WriteLine(@&amp;#34;OnContextMenuCommand&amp;#34;);
        return false;
    }

    public void OnContextMenuDismissed(CefSharp.IWebBrowser browserControl, CefSharp.IBrowser browser, CefSharp.IFrame frame)
    {
        System.Console.WriteLine(@&amp;#34;OnContextMenuDismissed&amp;#34;);
    }

    public bool RunContextMenu(CefSharp.IWebBrowser browserControl, CefSharp.IBrowser browser, CefSharp.IFrame frame, CefSharp.IContextMenuParams parameters, CefSharp.IMenuModel model, CefSharp.IRunContextMenuCallback callback)
    {
        System.Console.WriteLine(@&amp;#34;RunContextMenu&amp;#34;);
        return false;
    }

    public void OnBeforeContextMenu(CefSharp.IWebBrowser browserControl, CefSharp.IBrowser browser, CefSharp.IFrame frame, CefSharp.IContextMenuParams parameters, CefSharp.IMenuModel model)
    {
        System.Console.WriteLine(@&amp;#34;OnBeforeContextMenu&amp;#34;);
        model.Clear();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;调用：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;_chromiumWebBrowser = new CefSharp.Wpf.ChromiumWebBrowser();
_chromiumWebBrowser.MenuHandler = new MenuHandler();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;之后运行，会发现已经屏蔽鼠标右键。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>C语言函数指针基础</title>
      <link>http://coder.amazingdemo.top/post/c_c%E8%AF%AD%E8%A8%80%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88%E5%9F%BA%E7%A1%80/</link>
      <pubDate>Mon, 18 Jun 2018 10:45:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/c_c%E8%AF%AD%E8%A8%80%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88%E5%9F%BA%E7%A1%80/</guid>
      
        <description>&lt;p&gt;本文写的非常详细，因为我想为初学者建立一个意识模型，来帮助他们理解函数指针的语法和基础。如果你不讨厌事无巨细，请尽情阅读吧。&lt;/p&gt;
&lt;p&gt;函数指针虽然在语法上让人有些迷惑，但不失为一种有趣而强大的工具。本文将从C语言函数指针的基础开始介绍，再结合一些简单的用法和关于函数名称和地址的趣闻。在最后，本文给出一种简单的方式来看待函数指针，让你对其用法有一个更清晰的理解。&lt;/p&gt;
&lt;h1 id=&#34;函数指针和一个简单的函数&#34;&gt;函数指针和一个简单的函数&lt;/h1&gt;
&lt;p&gt;我们从一个非常简单的”Hello World“函数入手，来见识一下怎样创建一个函数指针。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
 
// 函数原型
void sayHello();
 
//函数实现
void sayHello(){
    printf(&amp;#34;hello world\n&amp;#34;);
}
 
// main函数调用
int main() {
    sayHello();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;我们定义了一个名为sayHello的函数，它没有返回值也不接受任何参数。当我们在main函数中调用它的时候，它向屏幕输出出”hello world“。非常简单。接下来，我们改写一下main函数，之前直接调用的sayHello函数，现在改用函数指针来调用它。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main() {
    void (*sayHelloPtr)() = sayHello;
    (*sayHelloPtr)();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;第二行void (*sayHelloPtr)()的语法看起来有些奇怪，我们来一步一步分析。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这里，关键字void的作用是说我们创建了一个函数指针，并让它指向了一个返回void（也就是没有返回值）的函数。&lt;/li&gt;
&lt;li&gt;就像其他任何指针都必须有一个名称一样，这里sayHelloPtr被当作这个函数指针的名称。&lt;/li&gt;
&lt;li&gt;我们用*符号来表示这是一个指针，这跟声明一个指向整数或者字符的指针没有任何区别。&lt;/li&gt;
&lt;li&gt;*sayHelloPtr两端的括号是必须的，否则，上述声明变成void *sayHelloPtr()，*会优先跟void结合，变成了一个返回指向void的指针的普通函数的声明。因此，函数指针声明的时候不要忘记加上括号，这非常关键。&lt;/li&gt;
&lt;li&gt;参数列表紧跟在指针名之后，这个例子中由于没有参数，所以是一对空括号()。&lt;/li&gt;
&lt;li&gt;将上述要点结合起来，void (*syaHelloPtr)()的意义就非常清楚了，这是一个函数指针，它指向一个不接收参数且没有返回值的函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在上面的第二行代码，即void (*sayHelloPtr)() = sayHello;，我们将sayHello这个函数名赋给了我们新建的函数指针。关于函数名的更多细节我们会在下文中讨论，现在暂时可以将其看作一个标签，它代表函数的地址，并且可以赋值给函数指针。这就跟语句int *x = &amp;amp;myint;中我们把myint的地址赋给一个指向整数的指针一样。只是当我们考虑函数的时候，我们不需要加上一个取地址符&amp;amp;。简而言之，函数名就是它的地址。接着看第三行，我们用代码’(*sayHelloPtr)();·‘解引用并调用了函数指针。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在第二行被声明之后，sayHelloPtr作为函数指针的名称，跟其他任何指针没有差别，能够储值和赋值。&lt;/li&gt;
&lt;li&gt;我们对sayHelloPtr解引用的方式也与其他任何指针一样，即在指针之前使用解引用符*，也就是代码中的*sayHelloPtr。&lt;/li&gt;
&lt;li&gt;同样的，我们需要在其两端加上括号，即(*sayHelloPtr)，否则它就不被当做一个函数指针。因此，记得声明和解引用的时候都要在两端加上括号。&lt;/li&gt;
&lt;li&gt;括号操作符用于C语言中的函数调用，如果有参数参与，就将其放入括号中。这对于函数指针也是相似的，即代码中的(*sayHelloPtr)()。&lt;/li&gt;
&lt;li&gt;这个函数没有返回值，也就没有必要将它赋值给任何变量。单独来说，这个调用跟sayHello()没什么两样。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来，我们再对函数稍加修改。你会看到函数指针奇怪的语法，以及用调用普通函数的方法来调用赋值后函数指针的现象。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int main() {
void (*sayHelloPtr)() = sayHello;
sayHelloPtr();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;跟之前一样，我们将sayHello函数赋给函数指针。但是这一次，我们用调用普通函数的方法调用了它。稍后讨论函数名的时候我会解释这一现象，现在只需要知道(*syaHelloPtr)()和syaHelloPtr()是相同的即可。&lt;/p&gt;
&lt;h1 id=&#34;带参数的函数指针&#34;&gt;带参数的函数指针&lt;/h1&gt;
&lt;p&gt;好了，这一次我们来创建一个新的函数指针吧。它指向的函数仍然不返回任何值，但有了参数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
 
//函数原型
void subtractAndPrint(int x, int y);
 
//函数实现
void subtractAndPrint(int x, int y) {
    int z = x - y;
    printf(&amp;#34;Simon says, the answer is: %d\n&amp;#34;, z);
}
 
//main函数调用
int main() {
    void (*sapPtr)(int, int) = subtractAndPrint;
    (*sapPtr)(10, 2);
    sapPtr(10, 2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;跟之前一样，代码包括函数原型，函数实现和在main函数中通过函数指针执行的语句。原型和实现中的特征标变了，之前的sayHello函数不接受任何参数，而这次的函数subtractAndPrint接受两个int作为参数。它将两个参数做一次减法，然后输出到屏幕上。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在第14行，我们通过’(*sapPtr)(int, int)’创建了sapPtr这个函数指针，与之前的区别仅仅是用(int, int)代替了原来的空括号。而这与新函数的特征标相符。&lt;/li&gt;
&lt;li&gt;在第15行，解引用和执行函数的方式与之前完全相同，只是在括号中加入了两个参数，变成了(10, 2)。&lt;/li&gt;
&lt;li&gt;在第16行，我们用调用普通函数的方法调用了函数指针。&lt;/li&gt;
&lt;li&gt;带参数且有返回值的函数指针&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一次，我们把subtractAndPrint函数改成一个名为subtract的函数，让它把原本输出到屏幕上的结果作为返回值。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
 
// 函数原型
int subtract(int x, int y);
 
// 函数实现
int subtract(int x, int y) {
    return x - y;
}
 
// main函数调用
int main() {
  int (*subtractPtr)(int, int) = subtract;
 
  int y = (*subtractPtr)(10, 2);
  printf(&amp;#34;Subtract gives: %d\n&amp;#34;, y);
 
  int z = subtractPtr(10, 2);
  printf(&amp;#34;Subtract gives: %d\n&amp;#34;, z);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这与subtractAndPrint函数非常相似，只是subtract函数返回了一个整数而已，特征标也理所当然的不一样了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在第13行，我们通过int (*subtractPtr)(int, int)创建了subtractPtr这个函数指针。与上一个例子的区别只是把void换成了int来表示返回值。而这与subtract函数的特征标相符。&lt;/li&gt;
&lt;li&gt;在在第15行，解引用和执行这个函数指针，除了将返回值赋值给了y以外，与调用subtractAndPrint没有任何区别。&lt;/li&gt;
&lt;li&gt;在第16行，我们向屏幕输出了返回值。&lt;/li&gt;
&lt;li&gt;18到19行，我们用调用普通函数的方法调用了函数指针，并且输出了结果。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这跟之前没什么两样，我们只是加上了返回值而已。接下来我们看看另一个稍微复杂点儿的例子——把函数指针作为参数传递给另一个函数。&lt;/p&gt;
&lt;h1 id=&#34;把函数指针作为参数来传递&#34;&gt;把函数指针作为参数来传递&lt;/h1&gt;
&lt;p&gt;我们已经了解过了函数指针声明和执行的各种情况，不论它是否带参数，或者是否有返回值。接下来我们利用一个函数指针来根据不同的输入执行不同的函数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
 
// 函数原型
int add(int x, int y);
int subtract(int x, int y);
int domath(int (*mathop)(int, int), int x, int y);
 
// 加法 x+ y
int add(int x, init y) {
    return x + y;
}
 
// 减法 x - y
int subtract(int x, int y) {
    return x - y;
}
 
// 根据输入执行函数指针
int domath(int (*mathop)(int, int), int x, int y) {
    return (*mathop)(x, y);
}
 
// main函数调用
int main() {
 
// 用加法调用domath
int a = domath(add, 10, 2);
printf(&amp;#34;Add gives: %d\n&amp;#34;, a);
 
// 用减法调用domath
int b = domath(subtract, 10, 2);
printf(&amp;#34;Subtract gives: %d\n&amp;#34;, b);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;我们来一步一步分析。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们有两个特征标相同的函数，add和subtract，它们都返回一个整数并接受两个整数作为参数。&lt;/li&gt;
&lt;li&gt;在第六行，我们定义了函数int domath(int (*mathop)(int, int), int x, int y)。它第一个参数int (*mathop)(int, int)是一个函数指针，指向返回一个整数并接受两个整数作为参数的函数。这就是我们之前见过的语法，没有任何不同。它的后两个整数参数则作为简单的输入。因此，这是一个接受一个函数指针和两个整数作为参数的函数。&lt;/li&gt;
&lt;li&gt;19到21行，domath函数将自己的后两个整数参数传递给函数指针并调用它。当然，也可以像这么调用。mathop(x, y);&lt;/li&gt;
&lt;li&gt;27到31行出现了我们没见过的代码。我们用函数名作为参数调用了domath函数。就像我之前说过的，函数名是函数的地址，而且能代替函数指针使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;main函数调用了两次domath函数，一次用了add，一次用了subtract，并输出了这两次结果。&lt;/p&gt;
&lt;h1 id=&#34;函数名和地址&#34;&gt;函数名和地址&lt;/h1&gt;
&lt;p&gt;既然有约在先，那我们就讨论一下函数名和地址作为结尾吧。一个函数名（或称标签），被转换成了一个指针本身。这表明在函数指针被要求当作输入的地方，就能够使用函数名。这也导致了一些看起来很糟糕的代码却能够正确的运行。瞧瞧下面这个例子。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
 
// 函数原型
void add(char *name, int x, int y);
 
// 加法 x + y
void add(char *name, int x, int y) {
    printf(&amp;#34;%s gives: %d\n&amp;#34;, name, x + y);
}
 
// main函数调用
int main() {
 
    // 一些糟糕的函数指针赋值
    void (*add1Ptr)(char*, int, int) = add;
    void (*add2Ptr)(char*, int, int) = *add;
    void (*add3Ptr)(char*, int, int) = &amp;amp;add;
    void (*add4Ptr)(char*, int, int) = **add;
    void (*add5Ptr)(char*, int, int) = ***add;
 
    // 仍然能够正常运行
    (*add1Ptr)(&amp;#34;add1Ptr&amp;#34;, 10, 2);
    (*add2Ptr)(&amp;#34;add2Ptr&amp;#34;, 10, 2);
    (*add3Ptr)(&amp;#34;add3Ptr&amp;#34;, 10, 2);
    (*add4Ptr)(&amp;#34;add4Ptr&amp;#34;, 10, 2);
    (*add5Ptr)(&amp;#34;add5Ptr&amp;#34;, 10, 2);
 
    // 当然，这也能运行
    add1Ptr(&amp;#34;add1PtrFunc&amp;#34;, 10, 2);
    add2Ptr(&amp;#34;add2PtrFunc&amp;#34;, 10, 2);
    add3Ptr(&amp;#34;add3PtrFunc&amp;#34;, 10, 2);
    add4Ptr(&amp;#34;add4PtrFunc&amp;#34;, 10, 2);
    add5Ptr(&amp;#34;add5PtrFunc&amp;#34;, 10, 2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这是一个简单的例子。运行这段代码，你会看到每个函数指针都会执行，只是会收到一些关于字符转换的警告。但是，这些函数指针都能正常工作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在第15行，add作为函数名，返回这个函数的地址，它被隐式的转换为一个函数指针。我之前提到过，在函数指针被要求当作输入的地方，就能够使用函数名。&lt;/li&gt;
&lt;li&gt;在第16行，解引用符作用于add之前，即*add，在返回在这个地址的函数。之后跟函数名一样，它被隐式的转换为一个函数指针。&lt;/li&gt;
&lt;li&gt;在第17行，取地址符作用于add之前，即&amp;amp;add，返回这个函数的地址，之后又得到一个函数指针。&lt;/li&gt;
&lt;li&gt;18到19行，add不断地解引用自身，不断返回函数名，并被转换为函数指针。到最后，它们的结果都和函数名没有区别。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;显然，这段代码不是优秀的实例代码。我们从中收获到了如下知识：其一，函数名会被隐式的转换为函数指针，就像作为参数传递的时候，数组名被隐式的转换为指针一样。在函数指针被要求当作输入的任何地方，都能够使用函数名。其二，解引用符*和取地址符&amp;amp;用在函数名之前基本上都是多余的。&lt;/p&gt;
&lt;h1 id=&#34;总结&#34;&gt;总结&lt;/h1&gt;
&lt;p&gt;我希望本文帮助你们认清了函数指针以及它的用途。只要你掌握了函数指针，它就是C语言中一个强大的工具。我也许会在以后的文章中讲述更多函数指针的细节用法，包括回调和C语言中基本的面向对象等等。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>解读C指针</title>
      <link>http://coder.amazingdemo.top/post/c_%E8%A7%A3%E8%AF%BBc%E6%8C%87%E9%92%88/</link>
      <pubDate>Mon, 18 Jun 2018 09:45:20 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/c_%E8%A7%A3%E8%AF%BBc%E6%8C%87%E9%92%88/</guid>
      
        <description>&lt;p&gt;我想对很多学习C语言的新手来说，指针无疑是一个难点。但是，我觉得指针也是C语言特别重要的一个特性。也许，你在除了C和C++以外的编程语言中，很少看到指针。而C++中，也多用引用，而非指针。指针，作为一种高效的工具，可谓是一把双刃剑——用得好，可以大大提高程序效率，但用的不好，就是很多bug的滋生地。&lt;/p&gt;
&lt;p&gt;这或许也是人们对指针褒贬不一的原因吧。就我个人而言，我还是很喜欢这个特性，因为我需要经常和硬件以及一些底层的软件打交道。这个时候，指针便体现出它独特的魅力。指针的知识很多，有一本经典的书叫《C和指针》，如果有兴趣可以读一读。这里，我主要总结一些如何去解读指针（说实话这个东西实在是很容易让人困惑）的方法，一方面给自己做查询用，另一方面，希望可以给别人一些帮助。&lt;/p&gt;
&lt;h1 id=&#34;基本概念&#34;&gt;基本概念&lt;/h1&gt;
&lt;p&gt;关于指针的基本概念，我就不详细介绍了，因为有许多书都介绍的很详细。这里我只介绍一部分。指针指向一个地址，而指针本身在大多数系统上都是一个无符号整数(在32bit机上是4byte，在64bit机上是8byte)。下面用一个例子来说明其机制：&lt;/p&gt;
&lt;p&gt;在上面的例子中，先定义了一个指针p，它的类型是int，也就是说它只能指向一个int型的变量，而不能指向其他类型的变量。最后我们将a变量的地址赋给p。在这个过程中，涉及到两个内存块，一个是存放指针p的内存(用&amp;amp;p可得到内存地址)，一个是存放a的值的内存块(用&amp;amp;a可以得到内存地址)。而第一个内存存的p的值经过赋值语句后也就是&amp;amp;a的值了。另外一个注意点是， *(星号)和变量类型以及变量名之间可以有任意个空格，也可以没有。比如下面三种方式都是一样的：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int a = 10;
int *p;    //声明一个指针，但未初始化，此时为野指针
p = &amp;amp;a;     //将a变量的地址赋给指针p&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;在上面的例子中，先定义了一个指针p，它的类型是int，也就是说它只能指向一个int型的变量，而不能指向其他类型的变量。最后我们将a变量的地址赋给p。在这个过程中，涉及到两个内存块，一个是存放指针p的内存(用&amp;amp;p可得到内存地址)，一个是存放a的值的内存块(用&amp;amp;a可以得到内存地址)。而第一个内存存的p的值经过赋值语句后也就是&amp;amp;a的值了。另外一个注意点是， *(星号)和变量类型以及变量名之间可以有任意个空格，也可以没有。比如下面三种方式都是一样的：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int* a;
int * a;
int *a;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;解读方法:&lt;/p&gt;
&lt;p&gt;首先从标示符开始阅读，然后往右读，每遇到圆括号的右半边就调转阅读方向。重复这个过程直到整个声明解析完毕。需要注意的是，已经读过的部分在后续作为一个整体来看。&lt;/p&gt;
&lt;p&gt;看下面一个例子：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;int *a[3];
//首先a右边是[]，说明a是一个具有3个元素的数组
//右边读完，则读左边。a左边是int*，说明a的元素是int类型的指针
 
int (*a)[3]
//首先，a右边是圆括号的右半边，转向，左边是一个*，说明a是一个指针
//遇到括号，再转向，是一个[]，说明a是一个指向3个元素的数组的指针
//左边是int，说明元素类型是int
//所以，a是一个指向具有3个整型元素的数组的指针
 
int (*func)(int p);
//相同的方法，func首先是一个指针
//然后右边是一个括号，说明(func)是个函数，而func是指向这个函数的指针
//这个函数具有int类型的参数，返回值类型为int
 
int (*func[3])(int p);
//同理，func首先是一个具有3个元素的数组
//其次，func左边是一个*，说明func数组的元素是指针。要注意修饰的是func[3]，而不是func。因为已经读过的部分在后面都作为一个整体来对待
//跳出第一个圆括号，右边又是一个圆括号，说明func数组的元素是函数类型的指针。这个函数具有int类型的参数和int型返回值&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;数组首地址aaa0&#34;&gt;数组首地址a，&amp;amp;a，&amp;amp;a[0]&lt;/h1&gt;
&lt;p&gt;注：a，&amp;amp;a，&amp;amp;a[0]的含义虽然不同，但是他们三个的值是相等的！&lt;/p&gt;
&lt;p&gt;以int a[3]为例说明：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a作为右值时，代表数组首元素的首地址，而非数组地址。 也就是a[0]的地址。int i = (a+1)，这里a是右值，所以代表首元素的首地址，a+1代表下一个元素的首地址，即&amp;amp;a[1]。&lt;/li&gt;
&lt;li&gt;a是整个数组的名字。所以sizeof(a)的值为sizeof(int) * 3 = 40，代表整个数组的大小。&lt;/li&gt;
&lt;li&gt;&amp;amp;a即为取a的首地址，也即整个数组的首地址。所以sizeof(&amp;amp;a) = 4。 int p = (int)(&amp;amp;a+1)中的&amp;amp;a+1代表下一个数组的首地址，显然是越界的。&lt;/li&gt;
&lt;li&gt;&amp;amp;a[0]代表首元素的首地址。 所以sizeof(&amp;amp;a[0]) = 4。&lt;/li&gt;
&lt;li&gt;&amp;amp;a[3]，很显然数组越界了，但它的sizeof是多少呢？ 也是4，因为关键字sizeof求值是在编译的时候，虽然并不存在a[3]这个元素，但是这里并没有真正访问a[3]，而是根据数组元素类型来确定其值的。所以sizeof(a[3])不会出错。&lt;/li&gt;
&lt;li&gt;a[-1]代表什么意思？首先要明白下标的形式被编译器解析成指针的形式，即a[1]被解析成(a+1)。那么，a[-1]被解析成*(a-1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关于数组首元素的首地址和数组的首地址的区别：其实，数组首元素的首地址和数组首地址的值是相同的，即&amp;amp;a[0]和a（以及&amp;amp;a）是相等的，但是而这含义不一样。首元素的首地址加1后，是第二个元素的首地址（之所以一直说首地址，是因为有的类型存储时会占多个地址），但数组的首地址加1后是“下一个数组的地址”，这里的下一个数组只是为了说明加1时加了整个数组的大小，而不是一个元素的大小。&lt;/p&gt;
&lt;p&gt;有一点比较容易混淆：a虽然代表整个数组，但(a+1)却代表下一个元素的首地址，即和(&amp;amp;a[0]+1)一样，下一个数组的形式为：(&amp;amp;a+1)。 下面以一个程序来说明：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include&amp;lt;stdio.h&amp;gt;
int main()
{
    int a[3] = {1, 2, 3};
    printf(&amp;#34;%ld\n&amp;#34;,sizeof(long unsigned int));
    printf(&amp;#34;*(a+1)=%d\n&amp;#34;,*(a+1));
    printf(&amp;#34;sizeof(a)=%ld\n&amp;#34;, sizeof(a));
    printf(&amp;#34;sizeof(&amp;amp;a[3])=%ld\n&amp;#34;, sizeof(&amp;amp;a[3]));
    printf(&amp;#34;a[-1]=%d\t*(a-1)=%d\n&amp;#34;,a[-1],*(a-1));
    printf(&amp;#34;a=%p\t&amp;amp;a=%p\t&amp;amp;a[0]=%p\n&amp;#34;,a, &amp;amp;a,&amp;amp;a[0]);
    printf(&amp;#34;a=%p\t(a+1)=%p\t(&amp;amp;a+1)=%p\n&amp;#34;,a,(a+1),(&amp;amp;a+1));
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;输出结果：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;8
*(a+1)=2
sizeof(a)=12
sizeof(&amp;amp;a[3])=8
a[-1]=0 *(a-1)=0
a=0x7fffcb4cb980        &amp;amp;a=0x7fffcb4cb980       &amp;amp;a[0]=0x7fffcb4cb980
a=0x7fffcb4cb980        (a+1)=0x7fffcb4cb984    (&amp;amp;a+1)=0x7fffcb4cb98c&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;说明（下面的行数只计算main函数内有代码的行）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;程序第1行定义了一个具有3个元素的整型数组。&lt;/li&gt;
&lt;li&gt;第2行打印了long型的大小。因为我是64bit的，所以一个long是8byte。&lt;/li&gt;
&lt;li&gt;第3行打印了*(a+1)的值，结果和a[1]的值相等。说明a虽然代表整个数组，但作为右值时，的确代表首元素的首地址。&lt;/li&gt;
&lt;li&gt;第4行输出值为12，是整个数组的大小。&lt;/li&gt;
&lt;li&gt;第5行打印了一个出界元素的大小，没有报错，验证了上面第5条。&lt;/li&gt;
&lt;li&gt;第6行打印了a[-1]和*(a-1)，输出值相等。验证了上面第6条。&lt;/li&gt;
&lt;li&gt;第7行打印了a和&amp;amp;a[0]，值相等。说明数组的首地址和首元素的首地址是相等的。&lt;/li&gt;
&lt;li&gt;第8行打印了a，(a+1)，(&amp;amp;a+1)，由结果就可以看出首元素的首地址加1是加了一个数组元素的大小，而数组首地址加1是加了一个数组的大小。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;指针数组和数组指针&#34;&gt;指针数组和数组指针&lt;/h1&gt;
&lt;p&gt;指针数组： 首先它是一个数组，数组的元素是指针，也成为“存储指针的数组”。&lt;/p&gt;
&lt;p&gt;数组指针： 首先它是一个指针，它指向一个数组，也可以理解为“数组的指针”。 也可以利用前面的“解读方法”去分析。&lt;/p&gt;
&lt;h1 id=&#34;函数指针和指针函数&#34;&gt;函数指针和指针函数&lt;/h1&gt;
&lt;p&gt;函数指针： 指向函数的指针变量。&lt;/p&gt;
&lt;p&gt;指针函数： 带指针的函数，也就是返回指针的函数。&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;char * fun(char* a, char* b)  //定义为指针函数
{
	...
	 ...
}

int main()
{
    char* (*p)(char* p1, char* p2); 	//定义函数指针
    p = &amp;amp;fun; 				//把函数地址赋给它
    //p = fun; 				//这样写也行
    (*p)(&amp;#34;aa&amp;#34;, &amp;#34;bb&amp;#34;); 			//使用函数指针

  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;指针常量和常量指针&#34;&gt;指针常量和常量指针&lt;/h1&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const char* p1;    //常量指针，指向常量的指针
char const* p2;    //同上
char* const p3;     //指针常量，指针是常量&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;怎么记？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以先把类型名去掉，然后看const离谁近，就修饰谁。&lt;/li&gt;
&lt;li&gt;也可以const在&lt;em&gt;左边的为常量指针，const在&lt;/em&gt;右边的为指针常量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;三五的万能钥匙&#34;&gt;三～五的万能钥匙&lt;/h1&gt;
&lt;p&gt;其实，关于“指针数组与数组指针、函数指针与指针函数、指针常量与常量指针”的判断，有一个万能钥匙。那就是根据我们强大的中文语法：前边是修饰词，后边才是主语。比如“指针数组”，前面的指针只是修饰词，后面的数组才是主语，所以它是一个数组。&lt;/p&gt;
&lt;h1 id=&#34;野指针&#34;&gt;野指针&lt;/h1&gt;
&lt;p&gt;野指针指没有确定指向的指针。造成野指针的情况有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;指针变量创建但没有初始化。&lt;/li&gt;
&lt;li&gt;指针p被free或者delete之后，没有置为NULL。&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>为什么越学反而越蠢？碎片化学习是个骗局</title>
      <link>http://coder.amazingdemo.top/post/%E7%A2%8E%E7%89%87%E5%8C%96%E5%AD%A6%E4%B9%A0%E6%98%AF%E4%B8%AA%E9%AA%97%E5%B1%80/</link>
      <pubDate>Sat, 02 Jun 2018 17:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E7%A2%8E%E7%89%87%E5%8C%96%E5%AD%A6%E4%B9%A0%E6%98%AF%E4%B8%AA%E9%AA%97%E5%B1%80/</guid>
      
        <description>&lt;p&gt;先给你讲个故事，看你有没有中招。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有一天，你的朋友给你「知识装逼」了一个术语，牛！怎么知道的？—— 他推给你一个公众号。&lt;/p&gt;
&lt;p&gt;你开始关注，觉得哇！大神！牛逼！长见识！
每天刷每天刷每天刷。很多问题也有了解决方法 —— 按照这个进度，过 3 年就能理解宇宙终极奥义了。&lt;/p&gt;
&lt;p&gt;慢慢你有了十多个类似的号，承包了你从专业、生活、工作、跑步、减肥、饮食、情感关系……等等所有的领域 —— 咳咳，有点看不过来的你决定提高效率碎片化学习！&lt;/p&gt;
&lt;p&gt;利用一切碎片时间，上地铁看一篇，吃饭时看一篇，睡前刷 2 篇。眼睛看不完，用音频刷，平时看不完，跑步还能刷 —— 但是，还是刷不完！&lt;/p&gt;
&lt;p&gt;还好有收藏功能，全他妈的收藏！&lt;/p&gt;
&lt;p&gt;这也叫「松鼠症」 —— 不断攒不断攒不断攒……&lt;/p&gt;
&lt;p&gt;等到收藏超过 100 篇未读的时候，你终于发现收藏 = 永不再读，这让你沮丧。&lt;/p&gt;
&lt;p&gt;更让人沮丧的是，你好像距离写文章的大牛越来越远了—— 在你「学习」的这段时间，这 100 位大牛好像都更牛逼了一点。&lt;/p&gt;
&lt;p&gt;而且牛逼的人好像越来越多，观点越来越新，每天都有人用全新的玩法颠覆原来的牛人 —— 哇，原来这才是对的！&lt;/p&gt;
&lt;p&gt;别说宇宙奥义了，你对于生活有了深深的怀疑……&lt;/p&gt;
&lt;p&gt;再好的鸡汤也掩饰不了你日益的郁闷——确实这个世界 LOW 的就是刷了无数文章的你。&lt;/p&gt;
&lt;p&gt;你越来越焦虑，终于有一天，你彻底瘫痪了，对自己说，妈的为什么活得这么累？老子就是一个俗人，干嘛没事要成长？我要做一只猪，一坨肉，一个死尸 —— 你开始转战娱乐、小说和短视频……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;中招的请举手&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更加让你想抽自己的是，过了几天你歇过来，你又开始了新一轮的循环 —— 刷刷刷。&lt;/p&gt;
&lt;p&gt;焦虑症 - 松鼠症 - 懒癌，本世纪三大绝症，其实是个死循环。&lt;/p&gt;
&lt;h1 id=&#34;为什么会这样&#34;&gt;为什么会这样？&lt;/h1&gt;
&lt;p&gt;因为，所有的当代人都面临两个问题 —— 信息超载和知识碎片化。&lt;/p&gt;
&lt;p&gt;信息超载老话题不多说了，讲几个数据：&lt;/p&gt;
&lt;p&gt;全世界每天有 4000 本书出版，超过 4 亿个字；&lt;/p&gt;
&lt;p&gt;纽约时报一天的文字量等于牛顿同时代的人一生的阅读量；&lt;/p&gt;
&lt;p&gt;一个专业领域，每天大概有 200 个公众号正在注册，有近 1000 篇文字正在产生……&lt;/p&gt;
&lt;p&gt;过去杂家、博物家在现代社会已经不可能存在，即使有，也干不过谷歌度娘。&lt;/p&gt;
&lt;p&gt;知识的储存能力，人类早就被电脑完胜了，知识的搜索和链接能力，才是学习的核心竞争力。&lt;/p&gt;
&lt;p&gt;另一个趋势也很明显：知识碎片化。这个碎片化从 3 个方面开始 ——&lt;/p&gt;
&lt;p&gt;首先时间和空间被打碎，移动手机允许你在三站地铁里听完一本书；&lt;/p&gt;
&lt;p&gt;其次被打碎的是信息，如果你翻知乎、今日头条或者某个博客，你并不会像看一本书一样，知道前面一章讲什么的，后面即将讲什么，除了标题党，你根本对下一个博客、内容毫无预期，只能被动接受；&lt;/p&gt;
&lt;p&gt;最糟糕的其实是知识结构碎片化 —— 你并不知道是谁，在什么情况下，针对什么问题讲的这句话。所有的信息缺乏了上下文，就是废话。&lt;/p&gt;
&lt;p&gt;比如说有段时间我刷管理类的文，你会发现在雷军、傅盛、彼得德鲁克、吴伯凡、马云、吴晓波……各位大佬针对同一个问题讲的内容完全不同。到底信哪个？&lt;/p&gt;
&lt;p&gt;其实雷军讲的是互联网行业；傅盛讲的是小企业逆袭，他正在做投资；彼得德鲁克用管理在讲哲学；吴伯凡老师在讲中西方文化；马云老师在对大众励志；吴晓波其实是用管理谈财经——更重要的是，他们讲话的场合，时间点和对象你一无所知。&lt;/p&gt;
&lt;p&gt;如果一个人没有搜索能力，他不会知道背景；没有思考能力，不会知道为什么——这样单纯知道一句话，比不知道更糟糕。&lt;/p&gt;
&lt;p&gt;常年刷微信的人的大脑&lt;/p&gt;
&lt;p&gt;把前面两个结论放一块，结果很清晰 —— 时间空间碎片化确实能够提高学习的效率（其实也干不过电脑），而信息碎片化和知识结构碎片化带来的，则是学习效率的倒退和焦虑之源 —— 因为他毁掉你的主动搜索能力和主动链接的能力，也就是深度思考的能力。&lt;/p&gt;
&lt;p&gt;这就是碎片化的陷阱，极大的提高了并没有什么卵用的信息，而降低了最重要的独立思考的能力。&lt;/p&gt;
&lt;p&gt;深度思考是件难事，也是件需要时间和空间的事，它远远比迅速点开另一个同样标题很好玩、内容很逗比的标题的文章难得多也长得多 —— 但这个时候，90% 的人会选择点开下一个话题 —— 你的浅薄之路也从此开始。&lt;/p&gt;
&lt;p&gt;这也形成一个上瘾机制 —— 瞬间点开新话题能让你获得「哇，我懂了」的廉价快感，但看的信息越多，就见到越多牛人的思想高度，你却无法越无法深度思考。为了抵御这种焦虑，你只好继续打开更多窗口，见证更多顶峰，然后更多焦虑。&lt;/p&gt;
&lt;p&gt;饮鸩止渴，一直到瘫痪。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我靠，我看了本书特好的书！」&lt;/p&gt;
&lt;p&gt;是吗？是吗？说什么的给我讲讲？」&lt;/p&gt;
&lt;p&gt;恩……特别好！特别牛！……我讲不出来，给个链接你自己看吧。」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;是不是这样？&lt;/p&gt;
&lt;p&gt;所以大部分的人学习，起初是为了解决问题，结果变成了逃避问题。他们越学越焦虑。&lt;/p&gt;
&lt;h1 id=&#34;什么是靠谱的学习方式&#34;&gt;什么是靠谱的学习方式？&lt;/h1&gt;
&lt;p&gt;观察下那些给你喂知识的专家们怎么做的。&lt;/p&gt;
&lt;p&gt;他们都挺贼的 —— 专注于一个小领域，以热点为关键词，搜索他们自己专业领域的内容；然后通过新的知识结构表达出来。&lt;/p&gt;
&lt;p&gt;他们一方面无限度的降低了知识的门槛，其实反而被拉高的是你和他之间的距离。&lt;/p&gt;
&lt;p&gt;知识创富时代，内容创业时代 —— 无论你怎么说这个领域的弄潮儿，靠的都是这三个能力：在某个专业领域的专注力；与热点、痛点和经典结合的搜索力；以及重新架构的链接能力。&lt;/p&gt;
&lt;p&gt;他们知道 —— 即使你拿回来一百万个知识的砂砾，也只是沙漠，人们爱看的，是沙子集合成的金字塔 —— 有组织的知识才是核心竞争力。有高度的知识，才有知识压强。&lt;/p&gt;
&lt;p&gt;在你不断的在沙漠闲逛，他们自己的金字塔盖了起来。&lt;/p&gt;
&lt;h1 id=&#34;怎么办呢&#34;&gt;怎么办呢？&lt;/h1&gt;
&lt;p&gt;下面是 3 个把抵御碎片化，让知识「零存整取」的方式：&lt;/p&gt;
&lt;h2 id=&#34;学习的起点不是焦虑而是问题&#34;&gt;学习的起点不是焦虑，而是问题&lt;/h2&gt;
&lt;p&gt;李小龙当年刚回香港，在《欢乐今宵》上节目，节目组请来了一名太极拳拳师，想考验他能不能推到对方。拳师也摆好架子等他来推，结果李小龙一拳打在对方脸上。&lt;/p&gt;
&lt;p&gt;（此处有人准备直接拉到下面准备开骂「你他妈懂个屁太极」，这就是碎片化太厉害，完全不看上下文）&lt;/p&gt;
&lt;p&gt;李小龙想表达的是 —— 在真实的实战中，没有人会等你摆好架势，只冲着你规定的地方打过来。&lt;/p&gt;
&lt;p&gt;学习也是一样 —— 在真实生活中，环境和知识都变化太多 —— 你不可能像学校里一样，系统的学好学完一门知识，然后坐在教室等着不超过知识点的考试。&lt;/p&gt;
&lt;p&gt;大部分时间，生活会先给你一个考试，然后你根据问题，自己找出关键词，开始学习。&lt;/p&gt;
&lt;p&gt;所以，学习的起点不是某人在朋友圈晒的一本书、推荐的一个微信文、或者这个行业的一个经典教材引发你的焦虑，而应该是你真实遇到的一个问题：&lt;/p&gt;
&lt;p&gt;通过不断的思考和对话，一直到能清晰的提出一个明确具体的问题 —— 这时候，已经解决了一半。&lt;/p&gt;
&lt;p&gt;把问题拆分出关键字，然后开始搜索认知资源。&lt;/p&gt;
&lt;p&gt;找到你要到资源。&lt;/p&gt;
&lt;h2 id=&#34;扩充学习资源人事网书&#34;&gt;扩充学习资源——人事网书&lt;/h2&gt;
&lt;p&gt;找学习资源的能力，就是搜索能力。&lt;/p&gt;
&lt;p&gt;大部分人找资源，第一反应是上网，或买本书。这很容易陷入困境，因为网络是个无底洞，这里翻翻那里看看，一不小心，2 天都出不来。要把书里面的内容消化用完至少2周，前提还是你选对了书。&lt;/p&gt;
&lt;p&gt;信息爆炸时代，最重要的不是增加信息，而是筛选和删除信息。&lt;/p&gt;
&lt;p&gt;所以其实最好的方式是：&lt;/p&gt;
&lt;p&gt;从有经验的人开始，根据你提出的问题，给你清晰的建议、方向和边界；&lt;/p&gt;
&lt;p&gt;找到行业对标，看看人家是怎么做的；&lt;/p&gt;
&lt;p&gt;进一步用网络搜索资料和书的总结和评论，最后才是系统的看书。&lt;/p&gt;
&lt;p&gt;这个方法展开又是一篇，我有机会另外开文讲&lt;/p&gt;
&lt;h2 id=&#34;721-法则留出足够多的时间练习思考和讨论&#34;&gt;721 法则：留出足够多的时间练习、思考和讨论&lt;/h2&gt;
&lt;p&gt;行动学习理论认为，人要掌握一门技能，需要有 10% 的时间学习知识和信息，70% 的时间练习和践行，还有 20% 的时间与人沟通和讨论。这个原则叫做 721 原则。&lt;/p&gt;
&lt;p&gt;碎片化学习对于 10% 的信息接收非常有用，而剩下独处练习的 70% 和讨论的 20%，则需要留出大量时间来系统学习 —— 碎片化学习永远只是系统化学习的辅助。你需要留出足够多整块时间学习。&lt;/p&gt;
&lt;p&gt;如果能看到这里，顺便说一句 —— 我每周只写一篇不那么好读的干货文，一方面因为我懒，另一方面也因为我并不认为天天刷文能对人有所帮助，每周三晚上，如果能重新思考一个观点，做一点行动和思辨，帮助远远更大。（关于这个话题你有什么想说，记得在后面留言，做讨论的 20%）&lt;/p&gt;
&lt;h2 id=&#34;输出倒逼输入&#34;&gt;输出倒逼输入&lt;/h2&gt;
&lt;p&gt;如何能保证让自己思考有体系？&lt;/p&gt;
&lt;p&gt;我看过、试过很多方法，最后最有效的只有一个，就是用输出倒逼输入。&lt;/p&gt;
&lt;p&gt;你可以写文章、讲课、做 PPT、做读书笔记、导图……只有一个要求 —— 你的输出有结构。&lt;/p&gt;
&lt;p&gt;你要先搭好图纸，设计出一个承重结构，然后铺钢筋，最后才放水泥和沙子。&lt;/p&gt;
&lt;p&gt;先建立一个结构，你的知识和信息量会自动吸附在结构之上。时间一长，你会成为一个能根据结构链接知识的人。&lt;/p&gt;
&lt;p&gt;很多人会说，我不会写该怎么办？&lt;/p&gt;
&lt;p&gt;研究新建构主义的王竹立老师有一个观点 —— 把知识零存整取：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;叫做积件式写作，就是用 ctrlc 加 ctrlV 的方法，先拼出一篇符合逻辑的文字。（引用别人东西记得注明来源哈）&lt;/li&gt;
&lt;li&gt;进入个性化改写阶段，在第一步的基础上，你可以根据自己的个性化，写出自己的案例，自己的风格，自己的侧重点——现在网络上大部分的写作和讲课都是此类。&lt;/li&gt;
&lt;li&gt;创造性重构，在持续的前两步积累下，创造出全新的思路和模型。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;大部分人的问题，都是看了第二第三步的文章，下结论说自己完全不适合写作，然后放弃。其实每个人都应该动笔从第一步做起 —— 写作不是为了传播，至少为了思考和学习。&lt;/p&gt;
&lt;p&gt;希望你建立一座自己的金字塔。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>长期刷朋友圈接收碎片化知识有什么弊端？</title>
      <link>http://coder.amazingdemo.top/post/%E9%95%BF%E6%9C%9F%E5%88%B7%E6%9C%8B%E5%8F%8B%E5%9C%88%E6%8E%A5%E6%94%B6%E7%A2%8E%E7%89%87%E5%8C%96%E7%9F%A5%E8%AF%86%E6%9C%89%E4%BB%80%E4%B9%88%E5%BC%8A%E7%AB%AF/</link>
      <pubDate>Fri, 01 Jun 2018 17:59:38 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/%E9%95%BF%E6%9C%9F%E5%88%B7%E6%9C%8B%E5%8F%8B%E5%9C%88%E6%8E%A5%E6%94%B6%E7%A2%8E%E7%89%87%E5%8C%96%E7%9F%A5%E8%AF%86%E6%9C%89%E4%BB%80%E4%B9%88%E5%BC%8A%E7%AB%AF/</guid>
      
        <description>&lt;p&gt;碎片信息通常具备这样的特征：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它们往往是一些事实的集合而非逻辑&lt;/li&gt;
&lt;li&gt;它们往往大量简化了推演过程&lt;/li&gt;
&lt;li&gt;它们往往将多路径简化为单一路径&lt;/li&gt;
&lt;li&gt;它们往往不够严谨、全面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;简而言之，碎片信息为了达到易于习得的目的，通常会显著降低认知成本，最明显的方式就是：将复杂的事物简单化。它们往往只告诉你表面上的东西，却不会告诉你背后的原理，以及它与其他事物之间的联系。&lt;/p&gt;
&lt;p&gt;我们所说的「知识」，由两部分组成：一是「事实」（或曰「观念」），二是「联系」。事实就是一个个点，联系则是把点连接起来的线，它们所构成的网络，就是我们的知识结构。&lt;/p&gt;
&lt;p&gt;「事实」决定了你的知识广度，「联系」决定了你的知识深度。如果你了解事物之间的联系，即使你只知道 ABC，你也可以根据这三者的内在逻辑，得出 DE，甚至 F，这个过程就叫做思考。但如果你不了解它们的内在逻辑，即使你知道 ABCDE，你也是没办法得出 F 的——你不知道需要把它们放在一起，更不知道放在一起之后它们能够呈现出怎样的内在逻辑关系。&lt;/p&gt;
&lt;p&gt;这就是碎片化信息的弊端。当我们接受碎片信息时，我们实际上是在扩充「事实」，但并没有增加「联系」。长此以往，会使我们的知识结构变成一张浮点图：孤零零的知识点漂浮在各个位置，却缺乏一个将它们有序串联起来的网络。&lt;/p&gt;
&lt;p&gt;这样的结果是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;碎片化知识通过连续的新鲜内容，不断刺激你的大脑，让你始终处于「啊！又知道了新的东西」的喜悦中，从而难以自拔，这也就是我们难以抑制刷微博、刷朋友圈的缘故，因为我们只需要付出很少，就可以沉浸在「获得了新东西」的刺激里面。&lt;/li&gt;
&lt;li&gt;但是，这些获得的信息，因为它们缺少跟其他信息的「联系」，因此难以被我们「提取」，而「提取」得越少的内容，会被提取得多的内容挤压在记忆的底部——因此，这些碎片化的信息极其容易被我们遗忘。你以为你得到了很多，但其实你什么都没有得到。&lt;/li&gt;
&lt;li&gt;前文已经说过，我们的知识网络，决定了我们如何思考。那么，长期接受碎片信息，对你的思考能力并没有提升——你的「网络」并没有扩大；甚至是有害的——你已经习惯了用孤立的知识点去看待问题，习惯了一层、两层的思维，难以对事物进行五层、六层乃至更深入的剖析。长此以往，你将弱化对于复杂事物的思考能力。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里要注意一点：碎片化信息，其实跟来源没有太大关系。如果你习惯了接受「A 是 B」、「C 是坏的」、「因为 A 所以 B」的简单观念，那么，无论你是在刷微博，还是在读书、看公开课，其实接受的都是碎片化知识。&lt;/p&gt;
&lt;p&gt;我之前在一个「聪明人的思考方式是什么」的问题中，提到过：聪明人的思考方式，其实很简单，就是三个字：元认知。同样一个问题，较为聪明的人，会去思考这个问题产生的背景、原因、合理性、必要性、可能性，等等。他们会拔高一层去看待这个问题，从而更容易找到一根线条，将它和远处的某个固有的「观念」联系在一起，从而拓展自己的思维网络。&lt;/p&gt;
&lt;p&gt;所以，如果你有这样的意识，那么其实无论刷微博还是读书，都可以避免碎片化认知。&lt;/p&gt;
&lt;p&gt;具体的方式是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先花一点时间，建立自己的知识体系。把你已经知道的东西梳理一遍。如何梳理呢？以你能够说出某个知识点的影响因素，以及它对其他事物的影响为准。顺着这样的知识点捋一遍，这个网络就是你已经构建完成的知识网络。&lt;/li&gt;
&lt;li&gt;找到知识网络的触点。亦即自己感兴趣的、但尚未进行探索和了解的知识点。阅读、学习的时候，有意识地去接触这些触点的知识，延展自己的知识网络。&lt;/li&gt;
&lt;li&gt;当接触到一个新的知识点时，先考虑如何将其纳入知识体系。亦即在脑子里回想你的知识网络，思考它可以如何跟你已经知道的东西联系起来。&lt;/li&gt;
&lt;li&gt;如果找到了对应的点，弄通路径。亦即，将这个新的知识点，跟已经知道的某个点之间的路径，查清楚、弄清楚，将它们连接起来，使这个知识点成为你新的「触点」，拓展你的思维网络。&lt;/li&gt;
&lt;li&gt;检验并输出。将这两个点之间的联系讲清楚。最简单的办法，就是通过口述、写文章，去教会别人这个知识。或者，在心里把它讲一遍，看是否能够讲得清晰易懂，没有障碍。只有能够输出的东西，才是真正属于你的东西。&lt;/li&gt;
&lt;li&gt;不符合以上方式的内容，果断舍弃。如果一个东西无法纳入你的认知体系，那说明你现在还不能掌握它，那就果断放弃，因为它对你来说是没有价值的，或者说（记忆的）成本是远高于收益的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;再多谈几点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读书不用追求「读完一本书」，而应该追求「从这本书中获得了什么东西」。一本书的内容不可能 100%对你有用，其中肯定有你所不感兴趣的东西，也有你所无法接受的东西，没关系，接受你所能接受的即可。不用务求全部读完。甚至，读一半，放回去，再跳着读别的书，也是很好的方式。读书应该为自己所用而不是让自己去迁就它。&lt;/li&gt;
&lt;li&gt;如何处理微博上、知乎上那些有趣的碎片化知识？个人建议，最好的方式，是将它们作为起点。如果你觉得一个知识很有趣，就以它为出发点，去探索它背后的原理、背景、应用，去查资料、Google，顺藤摸瓜。这个知识点本身是没有太大价值的，有价值的是你去探索的过程。你经过探索了解到的东西，才能纳入你的知识体系，成为你思维的一部分。&lt;/li&gt;
&lt;li&gt;以上种种都需要不菲的时间，但学习本就是一件艰难的事情，所以优秀的人永远是凤毛麟角，所谓聪明的人，无非他们把走路、等车、休息等更多的时间花在这上面罢了。再说，学习本身，岂非也是一件很有趣的事情？&lt;/li&gt;
&lt;/ol&gt;
</description>
      
    </item>
    
    <item>
      <title>RESTful API 设计</title>
      <link>http://coder.amazingdemo.top/post/restful_api_%E8%AE%BE%E8%AE%A1/</link>
      <pubDate>Sat, 28 Apr 2018 14:27:56 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/restful_api_%E8%AE%BE%E8%AE%A1/</guid>
      
        <description>&lt;p&gt;移动互联网时代，RESTful API成为越来越重要的移动端和服务器端交互的形式。尤其是在很多互联网公司或者传统行业拥抱移动互联网的时候，一套设计良好的Restful API能够帮助互联网产品支持单服务端＋多客户端的场景。RESTful架构本身是一个风格而不是一个标准，这也就意味着在具体设计时会有不同的实现。&lt;/p&gt;
&lt;p&gt;虽然 HTTP REST API 在互联网特别流行，但它们承载的流量却比传统的 RPC API 要少。例如，在美国高峰时期的约一半流量是视频内容，但因为效率问题，很少有人会考虑使用 REST API 来传输这些内容。在数据中心，许多公司使用基于 socket 的 RPC API 来传输大部分网络流量，这会比 REST API 高出几个数量级。&lt;/p&gt;
&lt;h1 id=&#34;什么是restful-api&#34;&gt;什么是RESTful API&lt;/h1&gt;
&lt;p&gt;表征性状态传输（Representational State Transfer,简称REST ）是Roy Fielding博士于2000年在他的博士论文中提出来的一种软件架构风格。如果一个架构符合REST原则，就称它为RESTful架构。说到Roy Fielding几乎可以称之为HTTP协议之父，他是HTTP1.0和1.1的主要设计者，这篇基于HTTP协议的软件架构风格一出世就引起了关注并对互联网开发产生了深远的影响。发展到今日主流开源框架都提供了对REST的支持，大多数互联网公司都采用了RESTful架构。&lt;/p&gt;
&lt;h1 id=&#34;资源resources是rest的核心&#34;&gt;资源（Resources）是REST的核心&lt;/h1&gt;
&lt;p&gt;REST开发又被称作“面向资源的开发”，这说明对于资源的抽象是设计RESTful API的核心内容。RESTful API建模的过程与面向对象建模类似，是以名词为核心的。这些名词就是资源，任何可命名的抽象概念都可以定义为一个资源。对于业务的抽象是设计一套好的RESTful API的基础，这就好比建房子打地基，如果地基没有打好，后面建的楼就很容易歪掉，其美观度，可维护性，可扩展性就会大大折扣。笔者会建议在设计初期一定要在资源的定义上多花功夫，抽象出适合业务发展的资源。也就是说一开始要把产品的RESTful风格定义下来，后面的扩展都可以基于这样的风格延续下去。&lt;/p&gt;
&lt;p&gt;下面是几条小的建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理清资源的层次结构，比如业务针对的范围是学校，那么学校会是一级资源(/school)，老师(/school/teachers)，学生(school/students)就是二级资源。&lt;/li&gt;
&lt;li&gt;资源尽量用准确的英文名词去表达，资源组都用复数来表达。一个好的资源定义一定是自解释的，也就是说你只需要很少的先验信息就能理解这个resource资源代表什么意思。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;资源的状态转化state-transfer&#34;&gt;资源的状态转化（State Transfer)&lt;/h1&gt;
&lt;p&gt;访问一个网站，代表的就是客户端和服务器的一个互动过程，这个过程中势必就涉及到数据和状态的变化。而广为使用的HTTP协议又恰恰是无状态的协议，这就意味这所有的状态都保存在服务器端。如果某个客户端想要操作服务器端必须通过某种手段让服务器发生状态转换。那么客户端可以操作的就是上文所定义的资源，而资源的状态转化就转化为对资源的各种操作。这些操作是通过HTTP协议的四种常用的方法来实现：GET，POST，PUT，DELETE。他们分别对应四种基本操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET－获取资源&lt;/li&gt;
&lt;li&gt;POST－新建或者更新资源&lt;/li&gt;
&lt;li&gt;PUT－更新资源&lt;/li&gt;
&lt;li&gt;DELETE-删除资源&lt;/li&gt;
&lt;li&gt;还有其它不常用的HTTP方法：PATCH/HEAD/OPTIONS方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于HTTP方法的应用业界一直有两种声音：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一种是尽量使用所有的HTTP方法去操作资源，请求里面只能带资源不能出现任何动词，如果发现资源上的操作过多以至于HTTP的方法不够用，应该考虑设计出更多的资源。这种方式适用于新产品的开发，产品是从建模开始的，可以充分的考虑各种业务场景定义出相应的资源。&lt;/li&gt;
&lt;li&gt;另一种是就是灵活适用，用GET去获取资源，其它涉及更改的操作都用POST，并当POST不能表达相应的动作时在URL中添加动词。比如在二手商品市场我发布了一个售卖手机的资源。那么对于这个资源，卖家本人可以对这个商品有取消的操作：POST /resources/123/cancel;买家可以对这个商品有购买的操作：POST /resources/123/buy。这种方式适用于对现有非RESTful架构进行升级改造而修改模型影响更大的业务产品。当然这种选择也有可能是前后端程序猿们协商的结果，毕竟用一种最自然的沟通方式去架起前后端的桥梁才是目的所在。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;合理使用url路径参数和请求&#34;&gt;合理使用URL路径参数和请求&lt;/h1&gt;
&lt;p&gt;在URL路径里的参数一定是代表某个资源的ID，路径参数也可以是多个代表几级资源的IDs，例如获取一个老师所带班级的详情/teachers/#teacher_id/classes/#class_id。&lt;/p&gt;
&lt;p&gt;对于HTTP GET，请求参数一般是作为可选参数获取某个资源列表的子集，例如获取年青老师的列表/teachers?group=young。&lt;/p&gt;
&lt;p&gt;对于HTTP POST，请求参数是在消息体里，代表需要新建或者更新的资源。&lt;/p&gt;
&lt;h1 id=&#34;合理使用http响应代码&#34;&gt;合理使用HTTP响应代码&lt;/h1&gt;
&lt;p&gt;HTTP响应状态代码，是HTTP协议这个统一接口中用来表达出错情况的标准机制。响应状态代码分成两部分：状态码和原因。两部分都是可定制的，也可以使用标准的状态码，只定制出错原因。在实际应用中也是两种选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一种是对于应用出错的情况扩展状态码，定制出错原因。&lt;/li&gt;
&lt;li&gt;一种是对于容器处理的出错情况默认使用容器返回的错误码，例如tomcat容器返回的503，404；而对于应用本身返回的状态码一律返回200，对于应用出错码和原因都反应在返回消息体中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;定义一套标准的返回体数据结构&#34;&gt;定义一套标准的返回体数据结构&lt;/h1&gt;
&lt;p&gt;对于所有的RESTful HTTP请求定义一套标准的返回结构体，前端可以根据这样的固定格式做标准化的解析，对于系统的可维护性起到很大的帮助。这个结构体里应该包含返回的具体资源，结果状态码和错误原因（如果有的话）。对于返回的资源，数据类型也尽量做到统一，比如日期，枚举类型都返回统一的数据类型避免不同的API对同一种数据有不同的处理方式。资源属性尽量做到可读也能大大减少前后端的沟通成本。&lt;/p&gt;
&lt;p&gt;关于Request 和 Response，不要忽略了http header中的Content-Type。以json为例，如果API要求客户端发送request时要传入json数据，则服务器端仅做好json数据的获取和解析即可，但如果服务端支持多种类型数据的传入，如同时支持json和form-data，则要根据客户端发送请求时header中的Content-Type，对不同类型是数据分别实现获取和解析；如果API响应客户端请求后，需要返回json数据，需要在header中添加Content-Type=application/json。&lt;/p&gt;
&lt;p&gt;Serialization 和 Deserialization即序列化和反序列化。RESTful API以规范统一的格式作为数据的载体，常用的格式为json或xml，以json格式为例，当客户端向服务器发请求时，或者服务器相应客户端的请求，向客户端返回数据时，都是传输json格式的文本，而在服务器内部，数据处理时基本不用json格式的字符串，而是native类型的数据，最典型的如类的实例，即对象（object），json仅为服务器和客户端通信时，在网络上传输的数据的格式，服务器和客户端内部，均存在将json转为native类型数据和将native类型数据转为json的需求，其中，将native类型数据转为json即为序列化，将json转为native类型数据即为反序列化。虽然某些开发语言，如Python，其原生数据类型list和dict能轻易实现序列化和反序列化，但对于复杂的API，内部实现时总会以对象作为数据的载体，因此，确保序列化和反序列化方法的实现，是开发RESTful API最重要的一步准备工作&lt;/p&gt;
&lt;h1 id=&#34;restful-api的版本控制&#34;&gt;RESTful API的版本控制&lt;/h1&gt;
&lt;p&gt;一个简单的做法是直接在URL中插入版本号，这样可以允许多个版本的API同时运行。在已经发布的版本中尽量做到向后兼容，包括URL和参数，对于返回值也是尽量增加新的冗余参数以兼容不同客户端不同的升级频率。等到所有的客户端升级以后再去除冗余的过程。&lt;/p&gt;
&lt;h1 id=&#34;restful-api的安全性&#34;&gt;RESTful API的安全性&lt;/h1&gt;
&lt;p&gt;安全性也是一个很大的话题，如果展开来讲又将是另外一篇文章。简单来讲主要是要考虑下面几个方面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对客户端做身份认证&lt;/li&gt;
&lt;li&gt;保证每个用户只能操作自己的资源，而不会CRUD属于别人的资源&lt;/li&gt;
&lt;li&gt;对于敏感数据做加密防止串改&lt;/li&gt;
&lt;li&gt;对于POST请求添加幂等机制以保证对资源的增加或者修改是安全的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;结语&#34;&gt;结语&lt;/h1&gt;
&lt;p&gt;最后作为结束语，想说明的是一套设计良好的RESTful一定是前后端反复沟通协商并不断迭代的过程。因为RESTful API作为前后端的桥梁我们需要同时考虑前后端的需求并达成一致的一个结果，桥梁之所以成为桥梁一定是双方都认可的沟通方式。架构是一门科学，也是一种艺术。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Linux开机启动十步骤</title>
      <link>http://coder.amazingdemo.top/post/linux%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E5%8D%81%E6%AD%A5%E9%AA%A4/</link>
      <pubDate>Wed, 25 Apr 2018 12:25:21 +0800</pubDate>
      <author>peach5460@qq.com (Peach)</author>
      <guid>http://coder.amazingdemo.top/post/linux%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E5%8D%81%E6%AD%A5%E9%AA%A4/</guid>
      
        <description>&lt;p&gt;&lt;img src=&#34;http://coder.amazingdemo.top/userfile/linux%E5%90%AF%E5%8A%A8.png&#34; alt=&#34;linux启动&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;启动第一步加载bios&#34;&gt;启动第一步&amp;ndash;加载BIOS&lt;/h1&gt;
&lt;p&gt;当你打开计算机电源，计算机会首先加载BIOS信息，BIOS信息是如此的重要，以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后，计算机心里就有谱了，知道应该去读取哪个硬件设备了。在BIOS将系统的控制权交给硬盘第一个扇区之后，就开始由Linux来控制系统了。&lt;/p&gt;
&lt;h1 id=&#34;启动第二步读取mbr&#34;&gt;启动第二步&amp;ndash;读取MBR&lt;/h1&gt;
&lt;p&gt;硬盘上第0磁道第一个扇区被称为MBR，也就是Master Boot Record，即主引导记录，它的大小是512字节，可里面却存放了预启动信息、分区表信息。可分为两部分：第一部分为引导(PRE-BOOT)区，占了446个字节;第二部分为分区表(PARTITION PABLE)，共有66个字节，记录硬盘的分区信息。预引导区的作用之一是找到标记为活动(ACTIVE)的分区，并将活动分区的引导区读入内存。&lt;/p&gt;
&lt;p&gt;系统找到BIOS所指定的硬盘的MBR后，就会将其复制到0×7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader，而具体到你的电脑，那就是lilo或者grub了。&lt;/p&gt;
&lt;h1 id=&#34;启动第三步boot-loader&#34;&gt;启动第三步&amp;ndash;Boot Loader&lt;/h1&gt;
&lt;p&gt;Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序，我们可以初始化硬件设备、建立内存空间的映射图，从而将系统的软硬件环境带到一个合适的状态，以便为最终调用操作系统内核做好一切准备。通常，BootL oade:是严重地依赖于硬件而实现的，不同体系结构的系统存在着不同的Boot Loader。&lt;/p&gt;
&lt;p&gt;Linux的引导扇区内容是采用汇编语言编写的程序，其源代码在arch/i386/boot中(不同体系的CPU有其各自的boot目录)，有4个程序文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bootsect.S，引导扇区的主程序，汇编后的代码不超过512字节，即一个扇区的大小&lt;/li&gt;
&lt;li&gt;setup.S， 引导辅助程序&lt;/li&gt;
&lt;li&gt;edd.S,辅助程序的一部分，用于支持BIOS增强磁盘设备服务&lt;/li&gt;
&lt;li&gt;video.S,辅助程序的另一部分，用于引导时的屏幕显示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Boot Loader有若干种，其中Grub、Lilo和spfdisk是常见的Loader，这里以Grub为例来讲解吧。&lt;/p&gt;
&lt;p&gt;系统读取内存中的grub配置信息(一般为menu.lst或grub.lst)，并依照此配置信息来启动不同的操作系统。&lt;/p&gt;
&lt;h1 id=&#34;启动第四步加载内核&#34;&gt;启动第四步&amp;ndash;加载内核&lt;/h1&gt;
&lt;p&gt;根据grub设定的内核映像所在路径，系统读取内存映像，并进行解压缩操作。此时，屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后，屏幕输出“OK, booting the kernel”。&lt;/p&gt;
&lt;p&gt;系统将解压后的内核放置在内存之中，并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备，完成Linux核心环境的建立。至此，Linux内核已经建立起来了，基于Linux的程序应该可以正常运行了。&lt;/p&gt;
&lt;p&gt;start_kenrel()定义在init/main.c中，它就类似于一般可执行程序中的main()函数，系统在此之前所做的仅仅是一些能让内核程序最低限度执行的初始化操作，真正的内核初始化过程是从这里才开始。函数start_kerenl()将会调用一系列的初始化函数，用来完成内核本身的各方面设置，目的是最终建立起基本完整的Linux核心环境。&lt;/p&gt;
&lt;p&gt;start_kernel()中主要执行了以下操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在屏幕上打印出当前的内核版本信息。&lt;/li&gt;
&lt;li&gt;执行setup_arch()，对系统结构进行设置。&lt;/li&gt;
&lt;li&gt;执行sched_init()，对系统的调度机制进行初始化。先是对每个可用CPU上的runqueque进行初始化;然后初始化0号进程(其task struct和系统空M堆栈在startup_32()中己经被分配)为系统idle进程，即系统空闲时占据CPU的进程。&lt;/li&gt;
&lt;li&gt;执行parse_early_param()和parsees_args()解析系统启动参数。&lt;/li&gt;
&lt;li&gt;执行trap_in itQ，先设置了系统中断向量表。0-19号的陷阱门用于CPU异常处理;然后初始化系统调用向量;最后调用cpu_init()完善对CPU的初始化，用于支持进程调度机制，包括设定标志位寄存器、任务寄存器、初始化程序调试相关寄存器等等。&lt;/li&gt;
&lt;li&gt;执行rcu_init()，初始化系统中的Read-Copy Update互斥机制。&lt;/li&gt;
&lt;li&gt;执行init_IRQ()函数，初始化用于外设的中断，完成对IDT的最终初始化过程。&lt;/li&gt;
&lt;li&gt;执行init_timers()， softirq_init()和time_init()函数，分别初始系统的定时器机制，软中断机制以及系统日期和时间。&lt;/li&gt;
&lt;li&gt;执行mem_init()函数，初始化物理内存页面的page数据结构描述符，完成对物理内存管理机制的创建。&lt;/li&gt;
&lt;li&gt;执行kmem_cache_init()，完成对通用slab缓冲区管理机制的初始化工作。&lt;/li&gt;
&lt;li&gt;执行fork_init()，计算出当前系统的物理内存容量能够允许创建的进程(线程)数量。&lt;/li&gt;
&lt;li&gt;执行proc_caches_init() , bufer_init()， unnamed_dev_init() ,vfs_caches_init()， signals_init()等函数对各种管理机制建立起专用的slab缓冲区队列。&lt;/li&gt;
&lt;li&gt;执行proc_root_init()Wl数，对虚拟文件系统/proc进行初始化。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在 start_kenrel()的结尾，内核通过kenrel_thread()创建出第一个系统内核线程(即1号进程)，该线程执行的是内核中的init()函数，负责的是下一阶段的启动任务。最后调用cpues_idle()函数：进入了系统主循环体口默认将一直执行default_idle()函数中的指令，即CPU的halt指令，直到就绪队列中存在其他进程需要被调度时才会转向执行其他函数。此时，系统中唯一存在就绪状态的进程就是由kerne_hread()创建的init进程(内核线程)，所以内核并不进入default_idle()函数，而是转向init()函数继续启动过程。&lt;/p&gt;
&lt;h1 id=&#34;启动第五步用户层init依据inittab文件来设定运行等级&#34;&gt;启动第五步&amp;ndash;用户层init依据inittab文件来设定运行等级&lt;/h1&gt;
&lt;p&gt;内核被加载后，第一个运行的程序便是/sbin/init，该文件会读取/etc/inittab文件，并依据此文件来进行初始化工作。&lt;/p&gt;
&lt;p&gt;其实/etc/inittab文件最主要的作用就是设定Linux的运行等级，其设定形式是“：id:5:initdefault:”，这就表明Linux需要运行在等级5上。Linux的运行等级设定如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0：关机&lt;/li&gt;
&lt;li&gt;1：单用户模式&lt;/li&gt;
&lt;li&gt;2：无网络支持的多用户模式&lt;/li&gt;
&lt;li&gt;3：有网络支持的多用户模式&lt;/li&gt;
&lt;li&gt;4：保留，未使用&lt;/li&gt;
&lt;li&gt;5：有网络支持有X-Window支持的多用户模式&lt;/li&gt;
&lt;li&gt;6：重新引导系统，即重启&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;启动第六步init进程执行rcsysinit&#34;&gt;启动第六步&amp;ndash;init进程执行rc.sysinit&lt;/h1&gt;
&lt;p&gt;在设定了运行等级后，Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序，它做的工作非常多，包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。如果你有兴趣，可以到/etc/rc.d中查看一下rc.sysinit文件。&lt;/p&gt;
&lt;p&gt;线程init的最终完成状态是能够使得一般的用户程序可以正常地被执行，从而真正完成可供应用程序运行的系统环境。它主要进行的操作有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;执行函数do_basic_setup()，它会对外部设备进行全面地初始化。&lt;/li&gt;
&lt;li&gt;构建系统的虚拟文件系统目录树，挂接系统中作为根目录的设备(其具体的文 件系统已经在上一步骤中注册)。&lt;/li&gt;
&lt;li&gt;打开设备/dev/console，并通过函数sys_dup()打开的连接复制两次，使得文件号0,1 ,2 全部指向控制台。这三个文件连接就是通常所说的“标准输入”stdin,“标准输出”stdout和“标准出错信息”stderr这三个标准I/O通道。&lt;/li&gt;
&lt;li&gt;准备好以上一切之后，系统开始进入用户层的初始化阶段。内核通过系统调用execve()加载执T子相应的用户层初始化程序，依次尝试加载程序&amp;quot;/sbin/initl&amp;quot;,&amp;quot; /etc/init&amp;quot;,&amp;quot; /bin/init&amp;rsquo;，和“/bin/sh。只要其中有一个程序加载获得成功，那么系统就将开始用户层的初始化，而不会再回到init()函数段中。至此，init()函数结束，Linux内核的引导 部分也到此结束。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;启动第七步启动内核模块&#34;&gt;启动第七步&amp;ndash;启动内核模块&lt;/h1&gt;
&lt;p&gt;具体是依据/etc/modules.conf文件或/etc/modules.d目录下的文件来装载内核模块。&lt;/p&gt;
&lt;h1 id=&#34;启动第八步执行不同运行级别的脚本程序&#34;&gt;启动第八步&amp;ndash;执行不同运行级别的脚本程序&lt;/h1&gt;
&lt;p&gt;根据运行级别的不同，系统会运行rc0.d到rc6.d中的相应的脚本程序，来完成相应的初始化工作和启动相应的服务。&lt;/p&gt;
&lt;h2 id=&#34;关于etcrcdinitd目录&#34;&gt;关于/etc/rc.d/init.d目录&lt;/h2&gt;
&lt;p&gt;首先提到的这个目录和运行级别和开机自动启动都没关系，但开机启动会用到该目录下的脚本。&lt;/p&gt;
&lt;p&gt;通常我们把系统各种服务的启动和停止脚本，都放在这个目录下。比如mysqld，ftpd，samba，zabbix等。这些脚本必须能接受start,stop参数，还有其它可选项：reload，restart，force-reload。&lt;/p&gt;
&lt;p&gt;另外，为了少打几个字，系统默认建了一个软链接/etc/init.d指向它。&lt;/p&gt;
&lt;h2 id=&#34;运行级别与rcxd&#34;&gt;运行级别与rcX.d&lt;/h2&gt;
&lt;p&gt;在Linux中，对每一个运行级别来说，可能需要启动的服务都不同。比如我有A,B,C,D四个应用服务，在level2，只需要运行A,B,C，在level3需要运行A,C,D,E。&lt;/p&gt;
&lt;p&gt;正是这个原因，如果单靠一个/etc/rc.d/rc脚本来控制，那将变得很庞大难以维护。那为了维护方便，在/etc/rc.d子目录中建立一个对应的子目录。这些子目录的命名方法是rcX.d，其中的X就是代表运行级别的数字。比如说，运行级别3的全部命令脚本程序都保存在/etc/rc.d/rc3.d子目录中。&lt;/p&gt;
&lt;p&gt;这里要注意，rcX.d放的都只是符号链接，所有真正的启动脚本是放置在 /etc/rc.d/init.d下。当前目录对应的级别需要启动哪些程序，就为哪些启动脚本建立一个指向至 /etc/rc.d/init.d下对应文件的软链。&lt;/p&gt;
&lt;p&gt;rcX.d中放置脚本的链接命名格式是:&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;S{number}{name} K{number}{name} 
S开始的文件向脚本传递start参数 
K开始的文件向脚本传递stop参数 
number决定执行的顺序&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&#34;将程序控制脚本加入自动启动&#34;&gt;将程序控制脚本加入自动启动&lt;/h2&gt;
&lt;p&gt;放在init.d目录下的控制脚本，需要手动执行&lt;/p&gt;
&lt;p&gt;比如，重新启动mysql服务：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;/etc/init.d/mysql start 或 service mysql start&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;如果想要Linux在运行级别为2或3启动时，自动mysql启动脚本怎么办呢？可以给需要自动运行的级别对应的rcX.d下做软链：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#cd /etc/rc.d/init.d &amp;amp;&amp;amp;
#ln -sf ../init.d/mysql ../rc2.d/K64mysql &amp;amp;&amp;amp;
#ln -sf ../init.d/mysql ../rc3.d/K64mysql &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;如果要让每个运行级别都启动自动运行mysql，那么就要重复创建6个（除等级0）软链。&lt;/p&gt;
&lt;p&gt;linux提供了一个命令：checkconfig。它提供了一种简单的方式来设置一个服务的运行级别。使用：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;chkconfig [--add][--del][--list][系统服务] 或 chkconfig [--level &amp;lt;等级代号&amp;gt;][系统服务][on/off/reset]

# 使用范例：

chkconfig –list 列出所有的系统服务 
chkconfig –add httpd 增加httpd服务 
chkconfig –del httpd 删除httpd服务 
chkconfig –level httpd 2345 on 
把httpd在运行级别为2、3、4、5的情况下都是on（开启）的状态。&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;例如，设置MySQL服务器在运行级别3和4上运行，只需三步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;#chkconfig &amp;ndash;add mysql 将MySQL添加为受chkconfig管理的服务&lt;/li&gt;
&lt;li&gt;#chkconfig &amp;ndash;level 35 mysql on 在级别3和5上设定服务为“on”&lt;/li&gt;
&lt;li&gt;#chkconfig &amp;ndash;level 01246 mysql off 在其他级别上设为off&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了确认配置修改成功，我们可以用list列出服务在不同运行级别的状态，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#chkconfig --list mysql
mysql       0:off       1:off       2:off       3:on 4:off       5:on     6:off&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;这里要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个被chkconfig管理的服务需要在对应的init.d下的脚本加上两行注释。&lt;/li&gt;
&lt;li&gt;第一行告诉chkconfig缺省启动的运行级以及启动和停止的优先级。如果某服务缺省不在任何运行级启动，那么使用 - 代替运行级。&lt;/li&gt;
&lt;li&gt;第二行对服务进行描述，可以用\ 跨行注释。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，random.init包含三行：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# chkconfig: 2345 20 80
# description: Saves and restores system entropy pool for \
# higher quality random number generation.
脚本的这两行注释是必须的，否则chkconfig --add会报错&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h1 id=&#34;启动九步执行etcrcdrclocal&#34;&gt;启动九步&amp;ndash;执行/etc/rc.d/rc.local&lt;/h1&gt;
&lt;p&gt;你如果打开了此文件，里面有一句话，读过之后，你就会对此命令的作用一目了然：&lt;/p&gt;
&lt;div class=&#34;highlight-container&#34;&gt;

  &lt;button class=&#34;copy-code-btn outline&#34;&gt;Copy&lt;/button&gt;

  
  &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# This script will be executed after all the other init scripts.
# You can put your own initialization stuff in here if you don’t
# want to do the full Sys V style init stuff.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;rc.local就是在一切初始化工作后，Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。&lt;/p&gt;
&lt;h2 id=&#34;suse&#34;&gt;SUSE&lt;/h2&gt;
&lt;p&gt;suse没有rc.local。使用如下文档&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;boot.local –&amp;gt; 此文件在系统启动后，进入运行级前就执行的用户脚本。注意这一文件执行时网络等设备都尚未就绪，使用时应小心。&lt;/li&gt;
&lt;li&gt;halt.local –&amp;gt; 在最后有动作&lt;/li&gt;
&lt;li&gt;before.local –&amp;gt; 此文件需要手动创建，在系统进入运行级前执行，但比boot.local晚，此时网络等设备也未就绪。&lt;/li&gt;
&lt;li&gt;after.local –&amp;gt; 此文件需要手动创建。它是在系统已完成运行级的启动后执行的脚本。与其它系统的rc.local相似。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;before.local 和 after.local默认是不存在的&lt;/p&gt;
&lt;h1 id=&#34;启动第十步执行binlogin程序进入登录状态&#34;&gt;启动第十步&amp;ndash;执行/bin/login程序，进入登录状态&lt;/h1&gt;
&lt;p&gt;此时，系统已经进入到了等待用户输入username和password的时候了，你已经可以用自己的帐号登入系统了。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;启动电源后，主机第一步先做的就是查询BIOS(全称：basic input/output system 基本输入输出系统)信息。了解整个系统的硬件状态，如CPU，内存，显卡，网卡等。嗯，这一步windows算和它是一家。不分彼此。&lt;/li&gt;
&lt;li&gt;接下来，就是主机读取MBR(硬盘的第一个扇区)里的boot loader了。这个可是重点哦，据说troubleshooting里就会考这点，给个坏了的loader，叫你修正。windows不支持linux的分区格式。所以，用windows的boot。ini是查不到linux的系统的。一般我装系统都是先装 windows再装linux，然后用grub来做boot loader。两个字：省心!因为linux不像windows那么小气。grub可是支持windows分区格式的哦。&lt;/li&gt;
&lt;li&gt;接上一步，主机读取boot loader后，会读取里面的信息，知道谁跟谁是待在哪，假如主机想进入linux系统，读取到linux核心是在/boot文件目录中后，将此核心加载到内存中。开始了接下来的分析启动之旅。&lt;/li&gt;
&lt;li&gt;OK，第一个运行程序是谁？就是/sbin/init程序。不信，就用top程序看下，是不是PID为1的就是这个东东，它，可是万物之祖啊，我简称它是女娲娘娘(不喜欢亚当夏娃)。&lt;/li&gt;
&lt;li&gt;init首先查找启动等级(run-level)。因为启动等级不同，其运行脚本(也就是服务)会不同。默认的等级有以下几项：
&lt;ul&gt;
&lt;li&gt;0 - halt (系统直接关机)&lt;/li&gt;
&lt;li&gt;1 - single user mode (单人模式，用于系统维护时使用)&lt;/li&gt;
&lt;li&gt;2 - Multi-user， without NFS (类似3模式，不过少了NFS服务)&lt;/li&gt;
&lt;li&gt;3 - Full multi-user mode (完整模式，不过，是文本模式)&lt;/li&gt;
&lt;li&gt;4 - unused (系统保留功能)&lt;/li&gt;
&lt;li&gt;5 - X11 (与3模式类似，不过，是X终端显示)&lt;/li&gt;
&lt;li&gt;6 - reboot (重新开机)&lt;/li&gt;
&lt;li&gt;(不要选择0或4，6 否则，进步了系统的)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OK。系统知道自己的启动等级后，接下来，不是去启动服务，而是，先设置好主机运行环境。读取的文件是/etc/rc。d/rc。sysinit文件。那究竟要设置哪些环境呢？
&lt;ul&gt;
&lt;li&gt;设置网络环境/etc/sysconfig/network，如主机名，网关，IP，DNS等。&lt;/li&gt;
&lt;li&gt;挂载/proc。此文件是个特殊文件，大小为0，因为它是在内存当中。里面东东最好别删。&lt;/li&gt;
&lt;li&gt;根据内核在开机时的结果/proc/sys/kernel/modprobe。开始进行周边设备的侦测。&lt;/li&gt;
&lt;li&gt;载入用户自定义的模块/etc/sysconfig/modules/*。modules&lt;/li&gt;
&lt;li&gt;读取/etc/sysctl。conf文件对内核进行设定。&lt;/li&gt;
&lt;li&gt;设定时间，终端字体，硬盘LVM或RAID功能，以fsck进行磁盘检测。&lt;/li&gt;
&lt;li&gt;将开机状况记录到/var/log/dmesg中。(可以用命令dmesg查看结果)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OK，接下来，就是启动系统服务了，不同的run-level会有不同的服务启动。到/etc/rc。d目录中，不同的level会有不同的目录。如启动 3模式，会有个rc3。d目录，里面就保存着服务。其中，S(start)开头的表明开机启动，K(kill)开头的表明开机不启动。数字表示启动顺序。数字越小，启动越早。注意，他们都是连接到etc/rc。d/init。d/目录中的相关文件。所以，想手工启动某一服务，可以用&amp;quot;/etc/rc。d/init。 d/某个服务 start&amp;quot;启动哦。相反，我们也可以把某个服务ln(链接命令)到不同run-level的目录中。记得打上S或者K+数字哦。&lt;/li&gt;
&lt;li&gt;读取服务后，主机会读取/etc/rc。d/rc。local文件。所以，如果需要什么开机启动的话，可以写个脚本或命令到这里面来。就不用像上面那么麻烦。以后删除也方便。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OK，经过一番长途跋涉后，系统终于可以安心的开启shell了。&lt;/p&gt;
</description>
      
    </item>
    
  </channel>
</rss>
