C++模版从精通到精神分裂
点击关注异步图书,置顶公众号
每天与你分享IT好书 技术干货 职场知识
by - 赵岩
先做个广告置入,如果喜欢这篇文章,你可以到zhaoyan.website/blog 去查看于此类似的C/C++文章。
这是一篇写软件的文章,但是很硬,提前预警一下,女生不要看!
所有写C++的文章,如果没有源代码都是在耍流氓。闲话不说, May the source be with you!
这是一个教科书般经典的例子。介绍C++的继承和多态。 这里唯一需要重点强调的是:对函数LetAnimalTalk和vector va 来说,我们可以想象他们是客户。[face=黑体]通过继承把变化封装到基类的后面,这样使用基类接口的客户就不需要改动![/face]对客户来说,无论基类后面怎么变化,你都影响不到我。例如,如果现在有一个经理狗加入了项目团队,你的LetAnimalTalk函数是不需要任何改变的。
So far so good! 现在看看引入模版后,发生了什么?
基本的应用场景是这样的。对于animal, 你可以用字符串来表示他的ID, 如果你想developer是不应该享有字符串名字的,那么你也可以用整型数来表示他的ID。上面整个的程序,如果你把main中换成下面的样子,除了猫会有点意见,其它一切都没有问题!
上面模版继承的一个最明显的弊端是语法变得更加臃肿和复杂了。例如,你不可能在子类中直接引用基类的变量了。如果你引用他,必须使用Animal::id_这样的语法。背后的原因是当编译器编译Cat的时候,Animal是根本不存在的!具体的细节你可以看后面的参考文献1。
另外的一个问题就是虚函数的效率问题。这种多态是发生在程序运行的时候,主要通过虚函数表进行调用分发。所以有一定的效率损失。下面我们再看另外一个例子。 由于猫不喜欢整型ID的名字,所以我们这里完全去掉这个feature,重点关注如何利用模版实现多态。
这段代码中,有几点注意一下:
1) 多态已经不需要用指针了,我们可以用引用来支持多态。
2)在函数LetAnimalTalk中,pa.talk()到底调用那一个 talkImplement是在编译的时候就决定了。这是一种静态多态技术。所以没有效率的损失。
3)但是函数LetAnimalTalk现在必须是模版函数,同时我们也失去了用vector同时保存Cat 和Developer的能力。这是效率提升带来的灵活性的损失!
4)这个是CRTP模式,更多介绍看参考文献2。翻译过来就是“好奇地不断追问自己!”这应该是一种精神分裂的明显的初期症状了。
目前为止,我们介绍了三个例子。还都遵循着一个基本的IS-A的逻辑关系。也就是说,Cat是一个Animal,Developer也是一个Animal。下面介绍三个IMPLEMENT-BY的逻辑关系。第一个例子完全没有使用继承。
1) 这段程序中,通过模版参数,在编译的时候就把不同的talk行为的实现方式传递给Animal类。这个方法在STL中运用的相当广泛。具体的例子像STL中的map类
其中, std::less就类似于我们上面的SayMiao。
2)由于导入的是某种行为,所有再叫做Cat就不合适了,所以这里把类的名字叫做SayMiao
为了实现IMPLEMENT-BY关系,我们也可以使用私有继承:
1) 有没有被
template
class Animal: private T{
这样的语法惊到!没关系,我们慢慢来。首先私有继承不是IS-A的关系。而是IMPLEMENT-BY的关系。关于什么时候使用私有继承,什么时候使用组合(composition)。请看参考文献3。
2)这种方式是Parameterised inheritance, 也是一种常见的设计模式,请看参考文献4
OK,最后的问题是,既然私有继承可以,共有继承行不行?在一个分裂的病人眼中,没啥是不行的!
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/
本文来源于异步社区,作者:赵岩,作品《C++模版从精通到精神分裂》,未经授权,禁止转载。
长按二维码,可以关注我们哟
每天与你分享IT好文。
在“异步图书”后台回复“关注”,即可免费获得2000门在线视频课程;推荐朋友关注根据提示获取赠书链接,免费得异步e读版图书一本。赶紧来参加哦!
点击阅读原文,查看更多

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Ubuntu apt-get和pip国内源更换
Ubuntu apt-get和pip源更换 更新数据源为国内,是为了加速安装包的增加速度。 更换apt-get数据源 输入:sudo -s切换为root超级管理员; 执行命令:vim /etc/apt/sources.list; 使用命令:%d 清空所有内容; 清华数据源地址:https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu/ 选择相应的版本复制内容,点击“i”键进入编辑文本模式,粘贴内容到vim编辑窗体,点击“ESC”键进入编辑模式,输入“:wq”保存离开; 更新源:sudo apt-get update 更新软件:sudo apt-get upgrade pip3的安装与升级 安装pip3:sudo apt-get install python3-pip 升级pip3:sudo pip3 install --upgrade pip 查看pip版本:pip -V pip源更换 根目录创建.pip文件:mkdir ~/.pip 创建文件pip.conf:vim .pip/pip.conf 点击“i”键,进入编辑模式,复制信息: [glob...
- 下一篇
Java并发编程基础-理解中断
章节 什么是中断 中断线程的方法 线程中断状态的判断以及何时被中断的线程所处 isInterrupted() 状态为 false? 1.什么是中断 线程标识位 中断可以理解为线程的一个标识位属性,它标识一个运行中的线程是否被其他线程进行了中断操作。 2.中断线程的方法 其他线程通过调用该线程的 interrupt() 方法对其进行中断操作。 其实就是其他线程对该线程打了个招呼,要求其中断。 3. 线程中断状态的判断 线程通过方法isInterrupted()方法来进行判断是否被中断。 如下两种情况需要注意: 1.如果被中断的线程已经处于终结状态,那么调用该线程对象的 thread.isInterrupted() 返回的仍是 false。 2.在Java API中可以看到,许多抛出 InterruptedException 的方法,(其实线程已经终结了,因为遇到了异常)如Thread.sleep( long mills) 方法)这些方法在抛出InterruptedException 异常之前,JVM会将中断标识位清除,然后抛出InterruptedException,此时调用isInte...
相关文章
文章评论
共有0条评论来说两句吧...