当前位置: 首页 > news >正文

外贸婚纱网站wordpress调用导航

外贸婚纱网站,wordpress调用导航,九台区建设银行网站,建设一个电影网站怎么做看了很多人写的程序,包括我自己写的一些代码#xff0c;发现很大的一部分bug是关于MFC类中的CString的错误用法的.出现这种错误的原因主要是对CString的实现机制不是太了解。 CString是对于原来标准c中字符串类型的一种的包装。因为#xff0c;通过很长时间的编程#xff0c… 看了很多人写的程序,包括我自己写的一些代码发现很大的一部分bug是关于MFC类中的CString的错误用法的.出现这种错误的原因主要是对CString的实现机制不是太了解。     CString是对于原来标准c中字符串类型的一种的包装。因为通过很长时间的编程我们发现,很多程序的bug多和字符串有关,典型的有缓冲溢出、 内存泄漏等。而且这些bug都是致命的会造成系统的瘫痪。因此c里就专门的做了一个类用来维护字符串指针。标准c里的字符串类是string 在microsoft MFC类库中使用的是CString类。通过字符串类可以大大的避免c中的关于字符串指针的那些问题。 这里我们简单的看看Microsoft MFC中的CString是如何实现的。当然要看原理直接把它的代码拿过来分析是最好的。MFC里的关于CString的类的实现大部分在strcore.cpp中。     CString就是对一个用来存放字符串的缓冲区和对施加于这个字符串的操作封装。也就是说CString里需要有一个用来存放字符串的缓冲区并且有 一个指针指向该缓冲区该指针就是LPTSTR m_pchData。但是有些字符串操作会增建或减少字符串的长度因此为了减少频繁的申请内存或者释放内存CString会先申请一个大的内存块用来 存放字符串。这样以后当字符串长度增长时如果增加的总长度不超过预先申请的内存块的长度就不用再申请内存。当增加后的字符串长度超过预先申请的内存 时CString先释放原先的内存然后再重新申请一个更大的内存块。同样的当字符串长度减少时也不释放多出来的内存空间。而是等到积累到一定程度 时才一次性将多余的内存释放。 还有当使用一个CString对象a来初始化另一个CString对象b时为了节省空间新对象b并不分配空间它所要做的只是将自己的指针指 向对象a的那块内存空间只有当需要修改对象a或者b中的字符串时才会为新对象b申请内存空间这叫做写入复制技术 (CopyBeforeWrite)。 这样仅仅通过一个指针就不能完整的描述这块内存的具体情况需要更多的信息来描述。 首先需要有一个变量来描述当前内存块的总的大小。其次需要一个变量来描述当前内存块已经使用的情况。也就是当前字符串的长度另外还需要一个变量来描述该内存块被其他CString引用的情况。有一个对象引用该内存块就将该数值加一。 CString中专门定义了一个结构体来描述这些信息:struct CStringData{ long nRefs;             // reference count int nDataLength;        // length of data (including terminator) int nAllocLength;       // length of allocation // TCHAR data[nAllocLength]  TCHAR* data()           // TCHAR* to managed data  { return (TCHAR*)(this1); }}; 实际使用时该结构体的所占用的内存块大小是不固定的在CString内部的内存块头部放置的是该结构体。从该内存块头部开始的sizeof(CStringData)个BYTE后才是真正的用于存放字符串的内存空间。这种结构的数据结构的申请方法是这样实现的:pData (CStringData*) new BYTE[sizeof(CStringData) (nLen1)*sizeof(TCHAR)];pData-nAllocLength nLen;其中nLen是用于说明需要一次性申请的内存空间的大小的。 从代码中可以很容易的看出如果想申请一个256个TCHAR的内存块用于存放字符串实际申请的大小是 sizeof(CStringData)个BYTE (nLen1)个TCHAR 其中前面sizeof(CStringData)个BYTE是用来存放CStringData信息的。后面的nLen1个TCHAR才是真正用来存放字符串的多出来的一个用来存放’\0’。    CString中所有的operations的都是针对这个缓冲区的。比如LPTSTR CString::GetBuffer(int nMinBufLength)它的实现方法是:首先通过CString::GetData()取得CStringData对象的指针。该指针是通过存放字符串的指针m_pchData先后偏移sizeof(CStringData)从而得到了CStringData的地址。然后根据参数nMinBufLength给定的值重新实例化一个CStringData对象使得新的对象里的字符串缓冲长度能够满足nMinBufLength。然后在重新设置一下新的CStringData中的一些描述值。最后将新CStringData对象里的字符串缓冲直接返回给调用者。 这些过程用C代码描述就是 if (GetData()-nRefs 1 || nMinBufLength GetData()-nAllocLength) {  // we have to grow the buffer  CStringData* pOldData GetData();  int nOldLen GetData()-nDataLength;   // AllocBuffer will tromp it  if (nMinBufLength nOldLen)   nMinBufLength nOldLen;  AllocBuffer(nMinBufLength);  memcpy(m_pchData, pOldData-data(), (nOldLen1)*sizeof(TCHAR));  GetData()-nDataLength nOldLen;  CString::Release(pOldData); } ASSERT(GetData()-nRefs 1);  // return a pointer to the character storage for this string ASSERT(m_pchData ! NULL); return m_pchData; 很多时候我们经常的对大批量的字符串进行互相拷贝修改等CString 使用了CopyBeforeWrite技术。使用这种方法当利用一个CString对象a实例化另一个对象b的时候其实两个对象的数值是完全相同的 但是如果简单的给两个对象都申请内存的话对于只有几个、几十个字节的字符串还没有什么如果是一个几K甚至几M的数据量来说是一个很大的浪费。因此CString 在这个时候只是简单的将新对象b的字符串地址m_pchData直接指向另一个对象a的字符串地址m_pchData。所做的额外工作是将对象a的内存应用CStringData:: nRefs加一。CString::CString(const CString stringSrc){  m_pchData stringSrc.m_pchData;  InterlockedIncrement(GetData()-nRefs);} 这样当修改对象a或对象b的字符串内容时首先检查CStringData:: nRefs的值如果大于一(等于一说明只有自己一个应用该内存空间)说明该对象引用了别的对象内存或者自己的内存被别人应用该对象首先将该应用值 减一然后将该内存交给其他的对象管理自己重新申请一块内存并将原来内存的内容拷贝过来。 其实现的简单代码是void CString::CopyBeforeWrite(){ if (GetData()-nRefs 1) {  CStringData* pData GetData();  Release();  AllocBuffer(pData-nDataLength);memcpy(m_pchData, pData-data(),  (pData- nDataLength1)*sizeof(TCHAR)); }}其中Release 就是用来判断该内存的被引用情况的。void CString::Release(){ if (GetData() ! _afxDataNil) {  if (InterlockedDecrement(GetData()-nRefs) 0)   FreeData(GetData()); }} 当多个对象共享同一块内存时这块内存就属于多个对象而不在属于原来的申请这块内存的那个对象了。但是每个对象在其生命结束时都首先将这块内存的引用减一然后再判断这个引用值如果小于等于零时就将其释放否则将之交给另外的正在引用这块内存的对象控制。 CString使用这种数据结构对于大数据量的字符串操作可以节省很多频繁申请释放内存的时间有助于提升系统性能。 通过上面的分析我们已经对CString的内部机制已经有了一个大致的了解了。总的说来MFC中的CString是比较成功的。但是由于数据结 构比较复杂(使用CStringData)所以在使用的时候就出现了很多的问题最典型的一个就是用来描述内存块属性的属性值和实际的值不一致。出现这 个问题的原因就是CString为了方便某些应用提供了一些operations这些operation可以直接返回内存块中的字符串的地址值用户 可以通过对这个地址值指向的地址进行修改但是修改后又没有调用相应的operations1使CStringData中的值来保持一致。比如用户可 以首先通过operations得到字符串地址然后将一些新的字符增加到这个字符串中使得字符串的长度增加但是由于是直接通过指针修改的所以描 述该字符串长度的CStringData中的nDataLength却还是原来的长度因此当通过GetLength获取字符串长度时返回的必然是不正 确的。 存在这些问题的operations下面一一介绍。 1. GetBuffer 很多错误用法中最典型的一个就是CString:: GetBuffer ()了.查了MSDN,里面对这个operation的描述是: Returns a pointer to the internal character buffer for the CString object. The returned LPTSTR is not const and thus allows direct modification of CString contents。这段很清楚的说明对于这个operation返回的字符串指针我们可以直接修改其中的值: 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 通过设置断点我们来运行并跟踪这段代码可以看出当运行到三处时str1的值是”This is the string 1”,并且nOldLen的值是20。当运行到5处时发现str1的值变成了”modified”。也就是说对GetBuffer返回的字符串指 针我们将它做为参数传递给strcpy试图来修改这个字符串指针指向的地址结果是修改成功并且CString对象str1的值也响应的变成了” modified”。但是我们接着再调用str1.GetLength()时却意外的发现其返回值仍然是20但是实际上此时str1中的字符串已经变 成了” modified”,也就是说这个时候返回的值应该是字符串” modified”的长度8而不是20。现在CString工作已经不正常了这是怎么回事 很显然str1工作不正常是在对通过GetBuffer返回的指针进行一个字符串拷贝之后的。 再看MSDN上的关于这个operation的说明可以看到里面有这么一段话:If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CString member functions.  原来在对GetBuffer返回的指针使用之后需要调用ReleaseBuffer这样才能使用其他CString的operations。上 面的代码中我们在45处增建一行代码:str2.ReleaseBuffer(),然后再观察nNewLen,发现这个时候已经是我们想要的值8了。 从CString的机理上也可以看出:GetBuffer返回的是CStringData对象里的字符串缓冲的首地址。根据这个地址我们对这个地 址里的值进行的修改改变的只是CStringData里的字符串缓冲中的值 CStringData中的其他用来描述字符串缓冲的属性的值已经不是正确的了。比如此时CStringData:: nDataLength很显然还是原来的值20但是现在实际上字符串的长度已经是8了。也就是说我们还需要对CStringData中的其他值进行修 改。这也就是需要调用ReleaseBuffer()的原因了。 正如我们所预料的ReleaseBuffer源代码中显示的正是我们所猜想的: CopyBeforeWrite();  // just in case GetBuffer was not called  if (nNewLength -1)  nNewLength lstrlen(m_pchData); // zero terminated  ASSERT(nNewLength GetData()-nAllocLength); GetData()-nDataLength nNewLength; m_pchData[nNewLength] ´\0´;其中CopyBeforeWrite是实现写拷贝技术的这里不管它。 下面的代码就是重新设置CStringData对象中描述字符串长度的那个属性值的。首先取得当前字符串的长度然后通过GetData()取得CStringData的对象指针并修改里面的nDataLength成员值。 但是现在的问题是我们虽然知道了错误的原因知道了当修改了GetBuffer返回的指针所指向的值之后需要调用ReleaseBuffer才 能使用CString的其他operations时我们就能避免不在犯这个错误了。答案是否定的。这就像虽然每一个懂一点编程知识的人都知道通过new 申请的内存在使用完以后需要通过delete来释放一样道理虽然很简单但是最后实际的结果还是有由于忘记调用delete而出现了内存泄漏。实 际工作中常常是对GetBuffer返回的值进行了修改但是最后却忘记调用ReleaseBuffer来释放。而且由于这个错误不象new和 delete人人都知道的并重视的因此也没有一个检查机制来专门检查所以最终程序中由于忘记调用ReleaseBuffer而引起的错误被带到了发行 版本中。 要避免这个错误方法很多。但是最简单也是最有效的就是避免这种用法。很多时候我们并不需要这种用法我们完全可以通过其他的安全方法来实现。比如上面的代码我们完全可以这样写: CString str1(This is the string 1); int nOldLen str1.GetLength(); str1 modified; int nNewLen str1.GetLength(); 但是有时候确实需要比如:我们需要将一个CString对象中的字符串进行一些转换,这个转换是通过调用一个dll里的函数Translate来完成的但是要命的是不知道什么原因这个函数的参数使用的是char*型的:DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );这个时候我们可能就需要这个方法了:CString strDest;Int nDestLen 100;DWORD dwRet Translate( _strSrc.GetBuffer( _strSrc.GetLength() ),  strDest.GetBuffer(nDestLen), _strSrc.GetLength(), nDestlen );_strSrc.ReleaseBuffer();strDest.ReleaseBuffer();if ( SUCCESSCALL(dwRet)  ){}if ( FAILEDCALL(dwRet) ){} 的确这种情况是存在的但是我还是建议尽量避免这种用法如果确实需要使用请不要使用一个专门的指针来保存GetBuffer返回的值因为 这样常常会让我们忘记调用ReleaseBuffer。就像上面的代码我们可以在调用GetBuffer之后马上就调用ReleaseBuffer来调 整CString对象。 2. LPCTSTR 关于LPCTSTR的错误常常发生在初学者身上。例如在调用函数DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );时初学者常常使用的方法就是:int nLen _strSrc.GetLength();DWORD dwRet Translate( (char*)(LPCTSTR)_strSrc),  (char*)(LPCTSTR)_strSrc), nLen, nLen);if ( SUCCESSCALL(dwRet)  ){}if ( FAILEDCALL(dwRet) ){} 他原本的初衷是将转换后的字符串仍然放在_strSrc中但是当调用完Translate以后之后再使用_strSrc时却发现_strSrc已经工作不正常了。检查代码却又找不到问题到底出在哪里。 其实这个问题和第一个问题是一样的。CString类已经将LPCTST重载了。在CString中LPCTST实际上已经是一个 operation了。对LPCTST的调用实际上和GetBuffer是类似的直接返回CStringData对象中的字符串缓冲的首地址。其C代码实现是:_AFX_INLINE CString::operator LPCTSTR() const { return m_pchData; } 因此在使用完以后同样需要调用ReleaseBuffer()。但是这个谁又能看出来呢? 其实这个问题的本质原因出在类型转换上。LPCTSTR返回的是一个const char*类型因此使用这个指针来调用Translate编译是不能通过的。对于一个初学者或者一个有很长编程经验的人都会再通过强行类型转换将 const char*转换为char*。最终造成了CString工作不正常并且这样也很容易造成缓冲溢出。 通过上面对于CString机制和一些容易出现的使用错误的描述可以使我们更好的使用CString。   本文来自CSDN博客转载请标明出处http://blog.csdn.net/benny5609/archive/2007/12/09/1926126.aspx 转载于:https://www.cnblogs.com/liwqiang/archive/2010/02/23/1672067.html
http://www.pierceye.com/news/551986/

