CString这个超级老古董,还是有不少Windows程序员爱用的,无他,但手熟尔。记得早些年CodeGuru还是什么地方曾经有人专门搞了一个VC6的宏,用来把VC6中的CString提取出来脱离ATL/MFC单独使用。
MFC的CString默认用的
CCRTHeap,从当前DLL/EXE的CRT堆中分配、释放内存;而ATL的CString默认用的CWin32Heap,从进程默认堆中分配、释放内存,进程默认堆是进程中的全部DLL/EXE模块共享的,没有实现隔离。要实现一定的隔离的话,还是使用
CCRTHeap较好,或者自定义一个分配器。
如何将ATL的CString默认切换到
CCRTHeap呢?MSDN上给出了Basic、Advanced两种方式:
Implementation of a Custom String Manager (Basic Method)
Implementation of a Custom String Manager (Advanced Method)
第一种方式,需要在CString构造时传递内存分配器,这样在同一个DLL/EXE模块中,不同的CString对象可以用不同的内存分配器,通常我们并无这种需求。我们的需求是将自己所关心的CString对象都统一切换成某种内存分配器,如果要实现这点,用这种方式是可以的,但是可能比较麻烦,方法之一是从CStringT派生出子类,并重新定义子类的相关的全部构造函数。
第二种方式,微软是暗示可以操作CStringT内部隐藏的那个CStringData结构。通常我们不需要搞这种细节又底层的hack。
其实看一下ATL CString的代码,发现它提供了StringTraits扩展机制,通过这种机制,就可以统一切换CString的默认内存分配器,而且比较优雅。
CStringT的一部分构造函数使用了StringTraits的GetDefaultManager()方法:
template< typename BaseType, class StringTraits >
class CStringT
{
public:
CStringT() throw() :
CThisSimpleString( StringTraits::GetDefaultManager() )
{
}
VC头文件中的CString默认提供三种StringTraits,分别用于三种不同的编译场合:
第一种是StrTraitATL,用于ATL,头文件是<atlmfc\include\atlstr.h>:
template< typename _BaseType = char, class StringIterator = ChTraitsOS< _BaseType > >
class StrTraitATL :
public StringIterator
{
public:
static HINSTANCE FindStringResourceInstance(_In_ UINT nID) throw()
{
return( AtlFindStringResourceInstance( nID ) );
}
static IAtlStringMgr* GetDefaultManager() throw()
{
return CAtlStringMgr::GetInstance();
}
};
第二种和第三种是StrTraitMFC、StrTraitMFC_DLL,用于MFC的静态链接、动态链接,头文件是<atlmfc\include\afxstr.h>:
template< typename _CharType = char, class StringIterator = ATL::ChTraitsCRT< _CharType > >
class StrTraitMFC :
public StringIterator
{
public:
static HINSTANCE FindStringResourceInstance( UINT nID ) throw()
{
return( AfxFindStringResourceHandle( nID ) );
}
static ATL::IAtlStringMgr* GetDefaultManager() throw()
{
return( AfxGetStringManager() );
}
};
template< typename _CharType, class StringIterator>
class StrTraitMFC_DLL : public StringIterator
{
public:
static HINSTANCE FindStringResourceInstance( UINT nID ) throw()
{
return( AfxFindStringResourceHandle( nID ) );
}
static ATL::IAtlStringMgr* GetDefaultManager() throw()
{
return( AfxGetStringManager() );
}
};
显然,我们只需要基于
StrTraitATL改出一个自己的traits,并且采用
CCRTHeap就好。
(下面的代码假定编译时采用了/Zc:threadSafeInit-选项禁用了编译器自带的静态对象的线程安全初始化特性以支持Windows XP)
#include <atlstr.h>
//这个单例类,参考:https://zhuanlan.kanxue.com/article-649.htm
#include "CSingletonT.h"
template< typename _BaseType = char, class StringIterator = ChTraitsCRT< _BaseType > >
class StrTraitCRT :
public StringIterator,
public CSingletonT<StrTraitCRT<_BaseType, StringIterator>>
{
public:
static HINSTANCE FindStringResourceInstance(_In_ UINT nID) throw()
{
return(AtlFindStringResourceInstance(nID));
}
//CSingletonT<>保证本函数只执行一次,且是线程安全的。
bool InitOnce(void)
{
static CCRTHeap s_CrtHeap;
static CAtlStringMgr s_AtlStringMgr(&s_CrtHeap);
m_pAtlStringMgr = &s_AtlStringMgr;
return true;
}
IAtlStringMgr *GetStringManager()
{
return m_pAtlStringMgr;
}
static IAtlStringMgr* GetDefaultManager() throw()
{
StrTraitCRT<_BaseType, StringIterator>::Instance().Init();
return StrTraitCRT<_BaseType, StringIterator>::Instance().GetStringManager();
}
private:
CAtlStringMgr * m_pAtlStringMgr;
};
typedef CStringT<wchar_t, StrTraitCRT<wchar_t>> CCrtStringW;
typedef CStringT<char, StrTraitCRT<char>> CCrtStringA;
typedef CStringT<TCHAR, StrTraitCRT<TCHAR>> CCrtString;
//以下的宏,只是为了避免去批量修改老代码中已经大量使用的CString、CStringA、CStringW拼写。
//把这些都放在"stdafx.h"中即可。
#define CStringA CCrtStringA
#define CStringW CCrtStringW
#define CString CCrtString