********也许是关于CString最全面的总结(2)**********

freelybird 2002-05-24 05:16:35
3 拷贝 & 赋值 & "引用内存块" 什么时候释放?

下面演示一段代码执行过程
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
//注意,str a b 所分别指向的引用内存块的计数均为0,这导致所分配的内存块释放

通过观察上面执行过程,我们会发现CString虽然可以多个对象指向同一引用内块存,但是它们在进行各种拷贝、赋值及改变串内容时,它的处理是很智能并且非常安全的,完全做到了互不干涉、互不影响。当然必须要求你的代码使用正确恰当,特别是实际使用中会有更复杂的情况,如做函数参数、引用、及有时需保存到CStringList当中,如果哪怕有一小块地方使用不当,其结果也会导致发生不可预知的错误

5 FreeExtra()的作用
看这段代码
(1) CString str("test");
(2) LPTSTR temp = str.GetBuffer(50);
(3) strcpy(temp, "there are 22 character");
(4) str.ReleaseBuffer();
(5) str.FreeExtra();
上面代码执行到第(4)行时,大家都知道str指向的引用内存块计数为1,长度为22,分配长度为50. 那么执行str.FreeExtra()时,它会释放所分配的多余的内存。(引用内存块计数为1,长度为22,分配长度为22)

6 Format(...) 与 FormatV(...)
这条语句在使用中是最容易出错的。因为它最富有技巧性,也相当灵活。在这里,我没打算对它细细分析,实际上sprintf(...)怎么用,它就怎么用。我只提醒使用时需注意一点:就是它的参数的特殊性,由于编译器在编译时并不能去校验格式串参数与对应的变元的类型及长度。所以你必须要注意,两者一定要对应上,
否则就会出错。如:
CString str;
int a = 12;
str.Format("first:%l, second: %s", a, "error");//result?试试

7 LockBuffer() 与 UnlockBuffer()
顾名思议,这两个函数的作用就是对引用内存块进行加锁及解锁。
但使用它有什么作用及执行过它后对CString串有什么实质上的影响。其实挺简单,看下面代码:
(1) CString str("test");
(2) str.LockBuffer();
(3) CString temp = str;
(4) str.UnlockBuffer();
(5) str.LockBuffer();
(6) str = "error";
(7) str.ReleaseBuffer();
执行完(3)后,与通常情况下不同,temp与str并不指向同一引用内存块。你可以在watch窗口用这个表达式(CStringData*)((CStringData*)(str.m_pchData)-1)看看。
其实在msdn中有说明:
While in a locked state, the string is protected in two ways:

No other string can get a reference to the data in the locked string, even if that string is assigned to the locked string.
The locked string will never reference another string, even if that other string is copied to the locked string.

8 CString 只是处理串吗?
不对,CString不只是能操作串,而且还能处理内存块数据。功能完善吧!看这段代码
char p[20];
for(int loop=0; loop<sizeof(p); loop++)
{
p[loop] = 10-loop;
}
CString str((LPCTSTR)p, 20);
char temp[20];
memcpy(temp, str, str.GetLength());
str完全能够转载内存块p到内存块temp中。所以能用CString来处理二进制数据

8 AllocSysString()与SetSysString(BSTR*)
这两个函数提供了串与BSTR的转换。使用时须注意一点:当调用AllocSysString()后,须调用它SysFreeString(...)

9 参数的安全检验
在MFC中提供了多个宏来进行参数的安全检查,如:ASSERT. 其中在CString中也不例外,有许多这样的参数检验,其实这也说明了代码的安全性高,可有时我们会发现这很烦,也导致Debug与Release版本不一样,如有时程序Debug通正常,而Release则程序崩溃;而有时恰相反,Debug不行,Release行。其实我个人认为,我们对CString的使用过程中,应力求代码质量高,不能在Debug版本中出现任何断言框,哪怕release运行似乎
看起来一切正常。但很不安全。如下代码:
(1) CString str("test");
(2) str.LockBuffer();
(3) LPTSTR temp = str.GetBuffer(10);
(4) strcpy(temp, "error");
(5) str.ReleaseBuffer();
(6) str.ReleaseBuffer();//执行到此时,Debug版本会弹出错框

10 CString的异常处理
我只想强调一点:只有分配内存时,才有可能导致抛出CMemoryException.
同样,在msdn中的函数声明中,注有throw( CMemoryException)的函数都有重新分配或调整内存的可能。

