CString與LPCWSTR、LPSTR、char*、LPWSTR等類型的轉換
_T的意思是通知編譯器,自行進行字符串的多字節/Unicode轉換。 而L表示,該字符串爲Unicode版本。http://www.blogjava.net/neumqp/archive/2006/03/09/34504.html
http://blog.csdn.net/sl159/article/details/6412171
http://www.cnblogs.com/fire-phoenix/archive/2010/09/04/1818248.html
先區別一下字符數組和字符指針變量
(1)字符數組是由若干個元素組成的,每個元素中放了一個字符,而字符指針變量中存放的是地址(字符串第一個字符的地址),絕不是將字符串存放到字符指針變量中
(2)賦值方式。對字符數組只能對各個元素賦值,不能用以下辦法對字符數組賦值:
char str[14];
str="I love you";
而對字符指針變量,可以採用下面方法賦值:
char* a;
a="I love you";
但是,注意賦給a的不是字符,而是字符串第一個元素的地址。
(3)對字符指針變量賦初值
char * a="I love you";
等價於
char* a;
a="I love you";
而對數組的初始化:char str[14]={"I love you"};
不能等價於
char str[14];
str[]="I love you";
即數組可以在定義時整體賦初值,但不能在賦值語句中整體賦值。
(4)如果定義了一個字符數組,在編譯時已經爲它分配了內存單元,它有確定的地址。而定義一個字符指針變量時,在沒有給其賦值的前提下,它並沒有一個確定的地址;只有賦值字符串後,才確定了地址,纔可以指向一個字符串。可以如下方式
char *a, str[10];
a=str;
然後,就可以用字符指針變量的下標形式引用字符了。
還有一點,特別注意,字符數組名雖然代表地址,它他是常量,它的值值不能改變的。下面是錯誤的
char str[]={"I love you"};
str=str+7;
printf("%s",str);
而指針變量的值是可以改變的,指向的是地址。下面是正確的:
char* a="I love you";
a=a+7;
printf("%s",a);//結果爲 you
========================================================================
char*,const char*和string的相互轉換
1. string轉const char*
string s = "abc";
const char* c_s =s.c_str();
2. const char*轉string
直接賦值即可
const char* c_s ="abc";
string s(c_s);
3. string轉char*
string s = "abc";
char* c;
const int len =s.length();
c = new char[len+1];
strcpy(c,s.c_str()); //s.data()也可以。主要,現將string轉const char*,然後再通過strcpy轉
4. char*轉string
char* c = "abc";
string s(c);//構造函數的方式
5. const char*轉char*
const char* cpc ="abc";
char* pc = newchar[100];//足夠長
strcpy(pc,cpc);
6. char*轉const char*
直接賦值即可
char* pc = "abc";
const char* cpc = pc;
extern "C" char* hello(char* name)
{
CString names="";
names.Format("%s",name);
CString cs="Hello JNA world, ";
cs+=names;
//錯誤,
//char* re=cs.GetBuffer(cs.GetLength());
//正確,需要複製
int len = cs.GetLength();
char* c=new char[len+1];
strcpy(c,cs.GetBuffer(len));
return c;
//AfxMessageBox(cs);
}
//char 數組相連
char*b="bc";
char*c="ef";
char* p=new char(27);
memset(p,0,27);//設置初值
strcpy(p,b);
strcat(p,c);
CString cs;
cs.Format("%s",p);
CString len;
len.Format("%d",cs.GetLength()); //注意此時的長度,並不是先前定義的27,與memset設置初值有關。請測試。
MessageBox(cs+"長度爲"+len);
//另外,將char型連接到char*的後面
char*b="bc";
char*c="ef";
char* p=new char(27);
memset(p,0,27);//先賦初值後,在通過下標引用。否則p中的內容會出現亂碼形式。
strcpy(p,b);
strcat(p,c);
char a='a';
p[strlen(p)]=a;//重點。
main()
{
char str1[10]="world!";
char str2[20]="hello ";
strcpy(str2+strlen(str2),str1);
printf("%s",str2);
}
//==================沒有上個簡單
char gray[] = "Gray";
char cat[] = "Cat";
char * graycat =(char*) malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);
//==================
CString 和stringCString->std::string 例子:
CString strMfc=“test“;
std::string strStl;
strStl=strMfc.GetBuffer(0);
strMfc.ReleaseBuffer();//要釋放
std::string->CString 例子:
CString strMfc;
std::string strStl=“test“;
strMfc=strStl.c_str();
CString和char*在第四部分
=======================================================================================
BSTR, _bstr_t, CComBSTR, CString
http://blog.csdn.net/chenwanming507507507/article/details/5884401
這裏總結了BSTR, _bstr_t, CComBSTR, CString四種字符串類型之間的轉換。其中BSTR爲基本數據類型,另三個爲字符串類。
首先聲明四個類型的變量:
BSTR strBigBSTR = ::SysAllocString( _T( "BSTR" ));
_bstr_t strSmallBSTR( _T( “_bstr_t” )) ;
CComBSTR strCComBSTR( _T( “CComBSTR” ))
CString strCString( _T( “CString” ));
CComBSTR strTempCComBSTR( _T( “TempCComBSTR” ));
1. BSTR to Other Type
BSTR轉換爲其他類型非常方便,直接使用類的構造函數即可
// BSTR to _bstr_t
strSmallBSTR = _bstr_t( strBigBSTR );
// BSTR to CComBSTR
strCComBSTR = CComBSTR( strBigBSTR );
// BSTR to CString
strCString = CString( strBigBSTR );
2. _bstr_t to Other Type
基本思想是先把_bstr_t轉化爲TCHAR*, 再轉化爲其他類型。
// _bstr_t to BSTR
strTempCComBSTR = CComBSTR( static_cast<TCHAR*>( strSmallBSTR ));
strBigBSTR = strTempCComBSTR.m_str;
// _bstr_t to CComBSTR
strCComBSTR = CComBSTR( static_cast<TCHAR*>( strSmallBSTR ));
// _bstr_t to CString
strCString = CString( static_cast<TCHAR*>( strSmallBSTR ));
_bstr_t類重載了操作符char*和wchar_t*(TCHAR*的兩種形式),所以這裏並不是使用強制轉換,而是調用了_bstr_t的函數。
3. CComBSTR to Other Type
基本思想是使用CComBSTR的公有成員變量m_str進行轉換。
// CComBSTR to BSTR
strBigBSTR = strCComBSTR.m_str;
// CComBSTR to _bstr_t
strSmallBSTR = _bstr_t( strCComBSTR.m_str );
// CComBSTR to CString
strCString = CString( strCComBSTR.m_str );
4. CString to Other Type
基本思想是先把CString轉化爲LPCTSTR, 再轉化爲其他類型。
// CString to BSTR
strTempCComBSTR = CComBSTR( static_cast<LPCTSTR>( strCString ));
strBigBSTR = strTempCComBSTR.m_str;
// CString to _bstr_t
strSmallBSTR = _bstr_t( static_cast<LPCTSTR>( strCString ));
// CString to CComBSTR
strCComBSTR = CComBSTR( static_cast<LPCTSTR>( strCString ));
CString類重載了操作符LPCTSTR,所以這裏並不是使用強制轉換,而是調用了CString的函數。
5. 注意事項:
其他類型轉化爲BSTR類型時都使用的CComBSTR作爲中轉,因爲這樣不需要自己釋放內存。但是要注意的是,轉換後BSTR指向的是CComBSTR的內存,所以一旦CComBSTR被銷燬了,BSTR也就失效了,這樣會導致不可預知的錯誤。所以一定要保證CComBSTR大於或等於BSTR的週期。
下面用法是錯誤的:
strBigBSTR = CComBSTR(( LPCTSTR )strCString ).m_str;
因爲CComBSTR在此條語句結束後就被析構了。
===============================================
VC裏一般都提供CString的接口
否則也有LPCTSTR的。
如果是LPCTSTR可以直接用CString轉換
CString str;
(LPCTSTR)str就是LPCTSTR的了。
char * 也可以這麼轉換成LPCTSTR。
char * 不能直接轉換成CString,CString 也不能直接轉換成char *
但是有了LPCTSTR就可以了。哈哈
char * p
CString str;
p = (char*)(LPCTSTR)str;
str = (CString)(LPCTSTR)p;
所以,對我來說,LPCTSTR是用來轉換的過度工具,它能幹什麼?我不管。反正我這麼用就足夠了。
LPTSTR 等價於 char *
LPCSTR 等價於 const char *
所以直接使用就是了。
LPTSTR lptstr = (LPTSTR)temp;
LPCTSTR lpctstr = (LPCTSTR)temp;
string wstring _bstr_t的互相轉換
string ansi字符串
wstring unicode字符串
_bstr_t com字符串
之間的轉換關係,示例如下:
// ex_02_wstring_cv_string.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
#include "string"
#include "icrsint.h"
#include "comutil.h"
using namespace std;
#import "C:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" no_namespace rename("EOF", "EndOfFile")
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
wstring ws1,ws2;
ws2 = L"春如舊,人空瘦,淚痕紅浥鮫綃透。桃花落,閒池閣,山盟雖在,錦書難託。莫、莫、莫。";
// 從 unicode 字符串轉化爲 ansi字符串
string s = (char *) _bstr_t ( ws2.c_str() );
// 從 ansi字符串轉化爲 unicode 字符串
ws1 = ( wchar_t *) ( _bstr_t ( s.c_str( ) ));
setlocale(LC_ALL, "chs");
wprintf( L"原wstring串=%s\n",ws2.c_str());
printf( "轉換爲string串=%s\n", s.c_str());
wprintf( L"轉換爲wstring串=%s\n",ws1.c_str());
::CoUninitialize();
getchar();
return 0;
}
--- 結果 ---
原wstring串=春如舊,人空瘦,淚痕紅浥鮫綃透。桃花落,閒池閣,山盟雖在,錦書難託。莫、莫、莫。
轉換爲string串=春如舊,人空瘦,淚痕紅浥鮫綃透。桃花落,閒池閣,山盟雖在,錦書難託。莫、莫、莫。
轉換爲wstring串=春如舊,人空瘦,淚痕紅浥鮫綃透。桃花落,閒池閣,山盟雖在,錦書難託。莫、莫、莫。
=======================================================================================
一、CString與LPCWSTR
兩者的不同:LPCWSTR 是Unicode字符串指針,初始化時串有多大,申請空間就有多大,以後存貯若超過則出現無法預料的結果,這是它與CString的不同之處。而CString是一個串類,內存空間類會自動管理。
1、 CString轉換成LPCWSTR
方法一:CString strFileName;
LPCWSTR lpcwStr = strFileName.AllocSysString();
方法二:CString str=_T("TestStr");
USES_CONVERSION;
LPCWSTR lpcwStr = A2CW((LPCSTR)str);
MFC中CString和LPSTR是可以通用(在Unicode字符集中不成立),其中A2CW表示(LPCSTR) -> (LPCWSTR),USER_CONVERSION表示用來定義一些中間變量,在使用ATL的轉換宏之前必須定義該語句。
2、LPCWSTR轉換成CString
LPCWSTR lpcwStr = L"TestWStr";
CString str(lpcwStr); //CString的其中一個構造函數
二、CString與LPWSTR
1、CString轉LPWSTR
CString str;
LPWSTR lpstr = (LPWSTR)(LPCWSTR)str;
2、LPWSTR轉CString (一般沒有用)
LPWSTR lpwStr = L"TestWStr";
CString str(lpwStr); //CString的其中一個構造函數
1、CString轉換成LPSTR:
方法一:CString strFileName;
LPSTR lpStr = strFileName.GetBuffer();
strFileName.ReleaseBuffer();
方法二:(比較下CString轉LPWSTR,都是先強制轉成const字符常量,然後再強制轉成字符變量)
CString strFileName;
LPSTR lpStr = (LPSTR)(LPCSTR)strFimeName;
2、 LPSTR轉換成CString(利用CString的構造函數):
LPSTR lpStr = L"TestStr";
CString str(lpStr);
注意:CString和LPCSTR可直接轉換,如下:
CString str;
LPCSTR lpcStr = (LPCSTR)str;
四、CString和char*轉換
CString 是一種很特殊的 C++對象,它裏面包含了三個值:一個指向某個數據緩衝區的指針、一個是該緩衝中有效的字符記數(它是不可存取的,是位於 CString 地址之下的一個隱藏區域)以及一個緩衝區長度。有效字符數的大小可以是從0到該緩衝最大長度值減1之間的任何數(因爲字符串結尾有一個NULL字符)。字符記數和緩衝區長度被巧妙隱藏。
(1) char*轉換成CString
若將char*轉換成CString,除了直接賦值外,還可使用CString::Format進行。例如:
char chArray[] = "Char test";
TCHAR * p = _T("Char test");
( 或LPTSTR p = _T("Char test");)
CString theString = chArray;
theString.Format(_T("%s"), chArray); theString = p;
(2) CString轉換成char*(非unicode字符集)
若將CString類轉換成char*(LPSTR)類型,常常使用下列三種方法:
方法一,使用強制轉換。例如:
CString theString( (_T("Char test "));
LPTSTR lpsz=(LPTSTR)(LPCTSTR)theString;
方法二,使用strcpy。例如:
這種方法在有些地方特別有效,特別在強制轉換不成功時
CString theString( (_T("Char test "));
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
_tcscpy(lpsz, theString);
需要說明的是,strcpy(或可移值的_tcscpy)的第二個參數是 const wchar_t* (Unicode)或const char* (ANSI),系統編譯器將會自動對其進行轉換。
方法三,使用CString::GetBuffer。
如果你需要修改 CString 中的內容(因爲CString是字符串常量,和LPCSTR等價),它有一個特殊的方法可以使用,那就是GetBuffer,它的作用是返回一個可寫的緩衝指針。
如果你只是打算修改字符或者截短字符串,例如:
CString s(_T("Char test"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = strchr(p, ''.'');
// 在這裏添加使用p的代碼
if(p != NULL)
*p = _T('');
s.ReleaseBuffer(); //
使用完後及時釋放,以便能使用其它的CString成員函數
char *p;
CString str="hello";
p=str.GetBuffer(str.GetLength());
str.ReleaseBuffer();將CString轉換成char * 時
----
CString str("aaaaaaa");
strcpy(str.GetBuffer(10),"aa");
str.ReleaseBuffer();
五、CString和int、float的轉換
可以使用atoi,atof,atol等函數來完成。
CString strFloat;
float flt;
//method1:
flt = (float)atof((char *)(LPTSTR)(LPCTSTR)mstrFloat);//強制轉換,不推薦
//method2:
flt = (float)atof((char *)m_eps.GetBuffer(strFloat.GetLength()));
strFloat.ReleaseBuffer();
六、LPSTR(char*)和LPWSTR的轉換
可以使用下面的ATL宏來進行,最好是將變量定義成TCHAR、LPTSTR等T類型,可以避免轉換。
ATL宏介紹:
A2BSTR OLE2A T2A W2A
A2COLE OLE2BSTR T2BSTR W2BSTR
A2CT OLE2CA T2CA W2CA
A2CW OLE2CT T2COLE W2COLE
A2OLE OLE2CW T2CW W2CT
A2T OLE2T T2OLE W2OLE
A2W OLE2W T2W W2T
A :ANSI 字符串,也就是 MBCS。
W、OLE 寬字符串,也就是 UNICODE。
T 中間類型T。如果定義了 _UNICODE,則T表示W;如果定義了 _MBCS,則T表示A
C const 的縮寫
利用這些宏,可以快速的進行各種字符間的轉換。使用前必須包含頭文件,並且申明USER_CONVERSION;使用 ATL 轉換宏,由於不用釋放臨時空間,所以使用起來非常方便。但是考慮到棧空間的尺寸(VC 默認2M),使用時要注意幾點:
1、只適合於進行短字符串的轉換;
2、不要試圖在一個次數比較多的循環體內進行轉換;
3、不要試圖對字符型文件內容進行轉換,因爲文件尺寸一般情況下是比較大的;
4、對情況 2 和 3,要使用 MultiByteToWideChar() 和 WideCharToMultiByte();
void Func1(LPSTR lpStr);
void Func2(LPWSTR lpwStr);
TCHAR name[256];
TCHAR* pName = new TCHAR[256];
Func1(name); // Func1(pName);
Func2(name); // Func2(pName);
(說明)注意在VS2005中上面用紅色標記的代碼已經不成立。VS2005中CString已經改爲寬字符型,一些轉換如下:
char name[10];
TCHAR sex[5] ;
char *p = name;
TCHAR *pw = sex;
LPSTR lpstr = name;
LPCSTR lpcstr = name;
lpcstr = lpstr;//可直接賦值,字符變量給字符常量賦值
lpstr = p; //本來相等
----------------------------------------------------------------------------
p = (char*)sex;
pw = (WCHAR*)name;
LPWSTR lpwstr = (LPWSTR)lpstr;
LPCWSTR lpcwstr = (LPCWSTR)lpstr;
lpwstr = (LPWSTR)lpcstr;
lpcwstr = (LPCWSTR)name;
--------------------------------------------------------------------------
//採用CString的構造函數
CString str(lpstr);
CString str1(lpcstr);
CString str2(lpwstr);
CString str3(lpcwstr);
CString str4(name);
CString str5(sex);
//特別注意,此處的轉換方式,CString與LPSTR, LPCSTR, 都是先轉成寬字符,然後再轉成對應的類型(見上面紅色說明)
lpwstr = (LPWSTR)(LPCWSTR)str;
lpstr = (LPSTR)(LPCWSTR)str;//在Unicode編碼下不成立,不能強制轉換
lpcstr = (LPCSTR)(LPCWSTR)str;
p = (char*)str.GetBuffer();//錯誤,不能強制轉換
pw = str.GetBuffer();
可以看出轉換更加簡單了,基本上可以直接轉換,A2W等宏基本上不需要啦
測試如下代碼會出現亂碼,需注意(在VS2008的ATL工程下,unicode字符集),強制轉換會出錯
LPCSTR p3="中華人民共和國";
LPCWSTR lpcwStr3 = (LPCWSTR)p3;
MessageBox(lpcwStr3);
如下代碼正確(在ATL中添加了MFC支持),採用CString中間轉換可以
LPCSTR p="中華人民共和國";
CString strFileName(p);
LPCWSTR lpcwStr = strFileName.AllocSysString();
MessageBox(lpcwStr);
或者下面也正確
LPCSTR p3="中華人民共和國3";
CString strFileName3(p3);
LPCWSTR lpcwStr3 = (LPCWSTR)strFileName3;
MessageBox(lpcwStr3);
CString轉wchar_t* CString theString=_T("你好!");
wchar_t* lpsz = new wchar_t[theString.GetLength()+1];
_tcscpy(lpsz, theString);
MessageBox(lpsz);
或者 CString theString=_T("你好!");
wchar_t* wcs=theString.GetBuffer();
MessageBox(wcs);
wcs=NULL;
theString.ReleaseBuffer();//此處要釋放
或者 CString theString=_T("你好!");
LPCWSTR lpcwStr = theString.AllocSysString();
MessageBox(lpcwStr);
在沒有MFC支持的情況下,參考下面的內容
===========================================================
多字節字符串與寬字符串的轉換可使用C API者Win32 API.C API: mbstowcs,wcstombsWin32 API: MultiByteToWideChar, WideCharToMultiByte下面着重介紹Win32 API的用法,C API的用法較爲簡單可參照Win32 API。首先是WideCharToMultiByte通常你需要配置4個參數(其他參數如是使用即可),依次是源寬字符串,需要轉換的長度(-1,則爲轉換整個字符串),目標多字節字符串,目標緩衝區長度。返回值表示轉換爲目標多字節字符串實際需要的長度(包括結束符)。所以通常需要調用WideCharToMultiByte兩次:第一次產生目標緩衝區長度,第二次產生目標字符串,像下面這樣
wchar_t* wcs = L"中國,你好!I Love You!";
int lengthOfMbs = WideCharToMultiByte( CP_ACP, 0, wcs, -1, NULL, 0, NULL, NULL);
char* mbs = new char[ lengthOfMbs ];
WideCharToMultiByte( CP_ACP, 0, wcs, -1, mbs, lengthOfMbs, NULL, NULL);
delete mbs;
mbs = NULL;
//=============
char* mbs = "中國,你好!I Love You!";
int lengthOfWcs = MultiByteToWideChar( CP_ACP, 0, mbs, -1, NULL, 0 );
wchar_t* wcs = new wchar_t[ lengthOfWcs ];
MultiByteToWideChar( CP_ACP, 0, mbs, -1, wcs, lengthOfWcs );
delete wcs;
wcs = NULL;
測試如下,正確、無亂碼
//=============
char* mbs2 = "中國,你好!I Love You!";
int lengthOfWcs = MultiByteToWideChar( CP_ACP, 0, mbs2, -1, NULL, 0 );
wchar_t* wcs2 = new wchar_t[ lengthOfWcs ];
MultiByteToWideChar( CP_ACP, 0, mbs2, -1, wcs2, lengthOfWcs );
LPCWSTR lpcwStr2 = (LPCWSTR)wcs2;
MessageBox(lpcwStr2);
delete wcs2;
wcs2 = NULL;
將CString轉爲char* LPSTR
CString mstr=_T("您好!");
char* mc= (LPSTR)(LPCWSTR)mstr;//此處錯誤
//先轉成寬字符串,然後再利用WideCharToMultiByte方法
wchar_t* wcs=mstr.GetBuffer();
int lengthOfMbs = WideCharToMultiByte( CP_ACP, 0, wcs, -1, NULL, 0, NULL, NULL);
char* mbs = new char[ lengthOfMbs ];
WideCharToMultiByte( CP_ACP, 0, wcs, -1, mbs, lengthOfMbs, NULL, NULL);
CString cs(mbs);
MessageBox(cs);
轉載//將單字節char*轉化爲寬字節wchar_t*
wchar_t* AnsiToUnicode( const char* szStr )
{
int nLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, szStr, -1, NULL, 0 );
if (nLen == 0)
{
return NULL;
}
wchar_t* pResult = new wchar_t[nLen];
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, szStr, -1, pResult, nLen );
return pResult;
}
//將寬字節wchar_t*轉化爲單字節char*
inline char* UnicodeToAnsi( const wchar_t* szStr )
{
int nLen = WideCharToMultiByte( CP_ACP, 0, szStr, -1, NULL, 0, NULL, NULL );
if (nLen == 0)
{
return NULL;
}
char* pResult = new char[nLen];
WideCharToMultiByte( CP_ACP, 0, szStr, -1, pResult, nLen, NULL, NULL );
return pResult;
}
===========================================================================================
也談C++中char*與wchar_t*之間的轉換
關於C++中的char*與wchar_t*這兩種類型的相互轉換,網上說的大多很繁瑣,可行性也不高。下面這個方法是在MSDN裏面找到的,個人認爲還比較不錯:
把char*轉換爲wchar_t*
用stdlib.h中的mbstowcs_s函數,可以通過下面的例子瞭解其用法:
char *CStr ="string to convert";
size_t len = strlen(CStr) + 1;
size_t converted = 0;
wchar_t *WStr;
WStr=(wchar_t*)malloc(len*sizeof(wchar_t));
mbstowcs_s(&converted, WStr, len, CStr, _TRUNCATE);
其結果是WStr中儲存了CStr的wchar_t版本。
把wchar_t*轉換爲char*
和上面的方法類似,用stdlib.h中的wcstombs_s函數,例子:
wchar_t *WStr = L"string to convert";
size_t len = wcslen(WStr) + 1;
size_t converted = 0;
char *CStr;
CStr=(char*)malloc(len*sizeof(char));
wcstombs_s(&converted, CStr, len, WStr, _TRUNCATE);
這時WStr中的內容將被轉化爲char版本儲存在CStr中。
另外還可以通過流的方法來char*類型轉換爲wchar_t*類型,但這樣的轉換得到的結果將是const類型,而類似的方法不能將wchar_t*類型轉換爲char*類型。
把(const)char*轉換爲const wchar_t*
需要用到 sstream 頭文件:
char *cstr="string to convert";
wstringstream wss;
wss<<cstr;
再調用wss.str().c_str();即可得到 const wchar_t*類型的返回值。
雖然stringstream流不能將wchar_t*轉換成char*,但可以用來進行數值類型和字符串之間的轉換,例如:
double d=2734792.934f;
stringstream ss;
ss<<d;
調用ss.str()可得到string類型字符串 ”273479e+006”,又如:
string str("299792458");
stringstream ss;
long i=0;
ss<<str;
ss>>i;
此時i=299792458。===========================================================
首先在編譯程序時經常會遇到這種問題:
錯誤 1 error C2664: “CWnd::MessageBoxW”: 不能將參數 1 從“const char [3]”轉換爲“LPCTSTR”
1、覺得很煩,一般的做法就是不管三七二十一,在字符串前面加一個‘L’:
如調用函數FUN(LPCTSTR str)時,不能 FUN("HELLO"); 而是FUN(L"HELLO");通常這樣做都比較能解決問題。
2、或者還可以用_T(), 即 FUN(_T("HELLO")); _T() 的別名還有 _TEXT(), TEXT()。
稍微研究了下,BSTR,LPSTR,LPWSTR,LPCTSTR,LPTSTR等這些讓人頭暈的東東。(還是C#裏簡單啊,直接tostring)
BSTR:是一個OLECHAR*類型的Unicode字符串,是一個COM字符串,帶長度前綴,與VB有關,沒怎麼用到過。
LPSTR:即 char *,指向以'/0'結尾的8位(單字節)ANSI字符數組指針
LPWSTR:即wchar_t *,指向'/0'結尾的16位(雙字節)Unicode字符數組指針
LPCSTR:即const char *
LPCWSTR:即const wchar_t *
注意下面兩個類型
LPTSTR:LPSTR、LPWSTR兩者二選一,取決於是否宏定義了UNICODE或ANSI
LPCTSTR: LPCSTR、LPCWSTR兩者二選一,取決於是否宏定義了UNICODE或ANSI,如下是從MFC庫中拷來的:
#ifdef UNICODE
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef LPSTR LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
3、相互轉換方法:
LPWSTR->LPTSTR: W2T();
LPTSTR->LPWSTR: T2W();
LPCWSTR->LPCSTR: W2CT();
LPCSTR->LPCWSTR: T2CW();
ANSI->UNICODE: A2W();
UNICODE->ANSI: W2A();
另外,CString轉爲CStringW方法(通過一個wchar_t數組來轉)
CString str;
CStringW strw;
wchar_t *text = new wchar_t[sizeof(wchar_t) * str.GetLength()];
MultiByteToWideChar(CP_ACP,0,str,-1,text,str.GetLength());
strw = text;
4、另外,還可以強行轉換,不過不一定能成功
5、還有_bstr_t ( 對BTSTR的封裝,需要#include<comdef.h> ),也可將單字節字符數組指針轉爲雙字節字符數組指針,還沒怎麼沒用到過。
==========================================================
L表示long指針
這是爲了兼容Windows 3.1等16位操作系統遺留下來的,在win32中以及其他的32爲操作系統中, long指針和near指針及far修飾符都是爲了兼容的作用。沒有實際意義。
P表示這是一個指針
C表示是一個常量
T表示在Win32環境中, 有一個_T宏,這個宏用來表示你的字符是否使用UNICODE, 如果你的程序定義了UNICODE或者其他相關的宏,那麼這個字符或者字符串將被作爲UNICODE字符串,否則就是標準的ANSI字符串。
STR表示這個變量是一個字符串
所以LPCTSTR就表示一個指向常固定地址的可以根據一些宏定義改變語義的字符串。或者爲LPCSTR ,或者爲LPCWSTR
同樣, LPCSTR就只能是一個ANSI字符串,在程序中我們大部分時間要使用帶T的類型定義。
LPCTSTR == const TCHAR *
CString 和 LPCTSTR 可以說通用。 原因在於CString定義的自動類型轉換,沒什麼奇特的,最簡單的C++操作符重載而已。
常量字符串ansi和unicode的區分是由宏_T來決定的。但是用_T("abcd")時,字符串"abcd"就會根據編譯時的是否定一_UNICODE來決定是char* 還是 w_char*。 同樣,TCHAR 也是相同目的字符宏。看看定義就明白了。簡單起見,下面只介紹 ansi 的情況,unicode 可以類推。
ansi情況下,LPCTSTR 就是 const char*, 是常量字符串(不能修改的)。
而LPTSTR 就是 char*, 即普通字符串(非常量,可修改的)。
這兩種都是基本類型, 而CString 是 C++類, 兼容這兩種基本類型是最起碼的任務了。
由於const char* 最簡單(常量,不涉及內存變更,操作迅速), CString 直接定義了一個類型轉換函數
operator LPCTSTR() {......}, 直接返回他所維護的字符串。
當你需要一個const char* 而傳入了CString時, C++編譯器自動調用 CString重載的操作符 LPCTSTR()來進行隱式的類型轉換。
當需要CString , 而傳入了 const char* 時(其實 char* 也可以),C++編譯器則自動調用CString的構造函數來構造臨時的 CString對象。
因此CString 和 LPCTSTR 基本可以通用。
CString 轉LPCTSTR:
CString cStr;
const char *lpctStr=(LPCTSTR)cStr;
LPCTSTR轉CString:
LPCTSTR lpctStr;
CString cStr=lpctStr;
但是 LPTSTR又不同了,他是 char*, 意味着你隨時可能修改裏面的數據,這就需要內存管理了(如字符串變長,原來的存貯空間就不夠了,則需要重新調整分配內存)。
所以 不能隨便的將 const char* 強制轉換成 char* 使用。
樓主舉的例子
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
就是這種不安全的使用方法。
這個地方使用的是強制類型轉換,你都強制轉換了,C++編譯器當然不會拒絕你,但同時他也認爲你確實知道自己要做的是什麼。因此是不會給出警告的。
強制的任意類型轉換是C(++)的一項強大之處,但也是一大弊端。這一問題在 vc6 以後的版本(僅針對vc而言)中得到逐步的改進(你需要更明確的類型轉換聲明)。
其實在很多地方都可以看到類似
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
地用法,這種情況一般是函數的約束定義不夠完善的原因,比如一個函數接受一個字符串參數的輸入,裏面對該字符串又沒有任何的修改,那麼該參數就應該定義成 const char*,但是很多初學者弄不清const地用法,或者是懶, 總之就是隨意寫成了 char* 。 這樣子傳入CString時就需要強制的轉換一下。
這種做法是不安全的,也是不被建議的用法,你必須完全明白、確認該字符串沒有被修改。
CString 轉換到 LPTSTR (char*), 預定的做法是調用CString的GetBuffer函數,使用完畢之後一般都要再調用ReleaseBuffer函數來確認修改 (某些情況下也有不調用ReleaseBuffer的,同樣你需要非常明確爲什麼這麼做時才能這樣子處理,一般應用環境可以不考慮這種情況)。
同時需要注意的是, 在GetBuffer 和 ReleaseBuffer之間,CString分配了內存交由你來處理,因此不能再調用其他的CString函數。
=============================================================
1.區別wchar_t,char,WCHAR
ANSI:即 char,可用字符串處理函數:strcat( ),strcpy( ), strlen( )等以str打頭的函數。
UNICODE:wchar_t是Unicode字符的數據類型,它實際定義在裏:
typedef unsigned short wchar_t;
另外,在頭文件中有這樣的定義:typedef wchar_t WCHAR; 所以WCHAR實際就是wchar_t
wchar_t 可用字符串處理函數:wcscat(),wcscpy(),wcslen()等以wcs打頭的函數。爲了讓編譯器識別Unicode字符串,必須以在前面加一個“L”,例如: wchar_t *szTest=L"This is a Unicode string.";
2.TCHAR
在C語言裏面提供了 _UNICODE宏(有下劃線),在Windows裏面提供了UNICODE宏(無下劃線),只要定了_UNICODE宏和UNICODE宏,系統就會自 動切換到UNICODE版本,否則,系統按照ANSI的方式進行編譯和運行。只定義了宏並不能實現自動的轉換,他還需要一系列的字符定義支持。
1. TCHAR
如果定義了UNICODE宏則TCHAR被定義爲wchar_t。
typedef wchar_t TCHAR;
否則TCHAR被定義爲char typedef char TCHAR;
2. LPTSTR
如果定義了UNICODE宏則LPTSTR被定義爲LPWSTR。
typedef LPTSTR LPWSTR;
否則TCHAR被定義爲char typedef LPTSTR LPSTR;
說明:在使用字符串常量的時候需要使用_TEXT(“MyStr”)或者_T("")來支持系統的自動轉換。
3.BSTR
BSTR是一個帶長度前綴的字符串,主要由操作系統來管理的,所以要用api.主要用來和VB打交道的(VB裏的string就是指它)要操作它的API函數有很多.比如SysAllocString,SysFreeString等等.
vc裏封裝它的類如_bstr_t,及ATL中的CComBSTR等.
一個 BSTR 由頭部和字符串組成,頭部包含了字符串的長度信息,字符串中可以包含嵌入的 null 值。
BSTR 是以指針的形式進行傳遞的。(指針是一個變量,包含另外一個變量的內存地址,而不是數據。) BSTR 是 Unicode 的,即每個字符需要兩個字節。 BSTR 通常以兩字節的 null 字符結束。 wstr是寬字符,以雙字節表示一個字符 bstr是爲了與原先的basic字符兼容,它的最前面的4個字節爲其長度,以'\0'結束.
4.更進一步的字符串以及其指針的類型定義
由於Win32 API文檔的函數列表使用函數的常用名字(例如, "SetWindowText"),所有的字符串都是用TCHAR來定義的。(除了XP中引入的只適用於Unicode的API)。下面列出一些常用的typedefs,你可以在msdn中看到他們。
type Meaning in MBCS builds Meaning in Unicode builds
WCHAR wchar_t wchar_t
LPSTR char* char*
LPCSTR const char* const char*
LPWSTR wchar_t* wchar_t*
LPCWSTR wchar_t* wchar_t*
TCHAR TCHAR char wchar_t
LPTSTR TCHAR* TCHAR*
LPCTSTR const TCHAR* const TCHAR*
5.相互轉換
(1) char*轉換成CString
若將char*轉換成CString,除了直接賦值外,還可使用CString::Format進行。例如:
char chArray[] = "This is a test";
char * p = "This is a test";
或
LPSTR p = "This is a test";
或在已定義Unicode應的用程序中
TCHAR * p = _T("This is a test");
或
LPTSTR p = _T("This is a test");
CString theString = chArray;
theString.Format(_T("%s"), chArray);
theString = p;
(2) CString轉換成char*
若將CString類轉換成char*(LPSTR)類型,常常使用下列三種方法:
方法一,使用強制轉換。例如:
CString theString( "This is a test" );
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
方法二,使用strcpy。例如:
CString theString( "This is a test" );
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
_tcscpy(lpsz, theString);
需要說明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二個參數是 const wchar_t* (Unicode)或const char* (ANSI),系統編譯器將會自動對其進行轉換。
方法三,使用CString::GetBuffer。例如:
CString s(_T("This is a test "));
LPTSTR p = s.GetBuffer();
// 在這裏添加使用p的代碼
if(p != NULL) *p = _T('\0');
s.ReleaseBuffer();
// 使用完後及時釋放,以便能使用其它的CString成員函數
(3) BSTR轉換成char*
方法一,使用ConvertBSTRToString。例如:
#include
#pragma comment(lib, "comsupp.lib")
int _tmain(int argc, _TCHAR* argv[]){
BSTR bstrText = ::SysAllocString(L"Test");
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText); // 用完釋放
delete[] lpszText2;
return 0;
}
方法二,使用_bstr_t的賦值運算符重載。例如:
_bstr_t b = bstrText;
char* lpszText2 = b;
(4) char*轉換成BSTR
方法一,使用SysAllocString等API函數。例如:
BSTR bstrText = ::SysAllocString(L"Test");
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
方法二,使用COleVariant或_variant_t。例如:
//COleVariant strVar("This is a test");
_variant_t strVar("This is a test");
BSTR bstrText = strVar.bstrVal;
方法三,使用_bstr_t,這是一種最簡單的方法。例如:
BSTR bstrText = _bstr_t("This is a test");
方法四,使用CComBSTR。例如:
BSTR bstrText = CComBSTR("This is a test");
或
CComBSTR bstr("This is a test");
BSTR bstrText = bstr.m_str;
方法五,使用ConvertStringToBSTR。例如:
char* lpszText = "Test";
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
(5) CString轉換成BSTR
通常是通過使用CStringT::AllocSysString來實現。例如:
CString str("This is a test");
BSTR bstrText = str.AllocSysString();
…
SysFreeString(bstrText); // 用完釋放
(6) BSTR轉換成CString
一般可按下列方法進行:
BSTR bstrText = ::SysAllocString(L"Test");
CStringA str;
str.Empty();
str = bstrText;
或
CStringA str(bstrText);
(7) ANSI、Unicode和寬字符之間的轉換
方法一,使用MultiByteToWideChar將ANSI字符轉換成Unicode字符,使用WideCharToMultiByte將Unicode字符轉換成ANSI字符。
方法二,使用“_T”將ANSI轉換成“一般”類型字符串,使用“L”將ANSI轉換成Unicode,而在託管C++環境中還可使用S將ANSI字符串轉換成String*對象。例如:
TCHAR tstr[] = _T("this is a test");
wchar_t wszStr[] = L"This is a test";
String* str = S”This is a test”;
方法三,使用ATL 7.0的轉換宏和類。ATL7.0在原有3.0基礎上完善和增加了許多字符串轉換宏以及提供相應的類,它具有如圖3所示的統一形式:
其中,第一個C表示“類”,以便於ATL 3.0宏相區別,第二個C表示常量,2表示“to”,EX表示要開闢一定大小的緩衝。SourceType和DestinationType可以是A、 T、W和OLE,其含義分別是ANSI、Unicode、“一般”類型和OLE字符串。例如,CA2CT就是將ANSI轉換成一般類型的字符串常量。下面 是一些示例代碼:
LPTSTR tstr= CA2TEX<16>("this is a test");
LPCTSTR tcstr= CA2CT("this is a test");
wchar_t wszStr[] = L"This is a test";
char* chstr = CW2A(wszStr);
轉自:
http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010647.html
根據ANSI C標準的賦值約束條件:
1. 兩個操作數都是指向有限定符或無限定符的相容類型的指針。
2. 左邊指針所指向的類型必須具有右邊指針所指向類型的全部限定符。
一、const char*和char*
const char*的類型是:“指向一個具有const限定符的char類型的指針”。(不能修改其值)
char*的類型是:“指向一個char類型的指針”。
因此const char*和char*都是指向char類型的指針,只不過const char*指向的char類型是const的。
因此對於代碼:
char* src;
const char* dest ;
dest = src;
這樣賦值是正確的,因爲:
* 操作數指向的都是char類型,因此是相容的
* 左操作數具有有操作數所指向類型的全部限定符(右操作數沒有限定符),同時自己有限定符(const)
如果反過來賦值就違反了賦值的約束條件:src指向的對象的值可以修改,而dest指向的對象的值不可修改
如果讓src去指向dest所指向的那個不可修改的對象,如果合法,豈不是變得可修改了?
src = dest; // 這樣賦值,左操作數指向的類型沒有右操作數指向類型的const限定符,不符合約束條件2
2. const char** 和char**
const char**的類型是:“指向一個有const限定符的char類型的指針的指針”。
char**的類型是:“指向一個char類型的指針的指針”。
對於const char** 和char**來說,二者都是沒有限定符的指針類型,但是它們指向的類型不一樣,前者指向char*, 而後者指向const char*,因此它們不相容,所以char**類型的操作數不能賦值給const char**類型的操作數。
即對於下列代碼,編譯器會報錯:char** src;
const char** dest;
dest = src;
// error: invalid conversion from `char**' to `const char**'
-------------------------------------------------------------------------------------------------------------
const
1. 限定符聲明變量只能被讀
const int i=5;
int j=0;
...
i=j; //非法,導致編譯錯誤
j=i; //合法
2. 必須初始化
const int i=5; //合法
const int j; //非法,導致編譯錯誤
3. 在另一連接文件中引用const常量
extern const int i; //合法
extern const int j=10; //非法,常量不可以被再次賦值
4. 便於進行類型檢查
用const方法可以使編譯器對處理內容有更多瞭解。
#define I=10
const long &i=10; /*dapingguo提醒:由於編譯器的優化,使得在const long i=10; 時i不被分配內存,而是已10直接代入 以後的引用中,以致在以後的代碼中沒有錯誤,爲達到說教效 果,特別地用&i明確地給出了i的內存分配。 不過一旦你關閉所 有優化措施,即使const long i=10;也會引起後面的編譯錯誤。*/
char h=I; //沒有錯
char h=i; //編譯警告,可能由於數的截短帶來錯誤賦值。
5. 可以避免不必要的內存分配
#define STRING "abcdefghijklmn\n"
const char string[]="abcdefghijklm\n";
...
printf(STRING); //爲STRING分配了第一次內存
printf(string); //爲string一次分配了內存,以後不再分配
...
printf(STRING); //爲STRING分配了第二次內存
printf(string);
...
由於const定義常量從彙編的角度來看,只是給出了對應的內存地址,
而不是象#define一樣給出的是立即數,所以,const定義的常量在
程序運行過程中只有一份拷貝,而#define定義的常量在內存中有
若干個拷貝。
6. 可以通過函數對常量進行初始化
int value();
const int i=value();
dapingguo說:假定對ROM編寫程序時,由於目標代碼的不可改寫,本語句將會無效,不過可以變通一下:
const int &i=value();
只要令i的地址處於ROM之外,即可實現:i通過函數初始化,而其值有不會被修改。
7. 是不是const的常量值一定不可以被修改呢?
觀察以下一段代碼:
const int i=0;
int *p=(int*)&i;
p=100;
通過強制類型轉換,將地址賦給變量,再作修改即可以改變const常量值。
8. 請分清數值常量和指針常量,以下聲明頗爲玩味:
int ii=0;
const int i=0; //i是常量,i的值不會被修改
const int *p1i=&i; //指針p1i所指內容是常量,可以不初始化
int * const p2i=ⅈ //指針p2i是常量,所指內容可修改
const int * const p3i=&i; //指針p3i是常量,所指內容也是常量
p1i=ⅈ //合法
*p2i=100; //合法
關於C++中的const關鍵字的用法非常靈活,而使用const將大大改善程序的健壯性,參考了康建東兄的const使用詳解一文,對其中進行了一些補充,寫下了本文。
1. const常量,如const int max = 100;
優點:const常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查,而對後者只進行字符替換,沒有類型安全檢查,並且在字符替換時可能會產生意料不到的錯誤(邊際效應)
2. const 修飾類的數據成員。如:
class A
{
const int size;
… }
const數據成員只在某個對象生存期內是常量,而對於整個類而言卻是可變的。因爲類可以創建多個對象,不同的對象其const數據成員的值可以不同。所以不能在類聲明中初始化const數據成員,因爲類的對象未被創建時,編譯器不知道const 數據成員的值是什麼。如
class A
{
const int size = 100; //錯誤
int array[size]; //錯誤,未知的size
}
const數據成員的初始化只能在類的構造函數的初始化表中進行。要想建立在整個類中都恆定的常量,應該用類中的枚舉常量來實現。如
class A
{…
enum {size1=100, size2 = 200 };
int array1[size1];
int array2[size2];
}
枚舉常量不會佔用對象的存儲空間,他們在編譯時被全部求值。但是枚舉常量的隱含數據類型是整數,其最大值有限,且不能表示浮點數。
3. const修飾指針的情況,見下式:
int b = 500;
const int* a = & [1]
int const *a = & [2]
int* const a = & [3]
const int* const a = & [4]
如果你能區分出上述四種情況,那麼,恭喜你,你已經邁出了可喜的一步。不知道,也沒關係,我們可以參考《Effective c++》Item21上的做法,如果const位於星號的左側,則const就是用來修飾指針所指向的變量,即指針指向爲常量;如果const位於星號的 右側,const就是修飾指針本身,即指針本身是常量。因此,[1]和[2]的情況相同,都是指針所指向的內容爲常量(const放在變量聲明符的位置無 關),這種情況下不允許對內容進行更改操作,如不能*a = 3 ;[3]爲指針本身是常量,而指針所指向的內容不是常量,這種情況下不能對指針本身進行更改操作,如a++是錯誤的;[4]爲指針本身和指向的內容均爲常 量。
4. const的初始化
先看一下const變量初始化的情況
1) 非指針const常量初始化的情況:A b;
const A a = b;
2) 指針const常量初始化的情況:
A* d = new A();
const A* c = d;
或者:const A* c = new A();
3)引用const常量初始化的情況:
A f;
const A& e = f; // 這樣作e只能訪問聲明爲const的函數,而不能訪問一
般的成員函數;
[思考1]: 以下的這種賦值方法正確嗎?
const A* c=new A();
A* e = c;
[思考2]: 以下的這種賦值方法正確嗎?
A* const c = new A();
A* b = c;
5. 另外const 的一些強大的功能在於它在函數聲明中的應用。在一個函數聲明中,const 可以修飾函數的返回值,或某個參數;對於成員函數,還可以修飾是整個函數。有如下幾種情況,以下會逐漸的說明用法:A& operator=(const A& a);
void fun0(const A* a );
void fun1( ) const; // fun1( ) 爲類成員函數
const A fun2( );
1) 修飾參數的const,如 void fun0(const A* a ); void fun1(const A& a);
調 用函數的時候,用相應的變量初始化const常量,則在函數體中,按照const所修飾的部分進行常量化,如形參爲const A* a,則不能對傳遞進來的指針的內容進行改變,保護了原指針所指向的內容;如形參爲const A& a,則不能對傳遞進來的引用對象進行改變,保護了原對象的屬性。
[注意]:參數const通常用於參數爲指針或引用的情況,且只能修飾輸入參數;若輸入參數採用“值傳遞”方式,由於函數將自動產生臨時變量用於複製該參數,該參數本就不需要保護,所以不用const修飾。
[總結]對於非內部數據類型的輸入參數,因該將“值傳遞”的方式改爲“const引用傳遞”,目的是爲了提高效率。例如,將void Func(A a)改爲void Func(const A &a)
對於內部數據類型的輸入參數,不要將“值傳遞”的方式改爲“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如void Func(int x)不應該改爲void Func(const int &x)
2) 修飾返回值的const,如const A fun2( ); const A* fun3( );
這樣聲明瞭返回值後,const按照"修飾原則"進行修飾,起到相應的保護作用。const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}
返回值用const修飾可以防止允許這樣的操作發生:Rational a,b;
Radional c;
(a*b) = c;
一般用const修飾返回值爲對象本身(非引用和指針)的情況多用於二目操作符重載函數併產生新對象的時候。 [總結]
1. 一般情況下,函數的返回值爲某個對象時,如果將其聲明爲const時,多用於操作符的重載。通常,不建議用const修飾函數的返回值類型爲某個對象或對 某個對象引用的情況。原因如下:如果返回值爲某個對象爲const(const A test = A 實例)或某個對象的引用爲const(const A& test = A實例) ,則返回值具有const屬性,則返回實例只能訪問類A中的公有(保護)數據成員和const成員函數,並且不允許對其進行賦值操作,這在一般情況下很少 用到。
2. 如果給採用“指針傳遞”方式的函數返回值加const修飾,那麼函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const 修飾的同類型指針。如:
const char * GetString(void);
如下語句將出現編譯錯誤:
char *str=GetString();
正確的用法是:
const char *str=GetString();
3. 函數返回值採用“引用傳遞”的場合不多,這種方式一般只出現在類的賻值函數中,目的是爲了實現鏈式表達。如:
class A
{…
A &operate = (const A &other); //負值函數
}
A a,b,c; //a,b,c爲A的對象
…
a=b=c; //正常
(a=b)=c; //不正常,但是合法
若負值函數的返回值加const修飾,那麼該返回值的內容不允許修改,上例中a=b=c依然正確。(a=b)=c就不正確了。
[思考3]: 這樣定義賦值操作符重載函數可以嗎?
const A& operator=(const A& a);
6. 類成員函數中const的使用
一般放在函數體後,形如:void fun() const;
任何不會修改數據成員的函數都因該聲明爲const類型。如果在編寫const成員函數時,不慎修改了數據成員,或者調用了其他非const成員函數,編譯器將報錯,這大大提高了程序的健壯性。如:
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; //const 成員函數
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void) const
{
++m_num; //編譯錯誤,企圖修改數據成員m_num
Pop(); //編譯錯誤,企圖調用非const函數
Return m_num;
}
7. 使用const的一些建議
1 要大膽的使用const,這將給你帶來無盡的益處,但前提是你必須搞清楚原委;
2 要避免最一般的賦值操作錯誤,如將const變量賦值,具體可見思考題;
3 在參數中使用const應該使用引用或指針,而不是一般的對象實例,原因同上;
4 const在成員函數中的三種用法(參數、返回值、函數)要很好的使用;
5 不要輕易的將函數的返回值類型定爲const;
6除了重載操作符外一般不要將返回值類型定爲對某個對象的const引用;
[思考題答案]
1 這種方法不正確,因爲聲明指針的目的是爲了對其指向的內容進行改變,而聲明的指針e指向的是一個常量,所以不正確;
2 這種方法正確,因爲聲明指針所指向的內容可變;
3 這種做法不正確;
在const A::operator=(const A& a)中,參數列表中的const的用法正確,而當這樣連續賦值的時侯,問題就出現了:
A a,b,c:
(a=b)=c;
因爲a.operator=(b)的返回值是對a的const引用,不能再將c賦值給const常量。
C函數形參裏面爲何要用:const char *inputString
保護實參指針指向的數據不被意外改寫。
const char *inputString;//定義指向常量的指針
指針本身地址值可以增減,也可以給指針更換指向的常量,但是指向的內容有常量性質,指向的內容不能被更改。即:
inputString++;//這是可以的。
(*inputString)++;//這是禁止的。
強制類型轉換的實質是告訴編譯器,"這可行, 這行的通... 至於程序會不會出錯, 編譯器則不管, 因爲你都說可行了."
兩個操作數都是指向有限定符或無限定符的相容類型的指針,左邊指針所指向的類型必須具有右邊指針所指向類型的全部限定符。
正是這個條件,使得函數調用中實參char *能夠與形參const char *匹配(在C標準庫中,所有的字符串處理函數就是這樣的)。
函數那部分的時候,有一個很重要的概念是區別:值傳遞、指針傳遞、引用傳值(好像是這三種說法)。
我覺得要理解這部分知識點,首先應該知道不同種類的變量在內存中是如何分配存儲的,它們的生命週期多長等這些問題,然後在理解哪三種情況就好理解了。函數的參數一般都
是在stack棧上分配的,所以它的生命週期就在它所屬的函數內,函數執行完畢的時候,它的內存將被回收。
如果我們想在函數內對實際參數進行操作(不是對形式參數的副本)的話,一般會使用引用,即聲明函數的形式參數爲引用類型,比如char * fun(char * &p),這樣實參和形參爲同一個變量,我們在函數中操作形參p就等於直接在操作實參變量。我看C++語法書的時候,書上說這樣用還有一個好處是,在調用函數的時候,不用再爲形式參數分配內存了,這樣執行效率會高一點兒。
下面是函數形參爲指針的幾種情況:
#include <iostream>
using namespace std;
char* func1(char *p);
void func2(char *p);
void func3(char * &p);
char s1[]="原來的";
char s2[]="指向我了嗎";
int main()
{
char *ptr=s1;
cout<<ptr<<endl;
ptr=func1(ptr); //返回值改變ptr使它指向另一個地址
//func2(ptr); //ptr的指向沒有改變,func2函數中改變的只是它的副本(一個局部變量)
//func3(ptr); //改變了ptr的指向,func3函數的形式參數爲引用類型,實參和形參是同一個變量
cout<<ptr<<endl;
return 0;
}
char* func1(char *p)
{
p=s2;
return p;
}
void func2(char *p)
{
p=s2;
}
void func3(char * &p)
{
p=s2;
}
===============================
在ActiveX編程中,會涉及到BSTR字符串的轉換
char*轉換成CString
若將char*轉換成CString,除了直接賦值外,還可使用CString::Format進行。例如:
char chArray[] = "This is a test";
char * p = "This is a test";
或
LPSTR p = "This is a test";
或在已定義Unicode應的用程序中
TCHAR * p = _T("This is a test");
或
LPTSTR p = _T("This is a test");
CString theString = chArray;
theString.Format(_T("%s"), chArray);
theString = p;
CString轉換成char*
若將CString類轉換成char*(LPSTR)類型,常常使用下列三種方法:
方法一,使用強制轉換。例如:
CString theString( "This is a test" );
LPTSTR lpsz =(LPTSTR)(LPCTSTR)theString;
方法二,使用strcpy。例如:
CString theString( "This is a test" );
LPTSTR lpsz = new TCHAR[theString.GetLength()+1];
_tcscpy(lpsz, theString);
需要說明的是,strcpy(或可移值Unicode/MBCS的_tcscpy)的第二個參數是 const wchar_t* (Unicode)或const char* (ANSI),系統編譯器將會自動對其進行轉換。
方法三,使用CString::GetBuffer。例如:
CString s(_T("This is a test "));
LPTSTR p = s.GetBuffer();
// 在這裏添加使用p的代碼
if(p != NULL) *p = _T(’\0’);
s.ReleaseBuffer();
// 使用完後及時釋放,以便能使用其它的CString成員函數
BSTR轉換成char*
方法一,使用ConvertBSTRToString。例如:
#include "comutil.h"
#pragma comment(lib, "comsupp.lib")
int _tmain(int argc, _TCHAR* argv[]){
BSTR bstrText = ::SysAllocString(L"Test");
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText); // 用完釋放
delete[] lpszText2;
return 0;
}
** 此方法不好,會造成內存泄露,SysFreeString也沒有效果。
方法二,使用_bstr_t的賦值運算符重載。例如:
_bstr_t b = bstrText;
char* lpszText2 = b;
* 不會有內存泄露,推薦方法
char*轉換成BSTR
方法一,使用SysAllocString等API函數。例如:
BSTR bstrText = ::SysAllocString(L"Test");
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
方法二,使用COleVariant或_variant_t。例如:
//COleVariant strVar("This is a test");
_variant_t strVar("This is a test");
BSTR bstrText = strVar.bstrVal;
方法三,使用_bstr_t,這是一種最簡單的方法。例如:
BSTR bstrText = _bstr_t("This is a test");
方法四,使用CComBSTR。例如:
BSTR bstrText = CComBSTR("This is a test");
或
CComBSTR bstr("This is a test");
BSTR bstrText = bstr.m_str;
方法五,使用ConvertStringToBSTR。例如:
char* lpszText = "Test";
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
(5) CString轉換成BSTR
通常是通過使用CStringT::AllocSysString來實現。例如:
CString str("This is a test");
BSTR bstrText = str.AllocSysString();
…
SysFreeString(bstrText); // 用完釋放
BSTR轉換成CString
一般可按下列方法進行:
BSTR bstrText = ::SysAllocString(L"Test");
CStringA str;
str.Empty();
str = bstrText;
或
CStringA str(bstrText);
NSI、Unicode和寬字符之間的轉換
方法一,使用MultiByteToWideChar將ANSI字符轉換成Unicode字符,使用WideCharToMultiByte將Unicode字符轉換成ANSI字符。
方法二,使用“_T”將ANSI轉換成“一般”類型字符串,使用“L”將ANSI轉換成Unicode,而在託管C++環境中還可使用S將ANSI字符串轉換成String*對象。例如:
TCHAR tstr[] = _T("this is a test");
wchar_t wszStr[] = L"This is a test";
String* str = S”This is a test”;
方法三,使用ATL 7.0的轉換宏和類。ATL7.0在原有3.0基礎上完善和增加了許多字符串轉換宏以及提供相應的類。
其中,第一個C表示“類”,以便於ATL 3.0宏相區別,第二個C表示常量,2表示“to”,EX表示要開闢一定大小的緩衝。SourceType和DestinationType可以是A、T、W和OLE,其含義分別是ANSI、Unicode、“一般”類型和OLE字符串。例如,CA2CT就是將ANSI轉換成一般類型的字符串常量。下面是一些示例代碼:
LPTSTR tstr= CA2TEX<16>("this is a test");
LPCTSTR tcstr= CA2CT("this is a test");
wchar_t wszStr[] = L"This is a test";
char* chstr = CW2A(wszStr);
=======================================================================================
一、BSTR、LPSTR和LPWSTR
在VisualC++.NET的所有編程方式中,我們常常要用到這樣的一些基本字符串類型,如BSTR、LPSTR和LPWSTR等。之所以出現類似上述的這些數據類型,是因爲不同編程語言之間的數據交換以及對ANSI、Unicode和多字節字符集(MBCS)的支持。
那麼什麼是BSTR、LPSTR以及LPWSTR呢?
BSTR(BasicSTRing,Basic字符串)是一個OLECHAR*類型的Unicode字符串。它被描述成一個與自動化相兼容的類型。由於操作系統提供相應的API函數(如SysAllocString)來管理它以及一些默認的調度代碼,因此BSTR實際上就是一個COM字符串,但它卻在自動化技術以外的多種場合下得到廣泛使用。圖1描述了BSTR的結構,其中DWORD值是字符串中實際所佔用的字節數,且它的值是字符串中Unicode字符的兩倍。
LPSTR和LPWSTR是Win32和VC++所使用的一種字符串數據類型。LPSTR被定義成是一個指向以NULL(‘\0’)結尾的8位ANSI字符數組指針,而LPWSTR是一個指向以NULL結尾的16位雙字節字符數組指針。在VC++中,還有類似的字符串類型,如LPTSTR、LPCTSTR等,它們的含義如圖2所示。
例如,LPCTSTR是指“long pointer to a constant genericstring”,表示“一個指向一般字符串常量的長指針類型”,與C/C++的constchar*相映射,而LPTSTR映射爲 char*。
一般地,還有下列類型定義:
#ifdef UNICODE
typedef LPWSTR LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef LPSTR LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
二、CString、CStringA 和 CStringW
VisualC++.NET中將CStringT作爲ATL和MFC的共享的“一般”字符串類,它有CString、CStringA和CStringW三種形式,分別操作不同字符類型的字符串。這些字符類型是TCHAR、char和wchar_t。TCHAR在Unicode平臺中等同於WCHAR(16位Unicode字符),在ANSI中等價於char。wchar_t通常定義爲unsignedshort。由於CString在MFC應用程序中經常用到,這裏不再重複。
三、VARIANT、COleVariant 和_variant_t
在OLE、ActiveX和COM中,VARIANT數據類型提供了一種非常有效的機制,由於它既包含了數據本身,也包含了數據的類型,因而它可以實現各種不同的自動化數據的傳輸。下面讓我們來看看OAIDL.H文件中VARIANT定義的一個簡化版:
struct tagVARIANT {
VARTYPE vt;
union {
short iVal; // VT_I2.
long lVal; // VT_I4.
float fltVal; // VT_R4.
double dblVal; // VT_R8.
DATE date; // VT_DATE.
BSTR bstrVal; // VT_BSTR.
…
short * piVal; // VT_BYREF|VT_I2.
long * plVal; // VT_BYREF|VT_I4.
float * pfltVal; // VT_BYREF|VT_R4.
double * pdblVal; // VT_BYREF|VT_R8.
DATE * pdate; // VT_BYREF|VT_DATE.
BSTR * pbstrVal; // VT_BYREF|VT_BSTR.
};
};
顯然,VARIANT類型是一個C結構,它包含了一個類型成員vt、一些保留字節以及一個大的union類型。例如,如果vt爲VT_I2,那麼我們可以從iVal中讀出VARIANT的值。同樣,當給一個VARIANT變量賦值時,也要先指明其類型。例如:
VARIANT va;
:: VariantInit(&va); // 初始化
int a = 2002;
va.vt = VT_I4; // 指明long數據類型
va.lVal = a; // 賦值
爲了方便處理VARIANT類型的變量,Windows還提供了這樣一些非常有用的函數:
VariantInit —— 將變量初始化爲VT_EMPTY;
VariantClear —— 消除並初始化VARIANT;
VariantChangeType —— 改變VARIANT的類型;
VariantCopy ——釋放與目標VARIANT相連的內存並複製源VARIANT。
COleVariant類是對VARIANT結構的封裝。它的構造函數具有極爲強大大的功能,當對象構造時首先調用VariantInit進行初始化,然後根據參數中的標準類型調用相應的構造函數,並使用VariantCopy進行轉換賦值操作,當VARIANT對象不在有效範圍時,它的析構函數就會被自動調用,由於析構函數調用了VariantClear,因而相應的內存就會被自動清除。除此之外,COleVariant的賦值操作符在與VARIANT類型轉換中爲我們提供極大的方便。例如下面的代碼:
COleVariant v1("This is a test"); // 直接構造
COleVariant v2 = "This is a test";
// 結果是VT_BSTR類型,值爲"This is a test"
COleVariant v3((long)2002);
COleVariant v4 = (long)2002;
// 結果是VT_I4類型,值爲2002
_variant_t是一個用於COM的VARIANT類,它的功能與COleVariant相似。不過在VisualC++.NET的MFC應用程序中使用時需要在代碼文件前面添加下列兩句:
#include "comutil.h"
#pragma comment( lib, "comsupp.lib" )
四、CComBSTR和_bstr_t
CComBSTR是對BSTR數據類型封裝的一個ATL類,它的操作比較方便。例如:
CComBSTR bstr1;
bstr1 = "Bye"; // 直接賦值
OLECHAR* str = OLESTR("ta ta"); // 長度爲5的寬字符
CComBSTR bstr2(wcslen(str)); // 定義長度爲5
wcscpy(bstr2.m_str, str); // 將寬字符串複製到BSTR中
CComBSTR bstr3(5, OLESTR("Hello World"));
CComBSTR bstr4(5, "Hello World");
CComBSTR bstr5(OLESTR("Hey there"));
CComBSTR bstr6("Hey there");
CComBSTR bstr7(bstr6);
// 構造時複製,內容爲"Hey there"
_bstr_t是是C++對BSTR的封裝,它的構造和析構函數分別調用SysAllocString和SysFreeString函數,其他操作是借用BSTRAPI函數。與_variant_t相似,使用時也要添加comutil.h和comsupp.lib。
五、BSTR、char*和CString轉換
(3) BSTR轉換成char*
方法一,使用ConvertBSTRToString。例如:
#include
#pragma comment(lib, "comsupp.lib")
int _tmain(int argc, _TCHAR* argv[]){
BSTR bstrText = ::SysAllocString(L"Test");
char* lpszText2 = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText); // 用完釋放
delete[] lpszText2;
return 0;
}
方法二,使用_bstr_t的賦值運算符重載。例如:
_bstr_t b = bstrText;
char* lpszText2 = b;
(4) char*轉換成BSTR
方法一,使用SysAllocString等API函數。例如:
BSTR bstrText = ::SysAllocString(L"Test");
BSTR bstrText = ::SysAllocStringLen(L"Test",4);
BSTR bstrText = ::SysAllocStringByteLen("Test",4);
方法二,使用COleVariant或_variant_t。例如:
//COleVariant strVar("This is a test");
_variant_t strVar("This is a test");
BSTR bstrText = strVar.bstrVal;
方法三,使用_bstr_t,這是一種最簡單的方法。例如:
BSTR bstrText = _bstr_t("This is a test");
方法四,使用CComBSTR。例如:
BSTR bstrText = CComBSTR("This is a test");
或
CComBSTR bstr("This is a test");
BSTR bstrText = bstr.m_str;
方法五,使用ConvertStringToBSTR。例如:
char* lpszText = "Test";
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
(5) CString轉換成BSTR
通常是通過使用CStringT::AllocSysString來實現。例如:
CString str("This is a test");
BSTR bstrText = str.AllocSysString();
…
SysFreeString(bstrText); // 用完釋放
(6) BSTR轉換成CString
一般可按下列方法進行:
BSTR bstrText = ::SysAllocString(L"Test");
CStringA str;
str.Empty();
str = bstrText;
或
CStringA str(bstrText);
(7) ANSI、Unicode和寬字符之間的轉換
方法一,使用MultiByteToWideChar將ANSI字符轉換成Unicode字符,使用WideCharToMultiByte將Unicode字符轉換成ANSI字符。
方法二,使用“_T”將ANSI轉換成“一般”類型字符串,使用“L”將ANSI轉換成Unicode,而在託管C++環境中還可使用S將ANSI字符串轉換成String*對象。例如:
TCHAR tstr[] = _T("this is a test");
wchar_t wszStr[] = L"This is a test";
String* str = S”This is a test”;
方法三,使用ATL7.0的轉換宏和類。ATL7.0在原有3.0基礎上完善和增加了許多字符串轉換宏以及提供相應的類,它具有如圖3所示的統一形式:
其中,第一個C表示“類”,以便於ATL3.0宏相區別,第二個C表示常量,2表示“to”,EX表示要開闢一定大小的緩衝。SourceType和DestinationType可以是A、T、W和OLE,其含義分別是ANSI、Unicode、“一般”類型和OLE字符串。例如,CA2CT就是將ANSI轉換成一般類型的字符串常量。下面是一些示例代碼:
LPTSTR tstr= CA2TEX<16>("this is a test");LPCTSTR tcstr= CA2CT("this is a test");
wchar_t wszStr[] = L"This is a test";
char* chstr = CW2A(wszStr);
==============================================================
CString與LPCWSTR的轉化
問題起因:在寫WritePrivateProfileString寫.ini配置文件時在msdn中看到,如果想要寫得配置信息即時生效必須在之前使用WritePrivateProfileStringW來re-read一下目標.ini文件,其原文如下:
// so that future invocations of the application will see it
// without the user having to reboot the system
WritePrivateProfileStringW( NULL, NULL, NULL, L"appname.ini" );
查了一下msdn中WritePrivateProfileStringW的原型如下:
LPCWSTR lpAppName,
LPCWSTR lpKeyName,
LPCWSTR lpString,
LPCWSTR lpFileName )
其中的每個參數的類型都爲LPCWSTR,實際中獲得的文件名都爲CString,問題產生。
問題分析:
LPCWSTR 是Unicode字符串指針,初始化時串有多大,申請空間就有多大,以後存貯若超過則出現無法預料的結果,這是它與CString的不同之處。而CString是一個串類,內存空間類會自動管理。LPCWSTR 初始化如下:
由於LPCWSTR必須指向Unicode的字符串,問題的關鍵變成了Anis字符與Unicode字符之間的轉換,不同編碼間的轉換,通過查找資料可知,可以ATL中轉換宏可以用如下方法實現:
CString str=_T("TestStr");
USES_CONVERSION;
LPWSTR pwStr=new wchar_t[str.GetLength()+1];
wcscpy(pwStr,T2W((LPCTSTR)str));
CString str=_T("TestStr");
USES_CONVERSION;
LPWCSTR pwcStr = A2CW((LPCSTR)str);
MFC中CString和LPSTR是可以通用,其中A2CW表示(LPCSTR) -> (LPCWSTR),USER_CONVERSION表示用來定義一些中間變量,在使用ATL的轉換宏之前必須定義該語句。
順便也提一下,如果將LPCWSTR轉換成CString,那就更加容易,在msdn中的CString類說明中提到了可以直接用LPCWSTR來構造CString,所以可以進行如下的轉換代碼:
CString str(pcwStr);
問題總結:
在頭文件<atlconv.h>中定義了ATL提供的所有轉換宏,如:
A2W (LPCSTR) -> (LPWSTR)
W2CA (LPCWSTR) -> (LPCSTR)
W2A (LPCWSTR) -> (LPSTR)
所有的宏如下表所示:
A2BSTR | OLE2A | T2A | W2A |
A2COLE | OLE2BSTR | T2BSTR | W2BSTR |
A2CT | OLE2CA | T2CA | W2CA |
A2CW | OLE2CT | T2COLE | W2COLE |
A2OLE | OLE2CW | T2CW | W2CT |
A2T | OLE2T | T2OLE | W2OLE |
A2W | OLE2W | T2W | W2T |
上表中的宏函數,非常的有規律,每個字母都有確切的含義如下:
2 | to 的發音和 2 一樣,所以借用來表示“轉換爲、轉換到”的含義。 |
A | ANSI 字符串,也就是 MBCS。 |
W、OLE | 寬字符串,也就是 UNICODE。 |
T | 中間類型T。如果定義了 _UNICODE,則T表示W;如果定義了 _MBCS,則T表示A |
C | const 的縮寫 |
利用這些宏,可以快速的進行各種字符間的轉換。使用前必須包含頭文件,並且申明USER_CONVERSION;使用 ATL 轉換宏,由於不用釋放臨時空間,所以使用起來非常方便。但是考慮到棧空間的尺寸(VC 默認2M),使用時要注意幾點:
1、只適合於進行短字符串的轉換;
2、不要試圖在一個次數比較多的循環體內進行轉換;
3、不要試圖對字符型文件內容進行轉換,因爲文件尺寸一般情況下是比較大的;
4、對情況 2 和 3,要使用 MultiByteToWideChar() 和 WideCharToMultiByte();
對於這些方面更多的知識可以參見MSDN相關文檔(如:Article 3. Strings the OLE Way),問題總結中的一些資料來源於楊老師的Com編程文章http://www.vckbase.com/document/viewdoc/?id=1488,在此表示感謝。