前言
上个周末在和个人同窗爬香山闲聊时,同窗说到STL中的string类曾经让他备受折磨,几年前他开发一个系统前对string类还比较清楚,而后随着程序的复杂度的加深,到了后期,他几乎对string类失去了信心和信任,他以为他对string类一头雾水。老实说,我几年前也有一样的痛苦(就是当我写下《标准C++类string的Copy-On-Write技术》以前的一段时间)。那时,我不得不研究那根本不是给人看的SGI出品的string类的源码,代码的可读性几乎为零,并且随着了解越深刻,就越以为C++的世界中处处都是陷阱和缺陷。愈来愈以为有时候那些类并不像本身所想象的那样工做。
为何会发生这样的状况呢?string类只是一个“简单”的类,若是是一些比较复杂的类呢?而这几年来,C++阵营声讨标准模板库中的标准string类愈演愈烈。C++阵营对这个“小子”的争讨就没有中止过。相信在下一个C++的标准出台时,string类会有一个大的变化。
了解
string
类
在咱们研究string类犯了什么毛病以前,还让我先说一下如何了解一个C++的类。咱们要了解一个C++的类,通常来讲,要从三个方面入手。
1、 意图(Intention)。知其然还要知因此然,string类的意图是什么?只有了解了意图,才知道它的思路。这是了解一个事物最重要最根本的部分。否则,你会发现它的行为并不会像你所指望的那样。string类的意义有两个,第一个是为了处理char类型的数组,并封装了标准C中的一些字符串处理的函数。而当string类进入了C++标准后,它的第二个意义就是一个容器。这两件事并不矛盾,咱们要需理解string的机制,须要从这两个方面考虑。
2、 规格(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()更没有必要存在。
3、 实现(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++将来之路都还有很长。