相关文章:

  • 网站备案 取消网上智慧团建官网入口
  • 网站开发 无代码app 外包开发公司
  • 做网站应该用什么配置的手提电脑免费微商城小程序模板
  • 义乌外贸网站建设公司服务外包和劳务外包区别
  • 四川长昕建设工程有限公司网站兰州网站哪里做
  • 电子商务网站规划与管理申请一个域名后怎么做网站
  • 中小企业网站制作方法桂林景区网站策划
  • shopify做全品类网站提交链接
  • 网站建设和运营哪家公司好宠物医疗设计素材网站
  • 泰州网站制作公司中国空间站机械臂
  • 信誉好的常州网站建设网监备案网站更换域名
  • 淮南品牌网站建设电话南昌网站建设q479185700棒
  • 富阳区住房和城乡建设局网站广州市住房保障和房屋管理局
  • 江门建设局网站上海住房和城乡建设部网站
  • 开一个网站需要什么建设商务网站的方案
  • asp.net网站开发 pdf全球互联网中心在哪里
  • 做外贸网站要有域名学什么可以做网站
  • 服装高级定制品牌app排名优化
  • 济南推广网站建设保定seo网络推广
  • 网站运营策略wordpress调用友情链接
  • 网站流量下降原因京津冀协同发展四区指的是
  • 北滘网站设计网站建设应解决的问题
  • 网站建设空间申请wordpress 学校主题
  • 长沙市建设工程质量安全监督站官方网站做网站入门看什么书
  • 网站设计 素材代账行业门户网站开发
  • 旅游公司网站开发与实现如何建立公司网站
  • 专门做金融培训的网站有哪些wordpress注册没反应
  • 网站备案部门建设厅焊工证什么样子
  • 南宁市建设工程质量监督站网站设计模式
  • 做理财的网站科技公司网站模板官网