使用 CStringT 导出字符串类

过去,MFC 开发人员从 CString 派生出专门化自己的字符串类。 在 Microsoft Visual C++.NET (MFC 8.0) 中,CString 类被称为 CStringT 的模板类取代。 这有若干优势:

  • 允许在 ATL 项目中使用 MFC CString 类,而无需链接到更大的 MFC 静态库或 DLL。

  • 使用新的 CStringT 模板类,可以使用指定字符特征的模板参数来自定义 CString 行为,类似于 C++ 标准库中的模板。

  • 使用 CStringT 从 DLL 导出自己的字符串类时,编译器还会自动导出 CString 基类。 由于 CString 本身是模板类,因此在使用时,编译器可能会将其实例化,除非编译器知道 CString 是从 DLL 导入的。 如果你已将项目从 Visual C++ 6.0 迁移到 Visual C++.NET,你可能会看到多重定义的 CString 的链接器符号错误,因为从 DLL 导入的 CString 与本地实例化的版本发生冲突。 下面介绍了执行此操作的正确方法。

以下方案将导致链接器生成用于多重定义的类的符号错误。 假设从 MFC 扩展 DLL 导出 CString 派生型类 (CMyString):

// MyString.h
class AFX_EXT_CLASS CMyString : public CString
{
   // Your implementation code
};

使用者代码混用 CStringCMyString。 预编译标头中不包含“MyString.h”,并且使用 CString 不会让 CMyString 可见。

假设在单独的源文件 Source1.cpp 和 Source2.cpp 中使用 CStringCMyString 类。 在 Source1.cpp 中,使用 CMyString 和#include MyString.h。 在 Source2.cpp 中,使用 CString,但不使用 #include MyString.h。 在这种情况下,链接器会报错:CStringT 被多重定义。 原因是,CString 既是从导出 CMyString 的 DLL 导入的,又是由编译器通过 CStringT 模板在本地实例化的。

若要解决此问题,请执行下列操作:

从 MFC90.DLL 导出 CStringACStringW(以及必要的基类)。 与以前的 MFC 实现一样,包含 MFC 的项目将始终使用导出的 MFC DLL CStringACStringW

然后使用 CStringT 模板创建一个可导出的派生类,例如下面的 CStringT_Exported

#ifdef _AFXDLL
   #define AFX_EXT_CSTRING AFX_EXT_CLASS
#else
   #define AFX_EXT_CSTRING
#endif

template< typename BaseType, class StringTraits >
class AFX_EXT_CSTRING CStringT_Exported 
   : public CStringT< BaseType, StringTraits >
{
   // Reimplement all CStringT<> constructors and
   // forward to the base class implementation
};

在 AfxStr.h 中,将之前的 CStringCStringACStringW typedef 替换如下:

typedef CStringT_Exported< wchar_t, 
      StrTraitMFC< wchar_t > > CStringW;

typedef CStringT_Exported< char,
      StrTraitMFC< char > > CStringA;

typedef CStringT_Exported< TCHAR,
      StrTraitMFC< TCHAR > > CString;

有几个注意事项:

  • 不应导出 CStringT 自身,因为这将导致仅 ATL 项目导出专用 CStringT 类。

  • 使用 CStringT 中的可导出派生类可以最大限度地减少重新实现 CStringT 功能的必要性。 其他代码仅限于将构造函数转发到 CStringT 基类。

  • CStringCStringACStringW 仅应在使用 MFC 共享 DLL 构建时标记为 __declspec(dllexport/dllimport)。 如果与 MFC 静态库链接,则不应将这些类标记为已导出;否则,在用户 DLL 内部使用 CStringCStringACStringW 也会将 CString 标记为已导出。

CStringT 类

另请参阅

使用 CStringT
使用 CString