C++模版从精通到精神分裂

by – 赵岩

先做个广告置入,如果喜欢这篇文章,你可以到zhaoyan.website/blog 去查看于此类似的C/C++文章。
这是一篇写软件的文章,但是很硬,提前预警一下,女生不要看!

所有写C++的文章,如果没有源代码都是在耍流氓。闲话不说, May the source be with you!

https://gist.github.com/zhaoyan/5d39a57e64f2929a95704c6253d35530

这是一个教科书般经典的例子。介绍C++的继承和多态。 这里唯一需要重点强调的是:对函数LetAnimalTalk和vector<Animal*> va 来说,我们可以想象他们是客户。通过继承把变化封装到基类的后面,这样使用基类接口的客户就不需要改动! 对客户来说,无论基类后面怎么变化,你都影响不到我。例如,如果现在有一个经理狗加入了项目团队,你的LetAnimalTalk函数是不需要任何改变的。

So far so good! 现在看看引入模版后,发生了什么?

https://gist.github.com/zhaoyan/c5acca04eda622984f2288c06eb7b0a2

基本的应用场景是这样的。对于animal, 你可以用字符串来表示他的ID, 如果你想developer是不应该享有字符串名字的,那么你也可以用整型数来表示他的ID。上面整个的程序,如果你把main中换成下面的样子,除了猫会有点意见,其它一切都没有问题!

https://gist.github.com/zhaoyan/d728d68b5b976d053fa47a310f076768

上面模版继承的一个最明显的弊端是语法变得更加臃肿和复杂了。例如,你不可能在子类中直接引用基类的变量了。如果你引用他,必须使用Animal<T>::id_这样的语法。背后的原因是当编译器编译Cat的时候,Animal是根本不存在的!具体的细节你可以看后面的参考文献1。

另外的一个问题就是虚函数的效率问题。这种多态是发生在程序运行的时候,主要通过虚函数表进行调用分发。所以有一定的效率损失。下面我们再看另外一个例子。 由于猫不喜欢整型ID的名字,所以我们这里完全去掉这个feature,重点关注如何利用模版实现多态。
https://gist.github.com/zhaoyan/c429cb015c556ad23a103a2068540fa6

这段代码中,有几点注意一下:
1) 多态已经不需要用指针了,我们可以用引用来支持多态。

2)在函数LetAnimalTalk中,pa.talk()到底调用那一个 talkImplement是在编译的时候就决定了。这是一种静态多态技术。所以没有效率的损失。

3)但是函数LetAnimalTalk现在必须是模版函数,同时我们也失去了用vector同时保存Cat 和Developer的能力。这是效率提升带来的灵活性的损失!

4)这个是CRTP模式,更多介绍看参考文献2。翻译过来就是“好奇地不断追问自己!”这应该是一种精神分裂的明显的初期症状了。

目前为止,我们介绍了三个例子。还都遵循着一个基本的IS-A的逻辑关系。也就是说,Cat是一个Animal,Developer也是一个Animal。下面介绍三个IMPLEMENT-BY的逻辑关系。第一个例子完全没有使用继承。

https://gist.github.com/zhaoyan/5d6f49d35aada5ff851e79244a5cd6e1

1) 这段程序中,通过模版参数,在编译的时候就把不同的talk行为的实现方式传递给Animal类。这个方法在STL中运用的相当广泛。具体的例子像STL中的map类 template<
class Key,
    class T,
    class Compare = std::less,
    class Allocator = std::allocator<std::pair >
> class map
其中, std::less就类似于我们上面的SayMiao。

2)由于导入的是某种行为,所有再叫做Cat就不合适了,所以这里把类的名字叫做SayMiao

为了实现IMPLEMENT-BY关系,我们也可以使用私有继承:

https://gist.github.com/zhaoyan/c7c02387f4b0867991ad837ae4fb82e7

1) 有没有被
template< typename T>
class Animal: private T{
这样的语法惊到!没关系,我们慢慢来。首先私有继承不是IS-A的关系。而是IMPLEMENT-BY的关系。关于什么时候使用私有继承,什么时候使用组合(composition)
。请看参考文献3。

2)这种方式是Parameterised inheritance, 也是一种常见的设计模式,请看参考文献4

OK,最后的问题是,既然私有继承可以,共有继承行不行?在一个分裂的病人眼中,没啥是不行的!

https://gist.github.com/zhaoyan/07a8c0eb8dadd6eb1e3c7372ef2629a8

1) 这就是在Modern C++ design中提到的Policy-Based design。一个小提示是:现在在Animal中已经不需要talk这个函数了

