话说CString这个东西困扰了很多年轻人,因为它会引起诡异的编译错误,今天跟着我一起来深入ATL、WTL头文件,来把这个东西搞个清清楚楚。
【涉及到头文件】
ATL : atlstr.h, atlsimpstr.h
MFC : cstringt.h、afxstr.h
WTL : atlmisc.h
ATL和MFC有关剪不断理还乱的关系,为了更容易分析,我们先要理清这四个头文件间的关系。观察相互间观察顺序,可以得出:
atlstr.h引用cstringt.h,cstring引用atlsimpstr.h,afxstr.h引用cstringt.h,由此可得出下图:
【atlsimpstr.h 都干了什么】
1、定义了 CStringData 和 CNilStringData 类(前篇已述,此处略)
2、定义了 ChTraitsBase 类,类如下:
此类比较简单,功能是为不同的字符类型,建立新的统一的名称。另外,这里使用到了模板特化技术。
3、定义了CSimpleStringT类,此类的功能是,在ChTraitBase定义的统一名称的基础上,提供字符串一些基本的操作功能函数。
t_bMFCDLL可无视。另外,注意,此处所操作的字符串对象,都是CStringData(前篇已述)
总述:在atlsimpstr.h中,我们发现了3个令人感兴趣的东西,CStringData 是字符串操作单元,ChTraitBase提供字符串变量统一命名服务,而CSimpStringT是一个基于CStringData字符串操作单元的简易的CString(注意,只是简易,其中并未提供我们常用的CString中的那些函数)。
【cstringt.h 都干了什么】
1、定义了 ChTraitsCRT 类,如下:
该类继承atlsimpstr.h中的 ChTraitsBase 类,然后在父类提供服务的基础上,提供一系列字符串底层操作函数。思考:为什么此处用继承?而CSimpleStringT使用提typedef?
2、定义了 _MFCDLLTraitsCheck 类,如下:
这里再一次用到了模板特化技术,该类用于检测当前使用的StringTraits是ATL定义的还是MFC定义的。(ATL定义的叫StrTraitATL,MFC定义的叫StrTraitMFC,后面会提到)
3、定义了 CStringT 类,如下:
注意了,CStringT 就是CString的真身!前面说到,CSimpStringT操作CStringData字符串操作单元,提供基本的字符串操作功能,而CStringT继承CSimpleStringT,利用StringTraits,包装更高级的功能函数。而这些更高级的功能函数,就是我们通常调用CString时所使用到的那些函数。
【atlstr.h 都干了什么】
1、定义了 CAtlStringMgr 类。(前文已述)
2、定义了 ChTraitsOS 类,如下:
和 ChTraitCRT相对应,ChTraitsOS继承atlsimpstr.h中的 ChTraitsBase 类,然后在父类提供服务的基础上,提供一系列字符串底层操作函数。
和 ChTraitCRT相比,他俩提供的函数大部相关,少数不同,另外相同函数名的实现不一定相同。
3、定义了 StrTraitATL 类,如下:
和 StrTraitMFC相对应,该类提供字符串资源管理函数和CStringData内存管理器的ATL版。
4、定义了CSTRING,如下:
【afxstr.h 都干了什么】
1、定义了 StrTraitMFC,如下:
上文已述,和StrTraitATL相对,本类提供的功能实现都封闭在MFC中。
2、定义了CString,如下:
【atlmisc.h 都干了什么】
打开atlmisc.h,可以发现此文件只不过是定义了一些结构体,定义了一个CString的简易版。因为ATL CString依赖于MFC头文件,所以,如果在使用CSTRING而又不想加入过多其它文件时,WTL CSTRING提供了一个很好的选择。
所有文件都分析后,我们发现,atl、mfc、wtl分别定义了一个CString。其中atl和mfc中的CString都是基于CStringT,只有一个地方是不同的,即CStringT所引用的StringTrait(即StrTraitsATL 还是 StrTraitMFC)。而WTL CString 的实现是独立的,是一个真正的类。另外 ,StringTrait引用的Iterator是可选的(即底层字符串操作封装)。
最后,我们发现ATL和MFC中涉及CString的类关系有些复杂。
为什么ATL和MFC的CString头文件要搅在一起?
上文的分析有些杂乱,我们通过一张图来更加清晰的观察,如下:
上图中,用圈圈住的头文件表示ATL头文件,没被圈的代表MFC头文件。另外,在头文件旁边字符,表示各个头文件中实现的类。
现在让我们仔细观看,整个貌似平衡的设计中,其实有着很多的不平衡。我想问:
1、为什么MFC头文件cstringt.h要包含atl头文件atlsimpstr.h?为什么ATL头文件要包含cstringt.h?ATL搞ATL的,MFC搞MFC的,各不相干,起不更好?
2、ChTraitOS 放在 atlstr.h 中,而 ChTraitCRT 放在 cstringt.h中,他俩功能是类似的,逻辑上是相对的,为神马在文件层次上却不对齐,要如此的别扭?
话说中国人看待问题的时候,往往把某一大类看作一蹴而就的完美的整体。譬如Tencent很赚钱,很大,产品推出快,人们就认为Tencent内部有着良好的组织架构。这里我要告诉大家的是,任何大规模的东西,都是一点一滴积累起来的。ATL也是,MFC也是。这两个东西不是一个月、两个月的开发就成了现在的样子的,他们都是逐步叠加,逐步改进才成了今天这个强大的库的。扯了这些,是要告诉大家,要以变化、积累、动态来看待和分析事物的衍生。
好了,现在我们来看为什么微软会搞出这么别扭的结构。
话说,某一天ATL开发小组发现老是操作char,太低效,STL的string也不是很好用,于是乎,他们做了一个为ATL服务的简单的字符串类,名字叫 CSimpleStringT,通过typedef,定义成了 CSimpleString。
后天某一天,MFC的人也发现char和string超不好用,他们发现公司的ATL小组已经做了一个成熟CSimpleString类,于是MFC小组拿来直接使用。在使用中,MFC小组的人发现CSimpleString做的太简单了,有很多的功能,它都不提供,另外,类函数设计的也不好用。于是MFC小组的成员决定扩展CSimpleString,他们将扩展的代码写进cstringt.h中,另外抽离出对外的接口头文件afxstr.h,将定义放在了这。(到这里,我们发现了上面提到问题的发生点,MFC的头文件继承了ATL头文件)。
又到后来,MFC小组的某个成员(主程级)转到了ATL小组,来到ATL小组后,他发现,平日用惯了CString,现在要用CSimpleString真TM憋屈,于是他建议在ATL中自封装一个CStrnig。ATL小组发现,继然MFC的CString已经做得这么成熟稳定且好用了,自己没必要再另起炉灶,另外,他们发现cstringt.h是独立于MFC其它文件的,虽然属于MFC小组(MFC小组所写),但是引用的却都是atl的头文件,因此,引用cstringt.h不会融入MFC其它旁大的文件,因此可行。所以综合以上考虑,ATL小组直接继承于cstringt头文件,派生了自己的CString,放在了atlstr.h中Y*NW[祂\W[祂
T饄KN魰O(u&