使用 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
};
使用者代码混用 CString
和 CMyString
。 预编译标头中不包含“MyString.h”,并且使用 CString
不会让 CMyString
可见。
假设在单独的源文件 Source1.cpp 和 Source2.cpp 中使用 CString
和 CMyString
类。 在 Source1.cpp 中,使用 CMyString
和#include MyString.h。 在 Source2.cpp 中,使用 CString
,但不使用 #include MyString.h。 在这种情况下,链接器会报错:CStringT
被多重定义。 原因是,CString
既是从导出 CMyString
的 DLL 导入的,又是由编译器通过 CStringT
模板在本地实例化的。
若要解决此问题,请执行下列操作:
从 MFC90.DLL 导出 CStringA
和 CStringW
(以及必要的基类)。 与以前的 MFC 实现一样,包含 MFC 的项目将始终使用导出的 MFC DLL CStringA
和 CStringW
。
然后使用 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 中,将之前的 CString
、CStringA
和 CStringW
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
基类。CString
、CStringA
和CStringW
仅应在使用 MFC 共享 DLL 构建时标记为__declspec(dllexport/dllimport)
。 如果与 MFC 静态库链接,则不应将这些类标记为已导出;否则,在用户 DLL 内部使用CString
、CStringA
和CStringW
也会将CString
标记为已导出。