CString 全面用法与最佳实践(MFC / ATL / Win32 / 全版本 VC)
CString 是 Windows C++ 桌面开发中使用最广泛的字符串类之一,广泛存在于 MFC、ATL、Win32 封装层、COM 接口 中。即使在大量引入 std::string / std::wstring 的现代 C++ 项目中,CString 仍然具有不可替代的工程价值。
本文从 基础用法 → 高级接口 → 跨库协作 → 性能与陷阱 → 现代实践,系统总结 CString 的全部常用能力。
一、CString 体系结构概览
1. 类型层级
1 2 3
| typedef CStringT<TCHAR, StrTraitMFC<TCHAR>> CString; typedef CStringT<char, StrTraitMFC<char>> CStringA; typedef CStringT<wchar_t, StrTraitMFC<wchar_t>> CStringW;
|
本质:CString 是 CStringT 的 typedef
2. 字符集关系
| 编译模式 |
CString 实际类型 |
| Unicode |
CStringW |
| ANSI |
CStringA |
1 2 3 4 5
| #ifdef UNICODE
#else
#endif
|
3. 内存模型(核心特性)
- 引用计数
- 写时拷贝(Copy-On-Write)
- 小字符串优化(部分实现)
1 2 3
| CString a = _T("hello"); CString b = a; b += _T("!");
|
二、CString 创建与初始化(全方式)
1. 默认构造
2. 字面量 / 指针
1 2 3
| CString s1(_T("Hello")); CString s2 = L"Unicode"; CString s3("ANSI");
|
3. 拷贝 / 移动(现代 VC)
1 2 3
| CString a(_T("A")); CString b(a); CString c(std::move(a));
|
4. 从字符数组
1 2
| TCHAR buf[] = _T("Buffer"); CString s(buf);
|
5. 从单字符 / 重复字符
1 2
| CString s(_T('A')); CString s2(_T('*'), 10);
|
三、CString 与数值互转(完整)
1. 数值 → CString
1 2 3 4 5 6
| int i = 10; double d = 3.14159;
CString s; s.Format(_T("%d"), i); s.Format(_T("%.3f"), d);
|
2. CString → 数值
1 2
| int i = _ttoi(s); double d = _ttof(s);
|
3. 安全替代(现代)
1
| int i = std::stoi(std::wstring(s));
|
1 2
| CString s; s.Format(_T("ID=%d, Name=%s"), 1, _T("Tom"));
|
1
| s.AppendFormat(_T(", Age=%d"), 18);
|
五、拼接 / 修改操作
1. 拼接
1 2 3
| s += _T(" World"); s.Append(_T("!")); s.AppendChar(_T('?'));
|
2. 插入 / 删除 / 替换
1 2 3
| s.Insert(5, _T(",")); s.Delete(5, 1); s.Replace(_T("old"), _T("new"));
|
六、长度 / 访问 / 遍历
1 2 3 4 5 6 7
| int len = s.GetLength(); bool empty = s.IsEmpty();
for (int i = 0; i < len; ++i) { TCHAR ch = s[i]; }
|
七、比较与排序
1 2 3
| s1 == s2; s1.Compare(s2); s1.CompareNoCase(s2);
|
八、查找与定位
1 2 3
| s.Find(_T("abc")); s.ReverseFind(_T('a')); s.FindOneOf(_T("xyz"));
|
九、子串截取
1 2 3
| s.Left(3); s.Right(4); s.Mid(2, 5);
|
十、大小写 / 规范化
1 2 3
| s.MakeUpper(); s.MakeLower(); s.MakeReverse();
|
十一、Trim / 清理字符
1 2 3 4
| s.Trim(); s.TrimLeft(); s.TrimRight(); s.Trim(_T(" \t\r\n"));
|
十二、CString 与 C 接口交互(重点)
1. CString → LPCTSTR
2. GetBuffer / ReleaseBuffer(高风险)
1 2 3
| TCHAR* p = s.GetBuffer(256); _tcscpy(p, _T("abc")); s.ReleaseBuffer();
|
⚠ 必须配对
3. 更安全的替代
1 2
| CString s; s.Format(_T("%s"), psz);
|
十三、与 STL 字符串互转
1. CString → std::wstring
1
| std::wstring ws(s.GetString());
|
2. std::string → CString
十四、在 STL 容器中使用
1 2
| std::vector<CString> vec; vec.emplace_back(_T("A"));
|
十五、MFC 控件 / UI 绑定
1 2
| SetDlgItemText(IDC_EDIT1, s); GetDlgItemText(IDC_EDIT1, s);
|
十六、路径 / 文件名操作
1 2 3 4
| CString path = _T("C:\\Temp\\file.txt"); int pos = path.ReverseFind(_T('\\')); CString dir = path.Left(pos); CString file = path.Mid(pos + 1);
|
十七、性能优化与工程实践
1. 预分配
❌
1 2
| for (...) s.Format(...);
|
✅
1 2 3
| s.Empty(); for (...) s.AppendFormat(...);
|
十八、线程安全说明
CString 不是线程安全
- 不同线程不能同时写同一个实例
- 可安全跨线程读(只读)
十九、CString vs std::string(工程结论)
| 场景 |
推荐 |
| MFC UI / WinAPI |
CString |
| 纯业务逻辑 |
std::string |
| COM / BSTR |
CStringW |
二十、常见陷阱汇总
| 陷阱 |
说明 |
| GetBuffer 忘记 Release |
内存破坏 |
| ANSI / Unicode 混用 |
乱码 |
%s 与 %S 混用 |
格式错误 |
| CStringA → CString |
编码丢失 |
二十一、最佳实践总结
工程建议:
- UI / 系统接口层:
CString
- 核心逻辑层:
std::string
- 明确编码边界
- 禁止随意 GetBuffer