前言
 
上个周末在和我的同学爬香山闲聊时,同学说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,然后随着程序的复杂度的加深,到了后期,他几乎对string类失去了信心和信任,他觉得他对string类一头雾水。老实说,我几年前也有同样的痛苦(就是当我写下《标准C++类string的Copy-On-Write技术》之前的一段时间)。那时,我不得不研究那根本不是给人看的SGI出品的string类的源码,代码的可读性几乎为零,而且随着了解越深入,就越觉得C++的世界中到处都是陷阱和缺陷。越来越觉得有时候那些类并不像自己所想象的那样工作。
 
为什么会发生这样的情况呢?string类只是一个“简单”的类,如果是一些比较复杂的类呢?而这几年来,C++阵营声讨标准模板库中的标准string类愈演愈烈。C++阵营对这个“小子”的争讨就没有停止过。相信在下一个C++的标准出台时,string类会有一个大的变化。
 
 
了解string
 
在我们研究string类犯了什么毛病之前,还让我先说一下如何了解一个C++的类。我们要了解一个C++的类,一般来说,要从三个方面入手。
 
一、            意图(Intention)。知其然还要知所以然,string类的意图是什么?只有了解了意图,才知道它的思路。这是了解一个事物最重要最根本的部分。不然,你会发现它的行为并不会像你所期望的那样。string类的意义有两个,第一个是为了处理char类型的数组,并封装了标准C中的一些字符串处理的函数。而当string类进入了C++标准后,它的第二个意义就是一个容器。这两件事并不矛盾,我们要需理解string的机制,需要从这两个方面考虑。
 
二、            规格(Specification)。我们注意到string类有太多的接口函数。这是目前C++阵营中声讨其最重的话题。一个小小的string类居然有106个成员接口函数。居然,C++标准委员会会容忍这种“ugly”的事情的发生?目前的认为导致“C++标准委员会脑子进水”的主流原因有两点,一个是为了提高效率,另一个是为了常用的操作。
 
1)让我们先来看效率,看看string类中的“==”操作符重载接口:
bool operator==(const string& lhs, const string& rhs);
 
bool operator==(const string& lhs, const char* rhs);
bool operator==(const char* lhs, const string& rhs);
 
头一个很标准,而后两个似乎就显得没有必要了。如果我们调用:(Str == “string”)如果没有后面两个接口,string的构造函数会把char*的 ”string”转成string对象,然后再调用第一个接口,也就是 operator==(str, string(“string”))。如此“多余”的设计只能说是为了追求效率,为了省去调用构造/析构函数和分配/释放内存的时间(这会节省很多的时间)。在后面两个接口中,直接使用了C的strcmp函数。如此看来,这点设计还是很有必要的。string类中有很多为了追求效率的算法和设计,比如:Copy-on-Write(参看我的《标准C++类string的Copy-On-Write技术》)等。这些东西让我们的string变得很有效率,但也带来了陷阱。如果不知道这些东西,那么当你使用它的时候发生不可意料的问题,就会让你感到迷茫和不知所措。
 
2)另一个让string类拥有这么庞大的接口的原因是常用的操作。比如string类的substr(),这是一个截取子字符串的函数。其实这个函数并不需要,因为string有一个构造函数可以从别的string类中指定其起始和长度构造自己,从而实现这一功能。还有就是copy()函数,这也是一个没有必要的函数,copy这个函数把string类中的内容拷贝到一个内存buffer中,这个方法实践证明很少有人使用。可能,1)为了安全起见,需要有这样一个成员把内容复制出去;2)设计者觉得copy要比strcpy或是memcpy好写也漂亮很多吧。copy()比起substr()更没有必要存在。
 
