傳統 String 拷貝建構/指派皆 分配新記憶體、複製資料。若僅讀取 (如傳參、回傳、存入容器),這造成大量無效配置與複製開銷。Copy-On-Write (COW) 讓多個物件共享同一緩衝區,只有在修改時才複製。
int* refCount)。++*refCount。--*refCount,歸零時 delete[] buffer; delete refCount;。operator[] 非 const 版、append) 檢查 *refCount > 1 → 分離:配置新緩衝、複製資料、遞減舊計數、綁定新緩衝。class String {
char* data;
int* refCount;
public:
String(const char* s = "") : data(new char[strlen(s)+1]), refCount(new int(1)) {
strcpy(data, s);
}
String(const String& rhs) : data(rhs.data), refCount(rhs.refCount) {
++*refCount;
}
~String() { release(); }
String& operator=(const String& rhs) {
if (this != &rhs) {
release();
data = rhs.data;
refCount = rhs.refCount;
++*refCount;
}
return *this;
}
// 讀取:直接共享
char operator[](size_t i) const { return data[i]; }
// 寫入:COW 分離
char& operator[](size_t i) {
if (*refCount > 1) detach();
return data[i];
}
private:
void detach() {
--*refCount;
data = new char[strlen(data)+1];
strcpy(data, data); // 注意:這裡應為 strcpy(data, 原data),略
refCount = new int(1);
}
void release() {
if (--*refCount == 0) { delete[] data; delete refCount; }
}
};

將 refCount 放在緩衝區前 4 位元組,一次 new 完成,減少配置開銷、提升快取局部性。
class String {
char* data; // 指向實際字元資料 (已偏移 4 bytes)
public:
String(const char* s = "") {
size_t len = strlen(s);
char* buf = new char[len + 1 + 4]; // 4 bytes for refCount
*reinterpret_cast(buf) = 1; // refCount = 1
data = buf + 4;
strcpy(data, s);
}
String(const String& rhs) : data(rhs.data) { ++ref(); }
~String() { if (--ref() == 0) delete[] (data - 4); }
String& operator=(const String& rhs) {
if (this != &rhs) {
if (--ref() == 0) delete[] (data - 4);
data = rhs.data;
++ref();
}
return *this;
}
char operator[](size_t i) const { return data[i]; }
char& operator[](size_t i) {
if (ref() > 1) detach();
return data[i];
}
private:
int& ref() { return *reinterpret_cast(data - 4); }
void detach() {
--ref();
size_t len = strlen(data);
char* buf = new char[len + 1 + 4];
*reinterpret_cast(buf) = 1;
data = buf + 4;
strcpy(data, data - 4 + 4); // 複製原字串
}
};

++/-- 需原子操作 (std::atomic)。if (ref>1) detach() 之間可能有其他執行緒修改計數 → 需鎖或無鎖演算法。std::string (C++11 起) 棄用 COW,改用小字串優化 (SSO) + 移動語意,避免多執行緒複雜度與 COW 的鎖開銷。void testCOW() {
String s1("hello");
String s2 = s1; // 共享緩衝,refCount=2
String s3; s3 = s1; // 共享緩衝,refCount=3
cout << s1[0]; // 讀取,無分離
s2[0] = 'H'; // 寫入,觸發分離 s2 獨享新緩衝
// s1/s3 仍共享原緩衝,s2 獨立
}
| 技術 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| COW | 讀取零拷貝、單執行緒高效 | 多執行緒需鎖、寫入延遲、實作複雜 | 舊標準庫、單執行緒高頻讀少寫 |
| SSO (小字串優化) | 短字串零配置、無共享狀態、移動語意友善 | 長字串仍需配置 | 現代 std::string (C++11+) |
| 移動語意 | 轉移擁有權零拷貝、無參考計數 | 需 C++11、來源物件狀態改變 | 現代 C++ 通用最佳實踐 |
結論:理解 COW 是掌握共享擁有權、參考計數、延遲複製核心概念的絕佳切入點,但現代 C++ 應優先使用 std::string (SSO+移動) 或 std::shared_ptr 管理共享緩衝區。