使用标准 C++ 库实现的 WTL CString 类






4.25/5 (8投票s)
将 CodeProject 的 CStdString 插件化为 WTL::CString,并获得基于标准 C++ 库实现的 WTL CString 支持。
引言
在继续深入之前,您应该阅读 Joe O'Leary 的文章 《CString-clone 使用标准 C++:对 CString 的即插即用替代,构建于标准 C++ 库的 basic_string 模板之上》。您可以 在这里 获取更多关于 CStdString
的信息。
本文展示了最初旨在替代 MFC CString
的 CStdString
,如何在任何 WTL 项目中,包括使用 eVC4 SP4 或 VS2005 编译的 WinCE 项目中,都能顺利地替代 WTL:CString
。随附的 StdString.h 是从 2005-Jan-10 的最新原始版本改编而来,以完全支持 WTL:CString
和 WinCE。
CStdString[X] 类
当在您的项目中包含随附的 StdString.h 时,您将访问 CStdString
类,该类实际被定义为 CStdStringA
或 CStdStringW
,具体取决于您项目的 Unicode/MBCS 设置。
// FILE: StdString.h // Changes tagged with '// AR' copyright (c) Alain Rist 2006: // AUTHOR: Joe O'Leary (with outside help noted in comments) //... template <CT>class CStdStr : public std::basic_string<CT> //... typedef std::basic_string<TCHAR> tstring; //... typedef CStdStr<char> CStdStringA; // a better std::string typedef CStdStr<wchar_t> CStdStringW; // a better std::wstring typedef CStdStr<OLECHAR> CStdStringO; // almost always CStdStringW //... #ifdef UNICODE typedef CStdStringW CStdString; #else typedef CStdStringA CStdString; //...
因此,您有四个 CStdString[X]
类,它们都派生自 std::basic_string
。每个类都可以安全地与 std::string
或 std::wstring
进行 static_cast
转换。
同时支持两种字符宽度
这些类拥有所有可能需要的类型构造函数和赋值运算符,因此以下代码在 UNICODE 和 MBCS 构建中都能无错误地编译并产生相同的结果
CStdString ss("My standard string"); // as with WTL::CString no _T() needed CStdStringW sw = ss; // no WTL::CStringW and WTL::CStringA available CStdStringA sa = "àáâãäåæçèéêëìíîïñòóôõöøùúûüýþÿ"; sw = sa; // different character widths
请注意,抛弃 _T()
宏对于像我这样懒惰的输入者来说非常方便,但运行时会付出转换的代价(如果两侧类型不同)。
相同的 WTL::CString 接口
在随附的 StdString.h 中定义的 CStdString
暴露了所有 WTL::CString
的构造函数、运算符和成员函数,但有一个**区别**:接受字符和计数的 CStdString[X]
构造函数,其参数顺序是 (count, value),这与 WTL::CString(TCHAR ch, int nLength)
的声明顺序相反。
除了这个构造函数之外,您可以对 CStdString
调用任何 WTL::CString
成员、运算符以及 (LPCTSTR)
转换,例如
CStdString ss1(MAKEINTRESOURCE(IDR_MYID)); // construct from resource ID ss1.LoadString(IDS_MYSTRING); // load from resource CWindow(hWnd).SetDlgItemText(ID_MYCONTROL, ss1); // use (LPCTSTR)cast ss1.MakeLower(); // change content CStdString ss2 = ss1.Right(1); // extraction if (!ss1.IsEmpty()) // anything here? ss2.Replace(ss1[ss1.GetLength() -1], '?'); // just to play
CStdString 在 WTL 项目中的集成
要在 WTL 项目中使用 CStdString
,请将 WtlStdString.zip 文件解压到您的编译器可以通过尖括号访问的某个位置,然后
- 在您的项目中,
#include <StdString.h>
- VS2005 或 VC Express:在包含 atlbase.h 之前,
#define _CRT_SECURE_NO_DEPRECATE
以避免弃用警告。 - WinCE 项目:**仅**使用 eVC4 或 VS2005 **提供的标准 C++ 库**
- eVC4 SP4:设置 /GX 编译器标志:为 C++ 异常处理程序启用展开语义。
- VS2005:修补 <Microsoft Visual Studio 8>\VC\ce\include\comdef.h 的第 3240 行,使其变成
int nLen = lstrlen(m_pszMsg); // was ::lstrlen(m_pszMsg);
WTL CString 支持
WTL 7.0 及以上版本根据编译时条件、一些宏定义以及头文件的包含顺序,设计为支持 ATL::CString
或 WTL::CString
。ATL::CString
随 ATL 版本 7.0 一起提供;而 VC++7.0 及以上版本、eVC 和 VCExpress/Platform SDK 使用 ATL 3.0,因此不包含它。
当 _ATL_NO_AUTOMATIC_NAMESPACE
和 _WTL_NO_AUTOMATIC_NAMESPACE
都未定义时,将适用以下规则:
- 如果您在
#include<atlmisc.h>
之前定义#define _WTL_NO_CSTRING
,则WTL::CString
不会被编译。 - 如果您在
#include<atlapp.h>
之前#include<atlstr.h>
,您将获得ATL::CString
支持。 - 当支持
ATL::CString
时,不应编译WTL::CString
。 - 如果规则 2 不适用,您将在定义
#define _WTL_USE_CSTRING
之后获得WTL::CString
支持。 - 如果规则 2 不适用,您将在
#include<atlmisc.h>
之后获得WTL::CString
支持。
根据规则 2,atlapp.h 中定义的 _CSTRING_NS
宏将展开为 ATL
或 WTL
,并默默地将应用程序的 CString
映射到 WTL::CString
或 ATL::CString
。因此,我们可以这样编写代码:
// MyWindow.h // ... CFindFile ff; ff.FindFile(_T("C:*.*")); CListBox lb = GetDlgItem(ID_MYLB); // CString sText = ff.GetFileName(); // actually returns a _CSTRING_NS::CString lb.GetText(0, sText); // actually requires a _CSTRING_NS::CString& //
插件化 CStdString 作为 WTL::CString
要利用 CStdString
实现的 WTL::CString
**并**获得 WTL CString
支持,请将 WtlStdString.zip 文件解压到您的编译器可以通过尖括号访问的目录。随附的 atlssmisc.h 将
#include "StdString.h"
(注意是带引号的文件名),- 在
WTL
命名空间中定义或声明一个基于::CStdString
的CString
类, - 编译 atlmisc.h **不含**
WTL::CString
代码,但**包含**WTL::CString
支持(使用一些#define
技巧)。
请注意,atlssmisc.h 依赖于 atlmisc.h 和 atlapp.h 中的实现细节,因此请不要使用高于 7.5 版本的 WTL 进行编译。
对于现有使用 WTL::CString 的项目
将头文件中的 #include <atlmisc.h>
更改为 #include <atlssmisc.h>
,并检查 CStdString 在 WTL 项目中的集成 的要求。**仅此而已**。
尝试使用 WTL 示例 Alpha (Win32) 和 ImageView (WinCE) 进行实验。
对于新项目
将以下模板粘贴到 stdafx.h 中,并根据需要调整注释掉的行。
///////////////////////////////////////////////////////////// // Recommended stdafx.h layout //... // Change these values to use different versions //... #define _CRT_SECURE_NO_DEPRECATE // avoid StdString.h deprecation warnings #include <atlbase.h> //#include <atlstr.h> // uncomment for WTL ATL::CString support //#define _WTL_NO_CSTRING // uncomment for WTL ATL::CString support // or no WTL CString suppport //#define _WTL_USE_CSTRING // uncomment for CMenuT<> and CDCT<> // WTL::CString support #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> //#include <atlmisc.h> // uncomment AND comment next line for original // WTL:CString defintion #include <atlssmisc.h> // CStdString based WTL:CString definition and support //... /////////////////////////////////////////////////////////////
- 采用此确切布局,项目将获得
WTL::CString
支持,但CMenuT<>::GetMenuString()
和CDCT<>::GetTextFace()
除外,并且WTL::CString
将声明为:typedef ::CStdString CString;
。 - 如果您取消注释
#define _WTL_USE_CSTRING // ...
,项目将获得完整的WTL::CString
支持,并且WTL::CString
将定义为class CString: public ::CStdString
。
对于 WinCE 项目,请检查 CStdString 在 WTL 项目中的集成 的要求。
对于现有使用 ATL::CString 的项目
编辑 stdafx.h 以获得 WTL::CString
支持,并在 atlwin.h 之后**首先** #include <atlssmisc.h>
。检查 CStdString 在 WTL 项目中的集成 的要求。
例如,要将基于 CStdString
的 WTL::CString
插件化到出色的 Wizard97Test 示例中,并使用 VC71 编译器,请按如下方式编辑 stdafx.h:
// stdafx.h: Wizard97Test WTL sample //... // Includes #include "resource.h" #include <atlbase.h> /********************************* comment or cut from here #if (_ATL_VER >= 0x0700) #include <atlstr.h> #include <atltypes.h> #endif // WTL related preprocessor definitions #if (_ATL_VER >= 0x0700) #define _WTL_NO_WTYPES #define _WTL_NO_UNION_CLASSES #define _WTL_NO_CSTRING #endif ************************************** to here */ #define _WTL_NEW_PAGE_NOTIFY_HANDLERS #include <atlapp.h> extern CAppModule _Module; #include <atlwin.h> #include <atlcom.h> #include <atlssmisc.h> // instead of <atlmisc.h> //...
结论
关于 std::basic_string
、ATL::CString
和 C++ 中其他字符串对象实现的效率比较,存在一些争议。我不会在此争论。
使用基于 CStdString
的 WTL::CString
- 链接外部基于
std::basic_string
的代码的 WTL 应用程序将使用共享的字符串实现代码进行构建。 - 同时支持两种字符大小(例如,在 Unicode 应用程序中来自网络的 ANSI
char
s)更容易实现。 - 像我这样遵循 Fortran->Basic->C->C++->MFC->WTL 路径的老程序员,在使用
std::string
对象时,可能会觉得CString
接口更舒服。
再次感谢 Joe O'Leary,他完成了这里所有的实际工作,**祝您使用 WTL 愉快**!