MFC 的 CString 封裝了動態記憶體管理、參考計數、Unicode/ANSI 雙模支援,極大簡化字串操作。本文涵蓋連接、格式化、型別轉換、資源載入、臨時物件陷阱、效能內幕等實戰主題。
+ 運算子CString gray("Gray"), cat("Cat");
CString graycat = gray + cat; // 自動處理記憶體配置/複製
遠優於手動 malloc/strcpy/strcat 的錯誤風險與樣板碼。
Format() 取代 sprintf
CString s;
s.Format(_T("The total is %d"), total); // 緩衝區自動管理
s.Format(_T("%d"), total); // 整數轉字串
關鍵優勢:無需預估緩衝區大小,CString 內部自動擴充。
_T() 巨集與字元/位元組陷阱_T("text"):ANSI 下為 "text",Unicode 下為 L"text"。lstrlen(字元數)而非 sizeof。lstrcpyn 要字元數,WriteFile 要位元組數。
TCHAR data[20];
lstrcpyn(data, longstr, DIM(data)-1); // 正確:字元數
WriteFile(f, data, sizeof(data), ...); // 正確:位元組數
WriteFile(f, data, lstrlen(data)*sizeof(TCHAR), ...); // 正確寫入實際內容
自訂 DIM(x) 巨集:#define DIM(x) (sizeof(x)/sizeof(x[0])),同時適用陣列元素計數。
CString ↔ int 轉換
CString s;
s.Format(_T("%d"), value); // int → CString
int i = _ttoi(s); // CString → int (ANSI: atoi, Unicode: _wtoi)
long l = _tcstol(s, 0, 16); // 任意進制字串 → 長整數 (十六進制 "FAB" → 4011)
使用 _ttoi/_tcstol 等 tchar.h 巨集函式,自動對應 ANSI/Unicode 版本。
CString ↔ char*/TCHAR* 互轉核心機制
CString s = _T("Hello"); // 由常數字串建構
CString s(p); // 由指標建構(內容被複製)
TCHAR* p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p; // 結果 "GrayCat",原資料未受影響
LPCTSTR 強制轉換:唯讀存取CString 重載 operator LPCTSTR() 回傳內部緩衝區位址。
CString file(_T("c:\\path"));
BOOL ok = DoSomethingCool(file); // 隱式轉為 LPCTSTR
CString s;
s.Format(_T("Mew! I love %s"), graycat); // 可變參數中自動轉換
嚴禁修改:轉換得來的指標指向 const 記憶體,嘗試寫入會編譯錯誤或執行期崩潰(緩衝區長度計數不會更新)。
GetBuffer()/ReleaseBuffer():可寫存取協定LPTSTR p = s.GetBuffer(n):取得可寫緩衝區,n 為最小容量(字元數),0 表不延長。GetBuffer 到 ReleaseBuffer 之間,絕不可呼叫該 CString 任何成員函式(長度、內容皆處於不一致狀態)。ReleaseBuffer() 會重新計算實際長度(尋找 \\0)。
CString s(_T("File.ext"));
LPTSTR p = s.GetBuffer();
LPTSTR dot = _tcschr(p, _T('.'));
if (dot) *dot = _T('\\0'); // 截斷副檔名
s.ReleaseBuffer(); // 長度自動更新
TVITEM 等)結構成員 pszText 為 LPTSTR(可寫),CString 轉換得來的 LPCTSTR 無法直接賦值。
// 寫入控制項(SetItem/InsertItem):內容不會被修改
tvi.item.pszText = (LPTSTR)(LPCTSTR)s; // 雙重轉型:先取位址,再去 const
// 讀取控制項(GetItem):需預留可寫緩衝區
tvi.pszText = s.GetBuffer(MY_LIMIT);
tvi.cchTextMax = MY_LIMIT;
tree.GetItem(&tvi);
s.ReleaseBuffer();
CString ↔ BSTR(COM/ActiveX 字串)BSTR b = s.AllocSysString();:配置新 BSTR(呼叫端負責 SysFreeString)。CString s(b);:Unicode 建構函式直接接受 BSTR(LPCWSTR)。WideCharToMultiByte 轉換,注意 NULL 檢查與內嵌 NULL 字元問題。VARIANT → CString 通用轉換
CString VariantToString(VARIANT* va) {
switch (va->vt) {
case VT_BSTR: return CString(va->bstrVal);
case VT_I4: CString s; s.Format(_T("%d"), va->lVal); return s;
case VT_R8: CString s; s.Format(_T("%f"), va->dblVal); return s;
// ... 其他型別
default: return CString(_T(""));
}
}
STRINGTABLE 資源,ID 以 IDS_ 開頭。CString fmt; fmt.LoadString(IDS_READING_FILE);CString s(MAKEINTRESOURCE(IDS_WHATEVER)); 直接由 ID 建構(內部自動 LoadString)。ID 範圍 1~65535,絕不與合法指標重疊。格式化字串國際化原則:完整句子做資源(含格式參數),避免動詞位置差異導致語序錯誤。
GetName().GetBuffer() 為何失敗?
// 錯誤:GetName() 回傳臨時物件,GetBuffer() 後臨時物件在分號處解構,記憶體釋放
char* szName = GetName().GetBuffer(20);
// 正確:先保存至具名物件,延長生命週期
CString name = GetName();
RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,
(CONST BYTE*)(LPCTSTR)name,
(name.GetLength()+1)*sizeof(TCHAR));
編譯器通常在「下一個分號」隱式呼叫臨時物件解構函式,導致緩衝區失效。
+= 連接實際上是優化後的 strcat + 長度重算,效能媲美 C 風格。避免 s = a + b + c + d(每個 + 產生臨時物件),改用 s = a; s += b; s += c; s += d;。
CString 是 MFC 中設計最精良的類別之一。掌握其擁有權語意(複製語意)、緩衝區協定(GetBuffer/ReleaseBuffer)、Unicode 移植要訣、資源表整合,即能寫出健壯、高效、可國際化的字串處理程式碼。