CString

本文详细介绍了CString的工作原理,包括其内部结构、内存管理机制以及写入复制技术。解释了CString如何高效地处理字符串长度的变化,避免频繁的内存分配与释放。
首先理清一下CString的基本概念

CString就是对一个用来存放字符串的缓冲区和对施加于这个字符串的操作封装。也就是说,CString里需要有一个用来存放字符串的缓冲区,并且有一个指针指向该缓冲区,该指针就是LPTSTR m_pchData。但是有些字符串操作会增建或减少字符串的长度,因此为了减少频繁的申请内存或者释放内存,CString会先申请一个大的内存块用来存放字符串。这样,以后当字符串长度增长时,如果增加的总长度不超过预先申请的内存块的长度,就不用再申请内存。当增加后的字符串长度超过预先申请的内存时,CString先释放原先的内存,然后再重新申请一个更大的内存块。同样的,当字符串长度减少时,也不释放多出来的内存空间。而是等到积累到一定程度时,才一次性将多余的内存释放。

还有,当使用一个CString对象a来初始化另一个CString对象b时,为了节省空间,新对象b并不分配空间,它所要做的只是将自己的指针指向对象a的那块内存空间,只有当需要修改对象a或者b中的字符串时,才会为新对象b申请内存空间,这叫做写入复制技术(CopyBeforeWrite)。

那么CString是否和我们平时常见的字符串空间一样呢?答案是否定的,上面说了,变量之间赋值是不会立刻分配空间的,除非一个变量内容发生变化,否则只是引用技术增加。我么看了CString结构体的定义之后会更加明白这个问题

struct CStringData
{
long nRefs; // 引用计数
int nDataLength; // 实际数据的长度包括结束符
int nAllocLength; // 总共分配的内存空间
// TCHAR data[nAllocLength]

TCHAR* data() // TCHAR* to managed data
{ return (TCHAR*)(this+1); }
};

看了这个结构,我想对于上面的叙述应该觉得本该如此了吧。

实际使用时,该结构体的所占用的内存块大小是不固定的,在CString内部的内存块头部,放置的是该结构体。从该内存块头部开始的sizeof(CstringData)个BYTE后才是真正的用于存放字符串的内存空间。这种结构的数据结构的申请方法是这样实现的:
pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
pData->nAllocLength = nLen;
其中nLen是用于说明需要一次性申请的内存空间的大小的。

从代码中可以很容易的看出,如果想申请一个256个TCHAR的内存块用于存放字符串,实际申请的大小是:
sizeof(CStringData)个BYTE + (nLen+1)个TCHAR

其中前面sizeof(CstringData)个BYTE是用来存放CstringData信息的。后面的nLen+1个TCHAR才是真正用来存放字符串的,多出来的一个用来存放’\0’。

CString中所有的operations的都是针对这个缓冲区的。比如LPTSTR CString::GetBuffer(int nMinBufLength),它的实现方法是:
首先通过CString::GetData()取得CStringData对象的指针。该指针是通过存放字符串的指针m_pchData先后偏移sizeof(CstringData),从而得到了CStringData的地址。
然后根据参数nMinBufLength给定的值重新实例化一个CStringData对象,使得新的对象里的字符串缓冲长度能够满足nMinBufLength。
然后在重新设置一下新的CstringData中的一些描述值。C
最后将新CStringData对象里的字符串缓冲直接返回给调用者。

假如修改对象a或对象b的字符串内容时,首先检查CStringData:: nRefs的值,如果大于一(等于一,说明只有自己一个应用该内存空间),说明该对象引用了别的对象内存或者自己的内存被别人应用,该对象首先将该应用值减一,然后将该内存交给其他的对象管理,自己重新申请一块内存,并将原来内存的内容拷贝过来。

常常通过Getbuffer或者CString的内存空间,但是很多时候因为理解的原因容易出错。

CString str1("This is the string 1");――――――――――――――――1
int nOldLen = str1.GetLength();―――――――――――――――――2
char* pstr1 = str1.GetBuffer( nOldLen );――――――――――――――3
strcpy( pstr1, "modified" );――――――――――――――――――――4
int nNewLen = str1.GetLength();―――――――――――――――――5

我们发现内容确实已经更改,但是长度还是20,为什么?下面细细道来。

从CString的机理上也可以看出:GetBuffer返回的是CStringData对象里的字符串缓冲的首地址。根据这个地址,我们对这个地址里的值进行的修改,改变的只是CStringData里的字符串缓冲中的值, CStringData中的其他用来描述字符串缓冲的属性的值已经不是正确的了。比如此时CStringData:: nDataLength很显然还是原来的值20,但是现在实际上字符串的长度已经是8了。也就是说我们还需要对CStringData中的其他值进行修改。这也就是需要调用ReleaseBuffer()的原因了。