11 跨模块时的CString.即一个DLL的接口函数中的参数为CString&时,它会发生怎样的现象。解答我遇到的
问题。我的问题原来已经发贴,地址为: http://www.csdn.net/expert/topic/741/741921.xml?temp=.2283136

构造一个这样CString对象时,如CString str,你可知道此时的str所指向的引用内存块吗?也许你会认为它指向NULL。其实不对,如果这样的话,CString所采用的引用机制管理内存块就会有麻烦了,所以CString在构造一个空串的对象时,它会指向一个固定的初始化地址,这块数据的声明如下:
AFX_STATIC_DATA int _afxInitData[] = {-1,0,0,0};
简要描述概括一下:当某个CString对象串置空的话,如Empty(),CString a等,它的成员变量m_pchData就会指向_afxInitData这个变量的地址。当这个CString对象生命周期结束时,正常情况下它会去对所指向的引用内存块计数减1,如果引用计数为0(即没有任何CString引用时),则释放这块引用内存。而现在的情况是如果CString所指向的引用内存块是初始化内存块时,则不会释放任何内存。

说了这么多,这与我遇到的问题有什么关系呢?其实关系大着呢?其真正原因就是如果exe模块与dll模块有一
个是static编译连接的话。那么这个CString初始化数据在exe模块与dll模块中有不同的地址,因为static连接则会在本模块中有一份源代码的拷贝。另外一种情况,如果两个模块都是share连接的,CString的实现代码则在另一个单独的dll实现,而AFX_STATIC_DATA指定变量只装一次,所以两个模块中_afxInitData有相同的地址。
现在问题完全明白了吧!你可以自己去演示一下。
__declspec (dllexport) void test(CString& str)
{
str = "abdefakdfj";//如果是static连接,并且传入的str为空串的话,这里出错。
}

最后一点想法:写得这里,其实CString中还有许多技巧性的好东东,我并没去解释。如很多重载的操作符、查找等。我认为还是详细看看msdn,这样会比我讲好多了。我只侧重那些情况下会可能出错。当然,我叙述如有错误,敬请高手指点,不胜感谢!
...全文
120 113 打赏 收藏 转发到动态 举报
写回复
用AI写文章
113 条回复
切换为时间正序
请发表友善的回复…
发表回复
Mars_xuxcr 2003-01-18
  • 打赏
  • 举报
回复
啊!你就是我们学习的指路明灯。
GoldenSword 2002-09-12
  • 打赏
  • 举报
回复
CString用做DLL入口函数参数时会导致内存泄露
goldolphin 2002-09-12
  • 打赏
  • 举报
回复
值得继续发展。
vericky 2002-09-12
  • 打赏
  • 举报
回复
pick up .
wolfpzp 2002-09-12
  • 打赏
  • 举报
回复
我要好好收藏,wolfpzp@sina.com
xtky_limi 2002-08-28
  • 打赏
  • 举报
回复
学习。
而且我在使用中发现CString当存储超过一定长度(至少64k)以上的字符时,会出错.
具体原因我没有去仔细查看,你可以测试一下。
52001314 2002-08-28
  • 打赏
  • 举报
回复
Up,辛苦了!
aben456 2002-08-28
  • 打赏
  • 举报
回复
up
kingmd2 2002-08-27
  • 打赏
  • 举报
回复
能把你的两篇文章email给我吗?
j_0104@yeah.net
谢谢!!
wltsui 2002-08-27
  • 打赏
  • 举报
回复
gz
firmamenthy 2002-08-27
  • 打赏
  • 举报
回复
up!

up!

up!

发现得好及时,我正被CString 弄得昏头转向:(
freelybird 2002-06-27
  • 打赏
  • 举报
回复
continue
freelybird 2002-06-19
  • 打赏
  • 举报
回复
还有一篇,不要忘记呀!


********也许是关于CString最全面的总结(1)**********

http://www.csdn.net/expert/topic/749/749795.xml?temp=.6299402

wengst0428 2002-06-17
  • 打赏
  • 举报
回复
你太棒了
xinghf 2002-06-17
  • 打赏
  • 举报
回复
up2u
jianxuan 2002-06-17
  • 打赏
  • 举报
回复
up
zhishao 2002-06-17
  • 打赏
  • 举报
回复
up
nuaawyd 2002-06-17
  • 打赏
  • 举报
回复
谢了,顶楼的兄弟辛苦了!
cainiao000 2002-06-17
  • 打赏
  • 举报
回复
good~
boysandgirls 2002-06-17
  • 打赏
  • 举报
回复
关注!up!
加载更多回复(93)

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