三、            实现(Implementation。C++标准并没有过多的干预实现。不同的产商会有不同的实现。不同的产商会考虑标准中的一件事情是否符合市场的需要,并要考虑自己的编译器是否有能够编译标准的功能。于是,他们总是会轻微或是颠覆地修改着标准。C++在编译器的差异是令人痛苦和绝望的,如果不了解具体的实现,在你使用C++的时候,你也会发现它并不像你所想象的那样工作。
 
只有从上述三个方面入手,你才能真正了解一个C++类,而你也才能用好C++。C++高手们都是从这样的三个方面剖析着C++现实中的各种类,并以此来验证C++类的设计。
 
 
 
String类犯了什么错?
 
string类其实挺好的。它的设计很有技术含量。它有高的效率、运行速度快、容易使用。它有很充足的接口可以满足各式各样的法,使用起来也很灵活。
 
然而,这个string类似乎有点没有与时俱进,它现在的设计还保持着10年以前的样子,10年来,整个技术环境都出现很多变革,人们也在使用C++的过程中得到了许许多的经验。比如:现在的几乎所有的程序都运行在一个多进/线程的环境中,而10年前主流还只是单进/线程的应用,这是一个巨大的变化。近几年来,C++阵营也在实践中取得了很多的经验,特别是模板技术,而我们的STL显然没能跟上脚步。
 
       首当其冲的是string类,目前C++阵营对string类的声讨主要集中在下面几个方面。对于下面的这些问题,C++阵营还是争论不休。不过,作为一个好的程序员,我们应该在我们的设计和编程中注意一下这些方面。
 
1)目前的标string类有106个接口函数(包括构造和析构函数),如果考虑上默认参数,那么就一共有134不同的接口。其中有5个函数模板还会产生无穷多个各种各样的函数。还有各种各样的性能上的优化。在这么从多的成员函数中,很多都是冗余不必要的。最糟糕的是,众多程序员们并不了解它们,导到要么浪费了它的优势,要么踩中了其中的陷阱
 
2)很多人认为string类提供的功能中,该有的没有,已有的又很冗余。string类在同一个功能上实现了多次,而有一些功能却没有实现。如:大小写不区分的比较,宽行字符(w_char)的支持,和字符char型数据的接口等等。
 
3)作为一个STL的容器,string类和的设计和其它容器基本一样。这些STL都不鼓励被继承。因为STL容器的设计者们的几乎都遗忘了虚函数的使用,这样阻止了多态性,也许,这也是一个为了考虑效率和性能的设计。对于STL容易这样的设计,大多数人表示接受。但对于string类,很多人认为,string类是一个特殊的类,考虑到它被使用的频率,string类应该得到特殊的照顾。
 
4)还有很多人认为标准的strng类强行进行动态内存分配(malloc),那怕是一个很短的字符串。这会导致内存碎片问题(我们知道内存碎片问题会让malloc很长时间才能返回,由于降低了整个程序的性能。而关于内存碎片问题,这是一个很严重的问题,目前除了重启应用程序,还没有什么好的方法)。他们认为,string类的设计加剧了内存碎片问题。他们希望string类能够拥有自己的栈上内存来存放一些短字符串,而不需要总是去堆上分配内存。(因为用于string类的字符串长度几乎都不会很长)
 
5)很多string类的实现,都采用了Copy-On-Write(COW)技术。虽然这是一个很有效率的技术。但是因为内存的共享,导致了程序在“多线程”环境中及容易发生错误,如果分别在两个线程中的string实例共享着同一块内存,很有可能发生潜在的内存问题(参看我的《标准C++类string的Copy-On-Write技术》最后一节示例)。目前这一技术很有可能从下一版本的标准中移去。(而一些新版本的STL都不支持COW了,如Microsoft VC8.0下的STL)
 
6)标准的string类不支持policy-base技术的错误处理。string遇到错误时,只是简单地抛出异常。虽然这是一个标准,但有一些情况下不会使用异常(GCC –fno-exception)。另外,不可能要求所有的程序都要在使用string操作的时候try catch,一个比较好的方法是string类封装有自己的error-handling函数,并且可以让用户来定义需要使用哪一种错误处理机制。
 
 
由于string类的的种种不如人意,特别是106个接口函数让许多人难以接受,有很多人都在写适合自己的string类。但总体来说,标准的string类是一个好坏难辨的类。无论你是否是一个高级咨深的程序员,你都会用到它。它和整个C++一样,都是一把双刃剑,在大多数情况下,它还是值得我们信赖。
 
目前,对string类的争论有很多很多。C++标准委员会也说:“The C++ Standard is the best we could make it. If we could have agreed on how to make it better, then we would have made it better.”在C++这么一个混乱的领地,虽然STL并不完美,但对比起来说,他还是显得那么地漂亮和精致辞。
 
 
后记
 
又到了关于C++文章结束的时候,我还是要老调重弹。C++这个世界是很复杂很危险的。单单本文章中的一个小小的string类就能引起这么多的讨论,何况是别的类?
 
让我们大胆地怀疑一下, C++是否真是一种高级的语言?目前的C++需要使用他的人有相当的专业技术水平,而寄希望于人的高水平看来是和技术的发展背道而驰的。好的一门开发语言是让人可以容易方便地把更多的精力集中在业务逻辑性上。而C++这门语言却需要技术人员比以住的C有着更为高深和专业的技术知识和能力。
 
