铁匠铺的工具册
青山镇
青山镇不大,镇上只有一家铁匠铺。铺子的主人叫老铁,打了四十年铁,手艺方圆百里闻名。
老铁的工具多得数不清——锤子三十六把,钳子二十四把,錾子、冲子、锉刀不计其数。镇上的人都来找老铁借工具:木匠借锤子,瓦匠借撬棍,连教书先生做教具也要来借一把小钢锯。
老铁人好,谁来借都给。但他有个毛病——从不记账。
麻烦来了
上个月,木匠老张来还锤子,却发现锤子少了两把。问遍镇上的人,谁都摇头说"不是我借的"。
瓦匠老李借了一把撬棍,三个月没还。老铁去问,老李一拍脑袋:"哎呀,忘在工地上了!"工地早拆干净了,撬棍找不到了。
更糟的是,剃头匠老王来借剪刀。老铁说:"剪刀?我记得还有三把的。"翻遍铺子,一把都没有。后来才知道——一把在张木匠家压箱底,一把被李瓦匠拿去撬钉子撬断了刃(也没还回来),还有一把三个月前借给了镇上来的货郎,货郎早走了。
老铁坐在铺子里叹气:"工具倒是都打得好好的,可到底在谁手里,我全不知道。"
工具册
老铁的儿子小铁从省城回来,看见父亲发愁,笑了。
"爹,我在城里的工厂干过。他们有几百号工人、上千件工具,从来没丢过一件。因为他们有一本——工具册。"
小铁拿出一本空账本,在第一页画了三栏:
| 工具 | 借用人 | 借用方式 |
|---|---|---|
| 大铁锤 | 张木匠 | 独占 |
| 钢锯 | 李瓦匠 | 独占 |
| 撬棍 | 教书先生 | 借用,不占 |
"爹,你看。"小铁指着这三栏,"每一件工具,我们都记清楚三件事:工具本身、谁拿着它、怎么个拿法。"
"第一栏 '工具'——就是这个工具叫什么,放在哪。对应的就是内存地址——new 出来的那块内存,总得有个名字。"
"第二栏 '借用人'——就是谁拿着这个工具。这个最重要。 工具弄丢了,第一个要问的人就是借用人。在程序里,这就叫 '所有权'——谁负责最后把工具还回来,谁就是所有者。"
"第三栏 '借用方式'——分三种。"
小铁在第三栏旁边画了三个圈:
第一种:独占。
"一把锤子只能一个人用。张木匠拿走了大铁锤,李瓦匠就不能同时拿。张木匠用完,他在工具册上把自己的名字划掉——锤子自动回到铺子里。他如果忘了划?工具册上他名字还在,谁都知道锤子在张木匠手里。"
这就是 unique_ptr。一把锤子,一个主人,主人走了锤子自动归还。
第二种:共用。
"一把大钢锯,有时候张木匠要用,有时候李瓦匠要用,有时候教书先生也要用。工具册上写三个人的名字,每个人用完就在自己名字后面打个勾。三个人都打了勾,钢锯才回铺子。"
这就是 shared_ptr。一个对象,多个持有者,最后一个持有者离开时释放。关键是:你知道有哪些人在共用,谁也不会忘了还。
第三种:借用,不占。
"教书先生明年来镇上教书,现在先来看看工具。小铁把撬棍递给他看看——但是工具册上不写他的名字。因为教书先生只是 '看看',不是 '拿走'。他不能把撬棍带回家,只是在铺子里用一下。工具归铺子管,教书先生不负责还。"
这就是裸指针和引用。你拿到了对象的地址,但你不是它的所有者。你只是借来看一眼,别把它带走,别对它负责。 对象的生死,由真正的所有者(那个在工具册第一栏签了字的人)决定。
不会丢工具的铺子
自从有了工具册,青山镇铁匠铺再也没有丢过一件工具。
每天早上,小铁翻开工具册,第一栏里列着所有工具,第二栏里记着谁在用,第三栏里标着怎么用。下午收工,小铁再翻一遍——独占的工具,借用人走了名字就自动划掉;共用的工具,最后一个用完的人自动把它送回铺子;借来看看的,看完就放回去,不在册子上留痕。
有一次,货郎又来了,想借一把剪刀。小铁翻开工具册看了一眼:"不好意思,三把剪刀都在用——张木匠独占一把,李瓦匠和教书先生共有一把,还有一把剃头匠老王只是借看,但也得等他用完。"
货郎说:"我就拿去看一眼,保证还。"
小铁摇头:"拿去看一眼'在工具册上也得记。不然你走了,剪刀找谁要?"
货郎懂了,在工具册上签了名。
老铁悟了
一个月后,老铁在铺子里喝着小酒,跟小铁说:"我以前以为,铁匠铺最重要的是工具——锤子要重,钳子要紧,錾子要利。"
"现在我知道了。铁匠铺最重要的,是知道每一把工具在谁手里。"
小铁笑了:"爹,这就是城里程序员说的——"
"别跟我提那什么指针不指针的。"老铁一摆手,"我听不懂。但我知道——工具可以借出去,但必须知道找谁要回来。"
后记
C++ 的内存管理,说到底就是老铁的工具册:
new= 打了一把新工具unique_ptr= 独占借用,借用人走了,工具自动归还shared_ptr= 共用借用,最后一个用完的人负责归还- 裸指针 / 引用 = 借用不占,只是看一眼,别带走
- 内存泄漏 = 工具借出去了,册子上没记,找不回来了
- use-after-free = 工具已经还回铺子了,你还当自己拿着,伸手去摸——空的
老铁以前丢工具,不是因为他不会打铁,是因为他没有记账。程序员写 new 忘了 delete,不是因为他不会写代码,是因为他没有想清楚——谁拥有这个对象。
铁匠铺最重要的不是工具,是工具册。代码里最重要的不是内存,是所有权。

