﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-huaxiazhihuo-随笔分类-c++技术探讨</title><link>http://www.cppblog.com/huaxiazhihuo/category/19468.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 27 May 2018 12:17:32 GMT</lastBuildDate><pubDate>Sun, 27 May 2018 12:17:32 GMT</pubDate><ttl>60</ttl><item><title>string类的设计</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/26/215679.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sat, 26 May 2018 03:51:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/26/215679.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215679.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/26/215679.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215679.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215679.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt">String<span style="font-family:宋体;">类的设计一点都不容易，先不论</span>C++<span style="font-family:宋体;">，那怕是其他语言，在面对</span>string<span style="font-family:宋体;">的时候，一不小心也会掉坑，好比</span>java<span style="font-family:宋体;">，好比</span>C#<span style="font-family:宋体;">，一开始假设</span>utf16<span style="font-family:宋体;">是定长编码，后来</span>Unicode<span style="font-family:宋体;">发展到两个字节就装不下一个码位，字符串在</span>java<span style="font-family:宋体;">下，就有点尴尬了。就算是昧着良心用</span>utf32<span style="font-family:宋体;">编码，码元与码位终于一一对应了，也会遇到物理字符与逻辑字符不对应的时候，好像有些语言的字符要用两个</span>unicode<span style="font-family:宋体;">值来表示（很奇怪），有些语言的一个小写字符对应着好几个大写字符。即便是字符串选定了一种编码方式，始终还是要解决与其他编码的交互问题，这些交互接口也不容易设计。另外，每次从长字符串中截取字符串都要重新</span>new<span style="font-family:宋体;">出来一条新的字符串，难免有一点点浪费，当然，现在计算机性能过剩，这纯粹是强迫症。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">而到了</span>c++<span style="font-family:宋体;">下，设计字符串所遇到的问题，就远比想象中复杂，无中生有的又凭空多出来很多不必要的要求，内存资源管理（这个在</span>C++<span style="font-family:宋体;">几乎是无解），异常安全（往字符串添加新内容，假如内存分配失败，必须保持原有值的完整性），还有性能要求（截取字符串避免生成新的字符串）。很多很多的要求，导致语言层面上压根就没法也不可能提供原生的字符串支持，而这一点上又引出来新的问题，代码里面，逻辑意义上看，就不止存在一种字符串类型。好在，大</span>C++<span style="font-family:宋体;">拥有丰富多彩的</span>feature<span style="font-family:宋体;">，应该足以实现字符串类型了，这也是大</span>C++<span style="font-family:宋体;">的设计哲学，既然语言上没法实现的东西，就提供用以支持这种东西的</span>feature<span style="font-family:宋体;">，用户要怎么实现就怎么实现，选择权交到用户手里。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所以，</span>C++<span style="font-family:宋体;">的库要怎么做出来一道</span>string<span style="font-family:宋体;">，这道菜的味道如何，就很让人好奇。一路考察下来，让人大跌眼镜，竟然没有一个</span>c++<span style="font-family:宋体;">库能提供品质优良字符串，</span> <span style="font-family:宋体;">其抽象充其量也就是比字符数组好一点点，完全就没有</span>Unicode<span style="font-family:宋体;">编码的抽象。</span>Stl<span style="font-family:宋体;">的字符串更让人发指，竟然有几个模板参数，本来多类型的字符串问题就更是雪上加霜了，另外</span>stl<span style="font-family:宋体;">的</span>string<span style="font-family:宋体;">还不能作为</span>dll<span style="font-family:宋体;">函数的参数类型。其实，很多时候，猿猴的要求真的不高，只要求一种</span>utf8<span style="font-family:宋体;">编码的</span>string<span style="font-family:宋体;">，带有格式化，还有一些</span>split<span style="font-family:宋体;">，</span>trim<span style="font-family:宋体;">，</span>FindOneOf<span style="font-family:宋体;">，</span>toupper<span style="font-family:宋体;">等常用字符串处理的操作就行了，只可惜，没有一个</span>c++<span style="font-family:宋体;">库能基本满足这样的基本要求。其实，这些要求，具体到</span>C++<span style="font-family:宋体;">下，要基本满足，也的确很困难。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">除了</span>c++<span style="font-family:宋体;">，很多语言的</span>string<span style="font-family:宋体;">类型都是原子属性，一个</span>string<span style="font-family:宋体;">值，但凡一点风吹草动，都要生成新的</span>string<span style="font-family:宋体;">值，原有的值必须保持不变。此外，其官方也提供了类似于</span>StringBuffer<span style="font-family:宋体;">或者</span>StringBuilder<span style="font-family:宋体;">用以构造很长很长，以弥补这种动不动就生成新</span>String<span style="font-family:宋体;">的性能问题。这两种类型泾渭分明。而</span>c++<span style="font-family:宋体;">的</span>string<span style="font-family:宋体;">，似乎是把这两种类型糅合在一块了，由此带来语义上的不清晰，也造成很多不必要的麻烦，因为绝大多数场合下，只需要使用</span>string<span style="font-family:宋体;">的原子属性，可变的</span>string<span style="font-family:宋体;">只是用来保存字符缓冲而已。知道吗，</span>stl<span style="font-family:宋体;">的</span>string<span style="font-family:宋体;">有一百多个成员函数，很多都是不必要的重载，不过是为了避免字符串的复制而已。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所以，首先要对只读的</span>string<span style="font-family:宋体;">做抽象，也即是</span>string_view<span style="font-family:宋体;">，只需两个成员字段，字符串的起始地址以及缓冲长度，并且不要求以</span>0<span style="font-family:宋体;">结束，它有一个很好的特性，字符串的任何一部分，也都是字符串，甚至，必要时，一个字符，通过取地址，也可以看做是长度为</span>1<span style="font-family:宋体;">的</span>string_view<span style="font-family:宋体;">。任何连续的内存字符块，都可以看做是</span>string_view<span style="font-family:宋体;">。其不必涉及内存的分配，显得非常的轻量级，可以在程序中到处使用，只需注意到字符缓冲的生命周期，就不必担心会引来什么问题。在</span>string_view<span style="font-family:宋体;">上，可以做</span>trim<span style="font-family:宋体;">，比较，查找，反向查找等操作，除了读取单个字节的迭代器，还提供两套迭代器，用以取到</span>unicode<span style="font-family:宋体;">码位值（</span>uin32<span style="font-family:宋体;">），和用以访问逻辑字符，其值也为</span>stirng_view<span style="font-family:宋体;">。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">剩下来就是可写可修改的</span>string<span style="font-family:宋体;">，要求以</span>0<span style="font-family:宋体;">结束，也即是</span>stl<span style="font-family:宋体;">的</span>string<span style="font-family:宋体;">，因为很多函数都在</span>string_view<span style="font-family:宋体;">上，所以这里基本上都只是插入、添加、删除、替换的操作，要注意的是，中括号操作符不能返回字符引用，因为那样完全没有任何意义，就算是保留中括号返回字符值，意义也很小。</span>Trim<span style="font-family:宋体;">、查找、比较等操作，必须通过其成员函数</span>view<span style="font-family:宋体;">来返回代表自己的</span>string_view<span style="font-family:宋体;">。</span>String<span style="font-family:宋体;">的很多成员函数，大多数参数类型就是</span>string_view<span style="font-family:宋体;">，因此也没有像是在</span>stl<span style="font-family:宋体;">下垃圾</span>string<span style="font-family:宋体;">的那么多乱七八糟的重载。很简明的设计，性能与简单的良好统一，不知为何，</span>stl<span style="font-family:宋体;">要到</span>c++17<span style="font-family:宋体;">的时候，才会加入</span>stirng_view<span style="font-family:宋体;">这么重要的类型，即便是如此，</span>stl<span style="font-family:宋体;">的</span>string<span style="font-family:宋体;">既有代码已成定局，也没办法用</span>string_view<span style="font-family:宋体;">来简化它的一百多个的成员函数了</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215679.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2018-05-26 11:51 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2018/05/26/215679.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>U8String的重构体会</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/22/215672.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Tue, 22 May 2018 09:10:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/22/215672.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215672.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2018/05/22/215672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215672.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215672.html</trackback:ping><description><![CDATA[<div>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">近两年来在写</span>C++<span style="font-family:宋体;">的运行时环境，反射、运行时类型信息、内存管理、并行、字符串、协程、</span>ORM<span style="font-family:宋体;">等等，基本上重写了一套</span><span style="font-family:宋体;">标准库以及运行库。对于在</span>c++<span style="font-family:宋体;">下使用字符串，深有体会。一开始呕心沥血，殚精竭虑，支持多种编码方式（</span>Utf8<span style="font-family:宋体;">、</span>Utf7<span style="font-family:宋体;">、</span>GB2312<span style="font-family:宋体;">、</span>Utf16LE<span style="font-family:宋体;">，</span>Utf16BE<span style="font-family:宋体;">等）的字符串类型，以及在此之上的对这些字符串提供格式化、字符串解析、</span>json<span style="font-family:宋体;">、</span>xml<span style="font-family:宋体;">、文件读写</span>BOM<span style="font-family:宋体;">等等功能，必须承认，大</span>C++<span style="font-family:宋体;">真是变态，像是这样变态无聊的概念都可以支持，还可以实现得很好，用起来确实也方便。可是，每次面临字符串操作的时候，都会心里发毛，都会嘀咕此时此刻，纠结的是哪门子的编码，也搞得很多代码必须以</span>template<span style="font-family:宋体;">的形式，放在头文件上，不放在头文件，就必须抽象出来一个通用的动态字符串类型，代表任意编码的一种字符串类型，代码里面引入各种各样臆造的复杂性。终于受不了啦，最后搞成统一用</span>utf8<span style="font-family:宋体;">编码，重构了几千行代码（十几个文件），然后，整个字符串世界终于清静了，接口</span>api<span style="font-family:宋体;">设计什么的，也一下子清爽了很多。整个程序内部，就应该只使用同一种编码的字符串。</span>stl<span style="font-family:宋体;">的带有多个模板的</span>string<span style="font-family:宋体;">设计，就是无病呻吟，画蛇添足。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">为什么选择</span>Utf8<span style="font-family:宋体;">编码，首先，非</span>unicode<span style="font-family:宋体;">编码的字符串是不能考虑的；其次，</span>utf16<span style="font-family:宋体;">也是变长的编码方式，而且还有大小端的区别，所以也不能考虑；</span>utf32<span style="font-family:宋体;">又太占用内存了。想来想去，终于下定决心，</span>utf8<span style="font-family:宋体;">简直就是唯一的选择了。虽然可能有这样那样的小问题，比如说，纯中文文本，</span>utf8<span style="font-family:宋体;">占用多</span>50%<span style="font-family:宋体;">内存（相比于</span>Utf16<span style="font-family:宋体;">），</span>windows<span style="font-family:宋体;">下</span>utf8<span style="font-family:宋体;">有点不友好。但其实都不是问题，也都可以解决。比如说，</span>windows<span style="font-family:宋体;">下，所有的涉及字符串与系统的</span>api<span style="font-family:宋体;">交互，先临时转换成</span>utf16<span style="font-family:宋体;">，然后再调用</span>api<span style="font-family:宋体;">。</span>api<span style="font-family:宋体;">的返回结果为</span>utf16<span style="font-family:宋体;">，再转换为</span>utf8<span style="font-family:宋体;">。好像有一点性能上的损失，其实没啥大不了的。</span>windows<span style="font-family:宋体;">对于多字节也是这样支持的，完全就感受不到性能上的影响。总之，</span>utf8<span style="font-family:宋体;">简直就是程序处理的唯一字符串编码。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">吐槽一下</span>std<span style="font-family:宋体;">的字符串，以及与此相关的一切概念，</span>iostream<span style="font-family:宋体;">，</span>locale<span style="font-family:宋体;">等等东西，垃圾设计的典范。接口不友好，功能弱，而且还性能差，更关键的是其抽象上的泄漏。一整天就只会在引用计数，写时复制，短字符串优化上做文章，时间精力都不用在刀刃上。</span>C++17<span style="font-family:宋体;">终于引入</span>string_view<span style="font-family:宋体;">的类型，情况稍微有些改善。由于字符串使用上不方便，也因此损失了一大片的用户，阵地一再失守。整体上讲，</span>stl<span style="font-family:宋体;">的设计，自然是有精心的考虑，但是，作出这些抽象的标准会上一大群的老爷子们，大概率上讲，应该是没有用</span>stl<span style="font-family:宋体;">正儿八经地开发工业级上的代码，臆造抽象，顾虑太多，表面上看起来好像是那么一回事，真正用起来的时候，就不太对劲，会有这样那样的不足，很不方便。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">简单说一下</span>U8String<span style="font-family:宋体;">的设计思路。</span>U8String<span style="font-family:宋体;">用以管理字符串编码缓存的生命周期，追加缩短替换字符串，支持通过下标可以读取字节</span>char<span style="font-family:宋体;">，但是不支持将字节写入到某个索引上的位置，当然支持往字符串中插入</span>unicode<span style="font-family:宋体;">编码的字符。至于字符串的比较、查找、</span>Trim<span style="font-family:宋体;">、截取子字符串这些常用操作，就全部压在</span>U8View<span style="font-family:宋体;">上。如果</span>U8String<span style="font-family:宋体;">要使用这些，要先通过</span>view<span style="font-family:宋体;">的函数，获取自己字节缓存下的视图。</span>U8View<span style="font-family:宋体;">表示一段连续的字符编码内存，</span>U8View<span style="font-family:宋体;">的任意一部分也是</span>U8View<span style="font-family:宋体;">，不要求以</span>0<span style="font-family:宋体;">结束。只要求</span>U8View<span style="font-family:宋体;">的生存周期不能比其宿主（</span>U8String<span style="font-family:宋体;">，字符数组，</span>U8<span style="font-family:宋体;">原生字符串）长命。事实上，很多</span>api<span style="font-family:宋体;">的字符串参数，其实只是要求为</span>U8View<span style="font-family:宋体;">就行了，不需要是什么</span>const string&amp;<span style="font-family:宋体;">类型。此外，还提供</span>U8PointPtr<span style="font-family:宋体;">的指针类型，用以遍历</span>U8View<span style="font-family:宋体;">，其取值为</span>unicode<span style="font-family:宋体;">编码值，也就是</span>wchar_t<span style="font-family:宋体;">类型。另外，既然有</span>U8View<span style="font-family:宋体;">，自然也就有</span>ArrayView<span style="font-family:宋体;">，代表连续内存块的任意类型。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">自然，库中必须提供格式化</span>Fmt<span style="font-family:宋体;">以及解析字符串</span>Scanf<span style="font-family:宋体;">的函数。</span>StrFmt<span style="font-family:宋体;">用以生成新的</span>U8String<span style="font-family:宋体;">，而</span>Fmt<span style="font-family:宋体;">格式化函数中传入字符串的话，就将格式化结果追加到字符串后面。</span>Fmt<span style="font-family:宋体;">可以格式化数据到控制台，文本文件，日志等等输出结果上。</span>StrFmt<span style="font-family:宋体;">的实现只是简单地调用</span>Fmt<span style="font-family:宋体;">并返回</span>U8String<span style="font-family:宋体;">。有了</span>Fmt<span style="font-family:宋体;">和</span>Scanf<span style="font-family:宋体;">，操作字符串就很方便很灵活了，同时也消除很多很多有关字符串相关的处理函数。</span>Fmt<span style="font-family:宋体;">不仅仅能格式化基本类型，自定义类型，还能格式化数组，</span>vector<span style="font-family:宋体;">，</span>list<span style="font-family:宋体;">，</span>pair<span style="font-family:宋体;">，</span>tuple<span style="font-family:宋体;">等模板类型的数据。库中也提供了类似于</span>iostream<span style="font-family:宋体;">重载</span>&lt;&lt;<span style="font-family:宋体;">和</span>&gt;&gt;<span style="font-family:宋体;">的操作符。大</span>C++<span style="font-family:宋体;">提高的</span>feature<span style="font-family:宋体;">，造出来的</span>string<span style="font-family:宋体;">类型，使用上的方便，一点都不逊色于其他任何语言的原生</span>string<span style="font-family:宋体;">类型。当然，</span>std<span style="font-family:宋体;">的那个</span>string<span style="font-family:宋体;">，简直就是废物。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">不管怎么说，本人还是很喜欢</span>C++<span style="font-family:宋体;">的，用</span>c++<span style="font-family:宋体;">写代码很舒畅，可比用</span>C#<span style="font-family:宋体;">、</span>haskell<span style="font-family:宋体;">、</span>lisp<span style="font-family:宋体;">、</span>scala<span style="font-family:宋体;">时要开心很多。</span>C++<span style="font-family:宋体;">发展到</span>C++11<span style="font-family:宋体;">，基本功能也都完备了，当然，</span>C++14<span style="font-family:宋体;">、</span>C++17<span style="font-family:宋体;">自然功能更加强大，特别是实现模板库的时候，就更方便了，也确实很吸引人。自然，</span>C++<span style="font-family:宋体;">也非十全十美，也有很多的不足，比如不能自定义操作符，不提供非侵入式的成员函数，缺乏延迟求值的语言机制，引用的修改绑定（只要不绑定到</span>nullptr<span style="font-family:宋体;">就好了），成员函数指针的无端限制。但是，世界上又哪里存在完美的</span>language<span style="font-family:宋体;">呢，特别是对于这种直接操纵内存的底层语言来说。至于</span>rust<span style="font-family:宋体;">，叫嚣着要取代</span>c++<span style="font-family:宋体;">，就它那副特性，还远着呢。</span></p>  </div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2018-05-22 17:10 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2018/05/22/215672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>私有继承小讨论</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/12/13/215423.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Wed, 13 Dec 2017 07:17:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/12/13/215423.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215423.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/12/13/215423.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215423.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215423.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">大家都知道，大</span>C++<span style="font-family:宋体;">里面可以私有继承，之后基类的一切，在子类中就成为</span>private<span style="font-family:宋体;">的了，不对外开放了。现在流行接口，组合优化继承，所以</span>private<span style="font-family:宋体;">继承这玩意，日渐式微，很久以前就很少使用了，嗯，不要说</span>private<span style="font-family:宋体;">，就算是大</span>c++<span style="font-family:宋体;">，也是江河日下。不过，存在即合理，</span>c++<span style="font-family:宋体;">语法里面的任何东西，都有其价值，平时可以用不到，但是关键时刻用一下，确实很方便，当然多数情况下，也可以其他途径来完成，但是，就是没那么舒服。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">废话就不说了，直入正题吧。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">假设，现在有接口，假设是</span>IUnknown<span style="font-family:宋体;">，里面有那三个著名的纯虚函数，</span>QueryInterface, AddRef, Release<span style="font-family:宋体;">，好像是这三个哥俩。</span></p>  <p><span style="font-family:宋体;">然后，有一个类，就叫</span>ClassA<span style="font-family:宋体;">，实现了</span>IUnknown<span style="font-family:宋体;">接口，其实就是继承</span>IUnknown<span style="font-family:宋体;">，也就是说，重写了那三个纯虚函数。此外，</span>ClassA<span style="font-family:宋体;">还有一大堆自己的东西，比如</span>public<span style="font-family:宋体;">的字段或者成员函数。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">现在，有</span>ClassB<span style="font-family:宋体;">，想基于</span>ClassA<span style="font-family:宋体;">来做一些事情，但是又不想让用户看到</span>ClassA<span style="font-family:宋体;">里面那些乱七八糟的玩意，因此，这种情况下，用</span>private<span style="font-family:宋体;">似乎很合适。代码如下：</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct IUnknown</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public:</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual HRESULT QueryInterface(REFIID riid,void** ppvObject) = 0;</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG AddRef() = 0;</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG Release() = 0;</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ClassA : IUnknown</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG AddRef() override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG Release() override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ClassB : private ClassA</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">这里，内存的使用上非常紧凑，可以说，没有多余的地方。但是，这里的</span>private<span style="font-family:宋体;">，不仅仅会</span>private ClassA<span style="font-family:宋体;">的一切，就连</span>IUnknown<span style="font-family:宋体;">也被</span>private<span style="font-family:宋体;">，这有时候就不符合要求了，因为这里意图是，</span>private ClassA<span style="font-family:宋体;">，但是又想</span>public IUnknown<span style="font-family:宋体;">，也就是说，对外界来说，</span>ClassB<span style="font-family:宋体;">不是</span>ClassA<span style="font-family:宋体;">，虽然其内部基于</span>ClassA<span style="font-family:宋体;">实现，但是，又希望</span>ClassB<span style="font-family:宋体;">是</span>IUnknown<span style="font-family:宋体;">。对此，有几种解决做法，但是都不能让人满意。</span></p>  <p><span style="font-family:宋体;">方法</span>1<span style="font-family:宋体;">、让</span>ClassB<span style="font-family:宋体;">再次实现</span>IUnknown<span style="font-family:宋体;">接口，如下所示：</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ClassB : private ClassA, public IUnknown</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG AddRef() override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual ULONG Release() override { ... }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">其好处是，</span>ClassB<span style="font-family:宋体;">的实例可以无缝用于</span>IUnknown<span style="font-family:宋体;">的一切场合，不管是引用或者指针，</span>const<span style="font-family:宋体;">非</span>const<span style="font-family:宋体;">。但是，代价也是很大的，首先要针对</span>IUnknown<span style="font-family:宋体;">的每个虚函数，都要一一手写，再次转发给</span>private<span style="font-family:宋体;">的基类，其次，</span>ClassB<span style="font-family:宋体;">比</span>ClassA<span style="font-family:宋体;">多了一个虚函数表指针，大小就比原来多了一个指针的大小，这就不是零惩罚了，这是最不该。</span></p>  <p><span style="font-family:宋体;">方法</span>2<span style="font-family:宋体;">，还是保持私有继承，再在</span>ClassB<span style="font-family:宋体;">中添加几个函数，用以返回</span>IUnknown<span style="font-family:宋体;">，代码如下</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ClassB : private ClassA</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span><span style="font-family:宋体;">也可以</span><span>using ClassA</span><span style="font-family:宋体;">的三个</span>IUnknown<span style="font-family:宋体;">里面的函数</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const IUnknown* GetUnknown()const { return this; }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IUnknown* GetUnknown()const { return this; }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">避开了方法</span>1<span style="font-family:宋体;">的不足，但是就不能无缝用于</span>IUnknown<span style="font-family:宋体;">下，每次使用必须调用一下</span>GetUnknown()<span style="font-family:宋体;">，对于引用的情况下，还必须加多一个星号</span>*<span style="font-family:宋体;">，也是挺不方便的。对了，这里就算添加了类型函数重载，也即是</span>operator IUnknown<span style="font-family:宋体;">，编译器也拒绝将</span>ClassB<span style="font-family:宋体;">无缝转换成</span>IUnknown<span style="font-family:宋体;">。</span></p>  <p><span style="font-family:宋体;">方法</span>3<span style="font-family:宋体;">，用包含，不用私有继承。如下：</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ClassB</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassA mA;</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operator const IUnknown&amp;()const { return *this; }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; operator IUnknown&amp;() { return *this; }</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">这样子，</span>ClassB<span style="font-family:宋体;">的实例可以无缝用于</span>IUnknown<span style="font-family:宋体;">引用下的情况。对于指针的话，可以仿造方法</span>2<span style="font-family:宋体;">那样子，写两个函数进行调用。貌似综合起来，方法</span>3<span style="font-family:宋体;">的整体分数最高。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">就个人而言，更偏向于，直接就让</span><span>ClassB public</span><span style="font-family:宋体;">继承</span>ClassA<span style="font-family:宋体;">好了，少了那么多鬼怪，虽然出现很多不必要的函数，其实也没什么不好。</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215423.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-12-13 15:17 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/12/13/215423.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>回顾C++</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215111.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sat, 15 Jul 2017 12:07:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215111.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215111.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215111.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215111.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215111.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">本人对于</span>c++<span style="font-family:宋体;">的认识，多年下来，经历了以下几个阶段，</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>1、&nbsp;</span>c++<span style="font-family:宋体;">很好很强大，盲目追求运行性能，简直巴普洛夫条件反射，贡献了一大坨垃圾代码；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>2、&nbsp;</span>c++<span style="font-family:宋体;">的面向对象对持很垃圾，什么鬼，代码很容易就耦合，于是迷上对象</span>+<span style="font-family:宋体;">消息发送的面向对象；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>3、&nbsp;</span>c++<span style="font-family:宋体;">太复杂了，</span>template<span style="font-family:宋体;">太抽象，天外飞仙，搞不懂，二进制复用又差。整个</span>c++<span style="font-family:宋体;">就是垃圾，简直程序设计语言里面的败类，</span>C<span style="font-family:宋体;">语言多好啊，小巧精致简单清晰；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>4、&nbsp;</span><span style="font-family:宋体;">使用其他语言做开发，</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">、</span>F#<span style="font-family:宋体;">、</span>elisp<span style="font-family:宋体;">、</span>scheme<span style="font-family:宋体;">、</span>python<span style="font-family:宋体;">、</span>haskell<span style="font-family:宋体;">、</span>javascript<span style="font-family:宋体;">、</span>php<span style="font-family:宋体;">等等一大坨语言，感概每一种语言都比垃圾</span>C++<span style="font-family:宋体;">不要好太多，发誓不再用</span>c++<span style="font-family:宋体;">写哪怕一行的代码；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>5、&nbsp;</span><span style="font-family:宋体;">某一天，突然有点理解了这种语言，一切变得清晰了，原来</span>c++<span style="font-family:宋体;">也相当不错，也可以做一些事情，看开之后，感觉开发效率也跟上来了，做同样的事情，用</span>c++<span style="font-family:宋体;">实现不会比</span>C#<span style="font-family:宋体;">、</span>python<span style="font-family:宋体;">等慢。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">相比于其他语言，</span>c++<span style="font-family:宋体;">的独特优势在于</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">预编译期的伪图灵完备，这一点，好多语言还是有的，并且更超级好，比如</span>rust<span style="font-family:宋体;">，</span>scheme</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">编译期间的</span>C++<span style="font-family:宋体;">是功能完备的解释器，其输出结果是正常运行的</span>c++<span style="font-family:宋体;">代码，结合宏，可以制造很多其他语言必须在语法层面上支持的语法糖。这个解释器的奇妙之处在于它运行于编译期，一旦错误的模板代码要进入运行期，就会出现编译错误，而不需要进入运行时的代码，即便天大错误，也都不要紧，而一旦这段代码要进入运行时，那么模板错误就逃不过编译期解释器的法眼。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">生成各种内存布局的便利语法糖和自由的内存操控；不同类型的对象，只要其内存布局一致，通过强制转换，就可按同一类型来处理，这一点作死能力，绝不被有</span>gc<span style="font-family:宋体;">的语言支持。内存的无节操玩弄，结合</span>template<span style="font-family:宋体;">，分分钟就能仿真出来其他必须语言层面上提供的数据结构，类型安全、运行性能、易用性，一点都不逊色，好比</span>string<span style="font-family:宋体;">，委托，元组，列表，可空类型；</span></p>  <p style="text-indent:21.0pt">C++<span style="font-family:宋体;">的专有特性，</span>raii<span style="font-family:宋体;">、多继承和全局变量。特别是全局变量，结合它的构造函数特点和类型推导，所能玩出来的丰富新花样，其他语言很难做到。全局变量是连接运行期和编译期的桥梁。如果没有全局变量，本座应该不会再次对</span>c++<span style="font-family:宋体;">产生热情。奇怪的是，至今为止，</span>c++<span style="font-family:宋体;">的基础库都不怎么挖掘全局变量的潜能。当然，对全局变量的使用，肯定是把它当做常量来用，全局变量有唯一的内存地址，就起到原子的作用，但它又可打包了丰富的静态类型信息。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">以上的独特，造就了</span>c++<span style="font-family:宋体;">层出不穷的新意，而卓越的运行性能，只是其微不足道的优点。虽然说，语言不重要，思想才重要，软件架构才重要，但是</span>c++<span style="font-family:宋体;">所能承载的思想，以及其到达的抽象高度，的确就真的大大降低框架的复杂性，诚然，</span>c++<span style="font-family:宋体;">的基础库开发要面临无穷无尽的细节纠结，其实，这也反映了</span>c++<span style="font-family:宋体;">编译器掌控细节的能力，因此，我们又可以让编译器自动完成很多很多细节重复，从而大幅度地减轻代码数量，还无损其运行性能。又由于</span>c++<span style="font-family:宋体;">完备强大的静态类型特性，在用动态语言风格的简洁来编写代码的同时，又无损其快速方便地代码重构。笔者的基础库项目，几十次大规模的重构，借助单元测试，保证了重构顺利快速的完成，深感</span>c++<span style="font-family:宋体;">在重构上的便利，这些代码，包括不到</span>1<span style="font-family:宋体;">千行却功能完整的</span>xml<span style="font-family:宋体;">库（还支持对象与</span>xml<span style="font-family:宋体;">数据的直接互相转换）；不到</span>1<span style="font-family:宋体;">千行却一点都不逊色于</span>boost<span style="font-family:宋体;">的</span>spirit<span style="font-family:宋体;">组合子解释器（编译速度却快了很多，语法上简洁很多，更能方便地解释各种语法）；才</span>1<span style="font-family:宋体;">千多行的异步</span>io<span style="font-family:宋体;">框架；输入输出，文件操作，数据库，协程等代码都简洁异常，所有这些代码都支持动态库上的二进制复用，让人很惊诧于</span>c++<span style="font-family:宋体;">的光怪陆离的强大。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">当然，</span>c++<span style="font-family:宋体;">的缺陷也震撼人心，</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>1、&nbsp;</span><span style="font-family:宋体;">语言特性太过繁杂抽象微妙，比如</span>template<span style="font-family:宋体;">、多继承、运算符重载、类型转换、兼容性考虑的很多糟糕语言特性，所以对使用者的节制力要求很高，要求他们时刻清楚自己在干什么，琐碎上的思考太多；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>2、&nbsp;</span><span style="font-family:宋体;">缺乏统一的二进制标准，基础库都用源代码的形式共享，这让原本就龟速的编译速度更加地令人大大感动；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>3、&nbsp;</span><span style="font-family:宋体;">缺乏高标准的基础库，</span>stl<span style="font-family:宋体;">和</span>boost<span style="font-family:宋体;">更在某些技术运用的展示上更起到很坏的影响；</span></p>  <p style="margin-left:39.0pt;text-indent:-18.0pt;"><span>4、&nbsp;</span><span style="font-family:宋体;">缺乏某些延迟求值的机制，缺乏必要的函数式语言机制，所以</span>c++<span style="font-family:宋体;">始终就无法成为堂堂正正的现代化高级语言！</span></p>  <p><span style="font-family:宋体;">就这样吧。</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-15 20:07 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215111.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++的非侵入式接口</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215110.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sat, 15 Jul 2017 09:01:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215110.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215110.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215110.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215110.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215110.html</trackback:ping><description><![CDATA[<p><span style="font-family:宋体;">终于写到</span>c++<span style="font-family:宋体;">的非侵入式接口了，兴奋，开心，失望，解脱，</span>&#8230;&#8230; <span style="font-family:宋体;">。在搞了这么多的面向对象科普之后，本人也已经开始不耐烦，至此，不想做太多阐述。</span></p>  <p><span style="font-family:宋体;">虽然，很早就清楚怎么在</span>c++<span style="font-family:宋体;">下搞非侵入式接口，但是，整个框架代码，重构了十几次之后，才终于满意。支持给基本类型添加接口，好比</span>int<span style="font-family:宋体;">，</span>char<span style="font-family:宋体;">，</span>const char*<span style="font-family:宋体;">，</span>double<span style="font-family:宋体;">；支持泛型，好比</span>vector<span style="font-family:宋体;">，</span>list<span style="font-family:宋体;">；支持继承，基类实现的接口，表示子类也继承了对该接口的实现，而且子类也可以拒绝基类的接口，好比鸭子拒绝基类鸟类&#8220;会飞&#8221;，编译时报错；支持接口组合；&#8230;&#8230;，但是，这里仅仅简单介绍其原理，并不涉及C++中各种变态细节的处理，C++中，但凡是要正儿八经的稍微做点正事，就要面临无穷无尽的细节纠结。</span></p>  <p><span style="font-family:宋体;">先看看其使用例子：</span></p>  <p>1<span style="font-family:宋体;">、自然是定义一个接口：取之于真实代码片段</span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;IFormatble<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br /><div>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; static TypeInfo* GetTypeInfo();</div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;Format(TextWriter</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;stream,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;FormatInfo</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;info)&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;Parse(TextReader</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;stream,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;FormatInfo</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;info)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPNotImplement();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br /></span></div><p><span style="font-family: 宋体;">  </span></p><p>2<span style="font-family:宋体;">、接口的实现类，假设为</span>int<span style="font-family:宋体;">添加</span>IFormatble<span style="font-family:宋体;">的接口实现，实际代码肯定不会这样对一个一个的基本类型来写实现类的代码。这里只是为了举例说明。类的名字就随便起好啦，</span></p>  <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;ImpIntIFormatble&nbsp;:&nbsp;IFormatble<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mThis;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">这一行是关键</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;Format(TextWriter</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;stream,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;FormatInfo</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;info)</span><span style="color: #0000FF; ">override</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;Parse(TextReader</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;stream,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;FormatInfo</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;info)</span><span style="color: #0000FF; ">override</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />}<br />&nbsp;&nbsp;&nbsp;&nbsp;};</span></div><p><span style="font-family:宋体;"></span></p><p><span style="font-family:宋体;">  </span></p><p><span style="font-family:宋体;">这里的关键是，实现类的字段被规定死了，最多只能包含</span>3<span style="font-family:宋体;">个指针成员字段，且第</span>1<span style="font-family:宋体;">个字段一定是目的类型指针，第二是类型信息对象（用于泛型），第三是额外参数，次序不能乱。成员字段如果不需要用到第二第三个成员字段数据，可以省略不写，好比这里。所有接口实现类必须遵守这样的内存布局；</span></p><p><span style="font-family:宋体;"><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">3</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">、装配，将接口的实现类装配到现有的类上，以告诉编译器该类对于某个接口（这里为</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">IFormatble</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">）的实现，用的是第</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">2</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">步的实现类</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">ImpIntIFormatble</span><span style="font-size: 10.5pt;font-family:宋体;Times New Roman&quot;;">；</span></span></p><p><span style="font-family:宋体;"><span style="font-size: 10.5pt;font-family:宋体;Times New Roman&quot;;"></span></span></p><div>PPInterfaceOf(IFormatble, int, ImpIntIFormatble);</div><p>&nbsp;</p><p>4<span style="font-family:宋体;">、将实现类注册到类型信息的接口实现列表中，这一步可以省略，只是为了运行时的接口查询，相当于</span>IUnknown<span style="font-family:宋体;">的</span>Query<span style="font-family:宋体;">。这一行代码是在全局对象的构造函数中执行的，放在cpp源文件中</span></p><p><span style="font-family:宋体;"></span></p><div>RegisterInterfaceImp&lt;IFormatble, int&gt;();</div><span style="font-size:10.5pt; font-family:宋体;Times New Roman&quot;;">然后就可以开开心心地使用接口了，比如<br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;aa&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">20</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextWriter&nbsp;stream(<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormatInfo&nbsp;info(<img src="http://www.cppblog.com/Images/dot.gif"  alt="" />);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TInterface</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">IFormatble</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;formatable(aa); //TInterface这个名字过难看，也没办法了<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formatable</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Format(stream,&nbsp;info);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">&nbsp;dd&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">3.14</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formatable&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;TInterface</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">IFormatble</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(dd);&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">假设double也实现IFormatble</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formatable</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Format(stream,&nbsp;info);<br /></span></div><span style="font-size:10.5pt; font-family:宋体;Times New Roman&quot;;">  </span><p><span style="font-family:宋体;">是否有点神奇呢？其实也没什么，不过就是在</span>trait<span style="font-family:宋体;">和内存布局上做文章，也就只是用了类型运算的伎俩。考察</span>ImpIntIFormatble<span style="font-family:宋体;">的内存布局，对于普遍的</span>c++<span style="font-family:宋体;">编译器来说，对象的虚函数表指针（如果存在的话），都放在对象的起始地址上，后面紧跟对象本身的成员数据字段，因此，</span>ImpIntIFormatble<span style="font-family:宋体;">的内存布局相当于，</span><span style="font-size:10.5pt; font-family:宋体;Times New Roman&quot;;"></span><br /><span style="font-size:10.5pt; font-family:宋体;Times New Roman&quot;;"></span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;ImpIntIFormatble<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;vtbl;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">*</span><span style="color: #000000;">&nbsp;mThis;<br />};<br /></span></div><p>&nbsp;</p><p><span style="font-family:宋体;">  </span></p><p><span style="font-family:宋体;">注意，这里已经没有继承了。这就是，实现了</span>IFormatble <span style="font-family:宋体;">接口的</span>ImpIntIFormatble<span style="font-family: 宋体;">对象的内存表示。因此，可以想象，所有的接口实现类的内存布局都强制规定为以下形式：</span></p>      <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;InterfaceLayout<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mVtbl;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mThis;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">对象本身</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TypeInfo</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mTypeInfo;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">类型信息</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mParam;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">补充参数，一般很少用到</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;};<br /></span></div><p><span style="font-family:宋体;"></span></p><p><span style="font-family: 宋体;">  </span></p><p><span style="font-family:宋体;">当然，如果编译器的虚函数表指针不放在对象起始地址的话，就没法这么玩了，那么非侵入式接口也无从做起。然后，就是</span>TInterface<span style="font-family:宋体;">了，继承于</span>InterfaceLayout</p>  <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;IT</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TInterface&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;InterfaceLayout<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;IT&nbsp;interface_type;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static_assert(is_abstract</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">IT</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">::value,&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">interface&nbsp;must&nbsp;have&nbsp;pure&nbsp;function</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static_assert(</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(IT)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">),&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Can't&nbsp;have&nbsp;data</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interface_type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interface_type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;result&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(interface_type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;result;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"  alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;};</span></div><p><span style="font-family:宋体;"></span></p><p><span style="font-family:宋体;">  </span></p><p><span style="font-family:宋体;">不管怎么说都好，</span>TInterface<span style="font-family:宋体;">对象的内存布局与接口实现类的内存布局一致。因此操作符</span>-&gt;<span style="font-family:宋体;">重载函数才可以粗暴的类型转换来顺利完成。然后构造</span>TInterface<span style="font-family:宋体;">对象的时候就是强制获取</span>ImpIntIFormatble<span style="font-family:宋体;">对象的虚函数表（也就是其起始地址的指针数据）指针赋值给</span>InterfaceLayout<span style="font-family:宋体;">的</span>mVtbl<span style="font-family:宋体;">，进而依次把实际对象的指针放在</span>mThis<span style="font-family:宋体;">上，获取到类型信息对象放在</span>mTypeInfo<span style="font-family:宋体;">中，如果有必要搭理</span>mParam<span style="font-family:宋体;">，也相应地赋值。</span></p>  <p><span style="font-family:宋体;">  </span></p><p style="text-indent:21.0pt"><span style="font-family:宋体;">然后，就是</span><span>template&lt;typename Interface, typename Object&gt;struct InterfaceOf</span><span style="font-family: 宋体;">各种特化的运用而已，就不值一提了。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">由于</span>c++<span style="font-family:宋体;">的</span>abi<span style="font-family:宋体;">没有统一标准，并且，</span>c++<span style="font-family:宋体;">标准也没有规定编译器必须用虚函数表来实现多态，所以，这里的奇技淫巧并不能保证在所有平台上都能够成立，但是，非侵入式接口真是方便，已经是本座写</span>c++<span style="font-family:宋体;">代码的核心工具，一切都围绕着非侵入式接口来展开。</span> </p>  <p style="text-indent: 21pt;"><span style="font-family:宋体;">原本打算长篇大论，也只有草草收场。之后，本座就解放了，会暂时离开</span>cppblog<span style="font-family:宋体;">很久，计划中的内容，消息发送，虚模板函数，字符串，输入输出，格式化，序列化，</span> locale<span style="font-family:宋体;">，全局变量，模板表达式，组合子解析器，</span>allocator<span style="font-family:宋体;">，智能指针，程序运行时，抽象工厂访问者等模式的另类实现，以求从全新的角度上来表现</span>C++<span style="font-family:宋体;">的强大，也只能中断了。</span></p>  <p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><p><span style="font-family:宋体;"><br /></span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215110.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-15 17:01 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215110.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再论接口</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215105.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sat, 15 Jul 2017 03:42:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215105.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215105.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215105.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215105.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">如果说，类的设计思路，是以数据为基础的纵向组织结构，只有唯一的分类方式，有相同基类的，就意味着其相似性，共同点都体现在基类上；那么，接口就是以功能以性质从横向上，来看待类的相似性，并且存在无数的横向视角（否则就失去意义）。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">静态面向对象语言，这里不考虑</span>template<span style="font-family:宋体;">，</span>c++<span style="font-family:宋体;">的</span>template<span style="font-family:宋体;">是鸭子类型，本质上，</span>c++<span style="font-family:宋体;">编译期就是一个功能完备的动态语言。代码上的复用就只能以基类为粒度来进行，比如，函数</span>int fn(Base* bb)<span style="font-family:宋体;">，只有</span>Base<span style="font-family:宋体;">的子类，才有资格成为函数</span>fn<span style="font-family:宋体;">的会员。函数</span>fn<span style="font-family:宋体;">之所以声明其变量</span>bb<span style="font-family:宋体;">的类型为</span>Base<span style="font-family:宋体;">，就是为了使用类型</span>Base<span style="font-family:宋体;">里面的一些东西，一般就是成员函数（对于清教徒来说，不是一般，而是必然）。假如，函数</span>fn<span style="font-family:宋体;">的实现中，就用到</span>Base<span style="font-family:宋体;">的几个成员函数，比如说</span>f1<span style="font-family:宋体;">，</span>f2<span style="font-family:宋体;">，</span>&#8230;<span style="font-family:宋体;">，</span>fn<span style="font-family:宋体;">。换句话说，虽然</span>fn(Base* bb)<span style="font-family:宋体;">表面上要求一定要</span>Base<span style="font-family:宋体;">的子孙后代才能担当重任，但实际上，只要别的</span>class<span style="font-family:宋体;">，不必跟</span>Base<span style="font-family:宋体;">有半毛钱关系，只要这个</span>class<span style="font-family:宋体;">里面支持</span>f1<span style="font-family:宋体;">，</span>f2<span style="font-family:宋体;">，</span>&#8230;<span style="font-family:宋体;">，</span>fn<span style="font-family:宋体;">这些操作，那么原则上他就有资格到</span>fn<span style="font-family:宋体;">里面一游。天下唯有德者居之，不必讲究什么贵族。但是，在没有接口的等级森严的封建社会里面，就算你有惊天之地之能，就因为你没有某种高贵的血统，所以你就不行。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">在单根类的王国中，所有对象都源于</span>Object<span style="font-family:宋体;">，也可以通过反射，通过函数名字运行时获取串</span>f1<span style="font-family:宋体;">，</span>f2<span style="font-family:宋体;">，</span>fn<span style="font-family:宋体;">等成员函数，然后再人肉编译器关于参数信息和返回值类型，以摆脱</span>Base<span style="font-family:宋体;">的类型桎梏，但是，估计也只有在最特殊的时候，才会这样玩。这样玩，简直置编译器的类型检查于不顾，静态语言就是要尽可能的挖掘编译器类型检查的最后一丝潜力。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">接口的出现，就在纵向的类型关系上撕开一道道口子，从而尽最大限度释放对象的能力。时代不同了，现在接口</span>IBase<span style="font-family:宋体;">里面声明</span>f1<span style="font-family:宋体;">，</span>f2<span style="font-family:宋体;">，</span>fn<span style="font-family:宋体;">等函数，然后函数</span>fn<span style="font-family:宋体;">的入参为</span>IBase<span style="font-family:宋体;">，也即是</span> int &nbsp;fn(IBase* bb)<span style="font-family:宋体;">，以明确表示</span>fn<span style="font-family:宋体;">里面只用到</span>IBase<span style="font-family:宋体;">的函数，语义的要求上更加精准。然后，任何</span>class<span style="font-family:宋体;">，只要其实现了接口</span>IBase<span style="font-family:宋体;">，就有资格被</span>fn<span style="font-family:宋体;">接纳，不必再是</span>Base<span style="font-family:宋体;">之后了。所以说，要面向接口编程，就是要面向功能来搬砖，选择的样本空间就广阔了很多。接口是比具体类型要灵活，但不意味着所有的地方就必须只出现接口，</span>class<span style="font-family:宋体;">类型就没用了，当然不是，有些地方就很有必要用具体类型，比如说</span>string<span style="font-family:宋体;">类型，比如说复数这些，就必须明确规定具体类型，无须用到接口的灵活性。总之，还是那句话，没有银弹，具体问题具体分析。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">使用对象，其实就是在使用对象的成员函数，那么，接口也可以看成是成员函数的粒度管理工具。所以，接口就表示了一批成员函数，需要用一批成员函数的时候，用接口最为方便。坊间有一些犯</span>virtual<span style="font-family:宋体;">恐惧症的</span>c++<span style="font-family:宋体;">猿猴，高高兴兴地用一批</span>function<span style="font-family:宋体;">代替接口，罔顾其性能（时间空间）的损失、使用上的不便，哎！面向对象是强有力的抽象工具，比之于面向过程，函数式，有着独特的优点，反正代码构架上，优先使用面向对象，绝不会错。而面向对象，就必然回避不了接口。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">坊间支持面向对象语言中对接口的支持，当以</span>rust<span style="font-family:宋体;">，</span>scala<span style="font-family:宋体;">的</span>trait<span style="font-family:宋体;">机制最为令人喜欢，非侵入式啊，自然狗语言的也还好，但是，本人最反感，反正，狗语言上一切独有特性，本人都本能地毫无理由排斥。自然，</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">或者</span>c++<span style="font-family:宋体;">的多继承，最为笨拙，呆板。</span></p>  <p style="text-indent:21.0pt">java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">里面，类能够实现的接口，在类的定义中，就已经定下来了。类一旦定义完毕，与该类相关的接口就定下来，铁板一块，密不透风，不能增不能减也不能改。你明明看到一个类就已经实现了某个接口的所有方法（函数名字和签名一模一样），但就是因为该类没有在定义中明确说明实现该接口，所以编译器就死活不承认该类实现这个接口。只能用适配器模式，也即是新造一个</span>class<span style="font-family:宋体;">，实现该接口，包含旧类的对象，将接口的所有方法都委托给对象的相应函数来做。</span>java<span style="font-family:宋体;">的繁文缛节就是这样来的，规规矩矩，毕恭毕敬，一步一个脚印。更麻烦的是，每次传递参数都要</span>new<span style="font-family:宋体;">一个适配器对象来满足参数的要求，这是最让人难受的地方。</span></p>  <p style="text-indent:21.0pt">java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">的这种接口机制，实在与现实对不上号，真是找不到任何原型，任何类型的物品，就算是新造的东西，我们都不可能一开始就穷尽它的所有性质所有功能。就算是药物，都有可能是歪打正着的功能，比如伟哥的功能，是其研发阶段中意想不到的。</span>java<span style="font-family:宋体;">、</span>c#<span style="font-family:宋体;">的这种接口，会很干扰类的完整最小化的设计原则，进而加大类的设计难度。当然，它也非一无是处，起码，类支持多少接口，一眼就看出来了，毫无疑义。问题是，接口这种东西，本质上就应该是不确定的横向视角来考察类的关系。</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">下的接口问题，大大限制了接口的使用场合。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">其次，继承时，子类就继承了基类的所有东西，包括其实现的接口。但是，有些时候，子类并不想拥有父类的某些接口。比如，鸭子应该算是鸟类的一个子类，而鸟类支持&#8220;会飞&#8221;这个接口，但是鸭子显然不会飞，也就是说，虽然鸭子包含了鸟类的所有数据，但是它不拥有会飞这个功能。对此，我们希望在编译期间，就能在要求会飞的场合下，传鸭子对象进去时，编译器报错。但是，对此，只能在运行中报错，而且，还是在调用会飞的成员函数里面才报错。原则上，编译器是可以知道鸭子不会飞这个概念的，但是，由于</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">的接口控制粒度单一，满足不了这种要求。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">再次，接口不能组合，比如说，函数</span>fn<span style="font-family:宋体;">的参数，假设名字为</span>pp<span style="font-family:宋体;">，</span>pp<span style="font-family:宋体;">要求同时实现接口</span>IA<span style="font-family:宋体;">，</span>IB<span style="font-family:宋体;">。对此，</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">中是没有语法满足这种多个接口的要求。遇到这种需求时，只能用强制类型转换，先随便让参数类型为</span>IA<span style="font-family:宋体;">或者</span>IB<span style="font-family:宋体;">，然后在必要时，强制转换为另外的类型，只能在运行时报错。又或者是，新造一个接口</span>IAB<span style="font-family:宋体;">从</span>IA<span style="font-family:宋体;">，</span>IB<span style="font-family:宋体;">上继承，然后函数</span>fn<span style="font-family:宋体;">的参数</span>pp<span style="font-family:宋体;">的类型为</span>IAB<span style="font-family:宋体;">，但是这样，依然存在不足，假如某个类实现</span>IA<span style="font-family:宋体;">和</span>IB<span style="font-family:宋体;">，但是没有表明它实现</span>IAB<span style="font-family:宋体;">，那么还是不能满足参数的要求。接口组合的问题，不管是</span>go<span style="font-family:宋体;">、</span>rust<span style="font-family:宋体;">，都没有很好的支持，只能到运行时类型转换才能发生。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">最重要的是，这种接口机制违反了零惩罚的机制。就以</span>c++<span style="font-family:宋体;">为例来说明，就只论接口好了，也即是只有虚函数但是没有成员字段的基类。为了方便描述，还是举例子。</span></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;IA&nbsp;{</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;fa()&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;};<br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;IB&nbsp;{</span><span style="color: #0000FF; ">virtual</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;fb()&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;};<br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Base{&#8230;};<br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Derived&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Base,&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;IA,&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;IB{&#8230;};</span></div><p style="text-indent:21.0pt"><span style="font-family: 宋体;">  </span></p><p><span style="font-family:宋体;">接口</span>IA<span style="font-family:宋体;">有虚函数，里面就要有一个指针指向其虚函数表，所以其内存占用就是一个指针的大小；同理，</span>IB<span style="font-family:宋体;">也如此。表面的意思是</span>Derived<span style="font-family:宋体;">实现了接口</span>IA<span style="font-family:宋体;">，</span>IB<span style="font-family:宋体;">，实际上，在</span>C++<span style="font-family:宋体;">中，接口实现就是继承，也就是说每个</span>Derived<span style="font-family:宋体;">的实例都要包含</span>IA<span style="font-family:宋体;">，</span>IB<span style="font-family:宋体;">里面的数据，指向对应虚函数表的指针字段，也即是有两个指针。这里做不到零惩罚的意思，是说，</span> Derived<span style="font-family:宋体;">为了表明自己有</span>IA<span style="font-family:宋体;">、</span>IB<span style="font-family:宋体;">的能力，每个对象付出了两个多余的内存指针空间的代价，即便是对象不需要在</span>IA<span style="font-family:宋体;">、</span>IB<span style="font-family:宋体;">的环境下使用，这个代价都避免不了。零惩罚抽象，就是要用到的时候才付出代价，哪怕这个代价可以大一点。用不到时，则不必消耗哪怕一点点空间时间上的浪费。空间上浪费的问题不在于节省内存，而在于丧失了精致的内存布局，进而影响到二进制的复用。这一点，非侵入式接口就不用也没办法在对象身上包含其所支持的所有接口的虚函数表指针，因为类型定义完毕，后面还可能在其上添加新的接口实现。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">而由这几点问题引申出来的其他缺陷就不必提了。反正，</span>C++<span style="font-family:宋体;">，包括</span>java<span style="font-family:宋体;">，</span>C#<span style="font-family:宋体;">的这种接口机制最不讨人喜欢了。</span></p>  <p style="text-indent: 21pt;"><span style="font-family:宋体;">至于狗语言的鸭子接口，有时会出现函数名字冲突的小问题，稍微改一下名字就好了。主要是这种接口机制只要一个类包含了某个接口的所有成员函数，就隐式认为它实现了这个接口。这里会有暗示（误导，诱惑），就是定义类的成员函数时，会有意或者无意地迁就现有接口的成员函数，同样，声明接口成员函数时，也会有意无意地往现有类的成员函数上靠。从而导致真正函数的语义上把控不够精准。并且，这种机制太过粗暴，万一这个类虽然支持某个接口的所有函数，但是并不一定就意味着它就要实现这个接口了。狗语言最令人反感之处就是各种自作聪明自以为是的规定。当然，由于狗语言的成员函数可以非侵入式，这个问题造成的不便一定程度上有所减轻，但是，说实在，就连非侵入式的成员函数，本座也不太喜欢了。另外，仅仅从语言层面上，不借助文档，很难知道一个类到底实现那些接口，某个接口被那些类实现，</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">的接口在这一点的表现上就很卓越。其实，本座反感狗语言的最大原因还是因为狗粉，相比之下，</span>java<span style="font-family:宋体;">粉、</span>php<span style="font-family:宋体;">粉等粉，就可爱多了。</span></p>  <p style="text-indent:21.0pt">rust<span style="font-family:宋体;">以</span>trait<span style="font-family:宋体;">形式实提供的接口机制就不多说了，语法形式上简洁漂亮，基本上梦寐以求的接口样子就是这样子的了。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">以上语言的接口，全部属于静态接口，也即是类型所实现的接口在编译期间就全部定下来了，运行时就不再有任何变化。但是，如果对象一直在变化，好比生物，就说人类好了，有婴儿少年青年中年老年死亡这些变化阶段，显然每一阶段的行为能力都大不一样，也拥有不同头衔，不同身份。也就是说，现实中，活生生对象的接口集合并非一成不变，它完全可以现在就不支持某个接口，高兴时候又可以支持了，不高兴时就又不支持了，聋了就听不到声音，盲了就看不见，好似消息发送那样子，显然以上语言是不支持这种动态需求的接口的。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">另外，</span>com<span style="font-family:宋体;">的接口查询虽然发生在运行时，但是，</span>com<span style="font-family:宋体;">的规范，比如对称性、传递性、时间无关性等规则，硬是把</span>com<span style="font-family:宋体;">从动态接口降维到静态接口，这也可以理解，因为动态接口的应用场景真的并不多。这些都没什么，</span>com<span style="font-family:宋体;">最根本的问题，还是在于接口要承载类的功能，当然，这样也有好处，比如语言的无关性。</span>IUnknown<span style="font-family:宋体;">的三大成员函数分明就是类的本职工作，</span>AddRef<span style="font-family:宋体;">，</span>Release<span style="font-family:宋体;">管理对象的生命周期，</span>Query<span style="font-family:宋体;">查询所要的接口。生命周期由对象粒度细化为接口粒度，就显得太琐碎，要谨记好几条规则，要小心翼翼地应付</span>AddRef<span style="font-family:宋体;">，</span>Release<span style="font-family:宋体;">的函数调用，智能指针也只能减轻部分工作量，这就是粒度过小带来的痛苦。而</span>Query<span style="font-family:宋体;">的本质就是对象所实现接口集合，这是对象的本分工作，现在搞成接口与接口之间的关系。由于接口越俎代庖，承接了类的职责，就要求每个接口都要继承</span>IUnknown<span style="font-family:宋体;">，本来接口之间就应该没什么关联性的才对，还导致</span>com<span style="font-family:宋体;">的实现以及使用，在</span>c++<span style="font-family:宋体;">下，非常繁复麻烦，令人头皮发麻。所以说，类与接口，一体两面，谁也不能代替谁。</span></p>  <p>---------------------------------------------------------------------------------------------------------------------------------</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">备注：现实世界中，一种或几种功能就能推导出来其他性质，对应到接口中，就是如果对象实现某些接口，就表示它能实现另外其他接口。目前的语言，也就是接口继承，子接口继承父接口，那么，如果一个类实现了子接口，就表示它也实现了父接口，语言明面上只支持这种接口的蕴含关系。对于其他的蕴含情况，只能用适配器来凑数，而在非侵入式接口中，其语言形式就显得更加的累赘，这一点，在</span>java<span style="font-family:宋体;">上尤为突出。其实，说到底，适配器模式只是弥补语言不支持接口蕴含机制的产物。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;"><br /></span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-15 11:42 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/15/215105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++面向对象的类设计</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/14/215101.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Fri, 14 Jul 2017 03:48:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/14/215101.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215101.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/14/215101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215101.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215101.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">类的设计在于用恰到好处的信息来完整表达一个职责清晰的概念，恰到好处的意思是不多也不少，少了，就概念就不完整；多了，就显得冗余，累赘，当然特例下，允许少许的重复，但是，这里必须要有很好的理由。冗余往往就意味着包含了过多的信息，概念的表达不够精准，好比</span>goto<span style="font-family:宋体;">，指针，多继承这些货色，就是因为其过多的内涵，才要严格限制其使用。好像，</span>more effective c++<span style="font-family:宋体;">上说的，</span>class<span style="font-family:宋体;">的成员函数，应该是在完整的情况下保持最小化。但是，这里我们的出发点，是成员数据的完整最小化。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">最小化的好处是可以保持概念最大的独立性，也意味着，可以用最小的代价来实现这个概念，也意味着对应用层的代码要求越少，非侵入式？好比</span>c++11 noexcept<span style="font-family:宋体;">取代</span>throw()<span style="font-family:宋体;">，好比从多继承中分化出来接口的概念，好比不考虑多继承虚继承的普通成员函数指针。又比如，如果不要求只读字符串以</span>0<span style="font-family:宋体;">结束，那么就可以把只读字符串的任何一部分都当成是只读字符串。类的对外功能固然重要，但是，类不能做的事情，也很重要。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">首先是要有清晰的概念以及这个概念要支持的最基本运算，然后在此基础上组织数据，务求成员数据的最小化。当然，概念的产生，并非拍着脑袋想出来的，是因为代码里面出现太多那种相关数据的次数，所以就有必要把这些数据打包起来，抽象成一个概念。好比说，看到</span>stl<span style="font-family:宋体;">算法函数参数到处开始结束的迭代器，就有必要把开始结束放在一起。比如说，</span>string_view<span style="font-family:宋体;">的出现，这里假设其字符存储类型为</span>char<span style="font-family:宋体;">，</span>string_view<span style="font-family:宋体;">就是连续</span>char<span style="font-family:宋体;">内存块的意思，可以这样表示</span></p>  <p>struct string_view</p>  <p>{</p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const char* textBegin;</span></p>  <p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size_t length;&nbsp;//</span><span style="font-family: 宋体;">或者</span> <span>const char* textEnd</span></p>  <p>};</p>  <p><span style="font-family:宋体;">这里的重点是，</span>string_view<span style="font-family:宋体;">里面的两个成员字段缺一不可，但是也不必再添加别的什么其他东西。然后，在这两个数据上展开实现一系列的成员函数，这里，成员函数和成员字段这两者，有一点点鸡生蛋生鸡的纠结，因为必要成员函数的集合（原始概念的细化），成员函数决定了成员字段的表示，而成员字段定下来之后，这反过来又能够验证成员函数的必要性。不管怎么说都好，成员函数的设计，也必须遵从最小完整化的原则。再具体一点，就是说但凡一个成员函数可以通过其他成员函数来实现，就意味着这个函数应该赶出类外，作为全局函数存在。当然，这也不是死板的教条，有些很特殊的函数，也可以是成员函数，因为成员函数的使用，确实很方便。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">可能会有疑惑，感觉所有的成员函数其实都可以是全局函数。或者说，我们可以对每一个成员字段都搞一对</span>set<span style="font-family:宋体;">、</span>get<span style="font-family:宋体;">的函数，那么所有的其他成员函数就可以是全局函数的形式，很容易就可以遵守最小完整化的原则。当然，这是明显偷懒，拒绝思考的恶劣行为。与其这样，还不如就开放所有的成员字段，那样子就落入</span>c<span style="font-family:宋体;">语言的套路了。所以的法论是，一个函数，这里假设是全局函数，如果它的实现必须要访问到成员字段，不能通过调用该类的成员函数（一般不是</span>get<span style="font-family:宋体;">，</span>set<span style="font-family:宋体;">）来达到目的，或者，也可以强行用其他函数来完成任务，但是很麻烦，或者要付出时间空间上的代价，那么就意味着这个函数应该是该类的成员函数。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">类的设计，就是必不可少的成员字段和必不可少的成员函数，它们一起，实现了对类的原始概念的完整表达，其他什么的，都不必理会。一个类如果不好写，往往意味着这个类的功能不专一，或者其概念不完整，这时，可以不要急着抽象，如果一个类有必要诞生，那么在代码的编写中，该类的抽象概念将一再重复出现，猿猴对它的理解也越来越清晰，从而，水到渠成地把它造出来。所有非需求推动，非代码推动的，拍着脑袋，想当然的造类行为，都是在臆造抽象，脱离实际生活的艺术，最终将被淘汰。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">类的设计，其着眼点在于用必要的数据来完整表达一个清晰的概念。而继承，则是对类的概念进行细化，也就是分类，好比说生物下面开出来动物、植物这两个子类，就是把生物分成动物、植物这两类，继承与日常生活的分类不太一样，继承的分类方式是开放式，根据需要，随时可以添加新的子类别。整个类的体系，是一颗严格的单根树，任何类只能有一个根类。从任何类开始，只能有一条路径回溯到最开始的根类，</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">中就是</span>Object<span style="font-family:宋体;">，所有的类都派生自</span>Object<span style="font-family:宋体;">，这是一棵大树。单根系下，万物皆是对象，这自然很方便，起码，这就从语言层面上直接支持</span>c++ std<span style="font-family:宋体;">的垃圾</span>any<span style="font-family:宋体;">了。而由于</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">完善的反射信息，抛弃静态类型信息，也可以做动态语言层面上的事情，而</span>c<span style="font-family:宋体;">，</span>c++<span style="font-family:宋体;">的</span>void*<span style="font-family:宋体;">，所有的动态类型信息全部都在猿猴的大脑中。</span>java<span style="font-family:宋体;">平台上生存着大把的动态语言，而且，性能都还很不错。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">相对很多语言来说，</span>c++<span style="font-family:宋体;">就是怪胎就是异数，自有其自身的设计哲学，它是多根系的，它不可能也没必要搞成单根系，当然，我们可以假设一个空类，然后所有的类都默认继承自这个空类。</span>c++<span style="font-family:宋体;">的所有类组成一个森林，森林里的树都长自大地。但是不管怎么说都好，只能允许单继承，千万不要有多继承，这是底线，千万千万不能违背（当然，奇技淫巧的场合，就不必遵守这个戒条，多继承千般不是，但是不可或缺，因为它可以玩出来很多花样，并且都很实用很必要）。最起码，单根系出来的内存布局直观可预测，一定程度上跨编译器，只有良好的内存布局，才有望良好的二进制复用。另外，父类对子类一无所知，不要引用到子类一丁点的信息，要保持这种信息的单向流动性。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">在这种单根系的等级分明的阶级体系下，一切死气沉沉，没有一点点的社会活力。显然，只有同属于同一父类的类别之间，才能共享那么一丁点可怜的共性。如果没有接口捣乱，将是怎样的悲剧，最好的例子，</span>mfc<span style="font-family:宋体;">，真是厉害，没有用到接口，居然可以做出来严谨满足大多数需要的</span>gui<span style="font-family:宋体;">框架，没有接口，并不表示它不需要，因为</span>mfc<span style="font-family:宋体;">开了后门，用上了更厉害的玩意</span>----<span style="font-family:宋体;">消息发送，即便如此，</span>mfc<span style="font-family:宋体;">有些地方的基类还有依赖到子类，这就很让人无语了。</span></p>  <p style="text-indent:21.0pt">c++<span style="font-family:宋体;">下，类的设计绝对不对儿戏，一定要清楚自己想要的是什么，抽象出来的概念才不会变成垃圾。大而全的类，远远不如几个小而专的细类。</span>java<span style="font-family:宋体;">，</span>C#<span style="font-family:宋体;">下的类开发很方便，但是粒度过大，把一揽子的东西都丢给你，强卖强买，反正只要类一定义，必然相应的就会出现一大坨完善的反射信息，而对象里面也包含了一些无关紧要的成员字段，而对象的访问，也全部都是间接引用的访问，虽然，现在计算机性能过剩，这些都无伤大雅。</span>c++<span style="font-family:宋体;">给了开发者最大的选择，而搞</span>c++<span style="font-family:宋体;">的猿猴，基本上都智力过剩，对于每种选择，都清楚其背后的代价以及所要到达的目的，所以虽然开发时候，存在心智包袱影响开发效率，但是，但内心就不会存在什么性能包袱的负罪感。就个人而言，还是喜欢</span>c++<span style="font-family:宋体;">这种最高自由度的语言，有时候，对于内存最细致的控制，可以得到更精简的设计，这里无关运行性能，好比说，在</span>c++<span style="font-family:宋体;">中，只要内存布局一致，即便是不同类型的对象，通过强制类型转换来统一对待，进而做匪夷所思之事，好比</span>COM<span style="font-family:宋体;">里面，为了聚合复用，一个类，竟然可以针对同一个接口提供两套实现方式。这种方便，在其他强类型语言中是不支持的。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">某种意义上讲，</span>c++<span style="font-family:宋体;">在面向对象上提供的语言机制，就是为了方便地生成各种内存布局，以及此内存布局上所能支持的操作，虚函数用以生成一堆成员函数指针，继承则用以方便地生成一坨成员字段，</span>&#8230;&#8230;<span style="font-family:宋体;">。所以，</span>c++<span style="font-family:宋体;">的面向对象就是面向内存布局地设计，而多继承、虚继承、模板这些鬼东西很容易就导致内存布局的失控，不过，如果使用得当，却又有鬼斧神工之奇效，创造出来其他语言所没有的奇迹。真的，论动态行为艺术，任何语言在</span>c++<span style="font-family:宋体;">这个大人面前都是幼儿园里的小学生。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">为了引出接口，本座花大力气做科普。这也没办法，因为类虽然是基础，但是静态面向对象的精华，全部都在接口上。只有清晰明确类的功能职责，才能理解接口的必要性以及其多样性。那么，可不可以只有接口，没有类的。可以，就好像com那样子，而代价是，使用起来，各种不方便。这个世界，从来就不存在包治百病之万能药。什么事情都能做的意思就是什么都做不好。</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-14 11:48 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/14/215101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++的面向对象之前传</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/12/215093.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Wed, 12 Jul 2017 10:17:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/12/215093.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215093.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/12/215093.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215093.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215093.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">此文只是杂乱的记录一点点对于面向对象的个人看法，有些观点也并非原创。没什么系统性可言，虽然笔者稍作整理，但始终还是显得很散乱，只是一些片段的堆积。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">由于涉及的题目过于庞大，反而不知道如何下笔。先罗列一下问题，之间没有严格的先后之分，纯粹就是笔者想到哪里，就写到哪里。也不一定就会解答。继承的本质是什么？为什么一定要有接口？</span>c++<span style="font-family:宋体;">多继承为何饱受非议，真的就一无是处？为何笔者就反感</span>go<span style="font-family:宋体;">接口，反正</span>go<span style="font-family:宋体;">独有的一切，笔者都是下意识的排斥？功能繁杂的</span>Com<span style="font-family:宋体;">，结合</span>C++<span style="font-family:宋体;">的自身特点，能否改头换面？</span> <span style="font-family:宋体;">&#8230;&#8230;</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">在原教旨眼里，面向对象的教义就是</span>&#8220;<span style="font-family:宋体;">对象</span>+<span style="font-family:宋体;">消息发送</span>&#8221;<span style="font-family:宋体;">，整个程序由对象组成，而对象之间的就仅仅只通过发送消息响应消息来交互，程序的功能都是在对象与对象的来回消息发送中完成，用现实事情类比，人类就是一个个活生生的对象，人类通过消息的往来，比如语音、文字、广播等，有人制造新闻，有人接受到这些消息后，各自反应，最后完成一切社会活动。好像说得有点抽象，展开来说，其实就是，消息的发送者，原则上不需要事先了解目标对象的任何背景资料，甚至他明知道对方不鸟消息，比如说，明明对方就是一个乞丐，但是并不妨碍你向他借</span>500<span style="font-family:宋体;">万人民币，反正，消息就是这样发送出去的。然后，对象接受到消息之后，就各自反应，比如说有人真的借钱给你；有人哭穷；有人嘀咕你到处借钱，无耻；</span>&#8230;&#8230;<span style="font-family:宋体;">，各式各样，不一而足。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">听起来好像人类社会活动就是消息的往来下推动，艰难的前进，但是，这能拿来搬砖吗？可以的，真的可以！即便是</span>C<span style="font-family:宋体;">语言，都可以来搞消息发送这种高大上的事情，就好像</span>win32<span style="font-family:宋体;">那样子，通过</span>SendMessage<span style="font-family:宋体;">函数给窗口发送消息，其签名如下：</span></p>  <p>LRESULT SendMessage(HWND hWnd,&nbsp;UINT Msg,&nbsp;WPARAM wParam,&nbsp;LPARAM lParam);</p>  <p><span style="font-family:宋体;">好像参数有点多。说白了，消息发送就相当于成员函数函数调用的一个新马甲，换了一种说法而已。成员函数调用，形式是这样子，</span>obj.fn(param1, param2, &#8230;)<span style="font-family:宋体;">，涉及到对象，函数名字，还有参数，可能参数数量不止一个，参数类型也各不一样，这些都没关系。</span>hWnd<span style="font-family:宋体;">为窗口，也即是对象；</span>Msg<span style="font-family:宋体;">为函数名称，现在用正整型编号来代表，有些消息发送系统用原子，</span>qt<span style="font-family:宋体;">好像是用字符串（性能堪忧啊）；</span>wParam<span style="font-family:宋体;">，</span>lParam<span style="font-family:宋体;">可以看成</span>void*<span style="font-family:宋体;">类型，也即是函数的参数，用这两个值封装所有的参数。天真，天下函数参数类型成千上万，参数数目或</span>0<span style="font-family:宋体;">个、或</span>1<span style="font-family:宋体;">个、或三五个、或七八个，就</span>wParam<span style="font-family:宋体;">，</span>lParam<span style="font-family:宋体;">这两个弱鸡，就能封装得过来？可以的，通过强制类型转换，就可以让</span>void*<span style="font-family:宋体;">的值保存</span>char<span style="font-family:宋体;">、</span>int<span style="font-family:宋体;">、</span>float<span style="font-family:宋体;">等值，又或者是将参数打包为结构体，这样子，就可以应付千千万万的函数参数要求，这样子，不要说，有两个</span>wParam<span style="font-family:宋体;">，</span>lParam<span style="font-family:宋体;">来传递参数，就算是只有一个，也都可以应付千千万万的函数要求。</span></p>  <p><span style="font-family:宋体;">那么，如何响应消息？可以参考</span>win32<span style="font-family:宋体;">的原生</span>api<span style="font-family:宋体;">开发，这里就不展开了。原理就是，每个对象都有一个函数指针，那个函数把全部的成员函数都压缩在一个庞大的</span>switch<span style="font-family:宋体;">语句里面，每个消息编号</span>case<span style="font-family:宋体;">分支，就代表一个成员函数，显然，这个分支，要先将</span>wParam<span style="font-family:宋体;">，</span>lParam<span style="font-family:宋体;">里面在还原成对应参数的实际情况，然后再执行相应操作。</span></p>  <p style="text-indent:21.0pt">SendMessage<span style="font-family:宋体;">显然抹去了所有窗口的具体类型信息，甭管你是按钮、漂亮按钮、菜单、编辑框、</span>&#8230;&#8230;<span style="font-family:宋体;">，全部一律都退化成窗口对象。要往编辑框里面添加文字，就给它发送添加文字的消息，</span>wParam<span style="font-family:宋体;">，</span>lParam<span style="font-family:宋体;">就带着要添加的文本和长度。而不是调用编辑框的添加文字的成员函数来做这个事情，最明显的事情，就是也可以给按钮窗口也发送添加文本的消息，虽然按钮窗口对此消息的反应是啥也不做。令人惊讶的是，你可以子类化一个按钮窗口，让它对添加文本的消息做出反应，这完全是可以的。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">显然，原教旨的面向对象教义，的而且确，灵活，解耦彻底，不同类型对象之间的耦合关系一律不复存在，之间只有消息的往来。随心所欲的发送消息（胡乱调用成员函数），自由自在的反应消息（一切全无契约可言），不理睬，或者这一刻不理睬下一刻又动了，或者这一刻动了下一刻又拒绝反应。甚至，消息还可以保存，排队，求反，叠加什么的，也即是消息已经是一种抽象数据类型了，支持多种运算。相比于不知所谓的基于类的静态面向对象（继承封装多态），简直不可同日而语，太多的约束，呆板的语法，深入的哲学思考，架床叠屋的类型关系，也好意思学人家叫面向对象。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">当然，对象</span>+<span style="font-family:宋体;">消息发送这种机制，付出的代价也是很巨大的，基本上，函数调用的静态类型检查不服存在，所有问题都要到运行时才能发现。并且，消息发送的语法也很不直观，必须各种类型转换，而响应消息时又必须转换回去。此外，为函数定义消息编号，也很恶心。不过，这些在动态语言里面都不是问题，反正，动态语言里面没有静态类型约束。另外，笔者用</span>template<span style="font-family:宋体;">、全局变量、宏等奇技淫巧，在</span>c++<span style="font-family:宋体;">里面，已经实现了类型安全的消息发送框架，比如，</span>Send(obj, kAppendText, U8String(&#8220;hello&#8221;))<span style="font-family:宋体;">，而对象实现对消息的响应，直接也是成员函数的形式，并且还是非侵入式的，也即是说，在</span>main<span style="font-family:宋体;">函数之前，可以随时在任意地方给对象添加新的消息反射，所有参数上类型转换以及返回值上的类型转换，全部都不需要了。</span> <span style="font-family:宋体;">但即便是这样，也不赞成原教旨的面向对象到处泛滥。原因是，用它写出来的程序，类型层次很不清晰，相比于架构良好的类形式的面向对象程序，可读性远远不如，也不好维护。更深刻的原因是，对象</span>+<span style="font-family:宋体;">消息发送的威力太惊人，用途太广，任何多态上的行为，都可以用它来做。什么都可以做，就意味着什么都尽量不要让他来做。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">其实，即便</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">这种继承封装多态的面向对象千般弱鸡各种繁文缛节，也不妨碍人家称霸天下，到处流行。你对象</span>+<span style="font-family:宋体;">消息发送再美妙，流行度都不及人家</span>java<span style="font-family:宋体;">一个零头，</span>obj c<span style="font-family:宋体;">还不是靠着</span>ios<span style="font-family:宋体;">的流行才有所起色，挤入排行榜十名内。虽然说市场不能说明什么，但是对比如此悬殊，自有其道理。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">再说，静态类型的成员函数调用模式，广泛存在于人类社会活动中。人与人之间的很多事情，其实只要满足一定的条件，必然就会发生，其后果也可以预料。很多消息的发送，其实是有考虑到对方的身份问题，才会发起，好比小孩子跟爸妈要零用钱的消息，小孩子再发送要零用钱的消息，一定是针对亲人才发起的。真相是，往往要满足一些必要条件，消息才得以发起，当然，只要你高兴，随时都可以发起任何消息，问题是，这种人多半不正常。量体裁衣，针对什么样的问题，就应该采用相应的手段，一招鲜吃遍全天下，行不通的。具体问题，必须具体分析。每种问题，都有自己最独特有效的解法。笔者在原教旨的面向对象上重复太多内容，连自己都恶心，以后应该很少再提及。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所以说，面向对象的设计，首先应该采用的必然还是继承封装多态的思路。在此基础上，根据不同的动态要求，采用不同策略来应对。企图用万能的消息发送来代替静态类型面向对象的荒谬就如同用僵化的面向对象来模拟一切动态行为，两者都是犯了同样的毛病。可是，静态面向对象做设计，又确实困难重重，而最终的开发成果，总是让人难以满意。那是因为，广大劳动群众对静态面向对象一些基本概念的理解，存在这样那样的误区，而由于面向对象语言（</span>java<span style="font-family:宋体;">，</span>C#<span style="font-family:宋体;">）还缺乏一些必要机制，导致设计上出现妥协，原则性的错误越积越深，以至于最后崩盘。其实，不要说一般人，就连大人物，在面向对象上，也都只是探索，好比</span>c++<span style="font-family:宋体;">之父</span>BS<span style="font-family:宋体;">，搞出来多继承，虚继承，</span>iostream<span style="font-family:宋体;">体系，在错误的道路上，越走越远，越走越远。</span></p>  <p><span style="font-family:宋体;">好吧，其实，多继承，还是很有作用的，在很多奇技淫巧上很有用武之地，很方便。但是，用多继承做架构的危险，就在于其功能太过强大。这就意味着它要沦落成为</span>goto<span style="font-family:宋体;">啊、指针啊那样的角色，先甭管它钻石尴尬。多继承的最重要角色，概念实现，也即是接口，也即是定义一批虚函数，里面没有任何数据，这个抽象就必须鲜明，这一点，</span>java<span style="font-family:宋体;">和</span>C#<span style="font-family:宋体;">就做得很到位。就应该从多继承上提炼出来这么一个好东西，咦，对了，为何要有接口？没有接口，就真的不行吗？是的，静态面向对象里面，接口确实必不可少。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">继承，本质上就是分类学。而分类，最重要一点，就是任何一件元素，必须也只能只属于其中一个类，不得含糊。可以存在多种分类方式，但是，一旦确定某种分类方式，那么集合里面的一个东西，就必须只能属于其中一大类。继承，就是分类的一再细化，也是概念的继续丰富。比如说，从生物到动物到哺乳动物，概念包含的数据越来越多。所以说，继承体现的是数据上的丰富关系，它强调的是数据的积累，从远古基类开始，一路积累下来的数据，全部必不可少，也不得重复，一旦违反这条底线，就意味着继承体系上的错乱。继承，相当于类型的硬件，缺乏硬件元器件时，就无法完整表达该类型的概念。比如说，人类可分为男人、女人，自然，男人有男人的阳刚，女人有女人的阴柔，那么阴阳同体怎么办，集两性之所长，难道就要阴阳人多继承与男人女人吗？那么，这样继承下来，阴阳人岂不是就是有两个头，四只手，四条腿了，啊，这不是阴阳人，这是超人，抑或是怪物。所以，阴阳人应该是人里面的一个分支，也即是，人的分类，就要有男人、女人、阴阳人这三大基类。再次强调，继承是为了继承数据，而不是为了功能，功能只不过是数据的附带品。那么，怎么描述男人的阳刚、女人的阴柔，怎么避免阴阳人引入后，分别从男人阳刚，女人阴柔上复制代码呢？此外，再次考虑平行四边形，下面好像又有菱形，有矩形两大类，然后身集菱形矩形的正方形，这里的分类该如何处理，难道忍不住要让正方形多继承菱形矩形吗？从这个意义上讲，在同一体系下，多继承的出现，理所当然，大错特错，由此可知，</span>iostream<span style="font-family:宋体;">就是败类。</span>iostream通过虚继承避免绝世钻石的出现，但是这个虚继承啊，真是要让人呵呵。C++中引入虚继承真是，怎么说呢，好吧，也算脑洞大开的优良物品，也不是完全一无是处，起码，在iostream上就大派用场了。你就说说，虚继承那点不好了？就一点，为了子子类的千秋基业，子类必须虚继承基类，子类受子子类影响，就这一点，你能忍。</p><p>&nbsp;</p>  <p><span style="font-family:宋体;">突然发现，文章已经很长了，不管了，这就打住。至于非侵入式接口，以后再说吧！</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215093.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-12 18:17 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/12/215093.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再议c++的面向对象能力之上</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/11/215082.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Tue, 11 Jul 2017 03:56:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/11/215082.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215082.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/11/215082.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215082.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215082.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt">C++<span style="font-family:宋体;">的面向对象设计能力，与</span>java<span style="font-family:宋体;">，</span>C#<span style="font-family:宋体;">这两个杂碎相比，一直都是一个大笑话，现在谁敢正儿八经地用</span>c++<span style="font-family:宋体;">搞面向对象的框架系统，业界都用</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">搞设计模式，那关</span>C++<span style="font-family:宋体;">什么事情了。而</span>C++<span style="font-family:宋体;">也很有自知之明，很知趣，</span>98<span style="font-family:宋体;">年之后，就不怎么对外宣称自己是面向对象的语言，就不怎么搞面向对象研究了（难道是</span>c++<span style="font-family:宋体;">下的面向对象已经被研究透彻？），一直在吃</span>template<span style="font-family:宋体;">的老本，一直到现在，</span>template<span style="font-family:宋体;">这笔丰厚的遗产，貌似还够</span>c++<span style="font-family:宋体;">吃上几十年。今时今日，</span>virtual<span style="font-family:宋体;">早就沦落为</span>template<span style="font-family:宋体;">的附庸，除了帮助</span>template<span style="font-family:宋体;">搞点类型擦除的行为艺术之外，就很难再见到其身影了。有那么几年，业界反思</span>c++<span style="font-family:宋体;">的面向对象范式，批斗</span>virtual<span style="font-family:宋体;">，特别是</span>function<span style="font-family:宋体;">出现之后，要搞动态行为，就更加不关</span>virtual<span style="font-family:宋体;">的什么事情了。而那几年，本座也学着大神忌讳</span>virtual<span style="font-family:宋体;">关键字。现在大家似乎已经达成共识，</span>c++<span style="font-family:宋体;">里头的面向对象能力很不完善，要玩面向对象就应该找其他语言，比如</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">杂碎；或者更动态类型的语言，好比</span>python<span style="font-family:宋体;">，</span>Ruby<span style="font-family:宋体;">；或者干脆就是原教旨的面向对象（消息发送），</span>object C<span style="font-family:宋体;">，</span>smalltalk<span style="font-family:宋体;">。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">是啊，</span>1<span style="font-family:宋体;">、没有垃圾回收；</span>2<span style="font-family:宋体;">、没有原生支持的完善反射能力；</span>3<span style="font-family:宋体;">、多继承、虚继承导致的复杂内存布局。这三座大山面前，</span>c++<span style="font-family:宋体;">的码猿哪敢染指什么面向对象，只在迫不得已的情况下，小心翼翼地使用</span>virtual<span style="font-family:宋体;">。但是，事实上，要玩面向对象，</span>c++<span style="font-family:宋体;">原来也可以玩得很炫，甚至，可以说，关于面向对象的能力，</span>c++<span style="font-family:宋体;">是最强的（没有之一）。这怎么可能？</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所谓的面向对象，说白了，就是对动态行为的信息支持，能在面向对象设计上独领风骚的语言，都是有着完善的运行时类型信息，就连</span>lisp<span style="font-family:宋体;">，其运行时元数据也都很完备。静态强类型语言（</span>java<span style="font-family:宋体;">、</span>C#<span style="font-family:宋体;">）与动态语言比，显然有着强大的静态类型能力（这不是废话吗），能在编译期就提前发现类型上的诸多错误，但是也因此带上静态约束，导致呆板、繁琐的代码，</span>java<span style="font-family:宋体;">的繁文缛节，就是最好证明；而动态语言恰好相反，代码简洁，废话少，但是丧失静态信息，所谓重构火葬场，那都是血和泪的教训。静态语言与动态语言真是一对冤家，如同光的波粒性，己之所长恰是彼之所短，己之所短又是彼之所长，鱼与熊掌不可兼得。而</span>C++<span style="font-family:宋体;">竟然能集两家之所长，在静态语言的领域中玩各种动态行为艺术，比如动态修改类型的反射信息，千奇百怪的花样作死（丧心病狂的类型转换）；在动态范畴里面，又可以在编译期榨取出来静态类型信息，比如，消息发送的参数信息，想想win32的无类型的wparam和lparam，每次都要猿猴对照手册解码，从而最大限度地挖掘编译器的最大潜力。所以说，</span>c++<span style="font-family:宋体;">是最强大的面向对象语言，没有之一。而把静态和动态融为一体之后，</span>c++<span style="font-family:宋体;">的抽象能力也到达一个全新的高度，自动代码生成，以后再发挥，这是一个庞大的课题。</span>C++<span style="font-family:宋体;">令人发指的强大，绝对远远超乎等闲猿猴的想象，特别是那批</span>c with class<span style="font-family:宋体;">的草覆虫原始生物。</span>C++<span style="font-family:宋体;">只在部分函数领域的概念上表现令人不满，比如</span>lambda<span style="font-family:宋体;">表达式的参数类型自动推导，</span>monad<span style="font-family:宋体;">表达式，缺乏原生的延迟求值等。当然，</span>c++<span style="font-family:宋体;">整个的设计理念非常繁杂随心所欲，但是，却可以在这一块混沌里面整理出来一些举世无双的思想体系，就是说，</span>c++<span style="font-family:宋体;">是一大堆原材料，还有很多厨房用具，包括柴火，让猿猴自行下厨，做出来的菜肴可以很难吃，也可以是满汉全席，全看猿猴的手艺。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">当然，要在</span>c++<span style="font-family:宋体;">里头搞面向对象，多继承，虚继承的那一套，必须彻底抛弃。最大的问题是，多继承会导致混乱未知的二进制内存布局，虚函数表也一塌糊涂，十几年前，</span>c++<span style="font-family:宋体;">设计新思维的基于</span>policy<span style="font-family:宋体;">的范式，虽然令人耳目一新，也因为这种范式下对象的内存布局千奇百怪，所以，即便是最轻微的流行也没有出现过。当然，也不可能大规模搞消息发送这种很</span>geek<span style="font-family:宋体;">的套路，功能太泛化了，其实，消息发送就是动态的给对象添加成员函数，并且可以在运行时知道对象有多少成员函数，那个成员函数可以对该消息做出反应，消息可以是字符串，整型</span>ID<span style="font-family:宋体;">（原子），</span> MFC<span style="font-family:宋体;">的消息映射表</span>(BEGIN_MESSAGE_MAP<span style="font-family:宋体;">，</span>&#8230;)<span style="font-family:宋体;">就是一个功能严重缩水版的好例子，</span>c++<span style="font-family:宋体;">下支持消息映射的库，绝对可以比破</span>mfc<span style="font-family:宋体;">的那一套要好上千百倍，不管是性能、类型安全、使用方便上。目前除了在</span>gui<span style="font-family:宋体;">这种变态的场合下才需要大搞消息发送，其他场景，完全可以说用不上，虽然说消息发送很强大很灵活，但也因为其杀伤力太厉害，反而要更加慎重。这好比</span>goto<span style="font-family:宋体;">，好比指针，好比</span>stl<span style="font-family:宋体;">的迭代器，什么都能做的意思，就是什么都尽量不让它做。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">那么，</span>c++<span style="font-family:宋体;">下搞面向对象，还有什么法宝可用呢？当然，在此之前，我们先要直面内存分配。内存既是</span>c++<span style="font-family:宋体;">的安身立命之本，又是</span>c++<span style="font-family:宋体;">沦落为落水狗丧家犬之幕后大黑手。假如不能为所欲为的操作内存，那么</span>c++<span style="font-family:宋体;">的折腾法子，奇技淫巧，起码要死掉一大半以上。而由于要支持各种花样作死的内存操作，</span>c++<span style="font-family:宋体;">的垃圾回收迟迟未曾出现，就连以巨硬之大能整出来的</span>.net<span style="font-family:宋体;">那么好的</span>gc<span style="font-family:宋体;">，霸王硬上弓，在给原生</span>c++<span style="font-family:宋体;">强硬加上托管功能（垃圾回收），都出力不讨好。可见未来垃圾回收，对</span>c++<span style="font-family:宋体;">来说，嗯，想想就好了。内存是资源，没错，用</span>raii<span style="font-family:宋体;">来管理，也无可厚非。但是，内存却是一种很特殊的资源，</span>1<span style="font-family:宋体;">、内存时对象的安身立命之所；</span>2<span style="font-family:宋体;">、不同于普通资源，内存很多，不需要马上用完就急急忙忙启动清理工作，只要系统还有大把空余的内存，就算还有很多被浪费了的内存，都不要紧，</span>gc<span style="font-family:宋体;">也是因为这个原因才得以存在。相比内存，普通资源给人的感觉就是数量及其有限，然后要提交工作结果，否则之前所做努力就废了。所以，对于内存，应该也要特别对待。就算</span>raii<span style="font-family:宋体;">，也要采用专门的</span>raii <span style="font-family:宋体;">。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">假设我们的程序里面使用多种内存分配器，比如说，每个线程都有自己专有的内存</span>allocator<span style="font-family:宋体;">对象，然后，线程之间的共享数据由全局的内存分配器分配，线程的内部对象都用线程的专属</span>allocator<span style="font-family:宋体;">来分配，那么，内存分配器就是一个线程局部变量（</span>tls<span style="font-family:宋体;">，</span>thread local storage<span style="font-family:宋体;">）。于是，可以规定，所有的内存分配都通过</span>GetTlsAllocator()<span style="font-family:宋体;">来</span>new<span style="font-family:宋体;">对象，当然，确定是全局共享变量的话，没办法，就只能用</span>GetGlobalAllocator()<span style="font-family:宋体;">来</span>new<span style="font-family:宋体;">对象。那么，有理由相信，启动一个任务时，我们先定义一个</span>arena allocator<span style="font-family:宋体;">变量，并令其成为当前线程的专属内存分配器，那么这个任务后面的所有</span>new <span style="font-family:宋体;">出来的对象，包括循环引用，都不必关心。只要任务一结束，这个</span>arena allocator<span style="font-family:宋体;">变量一释放，所有寄生在它身上的对象，全部也都消失得干干净净，没有任何一点点的内存泄露。就算任务内部有大量的内存泄露，那又如何，任务一结束，所有跟此任务有关的一切内存，全部成块清空。总之，不要以常规</span>raii<span style="font-family:宋体;">来解决内存困境，解放思想，在内存释放上，我们可以有九种办法让它死，而不是仅仅靠</span>shared_ptr<span style="font-family:宋体;">，</span>unique_ptr<span style="font-family:宋体;">，</span>weak_ptr<span style="font-family:宋体;">这些狭隘的思维。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">其次，完善的面向对象设计，避免不了完备的反射，用以在运行时提供动态类型信息，无参模板函数可以把静态类型映射成全局唯一变量，好比，</span>TypeOf&lt;vector&lt;int&gt;&gt;<span style="font-family:宋体;">，返回</span>vector&lt;int&gt;<span style="font-family:宋体;">的全局唯一的</span><span>const TypeInfo*</span><span style="font-family:宋体;">对象，这个对象包含了</span>vector&lt;int&gt;<span style="font-family:宋体;">的所有静态类型信息，可以这么说，在静态类型层面上</span>vector&lt;int&gt;<span style="font-family:宋体;">所能做的任何事情，比如定义一个</span>vector&lt;int&gt;<span style="font-family:宋体;">的变量，也即是创建对象；遍历、添加元素、析构、复制赋值、元素数量等等一切操作，与</span>vector&lt;int&gt;<span style="font-family:宋体;">对应的</span>TypeInfo<span style="font-family:宋体;">对象，统统都可以做到。所不同的是，</span>vector&lt;int&gt;<span style="font-family:宋体;">的静态类型代码，只能用于</span>vector&lt;int&gt;<span style="font-family:宋体;">自身的情况（这样子可放在源文件中），又或者是通过</span>template<span style="font-family:宋体;">，表现行为类似于</span>vector&lt;int&gt;<span style="font-family:宋体;">的数据类型（代码必须在头文件上）。而用</span>TypeInfo*<span style="font-family:宋体;">做的事情，全部都在运行时发生，所有的静态类型信息，全部被带到运行时来做，所以这些代码全部都可以处在源文件里面，甚至动态库里头，只不过是</span>TypeInfo*<span style="font-family:宋体;">操作的对象是一个二进制内存布局和</span>vector&lt;int&gt;<span style="font-family:宋体;">一模一样的内存块，可以通过强制类型转换，把运行时的内存块转换成静态编译时的</span>vector&lt;int&gt;<span style="font-family:宋体;">。其实这里的思想，就是想方设法将丰富多彩的静态类型信息无损的保存到运行时中，让编译时能做的事情，运行时也可以做。差别在于，一个是用静态类型信息来做事情，这里，任何一点点类型上的错误，都会让编译器很不高兴；一个则是用动态类型信息来做事情，这里，显然只能让猿猴人肉编译器。这里，可见动态类型信息和静态类型信息的表达能力是等价的，也即是同等重要性的意义，而静态类型信息的意义有多大，相信大家都知道。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">那么，如何建立完备的反射信息，这个必须只能用宏来配合完成，外部工具生成的反射信息代码，功能很不完备，另外，</span>c#<span style="font-family:宋体;">、</span>java<span style="font-family:宋体;">等的反射信息全部都是编译器生成的，可定制性很差。我们需要的是一点都不逊色于静态行为的动态行为。所以，只有由自己自行管理反射，才能做到真正意义上的完备反射。必要时，我们还可以在运行时修改反射信息，从而动态地增删对象的行为方式，改变对象的面貌。看到这里，是否觉得很多的设计模式，在这里会有更清晰更简洁的表达方式呢，甚至，轻而易举就可以出现新的设计模式。比如，以下定义对象反射信息的代码。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">在</span>c++<span style="font-family:宋体;">下，由于全局变量生命周期的随意性（构造函数调用顺序不确定，析构顺序也不确定），大家都很忌讳其使用，虽然全局变量功能很强大，很多时候都避免不了。但是，标准上还是规定了全局变量的顺序，所有的全局变量必须在</span>main<span style="font-family:宋体;">函数之前构造完成，其析构函数也只能在</span>main<span style="font-family:宋体;">函数结束后才调用。另外，函数的静态变量必须在其第一次访问之前构造完整。基于这两点，我们就可以在</span>main<span style="font-family:宋体;">函数之前构建全部的反射信息，流程是这样子，所有的类型的反射对象都是以函数内部的静态指针变量存在，他们都通过调用</span>GetStaticAllocator()<span style="font-family:宋体;">的内存分配器来创建，这样子，提供反射信息的函数，就避免了其内部</span>TypeInfo<span style="font-family:宋体;">对象的析构发生。最后，</span>main<span style="font-family:宋体;">结束后，由</span>GetStaticAllocator()<span style="font-family:宋体;">函数内的内存分配器的析构函数统一释放所有反射信息占用的内存。最后，附上一个例子</span></p><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Student<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">ClassCat表示为Student的基类，为空类，所以Student可以继承它，但是代码上又不需要明确继承它，非侵入式的基类。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">ClassCat提供二进制序列化操作，xml序列化，json序列化，数据库序列化等操作</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPInlineClassTI(ClassCat,&nbsp;Student,&nbsp;ti)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPReflAField(ti,&nbsp;name);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPReflAField(ti,&nbsp;age);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPReflAField(ti,&nbsp;sex,&nbsp;{&nbsp;kAttrXmlIgnore&nbsp;});&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">表示不参与xml的序列化操作</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AString&nbsp;name;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;age;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;sex;<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Config&nbsp;:&nbsp;Student<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPInlineClassTI(Student,&nbsp;Config,&nbsp;ti)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPReflAField(ti,&nbsp;map);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">U8String,&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;map;<br />&nbsp;&nbsp;&nbsp;&nbsp;};</span><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp; </span><span style="color: #000000; "><br /></span></div><p style="text-indent:21.0pt"><span style="font-family:宋体;"></span></p><p style="text-indent:21.0pt"><span style="font-family:宋体;">下期的主角是非侵入式接口，彻底替换</span>c++<span style="font-family:宋体;">上的多继承，功能远远好过</span>C#<span style="font-family:宋体;">、</span>java<span style="font-family:宋体;">杂碎的弱鸡接口，更超越狗语言的不知所谓的非侵入式接口。如果仅仅是完备的反射信息，而缺乏非侵入式接口，在</span>c++<span style="font-family:宋体;">下搞面向对象，其实还是很痛苦的。但是，有了非侵入式接口之后，一切豁然开朗。甚至可以说，感觉</span>c++<span style="font-family:宋体;">里面搞那么多玩意，都不过是为了给非侵入式接口造势。然而非侵入式接口一直未曾正式诞生过。</span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215082.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-11 11:56 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/11/215082.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>stl的抽象缺陷终结</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/10/215076.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Mon, 10 Jul 2017 10:30:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/10/215076.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215076.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/10/215076.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215076.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215076.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt"><span style="font-family:宋体;">古龙说过，一个人的最大优点往往将是其致命的弱点。这句话用在</span>stl<span style="font-family:宋体;">的迭代器上，最是合适不过。</span>stl<span style="font-family:宋体;">通过迭代器来解耦容器与算法，可谓击节赞叹；但是，让迭代器满世界的到处乱跑，未免就大煞风景。此话怎讲？</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">其实，有些语言就没有迭代器的概念，并且还活得很优雅，好比</span>haskell<span style="font-family:宋体;">的</span>list<span style="font-family:宋体;">啊、</span>tree<span style="font-family:宋体;">啊，压根就不需要什么迭代器，只需要模式匹配，体现其数据结构的递归特点，就可以很优雅地表达算法。就是</span>java<span style="font-family:宋体;">、</span>c#<span style="font-family:宋体;">、</span>C++<span style="font-family:宋体;">这几个破面向对象语言，才需要大用特用迭代器，没有迭代器就活不下去了。迭代器的出现就是为了弥补其语言丧失清晰表达递归数据结构的能力。看到</span>haskell<span style="font-family:宋体;">的</span>list<span style="font-family:宋体;">到</span>c++<span style="font-family:宋体;">的</span>stl<span style="font-family:宋体;">下的对应样子，很多人都表示很难过，因为</span>stl<span style="font-family:宋体;">里面，</span>list<span style="font-family:宋体;">根本就没有</span>tail<span style="font-family:宋体;">函数，更逞论支持</span>list<span style="font-family:宋体;">的</span>tail<span style="font-family:宋体;">还是一个</span>list<span style="font-family:宋体;">这样绝妙的</span>idea<span style="font-family:宋体;">。一切必须通过迭代器这个万金油来糊弄其尴尬的困境。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">随便来看看几行</span>stl<span style="font-family:宋体;">算法函数的代码</span><br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">Vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;nums&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{..};<br />find(nums.begin(),&nbsp;nums.end(),&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);<br />remove_if(nums.begin(),&nbsp;nums.end(),&nbsp;_1&nbsp;</span><span style="color: #000000; ">&gt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">);&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">为了省事，用了bll的风格，在c++11中，要从零开始造一个bll风格的轮子，不能更方便，大概也就两三百行的代码</span></div><p style="text-indent:21.0pt"><span style="font-family:宋体;">看到没有，你信不信，随便统计一下，一打的</span>algorithm<span style="font-family:宋体;">函数，起码就有</span>12<span style="font-family:宋体;">个函数的调用之道，必须传递</span>container.begin()<span style="font-family:宋体;">，</span>container.end()<span style="font-family:宋体;">。</span>begin<span style="font-family:宋体;">和</span>end<span style="font-family:宋体;">这对兄弟，总是成双成对的出现，说明了一件事情，就是从一开始，它们必须被打包在一起，而不应该硬生生地将它们拆开。知道这一拆开，带来多少问题吗？代码上的累赘还算是小事，比如，简洁清晰流畅的</span>find(nums, 2)<span style="font-family:宋体;">，却要生硬的写成</span>find(nums.begin(), nums.end(), 2)<span style="font-family: 宋体;">。当然，这种</span>api<span style="font-family:宋体;">设计，也并非一无是处，起码，在表达容器里面的部分区间时，很方便，好比下面的代码</span></p>  <p>int nums[10] = {&#8230;};</p>  <p>find(nums+1, end(nums)-1, 2);</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">看起来，好像的确挺方便的，将</span>begin<span style="font-family:宋体;">、</span>end<span style="font-family:宋体;">放在一起，要表达这样的概念，似乎就有些麻烦，但其实，这是假象，当角度变换时，我们可以会有更方便的方式来表达这样的需求。最起码，容器的部分区间也应该是由容器本身来表达，而不应转嫁给迭代器来应付，数组的部分也是数组，树的分支也是树，这样的概念，就应该由容器本身来定义。像是哈希表就不支持部分区间的概念。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">为何</span>algorithm<span style="font-family:宋体;">的算法，全部（不是基本）都要求一对迭代器。那是因为这些算法的输入对象，本来就是一个数据集合。而一个迭代器无法完整地表达一个容器，起码必须一对迭代器才能完整地表达一个数据集。但是，用一对迭代器来作为入参，和用一个区间作为入参，它所体现抽象的侧重点完全不同，而由于此种不同，最后的演变结果，也是天渊之别，即是一对迭代器设计思路是渊，自然，而区间的设计方案，显然是天。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">再次回顾上文的结尾，</span>find<span style="font-family: 宋体;"><span style="Times New Roman&quot;;">，</span></span><span style="Times New Roman&quot;;">find_if</span><span style="font-family:宋体;"><span style="Times New Roman&quot;;">，</span></span><span style="Times New Roman&quot;;">remove, remove_copy, remove_copy_if, remove_if,&#8230;&#8230;</span><span style="font-family:宋体;"><span style="Times New Roman&quot;;">，</span>有没有感受，一股浓浓的过程式风格，十分的笨重，明显的非正交，浓烈的</span>c<span style="font-family:宋体;">语言风格。对于这样的</span>api<span style="font-family:宋体;">，让本座对委员会的那帮老不死，彻底的绝望了。他们（它们）的审美观，停留在很低很低的层次上。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">将</span>begin<span style="font-family:宋体;">，</span>end<span style="font-family:宋体;">拆分开来的最大问题，其实也就只是，前一个函数的处理结果，不能平滑的传递到下一个函数里面去。比如说，现在函数</span>make_nums<span style="font-family:宋体;">返回</span>vector&lt;int&gt;<span style="font-family:宋体;">，试比较一下，高下立判。</span><br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">auto&nbsp;nums&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;make_nums();<br />find(nums.begin(),&nbsp;nums.end(),&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">一对迭代器作为入参</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">find(make_nums(),&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">);</span><span style="color: #008000; ">//</span><span style="color: #008000; ">直接数据区间作为入参</span></div><p style="text-indent:21.0pt"><span style="font-family:宋体;">说了这么多，我们强烈要求的仅仅是函数风格的</span>api<span style="font-family:宋体;">，正交式的函数设计，前一个函数的处理结果可以平滑地传递给下一个函数。总结</span>algorithm<span style="font-family:宋体;">的一坨函数，本质上只需</span>filter<span style="font-family:宋体;">，</span>fold<span style="font-family:宋体;">，</span>map<span style="font-family:宋体;">，</span>insert(copy)<span style="font-family:宋体;">这屈指可数的几个函数就可以自由地组合出来，并且还能组合出来</span>algorithm<span style="font-family:宋体;">上没有的效果。首先，这几个函数的返回结果都是数据区的数据对象</span>(<span style="font-family:宋体;">里面有</span>begin<span style="font-family:宋体;">和</span>end<span style="font-family:宋体;">的成员函数，用以返回迭代器</span>)<span style="font-family:宋体;">。其次，就是在迭代器上面做文章，以支持</span>filter<span style="font-family:宋体;">、</span>map<span style="font-family:宋体;">等操作，也就是在</span>*<span style="font-family:宋体;">、</span>++<span style="font-family:宋体;">、</span>!=<span style="font-family:宋体;">这几个运算符上做花样，要达到</span>filter<span style="font-family:宋体;">、</span>map<span style="font-family:宋体;">的效果，很容易的。至于像是要求随机访问迭代器概念的函数，太常用的就做到</span>array_view<span style="font-family:宋体;">里面好了，或者就明确规定入参就是</span>array_view<span style="font-family:宋体;">。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">然后</span>stl<span style="font-family:宋体;">里面还臆造了一种好像叫做</span>insert_iterator<span style="font-family:宋体;">迭代器类型的适配器，用以通过迭代器的语法往容器里头插入数据，好像很玄妙，实则就是强行拔高迭代器的用途，完全就违背了迭代器出现的初衷。这种扭曲的想法，完全就是上面那一坨病态</span>api<span style="font-family:宋体;">的产物。所以，原本的</span>api<span style="font-family:宋体;">设计，算法函数必须以容器（数据区间）为入参，内部调用其</span>begin<span style="font-family:宋体;">和</span>end<span style="font-family:宋体;">成员函数获得迭代器来遍历容器的函数，何其清晰的设计思路。但是，</span>stl<span style="font-family:宋体;">的设计思路，导致迭代器泛滥，甚至连客户层面的代码也大把大把的迭代器，于是迭代器的问题就接二连三的产生，什么失效啊，什么</span>first<span style="font-family:宋体;">和</span>last<span style="font-family:宋体;">匹对错误。还有，导致容器里面的关于迭代器的成员函数多了一倍，哈希表里面也没有类似于</span>C#<span style="font-family:宋体;">里</span>Dictionary<span style="font-family:宋体;">的</span>Keys<span style="font-family:宋体;">和</span>Values<span style="font-family:宋体;">属性函数，这些用起来很方便的，不是吗？</span></p>  <p style="text-indent:21.0pt">stl<span style="font-family:宋体;">的这种</span>api<span style="font-family:宋体;">设计思路完全不是以方便使用为主，而是以满足自己的独特口味为目的。看看</span>find<span style="font-family:宋体;">函数，它返回一个迭代器，所以，我们使用时，必须通过用</span>end<span style="font-family:宋体;">来判断要找的东西是否在区间里面，</span></p>  <p><span>auto found = find(nums.begin(), nums.end(), 2);</span></p>  <p>if (found != nums.end()){&#8230;}</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">依本座看，直接就返回指针好了，指针为</span>nullptr<span style="font-family:宋体;">，就表示元素找不到，代码变成这样</span></p>  <p>if (auto found = find(nums, 2)){&#8230;}</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">代码合并成一行，不用再和</span>end<span style="font-family:宋体;">比较了。更重要的是，返回结果就是指针，类型非常明确，可以平滑的传递到别的函数里；而不是迭代器类型，谁知道迭代器类型是什么类型。</span>template<span style="font-family:宋体;">这种东西的类型，能明确下来时，就尽快明确下来。至于说，有些区间的元素不支持返回地址，好比，</span>vector&lt;bool&gt;<span style="font-family:宋体;">，很简单，那就不支持好了。本座编写</span>c++<span style="font-family:宋体;">代码的原则之一，不求大而全，需求专一，绝不会因为个别同学，就牺牲大多数情况下清晰方便高效的</span>api<span style="font-family:宋体;">风格。对于这些异数，必要时，用奇技淫巧解决。你知道，因为多继承，虚继承，把成员函数指针这个简洁的概念搞得非常复杂，不能按正常人方式来使用了，严重影响成员函数的用范围，一直让本座耿耿于怀。其实，</span>95%<span style="font-family:宋体;">以上的情况下，我们就仅仅需要普通成员函数指针而已，另外的</span>5%<span style="font-family:宋体;">，也都可以用普通成员函数来封装。所以，为了弥补这个遗憾，本座做了一个精简版的</span>delegate<span style="font-family:宋体;">，只接受全局函数和普通成员函数，当字段</span>object<span style="font-family:宋体;">为空，就表示字段函数指针是全局函数，不为空，就表示函数指针是成员函数。至于其他一切奇奇怪怪的函数，本座的这个</span>delegate<span style="font-family:宋体;">就</span>say no<span style="font-family:宋体;">，明确拒绝。</span></p>  <span style="font-size:10.5pt;font-family: &quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的这种独特到处都是，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">boost</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">更是将其发扬光大，反正设计出来的</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">api</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，就是不考虑让你用的舒爽，二进制的布局，更加一塌糊涂。比如，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">any</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的使用，是这样子用的，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">cout &lt;&lt; any_cast&lt;int&gt;(anyValue)</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，这里还好，假如要分别针对</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">any</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的实际类型来写代码，必须这样子：<br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(anyValue.type()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;typeid(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;any_cast</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(anyValue);<br /></span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(anyValue.type()&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;typeid(</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">))<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;any_cast</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(anyValue);<br />&#8230;</span></div><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">  </span><p style="text-indent:21.0pt"><span style="font-family:宋体;">这种对类型安全无理取闹的强调，让人火冒三丈。要本座说，直接在</span>any<span style="font-family:宋体;">里面添加</span>Cast<span style="font-family:宋体;">模板成员函数，结果就返回指针好了，指针为空，就表示类型不匹配，代码就变成这样</span></p><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(auto&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;anyValue.Cast</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">())<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">value;<br /></span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(auto&nbsp;value&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;anyValue.Cast</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">())<br />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000; ">&lt;&lt;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">value;<br />&#8230;<br /></span></div>  <span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">  </span><p style="text-indent:21.0pt"><span style="font-family:宋体;">是否就没那么心烦呢。另外，鉴于</span>stl<span style="font-family:宋体;">对于反射的拒绝，采用</span>virtual+template<span style="font-family:宋体;">的类型拭擦大法来弥补，其实并不怎么完美。本座用反射重新实现的</span>any<span style="font-family:宋体;">，比</span>stl<span style="font-family:宋体;">的</span>any<span style="font-family:宋体;">好多了，不管是性能、编译速度、使用方便上，都是要好太多。还有，</span>stl<span style="font-family:宋体;">的</span>any<span style="font-family:宋体;">，要为每个用到的类型都要生成一个实实在在的多态具体类，每个类都要有一个专门的虚函数表对应，这些可都要写到二进制文件里面，代码就是这样膨胀起来的。总之，</span>stl<span style="font-family:宋体;">回避反射后，反射就以另一种形式回归，好比</span>virtual+template<span style="font-family:宋体;">，好比</span>%d<span style="font-family:宋体;">、</span>%s<span style="font-family:宋体;">，好比</span>locale<span style="font-family:宋体;">的那些</span>facet<span style="font-family:宋体;">实现，</span> <span style="font-family:宋体;">这些动态机制各自为政，各种混乱。还不如干脆就从源头上系统化公理化地给予终极解决。</span></p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">所以，总体上感受</span>stl<span style="font-family:宋体;">设计思路上存在的路线，就是太在意于</span>c++<span style="font-family:宋体;">语言本身上的特点，受语言自身的缺陷复杂影响太多，忽略了真正的需求，太多的臆造需求，强行让需求来迁就语言，而不是让语言来配合基础库的实际普遍需求，需求才是根本，为了可以最方便，最清晰，最性能的基础库，完全可以大规模地使用宏、挖掘语言里面最黑暗的边角料，甚至为了库的清晰性，可以拒绝那些用了复杂特性的数据结构，比如多继承，虚继承等无聊玩意。</span></p>  <p><span style="font-family:宋体;">概括起来，路线问题导致最终的正果，也即是</span>stl<span style="font-family:宋体;">的具体弱鸡表现就是，最根本是二进制接口使用上的重重阻碍，谁敢在动态库api使用stl的数据类型。其次是以下5小点：</span></p>  <p>1<span style="font-family:宋体;">、内存分配器不应该是容器的模板参数，对</span>allocator<span style="font-family:宋体;">的处理太过草率，当初这里必须做深入的挖掘，</span>c++<span style="font-family:宋体;">完全可以实现一定程度上的垃圾回收功能，比如</span>arean allocator<span style="font-family:宋体;">，不必一一回收在</span>arena allocator<span style="font-family:宋体;">上分配的对象，只需一次性释放</span>arena allocator<span style="font-family:宋体;">的内存，达到多次分配，一次释放的高性能效果，还避免内存泄露，也不用直接面对循环引用的怪胎设计问题。现有的内存管理策略，把压力都放在智能指针上；</span></p>  <p>2<span style="font-family:宋体;">、提供的通用容器不够完备；原本</span>stl<span style="font-family:宋体;">的数据结构就大可满足所有正常和非正常的使用场合，比如满足侵入式的链表需求，比如不管理元素生命周期的容器等；</span></p>  <p>3<span style="font-family:宋体;">、过多的暴露迭代器，迭代器的应用范围过广，</span>stl<span style="font-family:宋体;">的算法函数用起来很不方便；</span></p>  <p>4<span style="font-family:宋体;">、回避动态类型反射信息，对数据的输入输出支持非常单薄，包括字符串处理、文件读写、网络数据收发等，标准库上的现有那点小功能，仅仅是聊胜于无而已，难堪大任；</span></p>  <p>5<span style="font-family:宋体;">、非容器系的实用类太少；</span></p>  <p><span style="font-family:宋体;">一句话，目前</span>stl<span style="font-family:宋体;">的使用，还是远远不够爽。原本用上</span>stl<span style="font-family:宋体;">的代码，应该可以更短、更快、更小。只可惜，</span>stl<span style="font-family:宋体;">在通过迭代器实现算法与容器的分离之后，就停步不前，其设计体系在别的地方，鲜有建树创新。战略高度过于局促，很多复杂难搞的问题，其实都蕴含着绝大的机遇，而</span>stl<span style="font-family:宋体;">都一一回避，真是回避得好！</span></p>  <span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;"><br /></span><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215076.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-10 18:30 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/10/215076.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>stl的缺陷抽象不足</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/09/215067.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Sun, 09 Jul 2017 03:35:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/09/215067.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215067.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/09/215067.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215067.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215067.html</trackback:ping><description><![CDATA[<p style="text-indent:21.0pt;"><span style="font-family:宋体;">总的来说，</span>stl<span style="font-family:宋体;">整个的设计还是很有水准的，抽象度非常高，采用泛型</span>template<span style="font-family:宋体;">手法，回避面向对象里面的虚函数，回避了继承，做到零惩罚，达到了非侵入式的要求（非侵入式远比侵入式要好，当然设计难度也更高出许多）。高性能、可扩展，容器提供迭代器，而算法则作用在迭代器上，容器与算法之间通过迭代器完全解耦，同一种算法可用于多种容器，只要该容器的迭代器满足其算法的要求；而同一个容器，又可被多种算法操作。更重要的是，容器与算法之间完全是开放的，或者说整个</span>stl<span style="font-family:宋体;">的体系是开放式，任何新的算法可用于已有的容器，任何新的容器又可被已有的算法所运算。然后，考虑到某些场合下容器的内存分配的高性能要求，分配器</span>allocator<span style="font-family:宋体;">也是可以替换，虽然逼格不高。此外，容器算法体系外的边角料，比如智能指针、</span>any<span style="font-family:宋体;">、</span>iostream<span style="font-family:宋体;">、复数、</span>functio<span style="font-family:宋体;">等，也都高性能、类型安全、可扩展，基本上都是</span>c++<span style="font-family:宋体;">风格量身定制，这些都无可厚非。真的，</span>stl<span style="font-family:宋体;">作为</span>c++<span style="font-family:宋体;">的基础库，已经很不错了。只是，依个人观点，当然，以下纯属一家之言，某些情况下，</span>stl<span style="font-family:宋体;">可以做得更好，或者说</span>api<span style="font-family:宋体;">的使用上，可以更加清爽。以下对</span>stl<span style="font-family:宋体;">的非议，似有鸡蛋里挑骨头之嫌，吹毛求疵，强挑毛病。</span></p>  <p style="text-indent:21.0pt;">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">软件框架或者说库的设计，是由需求决定的。脱离需求的设计，不管多精致，代码多漂亮，一切都是废物，都是空谈。程序库所能提供的功能，当然是越强大越好，可扩展，高性能，零惩罚，这些明面上的概念自然重要。殊不知，程序库不能做的事情，或者说，其限制条件，或者说程序库对外界的依赖条件，也很重要。一个基础库，如果它敢什么都做，那就意味着它什么都做不好。事情是这样子的，如果只专注于某件目的，某些条件下的运用，往往可以获得更大的灵活性或者独立性。首先，代码必须很好地完成基本需求，在此基础上，才有资格谈论点什么别的更高层次的概念。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">上文提到</span>stl<span style="font-family:宋体;">由于对动态类型的排斥，所导致的功能残缺以及二进制复用上的尴尬，如果</span>stl<span style="font-family:宋体;">愿意正面在动态类型做完善的考虑，相信</span>stl<span style="font-family:宋体;">的格局将会大很多，动态类型的话题，本文就不再过多重复了。当然，动态类型的引入必须满足零惩罚的必要条件，</span>c++<span style="font-family:宋体;">的程序库，所有不符合零惩罚的概念，最后都将被抛弃。<span style="color:red">所谓的零惩罚，就是不为用不到的任何特性付出一点点代价。请注意，这里的零惩罚必须是一步都不能妥协。</span></span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">比如说，字符串实现的引用计数、短字符串优化这些奇技淫巧，就不是零惩罚，短字符串优化的惩罚度虽然很轻微，每次访问字符串内容时，都要通过字符串长度来确定其数据的地址，长度小，字符串就放在对象本身上，从而避免动态内存分配。长度大，字符串就放在对象外的堆内存上。短字符串优化空间上的惩罚，字符串对象的占用内存变大了，到了长字符串的时候，字符串对象里因为短字符串的内存空间就没有任何价值。在</span>32<span style="font-family:宋体;">位机上，字符串对象可以只包含内存分配器地址，字符缓冲起始地址，字符长度，缓冲的大小，满打满算，也就</span>16<span style="font-family:宋体;">个字节。而短字符串优化，就可能要用到</span>32<span style="font-family:宋体;">个字节。其实，如果有一个高性能的内存分配器，短字符串优化完全就可以没有任何必要。算了，扯远了，我们还是回到</span>stl<span style="font-family:宋体;">的设计思路上吧。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">大家都知道，</span>stl<span style="font-family:宋体;">的字符串其实顶多就是一个字符缓冲管理对象。都</span>98<span style="font-family:宋体;">年的标准库了，完全就没有考虑字符编码的需求，真是奇怪之极，令人发指的完全偷工减料。是啊，字符编码不好搞，但是既然有这个需求，就必须支持啊，鸵鸟政策是行不通的。虽然说框架上设计可以既然做不好，那就完全都不做。但是，作为字符串组件，不鸟编码，你真的好意思以字符串自居。撇开编码，</span>string<span style="font-family:宋体;">居然有一百多个函数，更让人惊喜的是，这一百多个函数用于日常的开发，还远远不能满足需求。仔细看，这一坨函数大多数仅仅是为了性能需要上的重载，为了避开临时</span>string<span style="font-family:宋体;">对象搞出来的累赘。所以，</span>stl<span style="font-family:宋体;">里面必须要有一个只读的字符串，不涉及任何动态内存分配，也即是</span>c++17<span style="font-family:宋体;">的</span>string_view<span style="font-family:宋体;">，</span>string_view<span style="font-family:宋体;">里面有一个很良好的性质，</span>string_view<span style="font-family:宋体;">的任何一部分还是</span>string_view<span style="font-family:宋体;">（不同于</span>c<span style="font-family:宋体;">语言字符串的以零结束，必须带零的右部分才是字符串），然后</span>string_view<span style="font-family:宋体;">就可以做只读字符串上的运算，比如比较，查找，替换，截取等，分摊</span>string<span style="font-family:宋体;">里面大部分的函数。很奇怪的是，</span>string_view<span style="font-family:宋体;">这么有用的概念，居然要到</span>c++17<span style="font-family:宋体;">里面才有，都不知道</span>stl<span style="font-family:宋体;">委员会的人才在想什么。由此可见，如果</span>class<span style="font-family:宋体;">里面的成员函数如果过多，好比一百多个，那么其设计思路就一定大有问题，甭管它的出处来自何方。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">同理可得，只读数组</span>array_view<span style="font-family:宋体;">也是很有用的概念，它是内存块的抽象。</span>array_view<span style="font-family:宋体;">的任何一部分都是</span>array_view<span style="font-family:宋体;">，不同于</span>string_view<span style="font-family:宋体;">，</span>array_view<span style="font-family:宋体;">仅仅是长度不能变，但是其元素可修改，可以把</span>array_view<span style="font-family:宋体;">看成其他语言的数组，但是</span>array_view<span style="font-family:宋体;">不能创建，只能从</span>vector<span style="font-family:宋体;">或者</span>c++<span style="font-family:宋体;">数组获得，或者又可以看成是切片，</span>array_view<span style="font-family:宋体;">本身可以有排序和二分查找的成员函数。</span>Array_view<span style="font-family:宋体;">可以取代大多数</span>vector<span style="font-family:宋体;">下的使用场合。很奇怪的是，这么强有力地概念，</span>c++17<span style="font-family:宋体;">上居然就可以没有。差评！另外，我想说的是，对于排序二分查找，就仅仅用于连续内存块上就好了，其他地方则可免就免，搞那么多飞机干什么，</span>stl<span style="font-family:宋体;">在排序二分查找的处理上显然就是过度抽象。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">或者有人反对，</span>array_view<span style="font-family:宋体;">和</span>string_view<span style="font-family:宋体;">不就是两个新的容器，把它们加进</span>stl<span style="font-family:宋体;">里，不就行了，</span>stl<span style="font-family:宋体;">体系设计完备，绝对对外开放。不是这样的，</span>array_view<span style="font-family:宋体;">和</span>string_view<span style="font-family:宋体;">的出现，严重影响现有的</span>string<span style="font-family:宋体;">和</span>vector<span style="font-family:宋体;">的设计，这两者必须基于</span>array_view<span style="font-family:宋体;">和</span>string_view<span style="font-family:宋体;">的存在整个重新设计。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt">Stl<span style="font-family:宋体;">就是对良好性质的基础数据结构缺乏抽象，容器的设计只到迭代器为止，提供迭代器之后，就高高兴兴对外宣称完成任务，不再深入地挖掘，可谓固步自封，浅尝辄止。在</span>stl<span style="font-family:宋体;">的世界观里面，就只有迭代器，什么都搞成迭代器，什么都只做到迭代器就止步不前，可悲可恨可叹！在其他基础容器的设计上，缺乏深入的考虑，罔顾需求，罔顾用户体验。对于链表的定位，就足以体现其眼光的狭隘。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">众所周知，单向链表的尾部也是单向链表，可类比</span>haskell<span style="font-family:宋体;">或者</span>lisp<span style="font-family:宋体;">的列表，这么强有力的好概念，</span>stl<span style="font-family:宋体;">里居然完全没有单向链表，更别说凸显此概念。当然，单向链表里面只有一个节点指针，不能包含内存分配器的，也不能有元素计数器，而且生命周期也要由用户控制，但是，用户控制就用户控制，这一点都不要紧，特别是存在</span>arena allocator<span style="font-family:宋体;">的情况下，拼命的</span>new<span style="font-family:宋体;">单向链表，最后由</span>arena allocator<span style="font-family:宋体;">来统一释放内存好了。总之，</span>stl<span style="font-family:宋体;">太中规中矩，对于离经叛道的</span>idea<span style="font-family:宋体;">，完全就是逃避就是无视，对于动态类型的处理，也是这种态度。</span>Stl<span style="font-family:宋体;">对</span>allocator<span style="font-family:宋体;">的处置，太过简单粗暴，一步错，步步错。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">而双向链表，在付出</span>O(n)<span style="font-family:宋体;">的访问代价后，在为每个元素都要付出前后节点的内存占用后，应该得到咋样的回报呢？显然，</span>stl<span style="font-family:宋体;">的</span>list<span style="font-family:宋体;">，</span>O(1)<span style="font-family:宋体;">插入，</span>O(n)<span style="font-family:宋体;">通过迭代器删除元素，无论如何，完全不能接受，回报太少。首先，</span>O(1)<span style="font-family:宋体;">删除元素，不能妥协。为达此目的，我们先开发一个隐式侵入式要求的循环链表，它不关心元素的生命周期，任何插入此链表的元素，其首地址之前必须存在前后节点的指针。然后，链表本身的首两个字段是头节点和尾节点，内存布局上看，循环链表自身就是一个链表节点，一开始，链表为空，其首尾字段都指向自身。这种内存布局下的循环链表和其节点的关系非常松散，节点的插入和删除，只需要修改其前后节点的前后指针的值，完全不需要经过链表本身来完成操作。要删除元素时，只要往前爬两个指针的位置，就得到包含此元素的节点，进而时间</span>O(1)<span style="font-family:宋体;">上删除节点，何其方便。显然，循环链表不能包含节点数量，否则每次删除插入节点，都要加</span>1<span style="font-family:宋体;">减</span>1<span style="font-family:宋体;">链表的计数器，节点和链表就不能彻底的解耦。这种内存布局上的循环链表，就可支持多态了，比如，比如，</span>xlist&lt;Base&gt; objects<span style="font-family:宋体;">，可把</span>Derived1<span style="font-family:宋体;">类型的对象</span>d1<span style="font-family:宋体;">，</span>Derived2<span style="font-family:宋体;">类型的对象</span>d2<span style="font-family:宋体;">，插入到循环链表</span>xlist<span style="font-family:宋体;">里，只要</span>d1<span style="font-family:宋体;">和</span>d2<span style="font-family:宋体;">的前面保留前后节点指针的内存空间。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <p style="text-indent:21.0pt"><span style="font-family:宋体;">然后，封装这个裸循环链表，用</span>allocator<span style="font-family:宋体;">管理其节点元素的生命周期，好像</span>stl<span style="font-family:宋体;">的</span>list<span style="font-family:宋体;">那样，创建删除节点元素。封装过得链表，节点和链表的关系就不再松散，因为节点必须通过链表的</span>allocator<span style="font-family:宋体;">来分配内存回收内存。但是，</span>O(1)<span style="font-family:宋体;">时间的删除节点，完全是没有任何问题。并且也能支持多态，可插入子类对象。相比之下，可见</span>stl<span style="font-family:宋体;">的</span>list<span style="font-family:宋体;">有多弱鸡，简直不知所谓。</span></p>  <p style="text-indent:21.0pt">&nbsp;</p>  <span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">不管怎么说都好，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">里面对字符串的支持很薄弱，要有多不方便就有多不方便，虽然比</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">C</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">要好多了，这个必须的。当然，有人会辩解，很多操作很容易就可以添加进去的，但是，如果标准库支持的话，毕竟情况会好很多，不一定要做成成员函数，之所以迟迟未添加这些常用的字符串函数，怀疑是因为找不到合适的方式添加这些操作。字符串的操作可分为两大类，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">1</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">、生成字符串；</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">2</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">、解析字符串。这两大类，都是格式化大显身手的地方，也即是</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">sprintf</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">和</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">scanf</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">c++</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">下的格式化函数，可以是类型安全，缓冲不会溢出，支持容器，支持成员变量名字。这样子，通过格式化，可以吸收大部分的字符串操作。可惜，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">对于反射的排斥，功能强大的格式化处理是不会出现的，而字符串操作，也将永远是</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的永远之痛。堂堂</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">c++</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">唯一的官方标准库，居然对最常用（可以说没有之一）的编程任务字符串操作支持如此灰头土脸，真是要笑死人了。为什么这么说，因为本座的库早就实现了这些想法（包括</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">string_view</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">array_view</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，不带</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">allocator</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">类型参数的各种容器），字符串的处理，简直不要太方便，比之</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的破烂，不可同日而语。比如，在</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">c++</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">中，完全就可以做如下的字符串操作。<br /></span><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;buf&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;{&#8230;};<br />u8string&nbsp;codes;<br />Fmt(codes,&nbsp;&#8220;{&nbsp;</span><span style="color: #000000; ">~%</span><span style="color: #000000; ">.2x}&#8221;,&nbsp;buf);</span><span style="color: #008000; ">//</span><span style="color: #008000; ">codes就是buf的16进制显示，小写，即是&#8221;xx&nbsp;xx&nbsp;&#8230;&nbsp;xx&#8221;。符号~表示前面的部分（这里是一个空格）作为元素之间的分隔符。</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">vector</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">byte</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;copied;<br />Scanf(codes,&nbsp;&#8220;{&nbsp;</span><span style="color: #000000; ">~%</span><span style="color: #000000; ">.2x}&#8221;,&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">copied);</span><span style="color: #008000; ">//</span><span style="color: #008000; ">这样就把文本codes解析到copied里面去</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">assert(Equals(buf,&nbsp;copied));<br /></span></div><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">  </span><p><span style="font-family:宋体;">不用格式化，在</span>stl<span style="font-family:宋体;">下，用</span>iostream<span style="font-family:宋体;">要实现这样的效果，不知道要写多少恶心的代码，反正很多人都已经想吐了。有了格式化之后，日子美好了好多。对了，上面的</span>vector&lt;byte&gt;<span style="font-family:宋体;">可换成</span>list&lt;byte&gt;<span style="font-family:宋体;">，代码完全都可以成立。</span>Fmt<span style="font-family:宋体;">的第一个参数表示格式化结果的目标输出对象，可以是文件，标准输出</span>stdout<span style="font-family:宋体;">，</span>gui<span style="font-family:宋体;">的文本框等。同时，</span>Scanf<span style="font-family:宋体;">的第一个参数表示格式化的输入源，可以是</span>stdin<span style="font-family:宋体;">，文件等。总之，</span>Fmt<span style="font-family:宋体;">，</span>Scanf<span style="font-family:宋体;">这两个函数就可以概括所有的格式化操作，这两个函数，基本上可以解决满足日常上大多数关于字符串的操作。其实格式化的实现，离不开运行时类型信息的支持，本座要有多大的怨念，才会一而再地抱怨</span>stl<span style="font-family:宋体;">在反射上的无所作为。</span></p>  <p><span style="font-family:宋体;">至于</span>iostream<span style="font-family:宋体;">和</span>locale<span style="font-family:宋体;">本座就不想批评太多，免得伤心难过，因为</span>iostream<span style="font-family:宋体;">竟然出自</span>c++<span style="font-family:宋体;">老父</span>bs<span style="font-family:宋体;">之手，必须是精品，某种意义上，确实是！</span></p><p><span style="font-family:宋体;"><br /></span></p>  <span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">啰里啰嗦一大堆不满，还没有写完。后一篇的文章，主角是迭代器，整个</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">的大亮点，同时也是大败笔。既造就了</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">框架的灵活性，同时也导致</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">函数使用上的不方便，特别是</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">stl</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">算法函数的冗余，非正交，不可组合。你看看，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">find</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">find_if</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">remove, remove_copy, remove_copy_if, remove_if,&#8230;&#8230;</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">，难道就不觉得面目可憎，低逼格，心里难受，堂堂大</span><span style="font-size:10.5pt;font-family:&quot;Calibri&quot;,&quot;sans-serif&quot;;Times New Roman&quot;;">c++</span><span style="font-size:10.5pt;font-family:宋体;Times New Roman&quot;;">标准库算法的正儿八经的函数，标准会要有多扭曲的审美观，才会这样设计如此丑陋的接口，难道就没有一点点的羞耻心理！这种接口怎么可以拿出来见人见证，丢人现眼。</span><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215067.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-09 11:35 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/09/215067.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>非完美的stl</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/07/215060.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Fri, 07 Jul 2017 08:52:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/07/215060.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/215060.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2017/07/07/215060.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/215060.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/215060.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++类库开发之难，举世公认，最好的证据就是，1983年到现在，面世几十年，就没有一个正儿八经的基础类库。是啊，零惩罚，要高性能，要跨平台，要可扩展，要人性化，又没有垃圾回收的支持，又没有运行时类型信息可用，&#8230;&#8230;，这些方方面面的因素纠结在一起，就好像一个巨大的意大利面线团，真的是众口难调至极。相比C#，java，php等，python等杂碎，它们面世不多久，马上就有官方的标准库，你要说这些杂碎的标准库有多好，那也未必，问题是就有大量人马心悦诚服高高兴兴地用之于开发，没有什么所谓的破心智包袱影响开发效率，甚至有人坚持认为直接用c开发，开发速度都可以快过c++。哪像c++的破事一大坨，总之就是没有一个好的基础库，能够让所有的c++开发者大爷满意。你要说这些c++大爷难侍候，也未必，因为的确就是，不管怎么呕心沥血捣鼓出来的库，确实就是是存在这样那样的问题，以至于后面的大量使用中，缺陷扩大越来越明显，难以忍受。<br /><br />c++之父一直在重复强调，c++本身美过西施，美得像杨玉环，c++本身没有问题，只是欠缺好用的基础库。问题是好用的基础库千喊万喊，迟迟就是不肯露面。这种情况下，就很让人怀疑c++的存在意义了。因为很明显的事实，其他的后生语言早就有庞大严谨的标准库，就你c++诸多借口，搞不出来合格的基础库，难道不是c++语言本身就存在重大缺陷，所以才有此困境。很多c++的老残党（包括本座），都很赞同c++之父的观点，c++本身很好，就是欠缺好用的基础库。因此大力出奇迹，集整个c++界的精英，花多年的研发，终于奋斗出来stl这个&#8220;精品&#8221;，另外，还准备了一个候补的boost，以满足非正常性的需求。<br /><br />平心而论，stl还是相当不错的，高性能，可扩展，零惩罚，跨平台等，基本上都满足要求了。除了二进制不能共用，除了编译速度慢，除了代码膨胀，除了出错的时候，可能铺天盖地的错误，这也是没有办法的事情，世上哪有十全十美之事。总之，在基础设施严重施缺乏的c++上面，能够做出来这么一个玩意，已经很不容易了。最显然的事实，面对着stl，除了一小撮乱党，广大劳动群众普遍都认可stl。只是，既然stl是c++里面如此官方的基础库，就有必要接受更高标准的考验。而最终，stl整个的设计，也不可避免地，也绝非完美。这由此可见，c++基础库开发的难度。<br /><br />stl里面的字符串，编码，iostream，locale，allocator，algorithm里面算法函数的重复（非正交）等的问题，都只是表象。根子上考察，stl的设计思路上犯了左倾和右倾的问题。具体表现如下：<br />1、对动态类型的畏惧，对静态类型的过度拥抱。这个问题在c++11之后，有一定程度的改善（出现了shared_ptr, any, variant，内里用到动态类型，起码有virtual的关键字使用）。最明显的表现就是，把内存分配器allocator做成静态类型信息，由此造成的麻烦，真是罄竹难书。同一个整型的vector，因为使用不同类型的allocator，也即是，vector&lt;int, xalloc&gt;和vector&lt;int, yalloc&gt;居然分属不同的类型，然后有一个函数要处理整型数组，要么只能做成模板函数，放在头文件上，c++原本就编译速度龟慢，再这样玩，简直雪上加霜；如果函数坚持放在cpp文件里面，就只能处理专门的allocator的整型vector。基本上，用stl打造的公共代码，都要做成头文件的共享方式，然后每次小小修改，都要引起连锁的编译雪崩，大型的c++项目，对于头文件的修改，考虑到感人的编译速度，从来都是非到不得已的时候，能不动就不动。岂有此理，天理何在。c++17，标准库终于接受多态的allocator，这算是对过去左倾激进的纠正。某种程度可以上改善这个问题，因为到时候就可以只专门接受多态的allocator，只可惜，还不完备。<br /><br />考虑批量分配arena类型的allocator，理想情况下，对于在此arena allocator上分配的对象，假如仅仅涉及到内存问题，其实大多数情况下，析构函数做的就只是释放内存。那么完全就可以不必苦逼的一个一个调用对象的析构函数，仅仅把arena allocator的内存归还给系统就好了，这对于运行性能的改善，意义重大，本座测过，真是快了很多。问题是，现有stl的体系下，不能保证容器的元素也使用和容器一样的allocator，或者说，容器的allocator对象无法传递给它的元素，让容器元素也使用同一个allocator对象来分配内存。比如说，vector&lt;string&gt;，vector和string的allocator都用polymorphic_allocator，但是，vector的allocator对象和string的allocator可能不是同一个。这样子，我们就不能仅仅简单的归还allocator对象内存，而必须像过去那样子，对vector&lt;string&gt;里面的每一个string都调用析构函数来归还内存了。差评！所以，一开始，allocator就不应该成为模板参数。另外，stl对allocator的粒度也考虑不周。allocator的迥异应用场合起码有几种：1、静态allocator，专门在main函数运行前的使用，用于生成元数据，这些元数据不必一一析构，主函数结束后，统一一次性释放；2、全局的allocator，考虑多线程考虑并发；3、scope，可以在一个任务下使用，任务完毕，统一释放，这里包括函数或者协程；4、gui下的allocator等；只可惜，stl的allocator就只关注全局的allocator。<br /><br />既然stl对allocator都可以搞成静态类型的鬼样子，那么整个stl对运行时类型信息的忽视，逃避，就可想而知了。typeid得到的type_info，除了起到类型的唯一标识符的作用（动态库下，同一种类型的type_info可能还不一样），并得到类型的名字之外，就不知道这个type_info还有什么鬼用。即便是这么一点小功能，还是能用于很多地方的，比如，any，variant，双分派(double dispatch)，由此可见运行时类型信息的重要性。<br /><br />动态类型信息，也即是反射的重要性，一点都不亚于静态类型信息。意义重大，有了反射，我们就可以将类型当成变量，到处传来传去，也可以保存起来，供后面使用，这里可做的文章，可挖掘的潜力太多了。假如c++的反射信息完善的话，很多头文件上的模板代码实现就可以放到源文件里面，模板函数仅仅是提取一下静态类型的运行时对象，类型擦除，具体实现代码就可以放到cpp代码里面去。然后，虚模板函数也可以成为可能了。可以用来创建对象，析构对象，消息发送，非侵入式的接口，序列化&#8230;&#8230;，甚至，连多继承也都是多余（当然，多继承还是很有用，只是这货不应该出现在正式的场合下）。最典型的例子，格式化printf，通过c++11的variadic template，提取类型的运行时类型对象再连同入参的地址，就可以实现现在c库里面的那个弱鸡sprintf，类型安全，缓冲安全，高性能的效果，不但类型可扩展，连同格式化的控制字符都可扩展，甚至还能支持变量名字。stl里面的iostream、locale的设计成这个鬼样子，也是因为运行时的缺失导致。c++里面要妥当地处理好字符编码、字符串、文件流、locale这几者的关系，绝对不是一件容易的事情，所以也难怪stl在这里的一塌糊涂。看过iostream，locale的实现源码，大家都说不好，大家都很难受，简直可以和mfc媲美，这是真的。<br /><br />c++的反射可以做到零抽象，也即是，只对必要的类型必要的信息做反射，不像java或者C#，不管是什么类型，不管是信息，一些很明显就是无关紧要的临时的东西，，不管三七二十一，全部一股脑儿都反射起来。甚至，c++的反射，还能添加用户自定义的反射信息，甚至，还能运行时修改反射数据。这里，C#、java等，除了attribute或者注解，就别无他法了。反射的意义就在于，它提供了统一的接口，将类型信息全部集中存放在同一个地方，任何关于类型的运行时信息，全部被标准化公理化。有了完善的反射信息，c++里面做一个eval都手到擒来。说白了，反射就是静态类型语言里把&#8220;代码做成数据&#8221;的最重要机制（没有之一），虽然比之于lisp的&#8220;代码即数据&#8221;弱一些，但是已经可以应付99%以上的需求了。甚至可以说，c++的基础库迟迟未出现的原因就是因为反射的缺席而导致的（当然，没有合适的内存管理机制也是重要原因）。而可惜，stl对运行时这一块的关注，不到%1，这真是令人扼腕叹息至极。<br /><br />2，stl的抽象缺陷：臆造抽象，过度抽象，抽象不足，想当然的抽象，大部分的精力都花在刀背上，或者说是很形式化的学术研究。<br />突然发现文章已经很长了，就先打住，以后有空再好好发挥。对了，cppblog人气太冷清，门可罗雀。再这样下去，本座只好转战知乎了。</div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/215060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2017-07-07 16:52 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2017/07/07/215060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>迭代器的抽象</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/14/213512.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Fri, 13 May 2016 18:10:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/14/213512.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/213512.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/14/213512.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/213512.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/213512.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp; &nbsp;&nbsp; 迭代器是好东西，也是猿猴工具箱里面的七种武器之一。代码中必然要操作一堆数据，因此就要有容器，有了容器，自然就不可缺少迭代器，没有迭代器，容器使用上就会非常不方便，并且还必须暴露其内部实现方式。比如，在可怜的C语言里面，操作数组容器就要通过整数索引来访问其元素，操作链表容器，就要通过while(node-&gt;next!=null）这样的方式来访问其元素。某种意义上讲，整数索引，node-&gt;next这些都是迭代器，只是它们的使用方式没有统一起来而已。既然如此，全世界的迭代器的使用方式都统一起来，那么这就是迭代器了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 基本上，现代化的语言，都会在语言层面上提供foreach之类的语法糖，其形式不外乎是，foreach(e:c）{}。就是这样，只要提供元素的名字和容器对象。后面跟着循环体。其思想就是从容器里面取出一个元素，用循环体对这个元素进行操作。循环完毕，就完成了对容器里面数据的操作。这种语法形式，简洁得不能再简洁了。很好很方便，什么额外重复的代码都不劳费心了，甚至连类型推导不用做。真的，类型可以推导的时候，就让编译器推导好了。代码里面必须大规模的使用auto，var这样的关键字。不要担心看不出来变量的类型。变量类型应该从变量的名字中体现出来其抽象意义，当然，不要搞什么匈牙利命名法，那个太丑陋了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 既然语法糖提供了这种对迭代器的支持操作语法，自然而然，只要涉及到一堆数据这样的概念，不必局限于具体的容器（数组，链表，哈希表），文件夹也是一堆数据，Composition模式也是一堆数据，数列，&#8230;&#8230;，等等所有这些，全部都是概念上的一堆数据，只要提供了迭代器，猿猴就可以很优雅的用foreach这样的语法糖来统一操作数据，多么方便，多么的多态。不管这一堆数据的内部实现方式是什么，后期怎么修改，在foreach这里代码全部都不会受影响。更何况，对于迭代器，语法上不仅仅提供foreach的便利得无以复加的甜糖，还有一大堆的标准库函数来让猿猴操作迭代器，什么排序，查找，映射&#8230;&#8230;。更令人发指的是，C#把迭代器捣鼓得好用得让人伤心难过悲愤欲绝，而linq语法上还可以把IEnumbale整成monad，可以用来作什么cps的变换。迭代器在手，天下我有。<br />&nbsp;&nbsp; &nbsp;&nbsp; 迭代器这个概念的抽象似乎很理所当然，但是不然，比如，刘未鹏举过Extended STL的例子，操作文件夹。C和C++代码对比。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;in&nbsp;C</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">DIR</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;&nbsp;dir&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;opendir(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br /></span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(NULL&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;dir)<br />{<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;dirent</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;&nbsp;de;<br />&nbsp;&nbsp;</span><span style="color: #0000FF; ">for</span><span style="color: #000000; ">(;&nbsp;NULL&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;(de&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;readdir(dir));&nbsp;)<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;stat&nbsp;st;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;stat(de</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">d_name,&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">st)&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S_IFREG&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;(st.st_mode&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;S_IFMT))<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(de</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">d_name);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;closedir(dir);<br />}<br />&nbsp;<br /></span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;in&nbsp;C++</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">readdir_sequence&nbsp;entries(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;readdir_sequence::files);&nbsp;<br />std::for_each(entries.begin(),&nbsp;entries.end(),&nbsp;::remove);</span></div><div>显然，前者是没有迭代器的抽象，后者是有迭代器抽象的简洁异常的代码。第一次看到，惊为天人，其实本就该如此，只是C将这一切搞复杂了。当然，还有一批C 粉反对，说什么代码不透明了，隐藏了代码背后可能的复杂实现。对于这一簇人的坚持不懈反对抽象的态度，真不知该说什么好呢？代码的能力里面，最最重要的事情就是抽 象，通过抽象，猿猴才可以避开细节，将精力集中于更加重要更加复杂的事情。通过抽象，可以减少重复的代码，可以提高类型安全。C++是唯一能在玩抽象概念的同时，又可以兼顾到底层细节的处理，从而不仅能写出高效代码，还能玩出更炫的技巧。很多时候，必须底层玩得越深，抽象的触角才能伸得越高。<br /><div>&nbsp;&nbsp; &nbsp;&nbsp; 其实，迭代器不必依存于容器。而是，先有了迭代器，才会有容器。请谨记，迭代器可以独立存在。begin和end就代表了一堆数据的概念。至于这一堆数据是如何存放的，这一切都无关紧要。基于此，有必要用class来表达一堆数据这么一个通用性极高的概念。其实，boost里面好像也有这么一个东西。就叫做DataRange吧。为何不叫Range，因为Range另有更重要用途，这么好的名字就是用来生成DataRange，代码不会直接看到DataRange，都是通过Range来生成DataRange。<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Iterator</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;DataRange<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Iterator&nbsp;IteratorType;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;DataRange(IteratorType&nbsp;beginIter,&nbsp;IteratorType&nbsp;endIter)&nbsp;:&nbsp;mBegin(beginIter),&nbsp;mEnd(endIter)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;IteratorType&nbsp;begin()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;mBegin;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;IteratorType&nbsp;end()</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;mEnd;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;IteratorType&nbsp;mBegin;<br />&nbsp;&nbsp;&nbsp;&nbsp;IteratorType&nbsp;mEnd;<br />};</span></div><div>&nbsp;&nbsp; &nbsp;&nbsp; 然后，随便搞两行代码试试<br />&nbsp;&nbsp; vector&lt;int&gt; vvv = { 1, 2, 3 };<br /><div><div>&nbsp;&nbsp;&nbsp; for (auto i : Range(vvv))<br />&nbsp;&nbsp; &nbsp;{<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;cout &lt;&lt; i &lt;&lt; endl;<br />&nbsp;&nbsp; &nbsp;}</div></div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实，C++11概念上就支持一堆数据的操作，只要一个类型struct或者class里面有begin()和end()这一对活宝，并且这一对活宝的返回类型是迭代器，那么就可以尽情的享用foreach的甜糖。那么，何谓迭代器。就是支持三种操作的数据类型：!=（判断相等，用来结束迭代操作）,前++（用来到迭代到下一个元素）,*（取值）。那么，这就是迭代器了，显然，指针就是原生的迭代器。虽然，整形int也可以++，!=，但是不支持取值操作，所以int不是迭代器。下面就要把int变成迭代器。<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty,&nbsp;typename&nbsp;Step</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;ValueIncrementIterator<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ValueIncrementIterator&nbsp;ThisType;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Ty&nbsp;ValueType;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Step&nbsp;StepType;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ThisType(ValueType&nbsp;val,&nbsp;StepType&nbsp;step)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:mValue(val),&nbsp;mStep(step){}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ThisType</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;other)&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;mValue&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;other.mValue;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ValueType&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000;">&nbsp;mValue;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ThisType</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">++</span><span style="color: #000000; ">&nbsp;()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mValue&nbsp;</span><span style="color: #000000; ">+=</span><span style="color: #000000; ">&nbsp;mStep;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ValueType&nbsp;mValue;<br />&nbsp;&nbsp;&nbsp;&nbsp;StepType&nbsp;mStep;<br />};</span></div>然后，再用一个函数FromTo（也不知叫什么名字更好），用来生成DataRange。请注意，我们的迭代器怎么实现，那都是细节。最后展示在用户层代码都是干干净净的function生成的DataRange，甚至连尖括号都不见了。也不用写具体是什么类型的DataRange，只须用auto让编译器自动推导类型就好了。<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;step&nbsp;=&nbsp;1是偷懒做法，万一Step的构造函数不能以1为参数就弱鸡了。比如DateTime和TimeSpan</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty,&nbsp;typename&nbsp;Step</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />auto&nbsp;FromTo(Ty&nbsp;from,&nbsp;Ty&nbsp;to,&nbsp;Step&nbsp;step&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;DataRange</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ValueIncrementIterator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty,&nbsp;Step</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; "><br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ValueIncrementIterator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Ty,&nbsp;Step</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;ValueType;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;DataRange</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ValueType</span><span style="color: #000000; ">&gt;</span><span style="color: #000000;">(ValueType(from,&nbsp;step),&nbsp;ValueType(to,&nbsp;step));<br />}<br /></span></div>于是，FromTo(1, 10, 2)就表示10以内的所有奇数，可以用for range的语法糖打印出来。<br /><div>&nbsp;&nbsp; &nbsp;&nbsp; 这里的FromTo是按照上升状态产生一系列数据，同样，也可以产生下降的一堆数据FromDownTo，如果愿意的话，同学们也可以用迭代器形式生成斐波那契数列。不知注意到了，请用抽象的角度理解++和*这两个操作符。++就是为新的数据做准备进入到下一个状态，根据情况，可以有不同方式，进入到下一个状态，比如上面的ValueIncrementIterator根据步长递增到新的数值，ValueDecrementIterator的++却是在做减法，甚至还可以做Filter操作；*就是取到数据，我们可以在*的时候，才生成一个新的数据，这里从某种意义上来讲，其实就是延迟求值；而!=判断结束条件的方式又多种多样。总之，凭着这三个抽象操作，花样百出，基本上已经能够覆盖所有的需求了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 为了体现这种抽象的威力，让我们给DataRange增加一个函数Concate，用于将两堆数据串联成一堆数据。首先，定义一个游走于两堆数据的迭代器，当它走完第一堆数据，就进入第二堆数据。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">不知道有什么语法能推导迭代器的值类型，所以搞这个辅助函数。可能写成type_trait形式更好，就算偷懒吧</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Iter</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />auto&nbsp;GetIteratorValueType(Iter</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;ptr)&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;decltype(</span><span style="color: #000000; ">**</span><span style="color: #000000; ">ptr)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">**</span><span style="color: #000000; ">ptr;<br />}<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Iter1,&nbsp;typename&nbsp;Iter2</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;ConcateIterator<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ConcateIterator&nbsp;ThisType;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Iter1&nbsp;Iter1Type;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;Iter2&nbsp;Iter2Type;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">typedef&nbsp;decltype(*mBegin1)&nbsp;ValueType;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;decltype(GetIteratorValueType((Iter1Type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)nullptr))&nbsp;ValueType;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ThisType(Iter1Type&nbsp;begin1,&nbsp;Iter1Type&nbsp;end1,&nbsp;Iter2Type&nbsp;begin2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:mBegin1(begin1),&nbsp;mEnd1(end1),&nbsp;mBegin2(begin2),&nbsp;mInBegin2(</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">){}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ThisType(Iter1Type&nbsp;end1,&nbsp;Iter2Type&nbsp;begin2)&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">这里有些蹊跷，不过也没什么</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:mBegin1(end1),&nbsp;mEnd1(end1),&nbsp;mBegin2(begin2),&nbsp;mInBegin2(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">){}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;ThisType</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;other)&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">mInBegin2&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;other.mInBegin2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">mInBegin2&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">!</span><span style="color: #000000; ">other.mInBegin2&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;mBegin1&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;other.mBegin1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(mInBegin2&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;other.mInBegin2&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;mBegin2&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;other.mBegin2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">false</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ValueType&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;mInBegin2&nbsp;</span><span style="color: #000000; ">?</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mBegin2)&nbsp;:&nbsp;(</span><span style="color: #000000; ">*</span><span style="color: #000000; ">mBegin1);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ThisType</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">++</span><span style="color: #000000; ">&nbsp;()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(mInBegin2)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">mBegin2;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(mBegin1&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;mEnd1)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">++</span><span style="color: #000000; ">mBegin1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #000000; ">!</span><span style="color: #000000; ">(mBegin1&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;mEnd1))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mInBegin2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;Iter1Type&nbsp;mBegin1;<br />&nbsp;&nbsp;&nbsp;&nbsp;Iter2Type&nbsp;mBegin2;<br />&nbsp;&nbsp;&nbsp;&nbsp;Iter1Type&nbsp;mEnd1;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">bool</span><span style="color: #000000; ">&nbsp;mInBegin2;<br />};<br /></span></div><br />有了<span style="color: #000000; ">ConcateIterator</span>，DataRange的Concate函数就很好办了。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;OtherRange</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;Concate(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;OtherRange</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;otherRange)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">DataRange</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ConcateIterator</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">IteratorType,&nbsp;decltype(otherRange.begin())</span><span style="color: #000000; ">&gt;&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ConcateIterator&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">&nbsp;IteratorType,&nbsp;decltype(otherRange.begin())</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;ResultIter;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;DataRange</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">ResultIter</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResultIter(mBegin,&nbsp;mEnd,&nbsp;otherRange.begin()),&nbsp;ResultIter(mEnd, otherRange.end()));<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><div>然后，试试<br /><div>&nbsp;&nbsp;&nbsp; list&lt;int&gt; numList = { 10, 11, 12 };</div><div>&nbsp;&nbsp;&nbsp; for (auto i : Range(vvv).Concate(FromTo(4, 10, 2)).Concate(numList))&nbsp;&nbsp; //后面随便接容器<br />&nbsp;&nbsp; &nbsp;{<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;cout &lt;&lt; i &lt;&lt; endl;<br />&nbsp;&nbsp; &nbsp;}</div>&nbsp; &nbsp;&nbsp; 这样，就把两堆数据串联在一块了，是不是很酷呢？用C++11写代码，很有行云流水的快感，又有函数式编程的风格。下期节目继续发挥，给DataRange加入Filter，Map，Replace等操作，都是将一个DataRange变换成另一个DataRange的操作，显然，这是一种组合子的设计方式，也是吸收了haskell和linq的设计思路。某种意义上讲，就是给迭代器设计一套dsl，通过.操作符自由组合其成员函数，达到用起来很爽的效果，目标就是仅仅通过几个正交成员函数的随意组合，可以在大多数情况下代替stl算法的鬼麻烦的写法。这种dsl的最大好处类似于linq，先处理的步骤写在最前面，避开了函数调用的层次毛病，最外层的函数反而写在顶层。其实迭代器这个话题要展开来说的话，很有不少内容，比如用stackless协程来伪装成迭代器，Foldl，Foldl1，Scan等。当然，真要用得爽，还要配合boost中lambda的语法，好比什么_1+30，_1%2，当然，那个也可以自己写，因为C++现在已经支持lambda了，所以，自己写boost lambda的时候，可以剪裁，取其精华，去其糟粕。如果，再弄一个支持arena内存批量释放又或者是Stack风格的allocator（线程相关），那么就更不会有任何心智负担了，内存的分配和释放飞快，这样的动多态的allocator写起来也很有意思，它可以根据不同情况表现不同行为，比如说多线程下，就会用到线程同步，单线程就无须同步，每个线程单独拥有一个allocator，根据用户需要，还能用栈式内存分配，也就是分配内存时只是修改指针而已，释放时就什么都不做了，最后通过析构函数，将此allocator的内存一次性释放。当拥有一个表现如此多样的allocator，stl用起来真是爽。<br /></div></div></div></div></div></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/213512.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2016-05-14 02:10 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2016/05/14/213512.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++单元测试框架关键点记录成员函数地址</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/11/213496.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Wed, 11 May 2016 10:01:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/11/213496.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/213496.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/11/213496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/213496.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/213496.html</trackback:ping><description><![CDATA[<div>原则上，C++下最好的单元测试代码应该长成这样子，用起来才是最方便的<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">TEST_CLASS(className)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;变量<img src="http://www.cppblog.com/Images/dot.gif" alt="" /></span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;TEST_METHOD(fn1)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" /></span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;TEST_METHOD(fn1)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" /></span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; "><img src="http://www.cppblog.com/Images/dot.gif" alt="" /></span><span style="color: #008000; "><br /></span><span style="color: #000000; ">}<br /></span></div><div>vczh大神的测试代码是这样子，这是最方便使用的形式，但因为是以测试方法为粒度，大括号里面就是一个函数体，所以显得功能上有些不足。<br /><div>TEST_CASE(ThisIsATestCase)<br />{<br />    TEST_ASSERT(1+1==2);<br />}</div><div>&nbsp;&nbsp; &nbsp;&nbsp; 当然，这里隐藏了很多宏的丑陋实现，但是，那又有什么要紧呢。好不好并不是在于用了什么东西，goto，多继承，宏，隐式类型转换，&#8230;&#8230;，这些，如果能够显著地减少重复性相似性代码，还能带来类型安全，然后又其潜在的问题又在可控的范围之内，那么，又有什么理由拒绝呢。老朽一向认为，语言提供的语法糖功能要多多益善，越多越好，当然，必须像C++那样，不用它们的时候，就不会带来任何代价，那怕是一点点，就好像它们不存在，并且它们最好能正交互补。但是，你看看，cppunit，gtest的测试代码又是什么货色呢。<br />&nbsp;&nbsp; &nbsp;&nbsp; 据说cppunit里面用了很多模式，其架构什么的非常巧妙。反正使用起来这么麻烦，要做的重复事情太多了，这里写测试函数，那里注册测试函数，只能表示，慢走不送。gtest据说其架构也大有讲究，值得学习，用起来，也比cppunit方便，但是，看看TEST_F，什么SetUp，TearDown，各种鬼麻烦，谁用谁知道。一句话，我们其实只需要class粒度的测试代码，其他的一切问题就都是小case了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 当然，class粒度的单元测试实现的难点在于收集要测试的成员函数。这里不能用虚函数。必须类似于mfc里面的消息映射成员函数表。也即是当写下TEST_METHOD(fn1)，宏TEST_METHOD就要记录下来fn1的函数指针。后面跟着的一对大括号体是fn1的函数体，已经越出宏的控制范围了，所以只能在前面大做文章。下面是解决这个问题的思路。这个问题在C++03之前的版本，比较棘手。但是，所幸，C++11带来很多逆天的新功能，这个问题做起来就没那么难了。下面的思路省略其他各种次要的细节问题。<br /><div>首先，我们定义一个空类和要测试的成员函数的形式。</div><div>struct EmptyClass{};<br />typedef void(EmptyClass::*TestMethodPtr)();<br /><div>还有存放成员函数地址的链表节点<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;MethodNode<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;MethodNode(MethodNode</span><span style="color: #000000; ">*&amp;</span><span style="color: #000000; "> head,&nbsp;TestMethodPtr&nbsp;method)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mNext&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; "> head;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; head&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mMethod&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;method;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;MethodNode</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mNext;<br />&nbsp;&nbsp;&nbsp;&nbsp;TestMethodPtr&nbsp;mMethod;<br />};</span></div>还有提取成员函数地址的函数<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; "><br />template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;OutputClass,&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />union&nbsp;horrible_union{<br />&nbsp;&nbsp;&nbsp;&nbsp;OutputClass&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;InputClass&nbsp;</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">;<br />};<br /><br />template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;OutputClass,&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />inline&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;union_cast(OutputClass</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;InputClass&nbsp;input){<br />&nbsp;&nbsp;&nbsp;&nbsp;horrible_union</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">OutputClass,&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;u;<br />&nbsp;&nbsp;&nbsp;&nbsp;static_assert(</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(InputClass)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(u)&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(InputClass)&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(OutputClass),&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">out&nbsp;and&nbsp;in&nbsp;should&nbsp;be&nbsp;the&nbsp;same&nbsp;size</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;u.</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;input;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;u.</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">;<br />}<br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />TestMethodPtr&nbsp;GetTestMethod(</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">(Ty::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">testMethod)())<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;TestMethodPtr&nbsp;methodPtr;<br />&nbsp;&nbsp;&nbsp;&nbsp;union_cast(methodPtr,&nbsp;testMethod);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;methodPtr;<br />}<br /></span></div>方法是每定义一个测试函数，在其上面就先定义一个链表节点变量，其构造函数记录测试函数地址，并把自身加入到链表中。但是，在此之前，我们将遭遇到编译器的抵触。比如<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TestCase<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;TestCase&nbsp;ThisType;<br />&nbsp;&nbsp;&nbsp;&nbsp;MethodNode</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mMethods&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nullptr;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;TestMethodPtr&nbsp;mTestMethodfn1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;GetTestMethod(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">fn1);<br /></span><span style="color: #0000FF; ">&nbsp;&nbsp;&nbsp; void</span><span style="color: #000000;">&nbsp;fn1(){}<br />};</span></div><div>&nbsp;&nbsp; &nbsp;&nbsp; vc下面，编译器报错 error C2276: &#8220;&amp;&#8221;: 绑定成员函数表达式上的非法操作<br />&nbsp;&nbsp; &nbsp;&nbsp; 原来在就地初始化的时候，不能以这种方式获取到地址。然后，试试在TestCase里面的其他函数中，包括静态函数，就可以将取地址符号用到成员函数前面。<br />&nbsp;&nbsp; &nbsp;&nbsp; 这好像分明是编译器在故意刁难，不过，任何代码上的问题都可以通过引入中间层来予以解决。用内部类。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;TestCase<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;TestCase&nbsp;ThisType;<br />&nbsp;&nbsp;&nbsp;&nbsp;MethodNode</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;mMethods&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;nullptr;<br /></span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Innerfn1&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;MethodNode<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Innerfn1(ThisType</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis)&nbsp;:&nbsp;MethodNode(pThis</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">mMethods,&nbsp;GetTestMethod(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">ThisType::fn1))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;mTestMethodfn1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;fn1(){}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Innerfn2&nbsp;:&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;MethodNode<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Innerfn2(ThisType</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis)&nbsp;:&nbsp;MethodNode(pThis</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">mMethods,&nbsp;GetTestMethod(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">ThisType::fn2))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;mTestMethodfn2&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;fn2(){}<br />};<br /></span></div><div>&nbsp;&nbsp; &nbsp;&nbsp; 有多少个测试方法，就动用多少种内部类。然后，一旦定义一个测试类的变量，那么这些内部类的构造函数就执行了，把测试方法串联在一块，逆序，也就是说最后定义测试方法反而跑到前面去了。这样子就自动记录下来所有的测试方法的地址。有了这些函数地址信息，后面怎么玩都可以。包括漂亮的测试结果显示，日志记录，甚至嵌入到vs的单元测试界面中，又或者是生成配置文件，各种花招，怎么方便就怎么玩。这个时候，可以拿来主义，把cppunit，gtest等的优点都吸收过来。<br />&nbsp;&nbsp; &nbsp;&nbsp; 是否觉得这还不够，好像有很多事情要做。比如说，测试方法逆序了，在同一个测试类的变量上执行这些测试方法，会不会就扰乱类的内部信息了，每次new一个测试类，所有的测试方法都要重复记录，内部类变量要占内存&#8230;&#8230;。咳咳，这些都可以一一解决。这里只是用最简明的方式展示自动记录测试方法，产品级的写法肯定大有讲究了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 可以看到上面的代码都是有意做成很相似的，这些都是准备给宏大展身手的。这些低级宏太容易编写了，任何经历mfc或者boost代码折磨的猿猴，都完全能够胜任，这就打住了。对了，这里的自动记录成员函数的宏手法，可以大量地使用到其他地方，比如说，自动生成消息映射表，比mfc的那一套要好一百倍，应用范围太广了。当初老朽以为就只能用于单元测试框架的编写上面，想不到其威力如此巨大，消息系统全靠它了。C++的每一项奇技淫巧和功能被发现后，其价值都难以估量，好像bs所说的，他老人家不会给c++增添一项特性，其应用范围一早就可以预料的。对付一个问题，C++有一百种解决方案，当然里面只有几种才最贴切问题领域，但是很多时候，我们往往只选择或者寻找到另外的那90多种，最后注定要悲剧。</div></div></div></div></div></div></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/213496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2016-05-11 18:01 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2016/05/11/213496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>挖坑，有空填坑</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213469.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Mon, 09 May 2016 12:36:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213469.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/213469.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213469.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/213469.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/213469.html</trackback:ping><description><![CDATA[<div><br />先挖坑，计划写出一系列文章，探讨将c++用成动态语言，或者函数式语言，以达到快速开发的目的，并且在需要优化的情况下，又能够方便快速的优化。现在事务太多，不知道何时能填坑<br /><br />宏的图灵完备，用宏生成代码，特别是反射，模式匹配，实在必不可少，以至于宏可以与c++的继承、template、exception等基本组件并列的重要必不可少的补充手段<br /><br />最小巧方便使用的单元测试框架，比gtest，cppunit要好用很多<br /><br />自定义内存管理器，stl中的allocator是作为模板参数来传递，尝试以tls来传递allocator参数，当然，必须相应的各种容器都要重写，修改其缺省构造函数，拷贝复制移动拷贝，给元素分配内存释放内存等。对了，还有各种容器的反射信息。每种类型的template的容器都有一个typeinfo对象，具体的容器又有自己独一的typeinfo对象<br /><br />完善完备的reflection，也就是，其他language能够做的反射的事情，这里只要愿意，也可以做到，非侵入式，可以给int，double等基本类型添加反射，给template类型的也添加反射信息，保证每种类型的反射对象是唯一的；<br /><br />史上功能最完善的fmt的实现，非template，当然，外层还需要variadic来包装，以类型信息。类型安全，缓冲安全，高效，通用。通用的意思是，可以fmt到文件，日志，字符串，文本框控件中；类型安全的意思是，可以是所有的类型都可以fmt，只要该类型实现了相应的接口，但是，这种接口是非侵入式的，通过模板特化。高效的意思是合sprintf系列一样。调用的时候如下：<br />fmt(text, "%s %s %d ", 20, 17.5, 'a'); //故意写错%s的，在这里，%s为通用符号<br />fmt(file, "{%s-}",{1, 2, 3}); //输出 1-2-3到文件中，也即是能够fmt容器对象，横线-为容器对元素的分隔符<br /><br />带有切片功能的数组，此数组类型还支持子类型数组到基类型数组的隐式转换，也即是需要用到基类型数组的参数，子类型数组都可以适应<br /><br />haskell的map，filter，fold算法在C++下的方便灵活组合性的改造，使用时，就好像C#的linq那么爽快，当然，没有lambda的参数自动推导，毕竟还不如<br /><br /><div>stackless协程</div><br />c++下的monad<br /><br /><div>wpf的依赖属性在c++下的实现，gui框架的不可缺少的要素</div><br />tupple的功能扩展，通过宏，不需要写类型，用起来就好像函数式语言原生的那么爽的可能<br /><br />好像haskell或者f#那样的模式匹配的结构体<br /><br />C++下完完全全实现狗语言的那种鸭子类型的接口<br /><br />面向对象的深入探讨，对于企鹅或者鸡是一种鸟，继承了鸟，但是没有继承了会飞的接口，在编译期就能报错，在运行期也不能对其找到会飞的接口<br /><br />具体类，基本类型，没有虚函数，但是又能实现接口的方式，是实实在在的接口，里面有纯虚函数，也即是非侵入式的实现接口，上面宇宙最强悍的fmt就是用到这里的技术<br /><br />vistor模式和抽象工厂的解耦合，或者又叫，multi dispatch<br /><br />类型安全的消息，一条消息就代表了一种函数调用，不是win32的那种一点也不安全的类型系统，然后可以向任何类发送消息，动态添加消息的反应，消息队列，消息和消息参数的保存，actor，command模式，redo或undo的轻松实现，消息广播<br /><br />空基类优化的运用，除了多继承(ATL)或者内嵌类(MFC)，还有其他方式，那是以组合方式，通过少量的模板和少量的宏，通过搭配组装（多继承空基类）各种基类，就能完成一个com组件<br /><br />消息系统的构建，gui框架的编写<br /><br />........<br /><br />博大精深的c++！只是想说，上面的一切，在C++下全部都是可行的，当然，宏，template，多继承必须大用特用，只是，奇妙的是，主类的内存布局却很干净，甚至可以没有虚函数<br /><div>不知道有生之年能否填完坑，以之为励吧！</div>c++的同学们也充分发挥想象力吧，太多的奇技淫巧了。</div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/213469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2016-05-09 20:36 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>stl中string的一种改造</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213468.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Mon, 09 May 2016 11:28:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213468.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/213468.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213468.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/213468.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/213468.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp; &nbsp;&nbsp; stl中最难看的组件（没有之一），无疑就是string这货了，一百多个成员函数，当然里面大多数是重载的，不必多想，一个class，如果拥有如此之多的函数，必然一定肯定是失败的，并且，即便是这么一大打函数，string的功能还是很不完备，要不然，就不会有boost里面的string算法。这真是尴尬，string作为最基本最基本的语言组件，又出自官方标准库，长成这样子，真是让无数的c++粉丝要失望，失望归失望，毕竟师出iso，用起来还是很有保障的，论性能什么，再怎样，也不会亏到那里去。只是，很让人好奇的是，这成百个函数又功能不完备的string，里面都有些什么货色，对此，c++exception系列中有过分析。但是，在此，想探讨一下，除了小胡子的方法之外，用其他方法压缩string的成员函数的数量。<br />&nbsp;&nbsp; &nbsp;&nbsp; 我们先来看看string的append成员函数，怪怪龙的东，总共有8个重载之多，好像还不止，突然想起狗语言的名言，少即是多，反过来说，多即是少。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;value_type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;_Ptr<br />);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;value_type</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;_Ptr,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;_Count<br />);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;_Str,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;_Off,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;_Count<br />);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;_Str<br />);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;_Count,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value_type&nbsp;_Ch<br />);<br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;InputIterator</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputIterator&nbsp;_First,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputIterator&nbsp;_Last<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const_pointer&nbsp;_First,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const_pointer&nbsp;_Last<br />);<br />basic_string</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">CharType,&nbsp;Traits,&nbsp;Allocator</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;append(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const_iterator&nbsp;_First,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const_iterator&nbsp;_Last<br />);<br /></span></div><div>&nbsp;&nbsp; &nbsp;&nbsp; 这么多的重载，其实可分为两类，一类是迭代器版本的append，对于插入n个相同的字符append，可以看做是特殊迭代器。另一类是连续字节内存块的append。这里，只关注后一类。虽然有4个之多，但其实只需要一个就行了，那就是 append(const basic_string&lt;CharType, Traits, Allocator&gt;&amp; _Str)。因为字符指针可以隐式转换为string，另外的两个重载可以临时构造string，然后传递进append就好了。之所以存在4个，老朽的猜想可能是因为效率，至于调用上的方便性，并没有带来多少提高。string的其他类似于用append的通过参数来string的操作，如replace，insert，+=，那么多的重载版本，应该也是同样的原因。<br />&nbsp;&nbsp; &nbsp;&nbsp; 假如，临时string对象的构造没有造成任何性能上的损失，那么，应该就可以减少几十个成员函数，这无疑很值得尝试。那么，能否存在廉价的string临时构造方法，因为它知道自己是临时对象，只作为临时参数传递的使命，不会在其上面作什么赋值，添加，修改等操作，也就是说，它是不可变的，那么，这个临时string对象就不需要分配内存了，只要节用ptr作为自己字符串的起始地址，然后以长度作为自己的长度。参数传递使命完成后，也不需要销毁内存了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 可是，C++中，也不仅仅是C++，所有的语言并没有这样的机制来判断对象它在构造的时候，就是仅仅作为参数传递来使用的。为了达到这种目的，很多时候还不惜使用引用计数，但是，很多场合，临时string对象始终要构造缓冲存放字符串，比如这里。<br />除了C++，任何语言的字符串都是不可变的，任何对于字符串的修改，都意味着要创建另一个全新的字符串来，那怕仅仅是修改了一个字符。其实，不可变的字符串，在C++中运用很广的，很多时候，我们仅仅只需要不可变的字符串，比如说，这里的append，全部只需要immutable的string。只要知道string是immutable的，那么，c++完全可以高效的应付，既然是immutable，就不需要考虑什么资源分配释放的龟毛问题了。下面，就尝试class一个immutable的字符串，这，太容易了。就是：<br /><div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all;"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;Str<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">char</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;PCStr;<br />&nbsp;&nbsp;&nbsp;&nbsp;PCStr&nbsp;start;<br />&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;length;<br />&nbsp;&nbsp;&nbsp;&nbsp;Str(PCStr&nbsp;text,&nbsp;size_t&nbsp;len)<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;text;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;length&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;len;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; "><img src="http://www.cppblog.com/Images/dot.gif"  alt="" /></span><span style="color: #008000; "><br /></span><span style="color: #000000;">};<br /></span></div><div>&nbsp;&nbsp; &nbsp;&nbsp; 然后，在basic_string中加入operator Str的函数，以完成从一个string到一个Str的隐式转换，这个隐式转换简直没有任何性能上的损失。还有，string中再增加一个Sub的成员函数，用于截取一段子字符串，也即是immutable的Str对象。显然，我们的Str其实表达了一个概念，内存中一节连续的字符内存，也即是数组。<br />&nbsp;&nbsp; &nbsp;&nbsp; 最后，append就变成append(Str str);了。Str加不加const，或者Str是否为引用，关系都不大。下面，看看它的运作。<br />对于，append(const char* text)，由于Str中有一个const char*参数的构造函数，text自动隐式转换为一个Str，很好；<br />对于，append(const char* text，size_t count)，用append(Str(text, count))，就地构造一个临时的Str对象，嗯，语法调用上多了一个Str和一对括号，多了5个字符，的确有点不便。<br />对于，append(const string&amp; text)，同上，string中有一个operator Str的函数，隐式转换自动完成。<br />对于，append(const string&amp; text，size_t offset，size_t count)，用append(text.Sub(offse, count))，就地构造一个临时的Str对象，嗯，语法调用上多了一个Sub和一对括号和一个点，但是少了一个逗号，多了5个字符，有点不便。<br />&nbsp;&nbsp; &nbsp;&nbsp; 即此以推，string中的replace，insert，assign，+=，=等函数，每个平均减少3个，总共差不多可以减少20个左右啦，而功能上没有任何减少，可喜可贺。<br />&nbsp;&nbsp; &nbsp;&nbsp; 然后，string中的各种查找比较操作的const的成员函数，比如find，find_first_not_of，rfind等，都可以挪到Str旗下了。因为这些函数，我们也希望可以用之于其他地方，只要那是一块连续的字符内存好比数组，那么我们就可以就地快速构造一个临时Str对象，进行find，rfind这些操作了。当然，原来string也可以有这个功能，但是想到仅仅为了做一个find或者find_first_not_of的查找，就要分配内存释放内存，对于性能优先的巴普洛夫反应的C++猿猴来说，这绝对是望而生畏的大事。现在通过不可变的Str，马上就释放出来string的成员函数的隐含的生产力了。 由于Str的廉价和透明性，就可以到处乱使用，想用就用，何其快哉。<br />&nbsp;&nbsp; &nbsp;&nbsp; 原来string没有了这些查找的函数，每次要用它们，必须转换这样调用，((Str)text).find，无疑很不方便，对此，我们只要在string中再增加一个Str的成员函数，以返回临时Str对象，就可以text.Str().find()，似乎有点不便，但也不是不能接受。<br />当然，Str也有缺点，那就是它不以0结束，导致很多对于要求以0结束的地方，就变成禁区了，这坑爹的C语言规定。<br />&nbsp;&nbsp; &nbsp;&nbsp; 这不是很明显吗？字符串的一部分也是字符串，随便取出字符串的一节，本来就应该是字符串，这么简明统一简洁明显的概念，这样可以简化多少代码呢，结果，偏偏只有带有0结束的那一节字符串，才是C语言承认的字符串。一个很好的概念，就这样在很多地方失去用武之地了。你因为以0结束的字符串很好吗，要不cstring头文件中也不会有那么多带有字符串长度版本的字符函数，如strncpy，来补充了。<br />&nbsp;&nbsp; &nbsp;&nbsp; 对了，有没有觉得string中的find_last_of，find_first_of，find_last_not_of，find_first_not_of很碍眼啊，显然这是一种不用组合思想下设计出来的api产物了。其实，别看stl是官方iso的嫡出亲子，但是，内中的很多api的设计都不咋样，实在不是学习的好对象。你还别不服，想想人家C#linq的链式调用，那个用起来，才叫痛快。<br /></div></div></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/213468.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2016-05-09 19:28 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2016/05/09/213468.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>非理性拥护C++</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/21/195453.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Wed, 21 Nov 2012 04:00:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/21/195453.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/195453.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/21/195453.html#Feedback</comments><slash:comments>16</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/195453.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/195453.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本来只想对C++赞叹复赞叹，后来就失控了，接着情绪化了，最后终于开始爆走，语无伦次。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 平心而论，C的而且确小巧精致，一切通通透透。老夫真心喜欢用它来编码，但一旦动用真格了，就立马叶公好龙，就会怀念C++的种种好处，class、 template、 virtual、 析构函数、甚至异常、const、引用等等，原来，离开了之后，才明白你的种种美妙动人之处，因此，朕已决定，有生之年，假如还在编码，那么C++，在心目中的，将是无可替代，它的一切，即便缺点，也是那么地令人回味无穷。因为它的一切，将自由贯彻到底，充分尊重用户的选择，不轻易剥夺用户的权利，更不强求用户用什么样的方式做设计。所谓自由的世界，独立的人格，手持C++利器，虽不敢说横行天下，但起码能愉快地编码。只有C++，当一个人独立使用，如此的耐人寻味，历久常新。多人一块开发，简直是大灾难，没必要的封装，种种自制的破烂轮子（前几年，出自本座手中的轮子不计其数，基本上惨不忍睹），错综复杂，交叉引用的类关系。这在其他语言中难以出现的怪现象，在C++中，平常得很，再一次证明了C++的博大精深，包罗万象。不说别的，就说C++中的最负盛名GUI框架MFC，其类层次的设计，糟糕透顶，而BCG的代码注入，毫无创意，笨拙无比的命名，垃圾般狗屎般的代码堆积，可怕的内存消耗，令人眼界大开，MFC的资源消耗已经够厉害，相比之下，居然显得那么节俭，而用BCG开发界面，居然比C#又或者JAVA做出来的软件，还不卡，这一切，都证明了C++过人之处。爱死你了，C++。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 近几年来看到某些人不知出于何因，对C++横加指责，说什么论效率不如C，论高级特性又不如其他的动态语言，实在莫明奇妙。说什么C++中的inline、继承、template破坏了模块的分离，&#8220;用C语言1000行源码能完成的工作千万不要用C++重写！&#8221;，实则用C++来写根本就无须1000行，并且可以精简那些字数多的代码行，并且还更加易读易懂，更加容易维护，效率或许还能更快一点点，得益于内联。如果还觉得用C++写1000行代码没有C那么漂亮，那只证明阁下没能力驾驭C++，请不要对C++乱加指责。他们那些所谓的C高手的代码，到处指针飞舞，又长又臭一再重复的表达式（本该内联大显身手），着实让人难受，当然，不否认他们的精妙设计。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 纵观他们对C++非议之例子，无一不暴露出其设计上的缺陷，本该成员函数指针大显伸手，他们却用上了虚函数；Template模式的函数（顺序依次，调用几本虚函数），本该做成全局函数，硬是整成员函数；多继承中的钻石抽象基类不该有任何东西，他们却偏要放某些东西，最后没办法，在虚继承中纠结。&#8230;&#8230;所有这一切根本无损于C++，却只显现出他们的愚蠢与无知。想展现自己也言行独立，到头来却做出拾人牙蠢之事。其实，他们更应该感谢C++，是C++的包容，才容许了如此丑陋的设计。本座平生最不齿这群宵小，自己毫无主见，风闻名人几句惊世骇俗之话语，就跟着瞎起哄，国人的毫无道理的盲目跟风，由来已久，也不必细表了。那些所谓的C高手，觉得用C能做出精妙的设计，为何用起C++就不行了，其实他们大可&#8220;用C做设计，用C++编码&#8221;，这样，根本就不会影响他们的伟大杰作构思。<br />并且要做到如同C那样的高效，C++中完全没有问题，完全可以放下身段，将C++的抽象降低到C那样的级别，在没有独立完整的概念之前，或者是没有很好的理由，绝不用类来封装代码，禁用慎用C++的一切高级特性，好比虚函数、继承、异常等。任何语言特性都可以写出垃圾代码，也容易用得不好，但不可因为这样，就否定此种特性的价值。特性作用越大，就越微妙，就越容易滥用误用。即此而观，C++中，应该以class最为难用，此关一过，必定神清气爽。<br />的确，C中，你可以也必须面对一切细节，在这种恶劣的环境下，手上能用的武器，也只有函数、结构体、数组和宏，程序员的潜能就这样被迫出来，爆发出来了，做出最合乎本质的设计，而这几样简单武器，互相组合，居然可以用得如此出神入化，其效果鬼斧神工，巧夺天工，直可惊天地，泣鬼神，手法更是精彩缤纷，巧妙绝伦，令人目不接暇，但是，不管如何，始终缺乏管理细节的有效武器。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 鄙人最惊叹C++的一强悍之处，对于各种匪夷所思的变态问题，会有更加变态的解决方式，而且还不止一两种，更可见其灵活多变自由丰富的个性，但众多迥异特性又能如此和谐的共存，为什么？窃以为C++是强类型的静态语言，虽然提供多种语言工具以让码农愉快轻松地编码，尽可能地在编译时期发现更多错误，各种微妙的语言特性不过是为了帮助码农愉快高效地编码，少出错，他们可以用这些语言工具整理组织C的各种凌散的表达式。<br />因为C中虽然能直面一切细节，却缺乏管理细节的语言工具。所有C中的细节，几乎可通过C++的各种丰富特性妥善整理，而效率的损失又甚少，并且，在其强大的静态系统的分析，能多发现点问题。但是强类型只是工具而已，必须善加利用，但C++的码农不会受束缚，必要的时候，大可突破。鄙人就曾经实现了一个微型的动态系统，对象之间没有用层次关系，都是平等的，但之间又能互相组合装配拆除，达到多继承的效果，又没有多继承的各种问题。虽然语法上别扭点，但习惯了就感觉挺不错。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要看到C++的对C代码的变态重组，为此，随便举例，qsort是代码上的典范境界，能排序所有的数组，只要提供了元素之间的比较函数，就能快速地排序，实至名归。但它是弱类型，其正确性全靠程序猿手工输入，参数出错了，编译器也检查不出来，当然C高猿不大容易出错。只是，依赖于C++强大类型推导威力，通过template整成以下样子，既不限制qsort的包容性，又不损失任何一点点效率<br />template&lt;typename _Ty&gt;<br />inline void Sort(_Ty* pItems, size_t nItemCount, int (__cdecl* funcCompare)(const _Ty&amp;, const _Ty&amp;)) <br />{ <br />&nbsp;&nbsp; &nbsp;int (__cdecl * _PtFuncCompare)(const void *, const void *);<br />&nbsp;&nbsp; &nbsp;union_cast(_PtFuncCompare, funcCompare);&nbsp;&nbsp; &nbsp;// 为忽弄编译器的强类型检查<br />&nbsp;&nbsp; &nbsp;qsort(pItems, nItemCount, sizeof(_Ty), _PtFuncCompare); <br />}<br />&nbsp;但已经是强类型的了，C++猿用起来就不大容易出错了，并且元素的比较函数也更加容易编写，没必要再用指针了，个人而言，引用比指针好，最起码少敲一下键盘，那行代码的长度可减少了一个字符。这样，用起来不是更爽吗？<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 又好比消息循环，判断消息类型，一遍又一遍地写着重复的表达式，好比，msg.message==WM_LBUTTONDOWN，不好玩，干脆class一CMsg，继承自MSG。好比这样：<br />class CMsg : public MSG<br />{<br />public:<br />&nbsp;&nbsp; &nbsp;bool Is(DWORD nMsg) const{ return message==nMsg; }<br />};<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 于是以上的那行判断语句，就精简成msg.Is(WM_LBUTTONDOWN)，感觉应该好点吧。这两例的代码整理手段，对C++来说稀松平常，但C中就做不出来了，大概也只能用宏了，但宏的问题，大家也知道。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 又有人说，C++高手的修成要经过两次转换，从C到C++，然后从C++回复C，实在异想天开，不值一晒，舍弃C++的强大类型检查，欲与一切细节肉博，吾不见其高明。这不是什么C++高手，充其量也只是C高手，其苦心孤诣在C中模仿C++的面向对象的伎俩，用C++来表达，不过小菜一碟，并且还不失强类型检查，必要时，只须用联合体或类型转换忽悠编译器。那些回归C的高猿的C++代码，其实，不甚精致。所以，大家也不必理会。只须老老实实地做出简简单单的设计，然后再用C++组织管理各种细节，大可将代码写得漂漂亮亮干干净净。 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要谨记的是，只用那些熟悉有把握的语言特性，对于每一个用到的C++关键字，一定要清楚其背后的机制并且由此所带来的各种副作用。最难用的就是class了，毫无必要的封装， 比赤裸裸的代码更加丑陋，请优先选择非成员函数。封装的出现，是因为代码的一再重复出现的需要，而并非想当然地推理演绎。只要是重复代码，不管是一行表达，连续多行，分散跨行，都可以给予包装在一起，只需一个函数调用。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再次重温C++的核心设计，尽可能利用静态强类型，尽可能地在编译期中找出程序的错误，提供多种丰富特性，协助码农充分地发挥强类型的一切优点，对抗一切细节，对抗一切重复代码，并且不必付出任何不必要的代价。当然，强类型只是忠实的奴仆，完全不必因为它而迁就你的设计，想要忽悠它，方法多种多样。 有人说，C++的语言特性太凌散，不系统，好像打补丁似的。但鄙人觉得挺好的，特性分散，各自为政，可随意自由组合，你讨厌某个特性，大可不必理睬，它就静静地站在一旁，丝毫不影响你的代码，这不就是设计的最高境界吗。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好了，终于狠狠地出了口恶气。在下承认很情绪化，有失高手风范。</div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/195453.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2012-11-21 12:00 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2012/11/21/195453.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>略说成员函数指针及其模板的精简实现</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/16/195258.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Fri, 16 Nov 2012 02:46:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/16/195258.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/195258.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2012/11/16/195258.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/195258.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/195258.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp; 请容许先发一句牢骚，&#8220;这万恶的成员函数指针的丑陋语法！&#8221;，C中的函数指针的语法已经够难看的了，但相比之下，成员函数指针却更加不堪入目，使用上又很不方便，很不人性化，简直是只能行走寸步。只可惜，函数指针的作用实在太大了，忽视不得。<br />&nbsp;&nbsp; &nbsp;大家都知道，函数指针（或又叫回调函数）是上层模块和底层模块进行通信的最佳手段，上层通过提供函数指针给底层，以使得底层在适当的时候，可以调用执行上层的代码，C库中的qsort足以说明这种使用，qsort在排序时，不知道如何比较两个数组元素的大小，必须通过上层提供的大小比较函数来进行比较。此外，操作系统提供的API中，有多少地方使用上了回调函数。假如没有函数指针，这简直没法想像，日子没法过了。函数指针是实现模块分层的不二法门，当然接口也可以，但是，用户代码必须继承或者实现底层硬性规定无理取闹的虚函数，本来很轻量级的POD，再也不轻快了，实在颇不方便，不，简直很有点恶心。说来说去，还是函数指针好。<br />&nbsp;&nbsp; &nbsp;既然，C中的回调函数这么重要，那么，可想而知，进入C++中，整个世界到处都是CLASS，将回调函数这个概念推广到CLASS上，也即是成员函数指针，将是多么迫切的事情。理论上，可以将成员函数指针视为函数指针的语法糖，只要规定函数指针的第一个参数为void* pThis，然后在函数指针的实现函数中，进行类型转换也能满足使用，在很长的一段时间里，因为这种方式的简单清晰，一直都用这种方式代替成员函数指针。但是，一遍又一遍地被迫编写重复代码，特别是枯燥的类型转换，任何人都无法忍受。因此，决定直面这个问题。<br />&nbsp;&nbsp; &nbsp;理论上，成员函数和普通函数一样，在内存中，都有自己的位置，只要有了地址信息，就可以通过指针来获取，保存起来，然后在未来的某个地方，通过这个指针来执行函数中的代码。差别之处，在于，调用成员函数前，要先将this推入ecx中，很久之前，成员函数指针确实和普通函数指针一样简单，只是后来，虚函数和多继承出现之后，简单的指针信息再也承载不了成员函数的重量，从此之后，.......（忽略，请大家自行BAIDU）。总之，C++中，成员函数指针并非指针类型，理所当然，也就不支持指针的大多数操作，比如，赋值NULL，或者类型转换。因此，所有能够让函数指针大放异彩的种种手段，在这里，都用不上了，原本在C中光芒四射的好东西，到了C++中，竟然黯然失色，所有本该让函数指针大显身手的地方，大家都绕道而行。<br />&nbsp;&nbsp; &nbsp;逼急了，也有尝试突破，MFC的仅仅作了有限争取的手段（为了这一点点好处，MFC可不知作了多大的努力），居然成为其消息映射的基石。但是，据说，MFC的成员函数指针的设计也非出于自愿，而是因为消息太多，实在没法整成虚函数来处理，每个窗口类背负成千上万个函数的虚函数表，可不是省油的灯。为了努力地支持虚函数和多继承，C++的编译器不惜在成员函数指针的使用上设下种种阻拦，令人又气又恨。而更加令人不解的是，C++横行天下十几年，函数指针似乎长期得不到重视，大师们都在面向对象上探索，很多本该成员函数指针发光发热的地方，几乎都退位给虚函数了，并美其名曰策略模式又或者是其他的什么模式，不过是换了一套更加难看的马甲，却又那么好听的名字，不，不好听，只要听到模式两字，就令人大倒胃口。所有大用特用模式的代码，如果用非模式来实现，其代码量将少掉很多，而且还更具扩展性，这是真的。先透露一下，正在构思一文章，将深度介绍模式，专注于WHY，并且类比现实，兼扯上WINDOWS、COM和MFC对模式的应用，说句良心话，如果只用接口来做设计，模式绝对是好东西。只可惜，接口其实是SB。写底层代码，如果要求用户必须实现某些接口，又或者是继承某些类，改写虚函数，这种侵入式的设计，实在无理取闹之至。<br />&nbsp;&nbsp; &nbsp;后来，大伙儿也终于开始重视成员函数指针，特别是C#的委托出现之后，网络上更是充斥着各种成员函数指针的版本代码，都可以很好地完成任务。特别是TR1又或者是BOOST中的function，功能相当的强悍得令人非大吃一惊不可。只可惜，大多数情况下，用户只想填饱肚子而已，但是BOOST或者其他的类库却硬要来一桌满汉全席，这也罢了，但是，它还要用户额外买单，并且还真不低呢，这就很让人受不了啦。其实，一般情况下，我们的要求不会太过分，仅仅想要针对普通的成员函数指针进行回调，它却为此在其内部new一个内部类出来，假如大规模使用，后果将不堪设想，其实也没那么严重，完成是C++迷们的强迫症。<br />&nbsp;&nbsp; &nbsp;但是，说真的，实在希望很精简，不要生成不必要的虚函数表，不要模板生成不必要的函数（没办法内联，有函数地址的那一种），只要求它如同对待C中的函数指针一样，参数入栈和一个简单的函数调用的指令，外加将this推入ecx即可，就好像直接调用成员函数那样就好了。好了，贡献上代码了，史上最轻量级，精简无比的成员函数指针，功能也最弱了。对不起，代码并不完整，实际的代码，用上了宏，所谓的宏的图灵完备。在下很懒，一向只介绍想法而已，只于具体的实现细节以及语法考究，窃以为，每个C++迷们应该完全能够胜任。俗话说，高手只要求创意就行了，本文详细介绍算法并给出代码，已经落了下乘。<br />&nbsp;&nbsp; &nbsp;这个实现，不考虑多继承，忽略了虚函数，也不支持非成员函数（也可以用上，只是，要多做一点点手脚，以下将给出示例，而且，普通函数指针已经完全可以胜任），只集中火力专注于普通成员函数，毕竟，在下的运用中，它占上了95%以上，所以才能如此的高效。单一职责啊！<br />&nbsp;&nbsp; &nbsp;此外，本文参考了《成员函数指针与高性能的C++委托》、《刘未鹏的BOOST源码解析》、《C++设计新思维》，请自行GOOGLE。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;OutputClass,&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />union&nbsp;horrible_union{<br />&nbsp;&nbsp;&nbsp;&nbsp;OutputClass&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;InputClass&nbsp;</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">;<br />};<br /><br />template&nbsp;</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;OutputClass,&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />inline&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;union_cast(OutputClass</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;InputClass&nbsp;input){<br />&nbsp;&nbsp;&nbsp;&nbsp;horrible_union</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">OutputClass,&nbsp;InputClass</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;u;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;ERROR_CantUseHorrible_cast[</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(InputClass)</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(u)&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">&amp;&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(InputClass)</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(OutputClass)&nbsp;</span><span style="color: #000000; ">?</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;:&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">1</span><span style="color: #000000; ">];<br />&nbsp;&nbsp;&nbsp;&nbsp;u.</span><span style="color: #0000FF; ">in</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;input;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;u.</span><span style="color: #0000FF; ">out</span><span style="color: #000000; ">;<br />}<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;FuncSignature</span><span style="color: #000000; ">&gt;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TMemFn;<br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;CCallbackObject{};<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;R</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;()</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;R&nbsp;ReturnType;<br />&nbsp;&nbsp;&nbsp;&nbsp;ReturnType&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">()()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">{</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(pThis</span><span style="color: #000000; ">-&gt;*</span><span style="color: #000000; ">func)();}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;_Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;Bind(_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pObj,&nbsp;R(_Ty::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">proc)())<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;union_cast(pThis,&nbsp;pObj);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;union_cast(func,&nbsp;proc);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ReturnType&nbsp;(CCallbackObject::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">FuncType)();<br />&nbsp;&nbsp;&nbsp;&nbsp;FuncType&nbsp;func;<br />&nbsp;&nbsp;&nbsp;&nbsp;CCallbackObject</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis;<br />};<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;R,&nbsp;typename&nbsp;P1</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;(P1)</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;R&nbsp;ReturnType;<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;P1&nbsp;Param1Type;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;ReturnType&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">()(Param1Type&nbsp;param1)&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">{</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(pThis</span><span style="color: #000000; ">-&gt;*</span><span style="color: #000000; ">func)(param1);}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;_Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;Bind(_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pObj,&nbsp;ReturnType(_Ty::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">proc)(Param1Type))<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;union_cast(pThis,&nbsp;pObj);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;union_cast(func,&nbsp;proc);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;ReturnType&nbsp;(CCallbackObject::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">FuncType)(Param1Type);<br />&nbsp;&nbsp;&nbsp;&nbsp;FuncType&nbsp;func;<br />&nbsp;&nbsp;&nbsp;&nbsp;CCallbackObject</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis;<br />};<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;R,&nbsp;typename&nbsp;_Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;()</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;MakeMF(_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis,&nbsp;R(_Ty::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">proc)())<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;()</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;res;&nbsp;res.Bind(pThis,&nbsp;proc);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;res;<br />}<br /><br />template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;R,&nbsp;typename&nbsp;_Ty,&nbsp;typename&nbsp;P1</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;(P1)</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;MakeMF(_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;pThis,&nbsp;R(_Ty::</span><span style="color: #000000; ">*</span><span style="color: #000000; ">proc)(P1))<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">R&nbsp;(P1)</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;res;res.Bind(pThis,&nbsp;proc);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;res;<br />}<br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;Test(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;a)<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;printf(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Hello&nbsp;World&nbsp;%d\n</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;a);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;a;<br />}<br /><br /></span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;_tmain(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;argc,&nbsp;_TCHAR</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;argv[])<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;_CTest<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;CallTest(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;a)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;Test(a);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;};<br />&nbsp;&nbsp;&nbsp;&nbsp;TMemFn</span><span style="color: #000000; ">&lt;</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">int</span><span style="color: #000000; ">)</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;aTest&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;MakeMF((_CTest</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)NULL,&nbsp;</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">_CTest::CallTest);<br />&nbsp;&nbsp;&nbsp;&nbsp;aTest(</span><span style="color: #000000; ">80</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />}</span></div><br /></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/195258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2012-11-16 10:46 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2012/11/16/195258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>轻量级共享对象的灵巧指针的实现</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2012/10/30/194091.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Tue, 30 Oct 2012 07:39:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2012/10/30/194091.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/194091.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2012/10/30/194091.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/194091.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/194091.html</trackback:ping><description><![CDATA[<div>&nbsp;&nbsp;&nbsp; 毫无疑问，shared_ptr的功能不可谓不强大，设计不可谓不精巧，它的抽象级别不是一般的高，不仅要管理一般的C++内存资源，更染指其他的非C++资源，比如文件、比如连接、&#8230;&#8230;，只要给它一个支点（释放资源的函数），不仅如此，还能顽强地生存于各种恶劣的环境，好比多线程、引用循环。当然，代价是有的，它背地里做了很多不为人知的勾当，表面上仅仅一行的带有构造函数shared_ptr的定义代码，编译器却要很无奈地生成一个莫明其妙的多态模板类（_Ref_count_base的继承类，带有虚函数表，意味着不能内联，用以在恰当的时机，释放资源），更别提要多了一堆指令，当然，在当今硬件性能蓬勃发展的美好时代，这点代价根本就不算什么，比之于那些什么所谓的虚拟机，甚至可以忽略不计。但是，总是有那么一批老古董，总会强迫假想自己写的程序会运行于各种资源非常苛刻的环境下，内心就是没法原谅shared_ptr所带来的极细微的损失。好比区区在下，每一次一用到shared_ptr，心里的那种负罪感啊，又多了几条废指令，又浪费多了十几个的堆字节，是否将生成内存碎片啊。终于有一刻顶不住了啦，去你妈的shared_ptr，老子不过想让你老老实实的代理内存资源，本本分分地做好你的分内之事，不劳你费心照顾其他的系统资源对象，那些场合本座自然有更好的解决方式。于是，制造轮子的悲剧又再次诞生了，虽然，他们一直在内心深处抵制新轮子的愚蠢行为，但是，&#8230;&#8230;，只能说，知我者谓我心忧，不知我者谓我何求。<br />&nbsp;&nbsp; &nbsp;每次想到shared_ptr要new一个_Ref_count_base的对象来管理计数，有人就恨得牙根发痒，巴不得把_Ref_count_base的数据成员搬过来，放之于那个要管理的对象的身上，以减少一小块内存。假如，客户传到shared_ptr构造函数的指针，此指针所指的内存，能再多几个字节（一个字节也行，最大值255，已足矣），以供我等存放一个long型的计数器，那就好办了。白痴也知道，这是不可能的事情。除非，此对象由shared_ptr来构造，那么还有办法再放多点额外内存进去。此话怎讲？大家都知道，C++中， new一个对象时，即意味着两步操作：1、分配一块内存；2、在此块内存上执行对象的构造函数。如果第1步的分配内存，能作多点手脚，比如说，分配一块比对象本身所占空间还要大的内存，那么我们的shared_ptr就可以把计数器放进对象之中了，也无须再new一个新的_Ref_count_base对象来管理计数器了。两块内存，合二为一，双剑合璧，妙哉妙哉。但，这如何做到呢？<br />&nbsp;&nbsp; &nbsp;以下，是一个类从简单到复杂的物种进化历程。C++中，只要是通用类，即使再简单的需求，要写得可以被普遍承认，可以高高兴兴地到处使用，都绝非易事。而且，更悲剧的是，辛辛苦苦，呕心沥血造出来的轮子，还很有可能一问世就直接被枪毙，就算能苟且活下来，也不会有车愿意组装这一个废轮子。<br />&nbsp;&nbsp; &nbsp;废话不说，书接上文，很明显，对象的new操作应该由我们的shared_ptr来掌控。任由用户来new，就太迟了，对象的内存块已经确定下来了，没文章可做啦。换句话说，shared_ptr必须模拟标准的new的两在操作分配内存和调用构造函数。由此可知，以下所探讨的shared_ptr运用场合也很有限，只适合于那些能看到构造函数并且知道其大小的C++类，所以，请大伙儿不要抱怨。唯其需求简单明确，所以才能高效。<br />首先，用一结构体__SharedObject来包含计数器和对象，如下所示：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">struct</span><span style="color: #000000; ">&nbsp;__SharedObject<br />{<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;Object()&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;返回对象的地址，由于不知对象的类型，所以只能是void*，表示内存地址</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;{ </span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">this</span><span style="color: #000000; ">+</span><span style="color: #000000; ">1</span><span style="color: #000000; ">; }<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;Incref()&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;InterlockedIncrement(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">m_nRef);&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;Decref()&nbsp;{&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;InterlockedDecrement(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">m_nRef);&nbsp;}<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000FF; ">long</span><span style="color: #000000; ">&nbsp;m_nRef;<br />};</span></div>&nbsp;&nbsp;&nbsp; 是否很简陋，本座没法也不想把它整得更加好看了。<br />&nbsp;&nbsp; &nbsp;我们的shared_ptr，就暂时叫TShared_Ptr好了，其包含的数据成员，暂时很简单。就只有一个__SharedObject的指针而已，后面由于多继承多态的原因，将被迫多增加一个指针。<br />&nbsp;&nbsp; &nbsp;好了，先看看TShared_Ptr的使用之道，此乃class template。由于共享对象由TShared_Ptr所造，所以，在其诞生之前，首先势必定义一TShared_Ptr变量，好比，TShared_Ptr&lt;int&gt; pInt；考虑TShared_Ptr的构造函数，如果在里面就急急忙忙给共享对象分配内存，将没法表达空指针这个概念，所以它的无参构造函数只是简单地将m_pShared置为NULL。然后，TShared_Ptr必须提供分配内存并执行构造函数的操作，叫Construct吧；然后，析构函数也绝不含糊，执行对象的析构函数并释放内存。于是，TShared_Ptr的基本代码就出炉了。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;_Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TShared_Ptr<br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;TShared_Ptr() {m_pShared&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;NULL; }<br />&nbsp;&nbsp;&nbsp;&nbsp;TShared_Ptr(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TShared_Ptr</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;_Other)<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(m_pShared&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;const_cast</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">__SharedObject</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">(_Other.m_pShared);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Incref();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;NULL;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">~</span><span style="color: #000000; ">TShared_Ptr()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(m_pShared&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Decref()&nbsp;</span><span style="color: #000000; ">&lt;=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">m_nRef&nbsp;</span><span style="color: #000000; ">==</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DestroyPtr(</span><span style="color: #0000FF; ">get</span><span style="color: #000000; ">());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free(m_pShared);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;_Ty</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">*</span><span style="color: #000000; ">()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;_THROW0() { </span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">get</span><span style="color: #000000; ">(); }<br />&nbsp;&nbsp;&nbsp;&nbsp;_Ty&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">operator</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;_THROW0(){</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(</span><span style="color: #0000FF; ">get</span><span style="color: #000000; ">());}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;Construct()<br />&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;::</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;(m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Object())&nbsp;_Ty();&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;调用构造函数</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Incref();&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;构造函数抛出异常，这一行将不执行</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp; </span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;alloc()&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;假设malloc总能成功</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;static_cast</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">__SharedObject</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">(malloc(</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(_Ty)</span><span style="color: #000000; ">+</span><span style="color: #0000FF; ">sizeof</span><span style="color: #000000; ">(__SharedObject)));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">m_nRef&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;_Ty&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #0000FF; ">get</span><span style="color: #000000; ">()&nbsp;</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;_THROW0() {&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; "></span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000; ">)(m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Object());}<br />&nbsp;&nbsp;&nbsp;&nbsp;__SharedObject</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;m_pShared;<br />};</span></div><br /><div>可以写代码测试了，<br />&nbsp;&nbsp; &nbsp;TShared_Ptr&lt;int&gt; pInt;<br />&nbsp;&nbsp; &nbsp;pInt.Construct();<br />&nbsp;&nbsp; &nbsp;(*pInt)++;<br />&nbsp;&nbsp; &nbsp;TShared_Ptr&lt;int&gt; pInt1 = pInt;<br />&nbsp;&nbsp; &nbsp;(*pInt1)++;<br /><div>&nbsp;&nbsp;&nbsp; 咦，假如共享对象的构造函数带有参数，咋办呢？不要紧，重载多几个Construct就行了，全部都是template 成员函数。由于要实现参数的完美转发，又没有C++2011的move之助，我还在坚持C++98ISO，要写一大打呢，先示例几个，很痛苦，或者，可通过宏来让内心好受一点。<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1&gt;void Construct(const T1&amp; t1);<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1&gt;void Construct(T1&amp; t1);<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1, typename T2&gt;void Construct(const T1&amp; t1, const T1&amp; t2);<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1, typename T2&gt;void Construct(const T1&amp; t1, T1&amp; t2);<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1, typename T2&gt;void Construct(T1&amp; t1, const T1&amp; t2);<br />&nbsp;&nbsp; &nbsp;template&lt;typename T1, typename T2&gt;void Construct(T1&amp; t1, T1&amp; t2);<br /><br />&nbsp;&nbsp; &nbsp;接下来就很清晰了，将shared_ptr的各种构造、赋值函数改写一遍就是了。然后，就可以高高兴兴地测试使用了。以上，是最理想环境下TShared_Ptr的很简单的实现，其操作接口多么的确简明扼要。<br />&nbsp;&nbsp; &nbsp;开始，考虑各种变态环境，其实也不变态，完全很正当的需求。各种也不多，就两个而已：1、构造函数抛出异常，这个不是问题，由于TShared_Ptr的构造函数不抛出异常，其析构函数将被执行，检查到计数器为0，所以不调用共享对象的析构函数；</div></div><div>&nbsp;&nbsp;&nbsp; 2、多继承，这个有点头痛了。先看看代码，假设 class D : public B1, public B2，B1、B2都非空类;然后，B2* pB2 = pD，可以保证，(void*)pB2的值肯定不等于pD，也即是(void*)pB2 != (void*)pD。个中原因，在下就不多说了。但是，TShared_Ptr完全没法模拟这种特性，假如，坚持这样用，设pD为TShared_Ptr&lt;D&gt; ; 然后TShared_Ptr&lt;B2&gt;=pD，后果将不堪设想。一切皆因TShared_Ptr只有一条指向共享对象的指针，它还须拥有指向共享对象的基类子对象的指针，为此，必须添加此指向子对象的指针m_ptr，为_Ty*类型。因此，TShared_Ptr将内含两个指针，大小就比普通指针大了一倍，无论如何，到此为止，不能让它增大了。此外，TShared_Ptr增加了m_ptr成员后，还带来一些别的好处，类型安全倒也罢了，关键是在VC中单步调试下，可清晰地看到共享对象的各种状态，原来没有m_ptr的时候，就只能闷声发大财。<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;_Ty</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br /></span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;TShared_Ptr<br />{<br /></span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">:<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;template</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">typename&nbsp;_OtherTy</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;TShared_Ptr(</span><span style="color: #0000FF; ">const</span><span style="color: #000000; ">&nbsp;TShared_Ptr</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">_OtherTy</span><span style="color: #000000; ">&gt;&amp;</span><span style="color: #000000; ">&nbsp;_Other)<br />&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_ptr&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;_Other.m_ptr;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">&nbsp;类型安全全靠它了</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;const_cast</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">__SharedObject</span><span style="color: #000000; ">*&gt;</span><span style="color: #000000; ">(_Other.m_pShared);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">&nbsp;(m_ptr&nbsp;</span><span style="color: #000000; ">!=</span><span style="color: #000000; ">&nbsp;NULL)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_pShared</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">Incref();<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/Images/dot.gif" alt="" /><br />&nbsp;&nbsp;&nbsp;&nbsp;__SharedObject</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;m_pShared;<br />&nbsp;&nbsp;&nbsp;&nbsp;_Ty</span><span style="color: #000000; ">*</span><span style="color: #000000;">&nbsp;m_ptr;<br />};</span></div>&nbsp;&nbsp;&nbsp; 本轮子自然不美观，使用也颇不方便，但胜在背地里做的勾当少，一切均在预料之中。好像多线程下，还有点问题，但那只是理论上的疑惑，实际运行中，该不会那么巧吧。<br /><div>&nbsp;&nbsp; &nbsp;咦，都什么年代，还在研究茴香豆的四种写法，在下也承认，确实没啥意义，但乐趣很无穷呢，我就是喜欢。珍惜生命，远离C++。对了，想要完整的代码吗，没那么容易</div></div></div><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/194091.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2012-10-30 15:39 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2012/10/30/194091.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>难以割舍的二段构造</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/14/178799.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Thu, 14 Jun 2012 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/14/178799.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/178799.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/14/178799.html#Feedback</comments><slash:comments>14</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/178799.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/178799.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 两段构造也是声名狼藉得很，比之于MFC，好不了多少，貌似MFC中到处都是两段构造，难道两段构造的声誉也是受MFC所累。定义完了一个对象变量之后，还要再调用一次该对象的Create函数，而且还要Create成功了之后，才能对该对象做进一步的操作，否则对象将一直处于非法状态。这种代码方式写起来确实很恶心，为何不直接在构造函数中直接Create，不成功就抛出异常，然后对象就流产了，好过它半死不活地一直苟延残喘于世上，累己累人。其实，MFC选择两段构造也是有苦衷：1、先是很久很久以前，VC编译器对异常的支持不怎么好，当然，现在的VC编译器，自然今时不比往日，但是，还要兼容以往的代码；2、然后是MFC的设计，它只是对API做了一层薄薄的包装，薄薄的意思，就是，不管怎么捣鼓，都难以将WINDOWS系统中的各种对象包装成一个干净的C++对象了，因为，API本身就采用两段构造。可不是吗？定义一个句柄变量，然后CreateXXX返回结果，返回值非法，表示创建失败。失败了，还要霸王硬上弓，后果会怎么样，这谁也不知道。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 理论上，构造函数抛出异常确实很优雅，代码也更具美感，并且，其行为也更加明确，要么就处理，要么，就等着程序异常退出。但是，实际上，异常这种东西，真正实现执行起来，却相当的困难。更何况，如果完全丢弃两段法，除了异常，还会引入一些新的问题，正所谓：&#8220;前门驱虎，后门进狼&#8221;，进来不只是一只狼，而是好几只。生活的奥妙，就在于制造出新的问题，以解决旧的问题。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 构造函数中直接调用Create，就表示了用户一定义一个类型变量，程序就会马上启动Create函数，也就意味着可能将创建窗口对象、内核对象、甚至启动新的线程等等，这些操作都不是省油的灯，构造函数中做了太多事情，会有隐藏太多细节之嫌，代码本来就是为了隐藏细节，这个多事之罪名暂且不论；但是，用户没法对创建过程Say NOT，也即是说，用户一定义对象变量，就只能接受它的高昂的创建过程。难道，一开始就让对象进入有效状态，这都有错吗？确实是的。有时候，用户只是先想声明（定义）对象，等必要（时机成熟）的时候，再让它进入有效状态。咦，用户这样写代码，不太好吧，应该强制他/她等到了那个时候，再定义对象变量。变量怎么可以随随便便就定义呢？应该在要使用的时候，才定义它，这才是良好的代码风格。但是，有些情况，确实需要先暂时定义非法状态下的对象变量，比如，这个对象是另一个对象（拥有者）的成员变量时，那也没什么，强制用户在必要的时候，才定义拥有者对象变量。但是，假如这个拥有者必须是全局变量，那该怎么办？那也没什么，将拥有者定义为指针变量就是了？好了，本来只是要对象创建失败的情况，现在还要考虑内存分配的细节，然后接着就是new delete，然后就是各种智能指针闪亮登台演出，更糟糕的是，对象有效无效的问题依然没有根除，因为，只要引入指针，每次使用指针，就必须检查指针是否有效，咦，难道操作空指针不会抛出异常吗？C++规范中，操作空指针属后果未确定的行为，对C++而言，未确定往往就是最糟糕的意思。此外，鉴于对象只能一直处于有效状态，它就不可能提供让对象进入无效状态的操作。如果想要让对象无效，唯一的办法，就是让它死去，强制对象启动析构函数，方法是离开作用域强者delete它。下次要使用它的时候，就再new一次或者定义一次，不，它已经是另外一条新生命了。但是，对于两段构造的对象，只须Destroy又或者Create，对象可以永远只有一个。此外，二段构造颇具扩展性，很轻易地就可搞成三段构造，每一步，用户都有选择的权利。但构造异常就没有这个优点。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 考虑到构造函数中的参数问题，比如，月份的参数，大家都知道，有效值只在1-12月之间。不讨论这种情况下，非法的参数传递是否属于代码的逻辑问题。对此，构造异常指导下的对象是不可能出现无参（没有参数或者参数都有缺省值）的构造函数，因此，它们也都不能用于数组，难以应用于全局变量、静态变量、作为其他对象的数据成员，如果非要在这些场合下使用它们，比如占位符的作用，唯有用上指针，于是伴随而来的，又如上文所述，使用指针之前，必须检查指针的有效性，只怕不会比检查二段构造的有效性好多少。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 二段构造不轻易剥夺用户的权利，提供更多选择，可用于数组、堆栈、STL中的容器，要它死，它就死，要它活，它就活，但是，它可以从来都未曾消失过，要做的，仅仅是在使用它时，清楚它是死是活就行了，不过多加几次判断而已。相比之下，构造异常就更具侵入性了，一旦用上，就只能被迫遵照它的规则行事。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实，两段构造与构造异常，都很恶心，只要一处代码中用到了它，所有与之相关的代码都没法脱身。差别不过在于谁比谁恶心而已，这个，视各人的口味而不同。对于本人这种害怕分配内存，释放内存，更加畏惧异常的人来说（这并不表示本人写不出异常安全的代码），当然优先选择二段构造，MORE EFFECTIVE的条款中，声称，如无必要，不要提供缺省的构造函数，以免对象陷入半死不活的状态中。而我的习惯作法则是，如无必要，必须提供缺省的构造函数，不要轻易剥夺用户想要使用对象数组的权利，或者是由于不提供缺省的构造函数，而由此引起的种种不便。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好了，既然程序中决定用二段构造了，那么，假如用户定义了一个对象，忘了再构造一次，但是又要执行其他操作，怎么办？嗯，那也没什么，既然用户不遵守契约，我们的对象自然可以做出种种不确定的行为。当然，别忘了，在其他的每一个操作上都添加几条assert语句，尽管这很恶心，也聊胜于无，减少点罪恶感，以便于在调试版中找出问题。<br /><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/178799.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2012-06-14 15:08 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2012/06/14/178799.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于堆栈上的字符串实现</title><link>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/08/177993.html</link><dc:creator>华夏之火</dc:creator><author>华夏之火</author><pubDate>Thu, 07 Jun 2012 17:11:00 GMT</pubDate><guid>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/08/177993.html</guid><wfw:comment>http://www.cppblog.com/huaxiazhihuo/comments/177993.html</wfw:comment><comments>http://www.cppblog.com/huaxiazhihuo/archive/2012/06/08/177993.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/huaxiazhihuo/comments/commentRss/177993.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/huaxiazhihuo/services/trackbacks/177993.html</trackback:ping><description><![CDATA[<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++</span><span style="font-family: 宋体">中，由于字符串一开始并非内置的类型，于是，史前时代，江湖上出现了种种的字符串，各有各的优点，但自然，也各有各的缺陷，群雄割据，天下大乱，民不聊生。大伙儿盼星星，盼月亮，盼着真正的字符串能出来一统江湖，好不容易，等到</span>1998<span style="font-family: 宋体">的</span>C++<span style="font-family: 宋体">标准出来了，官方的字符串</span>string<span style="font-family: 宋体">终于露面了，自然，它并不是语言内置支持的，而是在</span>STL<span style="font-family: 宋体">库中。当然，字符串这东西，就算要在</span>C++<span style="font-family: 宋体">编译器的层面上给予支持，其实也很不容易。可是，这个官方的</span>string<span style="font-family: 宋体">，单纯它复杂的模板定义和那一大陀函数成员，就足以吓退众多意志不坚定之人。好了，好不容易，鼓起勇气，再仔细瞅瞅</span>string<span style="font-family: 宋体">中的东西，发现它也不是很美妙，既不强大，部分函数的功能存在重复又或者</span>STL<span style="font-family: 宋体">的算法中已有提供，更要命的是，效率也不怎么高。总之，不能说</span>string<span style="font-family: 宋体">的设计不如史前的各种</span>stringS<span style="font-family: 宋体">，但也强不到那里去。当然，官方的总比非官方的更具权威，但每次使用</span>string<span style="font-family: 宋体">时，想到它背地里除了正常字符串操作之外，还可能做了各种的低效的内存分配释放的操作，又或者线程安全，又或者引用计数，内心就一直惴惴不安。于是，宁愿一再小心翼翼地用着字符数组。但是，用得多了，也很郁闷，字符数组自然高效、灵活，但总是要千编一律地一再写着容易出错的代码，我脆弱的心灵终于头晕眼花了。于是，我决定按照自己的意愿写一个字符串，不敢欲与群雄争锋，只是，为了能够在自己的代码中，替换字符数组。我对它的要求是，字符数组能做到的事情，它也要做到，并且，效率上，绝不妥协。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">俗话说，过早的优化是万恶之源。但在此，在设计本家字符串时，一开始，就要考虑到效率的细节上去了。首先，它要支持堆栈变量的形式，不要它进行内存的分配释放操作，就好像堆栈上的字符数组那样。咦，好像很神奇，其实，只要想到</span>TR1<span style="font-family: 宋体">中那个经典的</span>array<span style="font-family: 宋体">，通过使用神奇的模板技术，就有办法做到了。所以，此字符串的使用好比这样子，</span>CStackString &lt;MAX_PATH&gt; sFile<span style="font-family: 宋体">，暂时假定这个字符串的名字叫</span>CStackString<span style="font-family: 宋体">。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">但是，使用模板之后，字符串的字符数组的长度只要不一样，它们就都属于不同类型变量，并且之间还都不兼容呢，虽然它们都是字符串。此外，还会编译器还将生产出一堆重复的代码。这无论如何，都不能忍受。于是，自然而然，就想到了继承。</span>CStackString<span style="font-family: 宋体">模板类继承于非模板的</span>mybasestring<span style="font-family: 宋体">，</span>mybasestring<span style="font-family: 宋体">中实现了</span>CStackString<span style="font-family: 宋体">的各种各样的操作，而</span>CStackString<span style="font-family: 宋体">只要仿照</span>array<span style="font-family: 宋体">那样子，定义好自己的数据成员即可。嗯，还是看看代码，马上就明白到底是怎么一回事了。</p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;CMyBaseString<br /><img id="Codehighlighter1_21_1213_Open_Image" onclick="this.style.display='none'; Codehighlighter1_21_1213_Open_Text.style.display='none'; Codehighlighter1_21_1213_Closed_Image.style.display='inline'; Codehighlighter1_21_1213_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif"><img style="display: none" id="Codehighlighter1_21_1213_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_21_1213_Closed_Text.style.display='none'; Codehighlighter1_21_1213_Open_Image.style.display='inline'; Codehighlighter1_21_1213_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif"></span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_21_1213_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_21_1213_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">public</span><span style="color: #000000">:<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;size_t&nbsp;size_type;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">pointer;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;</span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #000000">const_pointer;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;CMyBaseString&nbsp;_Myt;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">public</span><span style="color: #000000">:<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">operator</span><span style="color: #000000">[](size_type&nbsp;nPos)</span><span style="color: #0000ff">const</span><span style="color: #000000"><br /><img id="Codehighlighter1_195_245_Open_Image" onclick="this.style.display='none'; Codehighlighter1_195_245_Open_Text.style.display='none'; Codehighlighter1_195_245_Closed_Image.style.display='inline'; Codehighlighter1_195_245_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_195_245_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_195_245_Closed_Text.style.display='none'; Codehighlighter1_195_245_Open_Image.style.display='inline'; Codehighlighter1_195_245_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_195_245_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_195_245_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(nPos&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;m_nLen);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;m_str[nPos];<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img id="Codehighlighter1_276_292_Open_Image" onclick="this.style.display='none'; Codehighlighter1_276_292_Open_Text.style.display='none'; Codehighlighter1_276_292_Closed_Image.style.display='inline'; Codehighlighter1_276_292_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_276_292_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_276_292_Closed_Text.style.display='none'; Codehighlighter1_276_292_Open_Image.style.display='inline'; Codehighlighter1_276_292_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;const_pointer&nbsp;c_str()</span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_276_292_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_276_292_Open_Text"><span style="color: #000000">{&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;m_str;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;const_pointer&nbsp;right(size_type&nbsp;nLen)</span><span style="color: #0000ff">const</span><span style="color: #000000"><br /><img id="Codehighlighter1_338_394_Open_Image" onclick="this.style.display='none'; Codehighlighter1_338_394_Open_Text.style.display='none'; Codehighlighter1_338_394_Closed_Image.style.display='inline'; Codehighlighter1_338_394_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_338_394_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_338_394_Closed_Text.style.display='none'; Codehighlighter1_338_394_Open_Image.style.display='inline'; Codehighlighter1_338_394_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_338_394_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_338_394_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(nLen&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;m_nLen);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;m_str</span><span style="color: #000000">+</span><span style="color: #000000">m_nLen</span><span style="color: #000000">-</span><span style="color: #000000">nLen;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;compare(const_pointer&nbsp;str)</span><span style="color: #0000ff">const</span><span style="color: #000000"><br /><img id="Codehighlighter1_435_467_Open_Image" onclick="this.style.display='none'; Codehighlighter1_435_467_Open_Text.style.display='none'; Codehighlighter1_435_467_Closed_Image.style.display='inline'; Codehighlighter1_435_467_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_435_467_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_435_467_Closed_Text.style.display='none'; Codehighlighter1_435_467_Open_Image.style.display='inline'; Codehighlighter1_435_467_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_435_467_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_435_467_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;strcmp(m_str,&nbsp;str);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;_Myt</span><span style="color: #000000">&amp;</span><span style="color: #000000">&nbsp;assign(</span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">*</span><span style="color: #000000">&nbsp;str)<br /><img id="Codehighlighter1_502_599_Open_Image" onclick="this.style.display='none'; Codehighlighter1_502_599_Open_Text.style.display='none'; Codehighlighter1_502_599_Closed_Image.style.display='inline'; Codehighlighter1_502_599_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_502_599_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_502_599_Closed_Text.style.display='none'; Codehighlighter1_502_599_Open_Image.style.display='inline'; Codehighlighter1_502_599_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_502_599_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_502_599_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_nLen&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;strlen(str);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(m_nLen&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;m_nBuffSize);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(m_str,&nbsp;str);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #0000ff">this</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;_Myt</span><span style="color: #000000">&amp;</span><span style="color: #000000">&nbsp;append(const_pointer&nbsp;str)<br /><img id="Codehighlighter1_636_773_Open_Image" onclick="this.style.display='none'; Codehighlighter1_636_773_Open_Text.style.display='none'; Codehighlighter1_636_773_Closed_Image.style.display='inline'; Codehighlighter1_636_773_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_636_773_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_636_773_Closed_Text.style.display='none'; Codehighlighter1_636_773_Open_Image.style.display='inline'; Codehighlighter1_636_773_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_636_773_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_636_773_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;nLen&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;strlen(str);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(m_nLen&nbsp;</span><span style="color: #000000">+</span><span style="color: #000000">&nbsp;nLen&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;m_nBuffSize);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(m_str</span><span style="color: #000000">+</span><span style="color: #000000">m_nLen,&nbsp;str);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_nLen&nbsp;</span><span style="color: #000000">+=</span><span style="color: #000000">&nbsp;nLen;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #0000ff">this</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;_Myt</span><span style="color: #000000">&amp;</span><span style="color: #000000">&nbsp;format(const_pointer&nbsp;sFormat,&nbsp;&nbsp;<img alt="" src="http://www.cppblog.com/Images/dot.gif" />)<br /><img id="Codehighlighter1_820_989_Open_Image" onclick="this.style.display='none'; Codehighlighter1_820_989_Open_Text.style.display='none'; Codehighlighter1_820_989_Closed_Image.style.display='inline'; Codehighlighter1_820_989_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_820_989_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_820_989_Closed_Text.style.display='none'; Codehighlighter1_820_989_Open_Image.style.display='inline'; Codehighlighter1_820_989_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_820_989_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_820_989_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;va_list&nbsp;argList;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;va_start(&nbsp;argList,&nbsp;sFormat&nbsp;);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_nLen&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;vsprintf(m_str,&nbsp;sFormat,&nbsp;argList);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;va_end(&nbsp;argList&nbsp;);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assert(m_nLen&nbsp;</span><span style="color: #000000">&lt;</span><span style="color: #000000">&nbsp;m_nBuffSize);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">*</span><span style="color: #0000ff">this</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000">//</span><span style="color: #008000"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /><img alt="" src="http://www.cppblog.com/Images/dot.gif" />.</span><span style="color: #008000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">protected</span><span style="color: #000000">:<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;CMyBaseString(pointer&nbsp;sBuf,&nbsp;size_type&nbsp;nBuffSize)<br /><img id="Codehighlighter1_1066_1142_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1066_1142_Open_Text.style.display='none'; Codehighlighter1_1066_1142_Closed_Image.style.display='inline'; Codehighlighter1_1066_1142_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_1066_1142_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_1066_1142_Closed_Text.style.display='none'; Codehighlighter1_1066_1142_Open_Image.style.display='inline'; Codehighlighter1_1066_1142_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_1066_1142_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_1066_1142_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_nBuffSize&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;nBuffSize;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_nLen&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_str&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;sBuf;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_str[</span><span style="color: #000000">0</span><span style="color: #000000">]&nbsp;</span><span style="color: #000000">=</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockEnd.gif" />&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">private</span><span style="color: #000000">:<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;m_nBuffSize;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;size_type&nbsp;m_nLen;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;pointer&nbsp;m_str;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" />}</span></span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />template</span><span style="color: #000000">&lt;</span><span style="color: #000000">size_t&nbsp;_size</span><span style="color: #000000">&gt;</span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span><span style="color: #0000ff">class</span><span style="color: #000000">&nbsp;CStackString&nbsp;:&nbsp;</span><span style="color: #0000ff">public</span><span style="color: #000000">&nbsp;CMyBaseString<br /><img id="Codehighlighter1_1282_1454_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1282_1454_Open_Text.style.display='none'; Codehighlighter1_1282_1454_Closed_Image.style.display='inline'; Codehighlighter1_1282_1454_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif"><img style="display: none" id="Codehighlighter1_1282_1454_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_1282_1454_Closed_Text.style.display='none'; Codehighlighter1_1282_1454_Open_Image.style.display='inline'; Codehighlighter1_1282_1454_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif"></span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_1282_1454_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_1282_1454_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">public</span><span style="color: #000000">:<br /><img id="Codehighlighter1_1355_1369_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1355_1369_Open_Text.style.display='none'; Codehighlighter1_1355_1369_Closed_Image.style.display='inline'; Codehighlighter1_1355_1369_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_1355_1369_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_1355_1369_Closed_Text.style.display='none'; Codehighlighter1_1355_1369_Open_Image.style.display='inline'; Codehighlighter1_1355_1369_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;CStackString(</span><span style="color: #0000ff">const</span><span style="color: #000000">&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">*</span><span style="color: #000000">&nbsp;str)&nbsp;:&nbsp;CMyBaseString(m_sMine,&nbsp;_size)&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_1355_1369_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_1355_1369_Open_Text"><span style="color: #000000">{&nbsp;assign(str);}</span></span><span style="color: #000000"><br /><img id="Codehighlighter1_1419_1420_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1419_1420_Open_Text.style.display='none'; Codehighlighter1_1419_1420_Closed_Image.style.display='inline'; Codehighlighter1_1419_1420_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedSubBlockStart.gif"><img style="display: none" id="Codehighlighter1_1419_1420_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_1419_1420_Closed_Text.style.display='none'; Codehighlighter1_1419_1420_Open_Image.style.display='inline'; Codehighlighter1_1419_1420_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedSubBlock.gif">&nbsp;&nbsp;&nbsp;&nbsp;CStackString()&nbsp;:&nbsp;CMyBaseString(m_sMine,&nbsp;_size)&nbsp;</span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_1419_1420_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_1419_1420_Open_Text"><span style="color: #000000">{}</span></span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" /></span><span style="color: #0000ff">private</span><span style="color: #000000">:<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">char</span><span style="color: #000000">&nbsp;m_sMine[_size];<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" />}</span></span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span><span style="color: #0000ff">int</span><span style="color: #000000">&nbsp;main()<br /><img id="Codehighlighter1_1468_1580_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1468_1580_Open_Text.style.display='none'; Codehighlighter1_1468_1580_Closed_Image.style.display='inline'; Codehighlighter1_1468_1580_Closed_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockStart.gif"><img style="display: none" id="Codehighlighter1_1468_1580_Closed_Image" onclick="this.style.display='none'; Codehighlighter1_1468_1580_Closed_Text.style.display='none'; Codehighlighter1_1468_1580_Open_Image.style.display='inline'; Codehighlighter1_1468_1580_Open_Text.style.display='inline';" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ContractedBlock.gif"></span><span style="border-bottom: #808080 1px solid; border-left: #808080 1px solid; background-color: #ffffff; display: none; border-top: #808080 1px solid; border-right: #808080 1px solid" id="Codehighlighter1_1468_1580_Closed_Text"><img alt="" src="http://www.cppblog.com/Images/dot.gif" /></span><span id="Codehighlighter1_1468_1580_Open_Text"><span style="color: #000000">{<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;CStackString</span><span style="color: #000000">&lt;</span><span style="color: #000000">20</span><span style="color: #000000">&gt;</span><span style="color: #000000">&nbsp;sTest(</span><span style="color: #000000">"</span><span style="color: #000000">hello</span><span style="color: #000000">"</span><span style="color: #000000">);<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">&nbsp;sTest.c_str()&nbsp;</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">&nbsp;endl;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">&nbsp;sTest.right(</span><span style="color: #000000">3</span><span style="color: #000000">)&nbsp;</span><span style="color: #000000">&lt;&lt;</span><span style="color: #000000">&nbsp;endl;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/InBlock.gif" />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/ExpandedBlockEnd.gif" />}</span></span></div>
<p></span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">于是通过基类</span>mybasestring<span style="font-family: 宋体">，各种不同类型的</span>template CStackString<span style="font-family: 宋体">就又联系在一块了。</span>Mybasestring<span style="font-family: 宋体">可看成定义了数据接口的基类，其子类的头部数据必须与它保持一致，嗯，很好。然后，在</span>mybasestring<span style="font-family: 宋体">中实现的各种功能，都可以用在</span>mystring<span style="font-family: 宋体">身上了，而</span>CStackString<span style="font-family: 宋体">中无须实现任何功能，它只负责在堆栈上分配内存。所以，</span>mybasestring<span style="font-family: 宋体">不仅可以用在堆栈上，还能用于堆上，只要再继续定义一个能在堆上分配内存的</span>mybasestring<span style="font-family: 宋体">的子类即可，然后都能相容于堆栈上的</span>CStackString<span style="font-family: 宋体">字符串。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">&#8230;&#8230;。</span> <span style="font-family: 宋体">经过一番努力，这个字符串类几乎包含了字符数组的一切基本功能，基本上可代替字符数组了。为了能够用到</span>STL<span style="font-family: 宋体">中的各种算法，再在其上增加一些返回迭代器的函数，好比</span>begin<span style="font-family: 宋体">，</span>end<span style="font-family: 宋体">，</span>rbegin<span style="font-family: 宋体">，</span>rend<span style="font-family: 宋体">，它们都很容易实现。还有，为了使用起来更加友好方便更具效率，貌似应该再实现一些全局操作符的重载运算；&#8230;&#8230;；好了，打住。如果打算将这个字符串很好地融入到</span>STL<span style="font-family: 宋体">中，需要做出更多的努力。原本只打算代替字符数组而已。代码在</span>VC2005<span style="font-family: 宋体">以上版本编译时，会出现一些警告，可以用</span>#pregma<span style="font-family: 宋体">的指令将其</span>disable<span style="font-family: 宋体">掉，或者使用其中的所谓的安全字符串操作函数。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">好不容易，终于就实现了一个基于堆栈上的字符串，它是窄字符的。别忘了，还有宽字符的字符串呢。这也没什么，只须将</span>mybasestring<span style="font-family: 宋体">重命名为</span>CMyBaseStringA<span style="font-family: 宋体">，</span>CStackString<span style="font-family: 宋体">改为</span>CStackStringA<span style="font-family: 宋体">。然后再分别实现与</span>CMyBaseStringA<span style="font-family: 宋体">与</span>CStackStringA<span style="font-family: 宋体">同样接口的</span>CMyBaseStringW<span style="font-family: 宋体">和</span>CStackStringW<span style="font-family: 宋体">。然后，再针对</span>UNICODE<span style="font-family: 宋体">，</span>Typedef<span style="font-family: 宋体">或</span>defined<span style="font-family: 宋体">一对</span>CMyBaseStringT<span style="font-family: 宋体">和</span>CStackStringT<span style="font-family: 宋体">。在这里，并不想模仿</span>STL<span style="font-family: 宋体">中的</span>string<span style="font-family: 宋体">或</span>MFC<span style="font-family: 宋体">中的</span>CString<span style="font-family: 宋体">那样子，</span>template<span style="font-family: 宋体">一个</span>basic_string<span style="font-family: 宋体">（</span>simple_string<span style="font-family: 宋体">），然后分别进行模板特化，近来越看越觉得这种模板特化的方式相当恶心，只是将代码搞得更加复杂，却没带来多大的好处。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">相比于其他的字符串实现，这个</span>mybasestring<span style="font-family: 宋体">不过是将内存分配拱手让人罢了。这样一来，就带来一些新的问题。首先，它要假设其子类给它分配了足够的内存，不过，在</span>C++<span style="font-family: 宋体">传统，经常假设用户分配了足够的内存；然后，因为脱离了内存管理，有一些功能自然也就无法实现出来了，</span>C<span style="font-family: 宋体">的字符串函数也还不是这样，当缓冲溢出时，该崩溃就还得崩溃。</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">再次向</span>C++<span style="font-family: 宋体">的无所不能顶礼膜拜。</span>C++<span style="font-family: 宋体">，你是电，你是光，</span> <span style="font-family: 宋体">你是唯一的神话，</span> <span style="font-family: 宋体">我只爱你，</span>You are my Super Star<span style="font-family: 宋体">！</span></p>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">再次声明，本字符串只为取代字符数组，至于其它的种种无理要求，均不在本座的考虑范围之内。</span></p>
<p></span></p><img src ="http://www.cppblog.com/huaxiazhihuo/aggbug/177993.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/huaxiazhihuo/" target="_blank">华夏之火</a> 2012-06-08 01:11 <a href="http://www.cppblog.com/huaxiazhihuo/archive/2012/06/08/177993.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>