是我们对string的“无知”搞乱了我们的程序,还是string这个类的设计把我们的程序搞乱了?是C++自己把软件开发搞得一团混乱?还是使用C++的人把其搞得一团混乱?好像兼而有之,目前我们无法定论,但我们知道,无论是C++标准委员会,还是开发人员,大家的C++未来之路都还有很长。

STL 的string类怎么啦?的更多相关文章

  1. STL:string类中size()与length()的区别

    结论是:两者没有任何区别 解释: C++Reference中对于两者的解释: 两者的具体解释都一模一样: 理解: length是因为C语言的习惯而保留下来的,string类最初只有length,引进S ...

  2. C++ STL介绍——String类

    目录 1.简介 2.string类成员函数汇总 3.String类的构造函数以及析构函数 4.获取字符串长度 5.获取字符串元素 6.字符串比较方法 7.字符串输入输出 8.字符串查找函数 1.简介 ...

  3. STL之string类详解

    通过在网站上的资料搜集,得到了很多关于string类用法的文档,通过对这些资料的整理和加入一些自己的代码,就得出了一份比较完整的关于string类函数有哪些和怎样用的文档了!下面先罗列出string类 ...

  4. STL库中string类内存布局的探究

    在STL中有着一个类就是string类,他的内存布局和存储机制究竟是怎么样的呢? 这就是建立好的string 可以看出,图中用黄色框框标注的部分就是主要区域 我们用来给string对象进行初始化的字符 ...

  5. C++标准模板库Stand Template Library(STL)简介与STL string类

    参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...

  6. C++STL(一)——string类

    STL--string类 初始化 string的赋值 string的连接 string的性质描述 遍历 字符指针和string的转化 查找.替换.交换 字符串的拼接 区间删除. 插入 大小写转换 比较 ...

  7. 自定义String类,并且实现在STL容器中添加自定义的类型

    13.44 编写标准库string类的简化版本,命名String.你的类应该至少有一个默认构造函数和一个接受C风格字符串指针参数的构造函数.使用allocator为你的String类分配所需内存. 1 ...

  8. 洛谷 P1308 统计单词数【string类及其函数应用/STL】

    题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给 ...

  9. C++——string类和标准模板库

    一.string类 1.构造函数 string实际上是basic_string<char>的一个typedef,同时省略了与内存管理相关的参数.size_type是一个依赖于实现的整型,是 ...

随机推荐

  1. 35个让人惊讶的CSS3动画效果

    1. Pure CSS Coke Can 2. Colorful Clock 3. jQuery DJ Hero 4. Animated Pricing Column 5. Slick jQuery  ...

  2. JAVA 报错exe4j中this executable was created with an evaluation 怎么办

    如果使用未破解注册的exe4j打包JAR文件为EXE,运行EXE的时候就会出现下面的提示   打开exe4j软件,Change License或者是输入序列号,然后用注册机算一个注册码即可  

  3. 基于FPGA的超声波测距(一)

    硬件平台:CycloneII EP2C5Q208C8+DYP-ME007 1:超声波原理 DYP-ME007超声波测距模块可提供3cm--3.5m的非接触式距离感测功能,图1为DYP-ME007外观, ...

  4. 微信小程序 - this.triggerEvent()

    组件之间数据通信 调用组件wxml bind+组件内的方法名 <dialog bindclose="handleClose" bindopen="handleOpe ...

  5. Volume Shadow Copy Service(VSS)如何工作

    VSS卷影拷贝服务其实不是一项新技术了,在2003年前后发布的Windows 2003和Windows XP SP1都提供了对VSS的支持.最近几年微软的一线产品对VSS支持的越来越多,包括Excha ...

  6. 转:nginx基础概念(connection)

    在nginx中connection就是对tcp连接的封装,其中包括连接的socket,读事件,写事件.利用nginx封装的connection,我们可以很方便的使用nginx来处理与连接相关的事情,比 ...

  7. bat脚本禁用和开启本地连接

    netsh interface set interface name="本地连接" admin=disabled //禁用本地连接 netsh interface set inte ...

  8. Communication API

    Stingray WIKI Stingray javascript Communication 主要的三个方法: Communication.LinkRequest - 页面跳转,调用比较简单,直接参 ...

  9. java线程同步实的现方式

    为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他 ...

  10. Delphi 7调用C语言编写的DLL

    DLL一定是要C语言导出的符号,也就是extern “C”. 当然,我们都知道DLL调用分为动态调用和静态调用. 动态调用的一般思想为,先LoadLibrary那个你想加载的DLL,然后通过GetPr ...