65.9K
CodeProject 正在变化。 阅读更多。
Home

CString-clone 使用标准 C++

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (122投票s)

2001年5月29日

CPOL

4分钟阅读

viewsIcon

4147852

downloadIcon

14160

CString 的直接替换方案,基于标准 C++ 库的 basic_string 模板

引言

尽管我经常使用并欣赏标准 C++ 库,但我一直不喜欢它的字符串模板 - basic_string<>。有时,感觉设计者刻意使其难以使用。

另一方面,我一直很喜欢 MFC 的 CString 类的易用性。它会检查 NULL 指针,隐式转换为 const TCHAR*,并且拥有一些非常方便的成员函数(FormatLoad 等),让字符串编程变得轻而易举。但当然,我不想再使用 MFC 了。事实上,我也不想依赖任何专有库,因为我想要可移植性。

因此,我决定结合两者的优点,创造了

CStdString

这是一个类(实际上是模板实例化),它继承自 basic_string<TCHAR>。它为 basic_string 添加了完整的 CString API。你将获得 CString 的易用性,同时具有 100% 的 basic_string 兼容性。简而言之,CStdString 对象是一个 basic_string ,它(除了下面提到的极少数例外)同时也是 CString 的直接替换方案。最棒的是,这两个 API(basic_string CString)都广为人知且文档齐全。

几年前,我最初将这篇文章提交给了另一个代码网站(此处不具名 :))。我非常喜欢 CodeProject,所以也想在这里提交。在过去 4 年里,我几乎在我做的每一个专业项目中都使用了这个类。它证明是我写过的最有用的代码。它也经过了广泛的调试。希望你喜欢它。如果你有任何问题,请给我发电子邮件。我很乐意提供帮助。

我在此提供了一个简单的源应用程序来证明一些 CString 函数可以正常工作,但这只是象征性的。目前市面上使用 CString 和/或 basic_string 的示例项目数不胜数。

特点

  • CString 的直接替换方案(例外情况请参见下文)
  • 随时提供两个实例化 - wchar_t-based 版本 CStdStringWchar-based 版本 CStdStringACStdString 这个名字只是其中一个的 typedef
  • 在所有函数中安全地检查 NULL 字符串 指针输入(类似于 CString
  • 额外的构造函数和赋值运算符,可自动在宽(wchar_t-based)和窄(char-based)string 之间进行转换。
  • 隐式转换为 c_str()。C++ 标准委员会不喜欢这样,但我很喜欢。
  • 可在多个平台构建,包括 Windows、Unix 和 Linux。可与多种标准 C++ 库实现配合使用,包括 Dinkumware、GNU、CodeWarrior 和 STLPort。
  • Win32 构建提供了一些额外的功能,如 UNICODE/MBCS 转换宏(类似于 MFC)以及用于将 CStdString 对象持久化到 DCOM IStreams 和从 DCOM IStreams 中恢复的成员函数。
  • 不使用基类模板(basic_string)的任何实现细节
  • 派生模板不向 basic_string 添加任何成员数据,也不添加虚函数

关于这段代码,有几个问题我应该指出。

CString 兼容性

我未能完全重现 CString API。有两个函数 CStringbasic_string 都共享,但实现方式不同。在这些情况下,我认为最好让 CStdString 的行为类似于 basic_string(基类),而不是 CString。具体来说:

  • CStdString::operator[] 按值返回字符(与按引用返回字符的 CString 不同)
  • 以字符和计数为参数的构造函数,它们的顺序是(count, value),这与 CString 的声明顺序相反。这是 basic_string<> 所需的顺序,无法同时实现两个版本。

另外,还有两个 CString 函数我无法实现 - LockBufferUnlockBuffer

从 basic_string<> 派生

我编写的模板继承自 basic_string,这是一个没有虚析构函数的类模板。任何 C++ 入门教程都会告诉你,从没有虚析构函数的类派生是危险的。这可能导致未定义行为。所以如果你编写了以下代码(通过基类指针删除 CStdStringA),你将技术上得到未定义行为

// assign DERIVED object to  BASE pointer
std::string* pstr = new CStdStringA("Hi"); 

// delete  DERIVED through BASE class pointer -- UNDEFINED
delete pstr;   

我个人认为这不是什么大问题。我是说,你多久真正对 string 对象这样做一次?我很少(甚至从不)需要在堆上动态分配 string 对象。如果我需要,我也不会使用基类指针。所以如果你不这样做,你就不必担心。事实上,即使你以这种方式编码,我也怀疑 CStdString 会给你带来任何问题。我可以告诉你,至少在 Microsoft Visual C++ 中,即使是上面的代码也能正常运行,没有任何错误或内存泄漏。我怀疑其他编译器也会给你带来麻烦。然而,我的怀疑并不能改变 C++ 世界的现实。买者自负。

历史

  • 2011 年 12 月 7 日:更新了源代码。
© . All rights reserved.