首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

CString 完全操作指南:從基礎操作到 Unicode 移植與效能優化

     編輯:關於C++

前言:為什麼選擇 CString?

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 內部自動擴充。

三、Unicode 移植關鍵:_T() 巨集與字元/位元組陷阱

  • _T("text"):ANSI 下為 "text",Unicode 下為 L"text"。
  • 字元數 ≠ 位元組數:Unicode 下每字元 2 位元組。
  • 取長度用 lstrlen(字元數)而非 sizeof。
  • API 參數區分: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* 互轉核心機制

1. 建構/賦值:自動複製


CString s = _T("Hello");      // 由常數字串建構
CString s(p);                 // 由指標建構(內容被複製)
TCHAR* p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;                       // 結果 "GrayCat",原資料未受影響

2. 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 記憶體,嘗試寫入會編譯錯誤或執行期崩潰(緩衝區長度計數不會更新)。

3. 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();          // 長度自動更新

4. 控制項介面互操作(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)。
  • ANSI 專案需手動 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(""));
    }
}

八、字串資源表:國際化最佳實踐

  1. 將所有 UI 字串置於 STRINGTABLE 資源,ID 以 IDS_ 開頭。
  2. CString fmt; fmt.LoadString(IDS_READING_FILE);
  3. 進階技巧: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));

編譯器通常在「下一個分號」隱式呼叫臨時物件解構函式,導致緩衝區失效。

十、效能內幕:量子分配策略與記憶體碎片權衡

  • VC6 Release 模式採量子分配(64/128/256/512 位元組),+= 連接實際上是優化後的 strcat + 長度重算,效能媲美 C 風格。
  • 精確分配(debug 模式)長期運行會產生大量微小碎片,導致分頁交換激增、最終耗盡記憶體。
  • 量子策略讓配置器回收再利用記憶體塊,減少碎片,支援應用程式數月不重啟穩定運行。

避免 s = a + b + c + d(每個 + 產生臨時物件),改用 s = a; s += b; s += c; s += d;。

總結

CString 是 MFC 中設計最精良的類別之一。掌握其擁有權語意(複製語意)、緩衝區協定(GetBuffer/ReleaseBuffer)、Unicode 移植要訣、資源表整合,即能寫出健壯、高效、可國際化的字串處理程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved