MFC 之 CString 类详细解析

MFC 入口文件为 AFX.H,此文件定义的第一个类就是 CString ,可见字符串操作在整个程序中应用之广泛,地位之重要。
要解析CString类,就要从类的定义及构造开始。打开AFX.H文件找到该类的位置,我们发现整个类中只定义了一个 LPTSTR m_pchData 类型的字符指针变量,而其他的全是函数。那么说明这个类结构大小只占用了4个字节的指针变量大小,如果我定义一个CString a 对象,那么 a 的地址就是其变量 m_pchData 的地址,即 &a = &m_pchData。那么 a 就相当于一个字符指针,可以指向任何字符串。例如一般性给 a 赋值:CString a = "abc"; 就如 给变量 LPTSTR m_pchData = "abc”; 一样。 这样理解了 a ,那么在实际编程中就可以更好的操作它。
在具体了解 CString 类之前,我们先来了解一个跟字符有关的结构体 CStringData,其定义如下:

struct CStringData
{
        long                nRefs;                // reference count
        int                nDataLength;        // length of data (including terminator) 
        int                nAllocLength;        // length of allocation 
        TCHAR* data()                         // TCHAR* to managed data
         { return (TCHAR*)(this+1); }
};

这个结构体可以看成是一个记录字符使用信息的数据结构,如保存了字符分配空间的大小,有效数据长度和字符首地址等信息。这个结构体只定义了3个成员整数变量,并没有定义字符指针信息。那么我们可以申请4个变量的地址空间,让最后一个变量成为字符指针。函数(TCHAR*)(this+1); 功能也刚好指向的是第4个变量的地址。this+1 就是 CStringData++ 偏移一个结构体尺寸大小的位置。
int _afxInitData[] = { -1, 0, 0, 0 }; // 定义了4个成员变量
CStringData _afxDataNil = (CStringData)&_afxInitData; // 前3个变量赋值给结构体
LPCTSTR _afxPchNil = (LPCTSTR)(((BYTE*)&_afxInitData) + sizeof(CStringData));
// 第4个变量转换成字符指针 初始为NULL
复制代码
我们是这样将一个字符指针和一个CString对象联系起来的:如 取字符指针 LPCTSTR _afxPchNil 地址转换成一个CString指针 (CString)&afxPchNil。然后再转换成一个 CString 对象:CString a = (CString*)&afxPchNil; a及其变量m_pchData占用同一个地址,它们的值就是_afxPchNil指向的值。如果_afxPchNil 为NULL,那么对象 a 相当于赋的空值。a. m_pchData = NULL;。
接下来看下 CString 的构造函数:

CString();                             // constructs empty CString
CString(const CString& stringSrc);       // copy constructor
CString(TCHAR ch, int nRepeat = 1);   // from a single character
CString(LPCSTR lpsz);                  // from an ANSI string (converts to TCHAR)
CString(LPCWSTR lpsz);                // from a UNICODE string (converts to TCHAR)
CString(LPCSTR lpch, int nLength);   // subset of characters from an ANSI string (converts to TCHAR)
CString(LPCWSTR lpch, int nLength);   // subset of characters from a UNICODE string (converts to TCHAR)
CString(const unsigned char* psz);   // from unsigned characters

第一个构造没有参数,定义如下:CString a; 在构造函数中 使用 Init() 函数 ,函数功能就是将 a. m_pchData = _afxPchNil; _afxPchNil就是上面定义的全局变量,它的值为NULL, 它的地址前面就是一个CStringData结构体,其值就是 -1,0,0;
第三个和第四个构造功能实际是一样的,只是用的字符集不同而已。它传递的是一个字符指针,定义如下:CString a("abc")或 CString a = “abc”; 内部首先使用了 Init() 函数 将对象 a 指向了全局变量的空字符串。然后取得参数字符串的长度,利用AllocBuffer(nLen)函数分配内存空间。
分配内存情况如下:

CStringData* pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1)*sizeof(TCHAR)];
pData->nAllocLength = nLen;
pData->nRefs = 1; 
pData->data()[nLen] = '\0';
pData->nDataLength = nLen;
m_pchData = pData->data();                // 申请的地址赋予指针

分配了一个CStringData结构体+字符串长度+1的大小,将对象 a.m_pchData 指向了结构体后面储存字符数据的地址。然后通过memcpy函数将参数数据拷贝到 a.m_pchData 指向的位置。
第二个构造函数是将一个CString对象引用作参数,实际传送的就是一个已知的对象变量。定义如下:CString c; CString a = c; 此时 c 指向的是全局变量_afxPchNil, 即为NULL, 那么 a 也指向_afxPchNil, 如果 CString c = "abc"; 赋了值,a = c ;就将 a.m_pchData = c.m_pchData 指向同一个字符地址。然后将字符地址的前面CStringData结构体的nRefs值+1,因为 c 初始分配的时候此值=1了,那么现在nRefs的值就为2,说明有两个对象都对这个字符地址作了引用。

赞(0) 打赏
取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

    暂无评论...