<think>我们正在讨论的是TCHAR和CString的相关知识。根据之前的引用内容,我们可以总结出以下几点: 1. TCHAR是一种通用的字符类型,用于支持Unicode和多字节字符集(MBCS)。在编译时,根据是否定义了_UNICODE宏,TCHAR会被定义为wchar_t(Unicode)或char(MBCS)。 2. CString是MFC中用于处理字符串的类,它封装了字符串操作,并且可以自动处理内存分配和释放。CString支持Unicode和MBCS,根据项目的字符集设置,CString内部使用TCHAR来存储字符。 接下来,我将详细介绍TCHAR和CString,包括它们的定义、转换方法以及使用示例。 ### TCHAR详解 TCHAR是为了实现代码的通用性而定义的类型。在Windows编程中,当我们希望同一份代码既可以编译为Unicode版本又可以编译为ANSI版本时,使用TCHAR。 ```c #ifdef _UNICODE typedef wchar_t TCHAR; #else typedef char TCHAR; #endif ``` 常见的TCHAR字符串处理函数以_tcs开头,例如_tcscpy(对应strcpy或wcscpy)、_tcslen(对应strlen或wcslen)等。 ### CString详解 CString是MFC中一个功能强大的字符串类,它提供了丰富的成员函数用于字符串操作。CString对象可以自动管理内存,当CString对象超出作用域时,其析构函数会自动释放内存。 主要特点: - 支持动态内存分配,字符串长度可动态变化 - 提供丰富的成员函数(如GetLength、TrimLeft、TrimRight、Format等) - 支持运算符重载(如=、+、+=、==、!=等) - 与基本类型的转换(如int、double转换为CString,以及反向转换) ### TCHAR与CString的转换 #### 1. CString 转 TCHAR* (LPTSTR) 有两种常用方法: **方法1:使用GetBuffer** ```cpp CString str = _T("Hello"); LPTSTR lpsz = str.GetBuffer(str.GetLength()); // 使用lpsz... str.ReleaseBuffer(); // 释放缓冲区,必须调用 ``` 注意:使用GetBuffer后必须调用ReleaseBuffer,否则可能导致CString内部状态不一致。 **方法2:分配内存并复制** ```cpp CString str = _T("Hello"); LPTSTR lpsz = new TCHAR[str.GetLength() + 1]; _tcscpy(lpsz, (LPCTSTR)str); // 使用lpsz... delete[] lpsz; // 记得释放内存 ``` #### 2. TCHAR* 转 CString 可以直接赋值: ```cpp TCHAR* szText = _T("World"); CString str = szText; // 直接赋值 ``` #### 3. CString 与 int/double 的转换 **CString转int** ```cpp CString str = _T("123"); int n = _ttoi(str); ``` **CString转double** ```cpp CString str = _T("3.14"); double d = _tstof(str); ``` **int/double转CString** ```cpp int n = 456; double d = 1.23; CString str1; str1.Format(_T("%d"), n); CString str2; str2.Format(_T("%f"), d); ``` #### 4. CString 与 std::string 的转换 **std::string 转 CString** ```cpp std::string s = "abc"; CString str(s.c_str()); // 对于多字节字符集 // 或者 CString str = CA2T(s.c_str()); // 使用ATL转换宏,需要包含<atlstr.h> ``` **CString 转 std::string** ```cpp CString str = _T("abc"); std::string s = CT2A(str.GetString()); // 使用ATL转换宏 // 或者对于多字节版本 CStringA strA(str); std::string s = strA.GetString(); ``` ### 使用示例 以下是一个综合示例,展示如何在MFC应用程序中使用CString和TCHAR: ```cpp void CMyClass::ProcessStrings() { // 创建CString CString str1 = _T("Hello"); CString str2 = _T("World"); // 字符串连接 CString str3 = str1 + _T(", ") + str2 + _T("!"); // 转换为TCHAR* LPTSTR lpsz = new TCHAR[str3.GetLength() + 1]; _tcscpy(lpsz, str3); // 使用TCHAR*进行操作 // ... // 释放内存 delete[] lpsz; // 格式化字符串 int nCount = 10; CString strMsg; strMsg.Format(_T("Count: %d"), nCount); // 显示消息 AfxMessageBox(strMsg); } ``` ### 注意事项 1. **字符集一致性**:确保项目设置(Unicode或多字节字符集)与代码中使用的字符类型一致。 2. **内存管理**:当使用`new`分配内存时,必须记得使用`delete[]`释放。 3. **GetBuffer/ReleaseBuffer**:使用`GetBuffer`获取内部缓冲区指针后,如果修改了字符串长度,必须调用`ReleaseBuffer`来更新CString内部状态。 4. **ATL转换宏**:在涉及不同字符集转换时(如Unicode到ANSI),推荐使用ATL转换宏(如CA2T、CT2A等),它们简化了转换过程并自动处理内存。 ### 相关问题 根据上述内容,生成相关问题如下: §§相关问题§§ 1. 如何在Unicode环境下正确使用CString和TCHAR? 2. CString的Format函数有哪些常用格式符? 3. 使用GetBuffer获取CString缓冲区时有哪些注意事项? 4. 如何避免在使用TCHAR指针时的内存泄漏问题? 5. 在MFC中,如何将CString与控件(如编辑框Edit Control)进行数据交换? </think>### TCHAR与CString详解 #### 一、TCHAR类型 TCHAR是Windows编程中的通用字符类型,用于支持Unicode和ANSI双模式编译: ```c #ifdef _UNICODE typedef wchar_t TCHAR; // Unicode模式 #else typedef char TCHAR; // ANSI模式 #endif ``` **核心特性**: 1. **跨平台兼容**:自动适配系统字符集(Unicode/ANSI)[^5] 2. **前缀规范**: - `_T()`宏定义文本:`_T("Hello")` - 字符串函数:`_tcscpy()`代替`strcpy()`/`wcscpy()` 3. **内存占用**: - Unicode模式:每个字符2字节 - ANSI模式:每个字符1字节 #### 二、CStringCString是MFC提供的字符串处理类(`afx.h`中定义),封装了高级字符串操作: ```cpp CString str1 = _T("Windows"); CString str2 = _T("Programming"); str1 += _T(" ") + str2; // 结果:"Windows Programming" ``` **核心功能**: 1. **内存管理**:自动分配/释放内存 2. **操作符重载**:支持`+`, `=`, `==`等运算符 3. **常用方法**: ```cpp str.GetLength(); // 获取长度 str.MakeUpper(); // 转大写 str.Find('P'); // 查找字符位置 str.Format(_T("%d"), 100); // 格式化 ``` #### 三、相互转换方法 ##### 1. CString → TCHAR* (LPTSTR) **安全方法(推荐)**: ```cpp CString str = _T("Test"); TCHAR* pBuffer = new TCHAR[str.GetLength() + 1]; _tcscpy(pBuffer, str.GetString()); // 使用后必须释放内存! delete[] pBuffer; ``` **直接访问(需谨慎)**: ```cpp LPTSTR psz = str.GetBuffer(str.GetLength()); str.ReleaseBuffer(); // 必须成对使用[^2] ``` ##### 2. TCHAR* → CString ```cpp TCHAR* szText = _T("Hello"); CString str(szText); // 直接构造 // 或 str.Format(_T("%s"), szText); ``` ##### 3. CString ↔ 基本类型 **转换示例**: ```cpp // int → CString int nVal = 100; CString strInt; strInt.Format(_T("%d"), nVal); // CString → double CString strDouble = _T("3.14159"); double dVal = _tstof(strDouble); // char* → CString char* pAnsi = "ANSI Text"; CString strAnsi = CString(pAnsi); // ANSI版本[^3] ``` #### 四、跨平台兼容方案 对于非Windows平台,可参考标准C++实现: ```cpp // 伪代码参考 class CString { private: std::string m_data; // ANSI版本 std::wstring m_wdata; // Unicode版本 public: const TCHAR* GetString() const { #ifdef _UNICODE return m_wdata.c_str(); #else return m_data.c_str(); #endif } }; ``` #### 五、关键注意事项 1. **内存安全**: ```cpp // 错误示例:返回临时指针 TCHAR* DangerousFunc() { CString tmp = _T("Temp"); return tmp.GetBuffer(); // 临时对象销毁后指针失效! } ``` 2. **Unicode一致性**: - 项目属性设置需一致(_UNICODE/_MBCS) - 混合使用会导致乱码或崩溃 3. **API兼容**: ```cpp MessageBox(NULL, str.GetString(), _T("Title"), MB_OK); // 正确 MessageBox(NULL, str, _T("Title"), MB_OK); // MFC隐式转换 ``` ### 应用场景示例 **注册表读写**: ```cpp CString ReadRegValue(HKEY hKey, LPCTSTR lpSubKey) { TCHAR szValue[MAX_PATH] = {0}; DWORD dwSize = sizeof(szValue); RegGetValue(hKey, NULL, lpSubKey, RRF_RT_REG_SZ, NULL, szValue, &dwSize); return CString(szValue); // 自动转换 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值