CString转char*的两种方法讨论

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(...)取出指针后,可以通过这个指针来修改里面的内容,或者做函数的入参。为什么呢?也许经常有这样的代码:  
                  CString   str("abcd");  
                  char*   p   =   (char*)(const   char*)str;  
                  p[2]   =   'z';        
              其实,也许有这样的代码后,你的程序并没有错,而且程序也运行得挺好。但它却是非常危险的。再看  
                  CString   str("abcd");  
                  CString   test   =   str;  
                  ....  
                  char*   p   =   (char*)(const   char*)str;  
                  p[2]   =   'z';        
                  strcpy(p,   "akfjaksjfakfakfakj");//这下完蛋了        
              你知道此时,test中的值是多少吗?答案是"abzd".它也跟着改变了,这不是你所期望发生的。但为什么会这样呢?你稍微想想就会明白,前面说过,因为CString是指向引用块的,str与test指向同一块地方,当你p[2]='z'后,当然test也会随着改变。所以用它做LPCTSTR做转换后,你只能去读这块数据,千万别去改变它的内容。  
               
              假如我想直接通过指针去修改数据的话,那怎样办呢?就是用GetBuffer(...).看下述代码:  
                  CString   str("abcd");  
                  CString   test   =   str;  
                  ....  
                  char*   p   =   str.GetBuffer(20);  
                  p[2]   =   'z';     //       执行到此,现在test中值却仍是"abcd"  
                  strcpy(p,   "akfjaksjfakfakfakj");       //         执行到此,现在test中值还是"abcd"  
              为什么会这样?其实GetBuffer(20)调用时,它实际上另外建立了一块新内块存,并分配20字节长度的buffer,而原来的内存块引用计数也相应减1.     所以执行代码后str与test是指向了两块不同的地方,所以相安无事。  
        (4)   不过这里还有一点注意事项:就是str.GetBuffer(20)后,str的分配长度为20,即指针p它所指向的buffer只有20字节长,给它赋值时,切不可超过,否则灾难离你不远了;如果指定长度小于原来串长度,如GetBuffer(1),实际上它会分配4个字节长度(即原来串长度);另外,当调用GetBuffer(...)后并改变其内容,一定要记得调用ReleaseBuffer(),这个函数会根据串内容来更新引用内存块的头部信息。  
        (5)   最后还有一注意事项,看下述代码:  
              char*   p   =   NULL;  
              const   char*   q   =   NULL;  
              {  
                      CString   str   =   "abcd";  
                      q   =   (LPCTSTR)str;  
                      p   =   str.GetBuffer(20);  
                      AfxMessageBox(q);//   合法的  
                      strcpy(p,   "this   is   test");//合法的,  
              }  
              AfxMessageBox(q);//   非法的,可能完蛋  
              strcpy(p,   "this   is   test");//非法的,可能完蛋  
              这里要说的就是,当返回这些指针后,   如果CString对象生命结束,这些指针也相应无效。  
  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当中,如果哪怕有一小块地方使用不当,其结果也会导致发生不可预知的错误


随笔 - 764 文章 - 3 评论 - 196 CString,string,char*之间的换() 这三种类型各有各的优点,比如CString比较灵活,是基于MFC常用的类型,安全性也最高,但可移植性最差。string是使用STL时必不可少的类型,所以是做工程时必须熟练掌握的;char*是从学习C语言开始就已经和我们形影不离的了,有许多API都是以char*作为参数输入的。所以熟练掌握三者之间的换十分必要。 以下我用简单的图示指出三者之间的关系,并以标号对应换的方法。 1 string to CString CString.format("%s",string.c_str()); 2 CString to string string str(CString.GetBuffer(str.GetLength())); 3 string to char * char *p=string.c_str(); 4 char * to string string str(char*); 5 CString to char * strcpy(char,CString,sizeof(char)); 6 char * to CString CString.format("%s",char*); CString的format方法是非常好用的。string的c_str()也是非常常用的,但要注意和char *换时,要把char定义成为const char*,这样是最安全的。 以上函数UNICODE编码也没问题:unicode下照用,加个_T()宏就行了,像这样子_T("%s") 补充: CString 可能是 CStringW/CStringA,在与 string 换时,如果是 CStringW,还涉及编码换问题。下面以 CStringA 来说明。 1 string to CString CString.format("%s",string.c_str()); CStringA = string.c_str() 就可以了 2 CString to string string str(CString.GetBuffer(str.GetLength())); GetBuffer 有参数的话,可能导致内部的分配空间动作,要进行后续 ReleaseBuffer 操作。 string = CStringA string = CStringA.GetBuffer(); 3 string to char * char *p=string.c_str(); 4 char * to string string str(char*); 5 CString to char * strcpy(char *,CString,sizeof(char)); 按照 3 风格,这里应该 char * = CStringA; 或者 char *p = CStringA.GetBuffer(); 6 char * to CString CStringA = char * 就可以了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值