CString剖析与详解

1 CString实现的机制
   CString是通过“引用”来管理串的,象Window内核对象、COM对象等都是通过引用来实现的。而CString也是通过这样的机制来管理分配的内存块。实际上CString对象只有一个指针成员变量,所以任何CString实例的长度只有4字节.
正因为如此,一个这样的内存块可被多个CString所引用.
怎么知道有多少个CString引用它呢?同样,它也会记录一些信息。如被引用数,串长度,分配内存长度。
这块引用内存块的结构定义如下:
struct CStringData
{long nRefs; //表示有多少个CString 引用它
int nDataLength; //串实际长度.
int nAllocLength; //总共分配的内存长度(不计这头部的12字节)
};
由于有了这些信息,CString就能正确地分配、管理、释放引用内存块。
如果你想在调试程序的时候获得这些信息。可以在Watch窗口键入下列表达式:(CStringData*)((CStringData*)(this->m_pchData)-1)或(CStringData*)((CStringData*)(str.m_pchData)-1)//str为指CString实例
正因为采用了这样的好机制,使得CString在大量拷贝时,不仅效率高,而且分配内存少。
2.LPCTSTR 与 GetBuffer(int nMinBufLength)
这两个函数提供了与标准C的兼容转换。这两个函数实际上返回的都是指针.
(1) LPCTSTR 它的执行过程其实很简单,只是返回引用内存块的串地址。它是作为操作符重载提供的,所以在代码中有时可以隐式转换,而有时却需强制转制。如:
CString str;
const char* p = (LPCTSTR)str;
//假设有这样的一个函数,Test(const char* p);你就可以这样调用
Test(str);//这里会隐式转换为LPCTSTR
(2) GetBuffer(int nMinBufLength) 它类似,也会返回一个指针,不过它有点差别,返回的是LPTSTR
(3)两者本质上完全不一样,一般说LPCTSTR转换后只应该当常量使用,或者做函数的入参;而GetBuffer(...)取出指针后,可以通过这个指针来修改里面的内容,或者做函数的出参。
(4) 不过这里还有一点注意事项:就是str.GetBuffer(20)后,str的分配长度为20,即指针p它所指向的buffer只有20字节长,给它赋值时,切不可超过,否则灾难离你不远了;如果指定长度小于原来串长度,如GetBuffer(1),实际上它会分配4个字节长度(即原来串长度);另外,当调用GetBuffer(...)后并改变其内容,一定要记得调用ReleaseBuffer(),这个函数会根据串内容来更新引用内存块的头部信息。
3.拷贝 & 赋值 & "引用内存块" 的释放
CString虽然可以多个对象指向同一引用内块存,但是它们在进行各种拷贝、赋值及改变串内容时,它的处理是很智能并且非常安全的,完全做到了互不干涉、互不影响。当然必须要求你的代码使用正确恰当,特别是实际使用中会有更复杂的情况,如做函数参数、引用、及有时需保存到CStringList当中,如果哪怕有一小块地方使用不当,其结果也会导致发生不可预知的错误.例如:
void Test()
{
CString str("abcd");
//str指向一引用内存块(引用内存块的引用计数为1,长度为4,分配长度为4)
CString a;
//a指向一初始数据状态,
a = str;
//a与str指向同一引用内存块(引用内存块的引用计数为2,长度为4,分配长度为4)
CString b(a);
//a、b与str指向同一引用内存块(引用内存块的引用计数为3,长度为4,分配长度为4)
{
LPCTSTR temp = (LPCTSTR)a;
//temp指向引用内存块的串首地址。(引用内存块的引用计数为3,长度为4,分配长度为4)
CString d = a;
//a、b、d与str指向同一引用内存块(引用内存块的引用计数为4, 长度为4,分配长度为4)
b = "testa";
//这条语句实际是调用CString::operator=(CString&)函数。 b指向一新分配的引用内存块。(新分配的引用内存块的 引用计数为1, 长度为5, 分配长度为5)
//同时原引用内存块引用计数减1. a、d与str仍指向原引用内存块(引用内存块的引用计数为3,长度为4,分配长度为4)
}
//由于d生命结束,调用析构函数,导至引用计数减1(引用内存块的引用计数为2,长度为4,分配长度为4)
LPTSTR temp = a.GetBuffer(10);
//此语句也会导致重新分配新内存块。temp指向新分配引用内存块的串首地址(新分配的引用内存块的引用计数为1,长度为0,分配长度为10)
//同时原引用内存块引用计数减1. 只有str仍 指向原引用内存块(引用内存块的引用计数为1, 长度为4, 分配长度为4)
strcpy(temp, "temp");
//a指向的引用内存块的引用计数为1,长度为0,分配长度为10 a.ReleaseBuffer();//注意:a指向的引用内存块的引用计数为1,长度为4,分配长度为10
}
//执行到此,所有的局部变量生命周期都已结束。对象str a b 各自调用自己的析构构
//函数,所指向的引用内存块也相应减1
4 重要的函数
FreeExtra() 会释放所分配的多余的内存。
Format(...) 与 FormatV(...)
实际上sprintf(...)怎么用,它就怎么用。使用时需注意一点:就是它的参数的特殊性,由于编译器在编译时并不能去校验格式串参数与对应的变元的类型及长度。所以必须注意,两者一定要对应上,否则就会出错。
AllocSysString()与SetSysString(BSTR*)
这两个函数提供了串与BSTR的转换。使用时须注意:当调用AllocSysString()后,须调用它SysFreeString(...) .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值