上面我一共给出了六个程序。到 https://www.onlinegdb.com/ 把这六段代码拷贝进去,根据自己的理解和问题修改一下。“纸上得来终觉浅,绝知此事要运行”。 这其实是陆游给广大程序猿的一句忠告。 如果你有足够的耐心,你可以慢慢地深入的体会,这里好玩的东西还挺多的。由于篇章关系(主要是再展开我也不会了!)我就不多说了。

以上这六段程序,分别代表着六种不同的语法方式,表达出两种最基本的设计模式 IS-A还是IMPLEMENT-BY。首先,没有什么优劣之分,在不同的场景下,各有优缺点。另外,C++的模版完全不同于传统的C++编程。他的语法和想表达的语义有非常明显的分裂趋势,非常容易把传统的C++程序猿也搞分裂了。正所谓范型是C++最大的坑,但是不跳此坑,不足以谈人生!

如果没有看懂就算了!你完全可以说:“这个人已经疯了!” 这个我在标题中已经承认了!

参考文献

1)https://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates

2)https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

3)https://isocpp.org/wiki/faq/private-inheritance#priv-inherit-vs-compos

4)https://blog.feabhas.com/2014/06/template-inheritance/

我眼中的某主义

本来最烦写这类文章的。大部分看客的心理我懂,无非就是觉得你愤青。真是贱人多矫情,废柴总愤青。只不过是最近国内某主义大火,我也想趁机蹭点热度,表一下衷心。还有一个原因,我不知道国内是不是真的还有人认为某主义无比正确,如果真的还有这种人,也让这些人听听平时他们根本听不到的不一样的声音。

要说某主义,那就要提另外一个大名鼎鼎的主义:法西斯主义。这两个主义还真是有一个很基础的共同点:那就是非常极端地看不上某一类人,而且一定要从肉体上消灭他们!法西斯是看不上某个民族的人,而某主义主要是看不上某个阶级的人。它没钱的时候,就看不上有钱人,它有钱了吧,又看不上没钱的人。从这点上看,某主义对本国人杀伤力更大。

要是看不上人这种事,本来就是人的天性。我小的时候就是看不上邻居的人孩子。他简直是我童年生活的噩梦。上初中就看不上我同桌,总感觉就是因为她,班花才总不理我。再大点看不上的人那就更多了,主要是看不上比我过的好的人。人啊!都这德性,这也没啥大惊小怪。

话说100年前,一个德国人,既然是人,当然也这德行!由于面包太贵有点吃不起,开始思考一些哲学上的问题。为啥我吃不起面包?想来想去,终于找到了原因,主要是面包店老板的原因。一块钱的面包,老板把5毛钱都拿走了。当老板就要赚钱,本来天经地义的事,知识分子却总爱剑走偏锋。面包店老板居然不是因为爱我才给我做面包,而是要赚我的钱。就是因为他赚我的钱,我都吃不起面包了!这不行,必须把面包店老板杀掉才行。这样面包立马就能便宜一半!

这个想法,YY一下也就算了。可是不巧的是,由于工业文明带来的产业升级,突然有很多人吃不起面包了。一些头脑灵光的人偶然看到这个德国人的某个主义,感觉如获至宝。原来可以通过这么高大上的一堆专业名词,干掉面包店老板,这样面包店里的面包就都归我了。太他妈好了。就这么干!

很快的,就是好几声的炮响,面包店老板人头落地。再然后就是他们突然发现,随着面包店老板被干掉后,一起消失的还有面包。这种复杂的经济学模型,我就不详细解释了!反正说了你也不懂。你要是还想不明白,你就问问自己:没有老板管你,你还干活吗? 不过最要命的还不是面包,要干掉面包店老板,首先要干掉两样东西,那就是民主和法律。

这个时候一些跟随者有些疑问了。面包不仅更贵,更差,而且更少了!!!但是非常遗憾的是,领头杀死面包店老板的一批人,现在已经有了不受法律和民主约束的权力,他们已经有面包,还有牛排,还有红酒,有军队。他们不允许你有任何疑问来动摇他的统治,随其自然的,运动开始了!

整人,杀人,贫穷和杀戮,这几乎和某主义如影相随。几乎就是老一套!苏联,中国,北朝鲜,柬埔寨,东德,波兰,越南….. 没有任何一个国家能逃脱出这个规律,好像被人划着圈圈诅咒着。

一切就这么简单,没有什么高深的道理可讲,一切都是从看不上某一类人开始,以贫穷和杀戮结局。人性细节处魔鬼丛生,这本没什么大不,但是不幸的是,某主义将人性的恶放大到最大!

要说千年最伟大的思想家,我只服卢梭:“人生来平等!”,排第二的是契诃夫:“大狗叫,也得让小狗叫”。排第二的主要原因就是他没有考虑中狗的感受。这才是解决社会问题的终极之道!至于其它的伟大的什么什么家,统统负分!