来源:蜘蛛抓取(WebSpider)
时间:2016-04-29 05:39
标签:
catl是什么意思
剖析 ATL - CStringData - Tekkaman - 博客园
随笔 - 1204
要想理解好某个库的整个系统,我们需要准确地知道我们当前所阅读的代码位于哪一个库目录中。譬如WTL和ATL的头文件常常容易让初学者混淆。
这里我们要分析两个类,CAtlStringMgr(存在于atlstr.h中)和 CStringData(存在于atlsimpstr.h中)。
首先给出类关系图:(因画图水平有限,下图中CAtlStringMgr到IAtlStringMgr的继承线应该改为实现线)
1、使用到的设计模式
这里我看到一个,是策略模式。细看ATL中,有若干实现IAtlMemMgr接口的类,分别为各个实现类。CAtlStringMgr通过IAtlMemMGr指针来引用,因而可以随意使用各个内存管理类,拥有着良好的扩展性。
2、良好的功能拆分
CAtlStringMgr提供内存管理,CStringData提供共享管理。而更上层的字符串操作,跟这两个类无关,放在别处。(这个以后的文章给出分析)
3、良好的设计理念
类关系图如下,整个结构,所体现出来的设计目的是,提供一个字符串元数据的内存和共享的服务子系统。
4、技术细节
A)CStringData的实现中,把字符内存的管理内容放于起始地址前。(这招其实很老套)
B)所有的CNilStringData引用的都是CAtlStringMgr中的。
5、内存管理和共享管理的关系
搜索整个ATL头文件目录,发现只有一个全局CAtlStringMgr对象,再分析上图涉及的代码,可以发现CAtlStringMgr和CStringData是一对多的关系。所有的CStringData对象都由CAtlStringMgr来分配。
6、进一步分析
其实CAtlStringMgr应该改名为CAtlStringDataMemMgr,它只不是为IAtlMemMgr接口的再包装,提供一个专门为CStringData服务的内存分配器。
阅读(...) 评论()查看:2373|回复:1
VC.NET程序中的CString类应用解析,希望基础较差的朋友有所帮助吧!
& && &通过对CString类源代码的一些分析,使得大家对于这个类具有更深入的了解,避免在以后使用过程中出错。
在进入正题前,要说点题外话:为什么是VC.NET的CString类?
答案很简单,在M$将MFC从4.X(VC6)升级到7.X(VC.NET)开始,原来的CString被重新设计以便与ATL共用,并且支持了template特性。而由于VC6对于C++标准支持差得令人发指,成为众矢之的,且逐渐式微,将成明日黄花,故这里分析VC.NET的CString类
1. 亲子疑团之谁是我爹
在新的MFC体系中,CString并不是一个实际存在的类,实际上存在的是CSimpleStringT和CStringT,而我们常用的CString只是一个typedef-class.
这种变动,无疑让CString的身世显得扑朔迷离。
CString童鞋内牛满面,不禁高呼:你们到底谁TM是俺爹?
CString是谁呢?呃,在经过一番从CString到它祖宗十八代的调查后,我们发现:CString的爹,其实是CSimpleStringT.而CStringT,则是CString的前世
2.亲子疑团之混乱的关系
CSimpleString是CString体系变化的一个代表。
在过去,CString实际上是个独立的类,据说连万物之祖——CObject和它都没有半毛钱关系。CString一个人负责内存管理、字符串操作。etc
而到了新体系下,AFX小组为了更好地与ATL重用,将CString功能分拆成两块:基本的内存管理和缓存操作以及高级的字符串处理。CSimpleStringT便是负责前者。
至于CStringT,便继承自CSimpleStringT,然后封装了高级的字符串处理。
那么,我们为什么说CStringT是CString的前世呢?因为下面一行代码
view sourceprint?1 typedef ATL::CStringT& TCHAR, StrTraitMFC& TCHAR & & CString;
对于采用了模板的类来说,这种typedef并不少见,std::string也是一个typedef-class
PS1:CStringT最初在ATL中设计,并有了MFC支持版(二者应该区别不大),于是编译器分别提供了cstringt.h和atlstr.h
PS2:即使是现在,CSimpleStringT也是独立的类,与CObject仍然没有半毛钱关系
3.神秘的老爸——CSimpleStringT
CSimpleStringT在atlsimplestr.h中,而且是一个比较大的类。CSimpleStringT大多数函数都有文档说明了,我也没必要做无用功,所以我们来看一些没有文档说明的东西
CSimpleStringT为一些常用的字符/字符串类型定义了别名,便于使用
public:
& &&&typedef typename ChTraitsBase& BaseType &::XCHAR XCHAR;
& &&&typedef typename ChTraitsBase& BaseType &::PXSTR PXSTR;
& &&&typedef typename ChTraitsBase& BaseType &::PCXSTR PCXSTR;
& &&&typedef typename ChTraitsBase& BaseType &::YCHAR YCHAR;
& &&&typedef typename ChTraitsBase& BaseType &::PYSTR PYSTR;
& &&&typedef typename ChTraitsBase& BaseType &::PCYSTR PCYSTR;
ChTraitsBase类的信息如下:
template& typename BaseType = char &
class ChTraitsBase
public:
typedef char XCHAR;
typedef LPSTR PXSTR;
typedef LPCSTR PCXSTR;
typedef wchar_t YCHAR;
typedef LPWSTR PYSTR;
typedef LPCWSTR PCYSTR;
template&&
class ChTraitsBase& wchar_t &
public:
typedef wchar_t XCHAR;
typedef LPWSTR PXSTR;
typedef LPCWSTR PCXSTR;
typedef char YCHAR;
typedef LPSTR PYSTR;
typedef LPCSTR PCYSTR;
其实偌大一个的CSimpleStringT只有一个成员变量,你猜得没错,就是指向字封装的符串的指针
PXSTR m_pszData;
到这里你可能会有一个很大的疑问:既然CString是带引用计数的,那么这个东西被放到哪里了呢?
答案在CStringData这个结构上,CSimpleStringT并不直接管理字符串内存和引用计数这些,而是利用CStringData来管理
CStringData是一个struct,这意味着无论是member-varialbes还是member-functions,都是public的
struct CStringData
IAtlStringMgr* pStringMgr;&&// String manager for this CStringData
int nDataLength;&&// Length of currently used data in XCHARs (not including terminating null)
int nAllocLength;&&// Length of allocated data in XCHARs (not including terminating null)
long nRefs;& &&&// Reference count: negative == locked
// XCHAR data[nAllocLength+1]&&// A CStringData is always followed in memory by the actual array of character data
void* data() throw()
return (this+1);
void AddRef() throw()
ATLASSERT(nRefs & 0);
_AtlInterlockedIncrement(&nRefs);
bool IsLocked() const throw()
return nRefs & 0;
bool IsShared() const throw()
return( nRefs & 1 );
void Lock() throw()
ATLASSERT( nRefs &= 1 );
nRefs——;&&// Locked buffers can't be shared, so no interlocked operation necessary
if( nRefs == 0 )
nRefs = -1;
void Release() throw()
ATLASSERT( nRefs != 0 );
if( _AtlInterlockedDecrement( &nRefs ) &= 0 )
pStringMgr-&Free( this );}
void Unlock() throw()
ATLASSERT( IsLocked() );
if(IsLocked())
nRefs++;&&// Locked buffers can't be shared, so no interlocked operation necessary
if( nRefs == 0 )
nRefs = 1;
我们见到了熟悉的引用计数,而且CStringData并不直接操作nRef,而是通过几个API.这么做是基于多线程情况下,保证计数是真正正确的操作。
Lock和Unlock用于在不需要计数的情况下锁定和解锁计数
我们现在来看一个大头:Data
代码很简单,只有一行,return (this + 1)
但是你确定你自己明白这背后的意义么?
this+1指的不是this地址加上一个地址偏移量,而是this地址加上一个对象类型大小的偏移量
也就是说,这个返回的是紧跟CStringData首地址后面的首地址,其实就是CSimpleStringT的m_pszData的地址。
为什么?这得从CString在内存中的存储说起,简言之如下图所示
在CStringData里,还有一个IAtlStringMgr的接口指针
IAtlStringMgr是ATL接口(接口只定义名字,不定义行为,和。NET中类似)
__interface IAtlStringMgr
public:
// Allocate a new CStringData
CStringData* Allocate( int nAllocLength, int nCharSize ) throw();
// Free an existing CStringData
void Free( CStringData* pData ) throw();
// Change the size of an existing CStringData
CStringData* Reallocate( CStringData* pData, int nAllocLength, int nCharSize ) throw();
// Get the CStringData for a Nil string
CStringData* GetNilString() throw();
IAtlStringMgr* Clone() throw();
这个东西定义了字符串缓冲区的管理函数,在atlstr.h中有一个类CAtlStringMgr继承并实现了这个接口
01 // IAtlStringMgr
02 public:
03& &&&_Ret_opt_bytecap_x_(sizeof(CStringData) + nChars*nCharSize) virtual CStringData* Allocate( _In_ int nChars, _In_ int nCharSize ) throw()
04& &&&{
05& && && &size_t nTotalSize;
06& && && &CStringData* pData;
07& && && &size_t nDataBytes;
09& && && &nChars = AtlAlignUp( nChars + 1, 8 );&&// Prevent excessive reallocation.&&The heap will usually round up anyway.
11& && && &if( FAILED(::ATL::AtlMultiply(&nDataBytes, static_cast&size_t&(nChars), static_cast&size_t&(nCharSize))) ||
12& && && && & FAILED(::ATL::AtlAdd(&nTotalSize, static_cast&size_t&(sizeof( CStringData )), nDataBytes)))
13& && && &{
14& && && && & return NULL;
15& && && &}
16& && && &pData = static_cast& CStringData* &( m_pMemMgr-&Allocate( nTotalSize ) );
17& && && &if( pData == NULL )
18& && && &{
19& && && && & return( NULL );
20& && && &}
21& && && &pData-&pStringMgr = this;
22& && && &pData-&nRefs = 1;
23& && && &pData-&nAllocLength = nChars - 1;
24& && && &pData-&nDataLength = 0;
26& && && &return( pData );
27& &&&}
28& &&&virtual void Free( _Inout_ CStringData* pData ) throw()
29& &&&{
30& && && &ATLASSERT( pData-&pStringMgr == this );
31& && && &m_pMemMgr-&Free( pData );
32& &&&}
33& &&&_Ret_opt_bytecap_x_(sizeof(CStringData) + nChars*nCharSize) virtual CStringData* Reallocate( _Inout_ CStringData* pData, _In_ int nChars, _In_ int nCharSize ) throw()
34& &&&{
35& && && &CStringData* pNewData;
36& && && &ULONG nTotalSize;
37& && && &ULONG nDataBytes;
39& && && &ATLASSERT( pData-&pStringMgr == this );
40& && && &nChars = AtlAlignUp( nChars+1, 8 );&&// Prevent excessive reallocation.&&The heap will usually round up anyway.
42& && && &if( FAILED(::ATL::AtlMultiply(&nDataBytes, static_cast&ULONG&(nChars), static_cast&ULONG&(nCharSize))) ||
43& && && && & FAILED(::ATL::AtlAdd(&nTotalSize, static_cast&ULONG&(sizeof( CStringData )), nDataBytes)))
44& && && &{
45& && && && & return NULL;
46& && && &}
47& && && &pNewData = static_cast& CStringData* &( m_pMemMgr-&Reallocate( pData, nTotalSize ) );
48& && && &if( pNewData == NULL )
49& && && &{
50& && && && & return NULL;
51& && && &}
52& && && &pNewData-&nAllocLength = nChars - 1;
54& && && &return pNewData;
55& &&&}
56& &&&virtual CStringData* GetNilString() throw()
57& &&&{
58& && && &m_nil.AddRef();
59& && && &return &m_nil;
60& &&&}
61& &&&virtual IAtlStringMgr* Clone() throw()
62& &&&{
63& && && &return this;
64& &&&}
65 protected:
66& &&&IAtlMemMgr* m_pMemMgr;
67& &&&CNilStringData m_nil;
很多,我们主要就看一个Allocate吧,其他的其实类似的
Allocate其实就两部分:计算实际需要的长度;分配内存
nChars = AtlAlignUp( nChars + 1, 8 ); // Prevent excessive reallocation. The heap will usually round up anyway.
这里是内存字节按对齐(其实就是申请的大小是8的倍数),AtlAlignUp代码如下
1 template& typename N &
2 inline N WINAPI AtlAlignUp( N n, ULONG nAlign ) throw()
4& &&&return( N( (n+(nAlign-1))&~(N( nAlign )-1) ) );
位运算呵呵~内存分配的代码是
1 pNewData = static_cast& CStringData* &( m_pMemMgr-&Reallocate( pData, nTotalSize ) );
3 IAtlMemMgr是ATL的内存管理接口,实现如下(在atlmem.h中)
5& &&&virtual void* Allocate( size_t nBytes ) throw()
6& &&&{
7& && && &return( malloc( nBytes ) );
8& &&&}
看到了啥?malloc!多熟悉啊,直接就是CRT了~
其他的诸如Reallocate、free什么的都大同小异
4.几个无文档的成员函数
我们来看几个CSimpleStringT的protected-functions.这些函数显然都是不会有文档说明的
1 void Attach( _In_ CStringData* pData ) throw()
2& &&&{
3& && && &m_pszData = static_cast& PXSTR &( pData-&data() );
4& &&&}
Attach用来设置引用字符串内存时的指针
1 CStringData* GetData() const throw()
2& &&&{
3& && && &return( reinterpret_cast& CStringData* &( m_pszData )-1 );
4& &&&}
GetData返回这个CSimpleStringT对象的CStringData数据。至于指针问题,还记得CString内存分布么?
01 ATL_NOINLINE void Fork( _In_ int nLength )
02& &&&{
03& && && &CStringData* pOldData = GetData();
04& && && &int nOldLength = pOldData-&nDataLength;
05& && && &CStringData* pNewData = pOldData-&pStringMgr-&Clone()-&Allocate( nLength, sizeof( XCHAR ) );
06& && && &if( pNewData == NULL )
07& && && &{
08& && && && & ThrowMemoryException();
09& && && &}
10& && && &int nCharsToCopy = ((nOldLength & nLength) ? nOldLength : nLength)+1;&&// Copy '\0'
11& && && &CopyChars( PXSTR( pNewData-&data() ), nCharsToCopy,
12& && && && & PCXSTR( pOldData-&data() ), nCharsToCopy );
13& && && &pNewData-&nDataLength = nOldLength;
14& && && &pOldData-&Release();
15& && && &Attach( pNewData );
16& &&&}
Fork的意图很明显:当引用的CString对象发生改变时,需要有自己的独立空间,并从原来的共享中分离。
其他的都大同小异,大家自己去分析即可
5.扑朔迷离的前世——CStringT
其实CStringT是一个比较简单的类,说穿了就是一句话:
CStringT是CSimpleStringT加上一层封装了大多数CRT字符串操作函数的类
CStringT的SRC在cstringt.h里
一个最大的特点是:构造函数非常多,这个适应了各种情况下CString的产生
还有一个特点是,CStringT并不直接在各种成员函数中使用CRT函数,而是在中间加了一层——ChTraitsCRT.通过这个类来访问CRT
ChTraitsCRT封装了几乎所有的CRT字符串操作函数,当然,都是static的,可以独立于实际对象存在。
M$这么做的原因估计是,后期维护方便,因为ChTraitsCRT相当于一个CRT字符串函数的接口,如果需要什么变动,可以保持调用接口代码不变而去修改接口代码
CStringT还自动帮你处理了MBCS和UNICODE问题,通过类模板的类型制定和两中字符类型间的转换API来保证结果的正确性
如果有更多原创内容就好了。而不是转载。日常工作问题积累(34)
&&&&&& 这个问题之前没碰到过,也没听过,有个朋友在群里提了这个问题,没人解答,对于问题不知道答案,我是睡不着觉的。
&&&&&&&这篇博文对于我对CString安全问题的理解提到了很大的帮助,在网上查了很多相关问题,但是光看一篇文章还是不能完全明白,我想弄明白:(1.)导致安全问题的根源在哪里?为什么会出现?
【问题分析】:&&&&&&&&
&&&&&&&参考上面博文中的CString的根源代码,正如里面所说,在Debug和Release模式下对于CString对象处理方式是不一样的,Debug模式下是new出来的,而Release模式下是定长内存。
&&&&&&& 里面演示了由于线程A没能够执行LeaveCriticalSection(&m_protect);线程B再次唤醒时就会发生堵塞。但是为什么会堵塞?我们得查一下EnterCriticalSection(&m_protect);这个函数的功能是什么
函数 EnterCriticalSection 和 LeaveCriticalSection 声明如下:
WINBASEAPIVOIDWINAPIEnterCriticalSection(
__inout LPCRITICAL_SECTION lpCriticalSection);
是多线程中用来确保同一时刻只有一个线程操作被保护的数据的操作函数,相关的多线程数据操作函数还有:
InitializeCriticalSection(&cs);//初始化临界区EnterCriticalSection(&cs);//进入临界区//操作数据MyMoney*=10;//所有访问MyMoney变量的程序都需要这样写Enter.. Leave...LeaveCriticalSection(&cs);//离开临界区DeleteCriticalSection(&cs);//删除临界区
多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。
为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。
这个过程实际上是通过限制有且只有一个函数进入CriticalSection变量来实现代码段同步的。简单地说,对于同一个 CRITICAL_SECTION,当一个线程执行了EnterCriticalSection而没有执行LeaveCriticalSection的时 候,其它任何一个线程都无法完全执行EnterCriticalSection而不得不处于等待状态。
CFixedAlloc::Alloc使用CPlex::Create完成空间申请,不过最显眼的是整个分配过程都在关键代码段内。与之对应的是空间的释放也同样在关键代码段内。在没看到源码前,我怎么也想不到CString会在这个地方使用了关键代码段。这意味着只要使用了CString,各个线程即便毫无关联,也会由此隐晦的联系起来。想像一下这种情况,两个线程都使用了CString对象,由于某种原因,线程A在CString的分配(或者释放)的过程中发生了意外,没有能够调用LeaveCriticalSection,那么线程B在使用CString的时候就会被“意外”的阻塞。
【解决办法】:
strstate.Format(&正在解密,请稍后... (共 %d 张地图)&,p-&m_countmap);
CWin32Heap stringHeap( HEAP_NO_SERIALIZE, 0, 0 );
CAtlStringMgr stringMgr( &stringHeap );
CString strstate(&stringMgr );
strstate.Format(&正在解密,请稍后... (共 %d 张地图)&,p-&m_countmap);
为字符串数据自定义内存分配方案的最简单的方式是使用 ATL 提供的 CAtlStringMgr 类,但您需要自己提供内存分配例程。
CAtlStringMgr 的构造函数采用单一参数:即指向 IAtlMemMgr 对象的指针。IAtlMemMgr 是提供到堆的一般接口的抽象基类。通过IAtlMemMgr 接口,CAtlStringMgr 分配、重新分配和释放用于存储字符串数据的内存。您既可以自已实现IAtlMemMgr 接口,也可以使用由
ATL 提供的五个内存管理器类之一。ATL 提供的内存管理器只包装现有的内存分配功能:
CCRTHeap&& 包装标准 CRT 堆功能(malloc、free 和 realloc)CWin32Heap&& 使用 HeapAlloc、HeapFree 和HeapRealloc 包装 Win32 堆句柄CLocalHeap&& 包装 Win32 API:LocalAlloc、LocalFree 和LocalReallocCGlobalHeap&& 包装 Win32 API:GlobalAlloc、GlobalFree 和 GlobalReallocCComHeap&& 包装 COM 任务分配器 API:CoTaskMemAlloc、CoTaskMemFree 和 CoTaskMemRealloc
要进行字符串内存管理,最有用的类是 CWin32Heap,因为它使您能够创建多个独立的堆。例如,如果使用仅用于字符串的独立堆,可进行以下操作:
//Declare a thread-safe, growable, private heap with initial size 0
CWin32Heap g_stringHeap( 0, 0, 0 );
// Declare a string manager that uses the private heap
CAtlStringMgr g_stringMgr( &g_stringHeap );
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:36297次
积分:1362
积分:1362
排名:第19783名
原创:97篇
转载:15篇
(1)(1)(3)(15)(8)(30)(5)(29)(20)