在 C++ 中,物件的複製、建構、解構往往伴隨著昂貴的記憶體分配與資料拷貝。參考計數技術讓多個智慧指標共享同一個物件實體,僅在最後一個引用釋放時才真正刪除物件,極大降低了複製成本。這是 Java 等語言自動記憶體管理的核心思想之一,在 C++ 中透過智慧指標實現。
Scott Meyers 在《More Effective C++》中給出的基礎框架:
class RCObject {
public:
RCObject() : refCount(0), shareable(true) {}
RCObject(const RCObject&) : refCount(0), shareable(true) {}
RCObject& operator=(const RCObject&) { return *this; }
virtual ~RCObject() = 0;
void addReference() { ++refCount; }
void removeReference() { if (--refCount == 0) delete this; }
void markUnshareable() { shareable = false; }
bool isShareable() const { return shareable; }
bool isShared() const { return refCount > 1; }
private:
int refCount;
bool shareable;
};
RCObject::~RCObject() {}
template <class T>
class RCPtr {
public:
RCPtr(T* realPtr = 0) : pointee(realPtr) { init(); }
RCPtr(const RCPtr& rhs) : pointee(rhs.pointee) { init(); }
~RCPtr() { if (pointee) pointee->removeReference(); }
RCPtr& operator=(const RCPtr& rhs) {
if (pointee != rhs.pointee) {
if (pointee) pointee->removeReference();
pointee = rhs.pointee;
init();
}
return *this;
}
T* operator->() const { return pointee; }
T& operator*() const { return *pointee; }
private:
T* pointee;
void init() {
if (!pointee) return;
if (!pointee->isShareable()) pointee = new T(*pointee);
pointee->addReference();
}
};
利用 RCPtr<StringValue> 實現 Copy-on-Write (COW) 字串:
class String {
public:
String(const char* value = "") : value(new StringValue(value)) {}
const char& operator[](int index) const { return value->data[index]; }
char& operator[](int index) {
if (value->isShared()) value = new StringValue(value->data);
value->markUnshareable();
return value->data[index];
}
private:
struct StringValue : public RCObject {
char* data;
StringValue(const char* initValue) { init(initValue); }
StringValue(const StringValue& rhs) { init(rhs.data); }
void init(const char* v) { data = new char[strlen(v)+1]; strcpy(data, v); }
~StringValue() { delete [] data; }
};
RCPtr<StringValue> value;
};
讀取操作直接回傳引用;寫入時若發現共享(isShared()),則觸發深拷貝並標記不可共享,確保修改不影響其他引用者。
當被管理物件(如 Widget)無法繼承 RCObject 時,可引入中介層 CountHolder:
template <class T>
class RCIPtr {
struct CountHolder : public RCObject {
~CountHolder() { delete pointee; }
T* pointee;
};
CountHolder* counter;
// ... 類似 RCPtr,但透過 counter 間接管理 pointee
void makeCopy() {
if (counter->isShared()) {
T* old = counter->pointee;
counter->removeReference();
counter = new CountHolder;
counter->pointee = new T(*old);
counter->addReference();
}
}
};
此模式將參考計數邏輯與業務物件完全解耦,適用於第三方庫或無法修改的舊代碼。
| 適用條件 | 說明 |
|---|---|
| 大量物件共享少量實值 | 如字串池、資源快取、飛行權模式 |
| 物件建構/銷毀成本高 | 大型資料結構、資料庫連線、GPU 資源 |
| 記憶體佔用敏感 | 嵌入式系統、大規模並發服務 |
refCount 的增減必須是原子操作,鎖或原子指令的成本可能抵消共享收益。C++11 標準庫已提供成熟的實現:
std::shared_ptr:標準參考計數智慧指標,執行緒安全的控制塊。std::weak_ptr:打破循環引用的觀察者。std::make_shared:單次分配物件與控制塊,提升效率並減少記憶體碎片。除非有極特殊需求(如自定義刪除器、異構控制塊),生產代碼應優先使用標準庫組件,避免重複造輪子帶來的維護負擔。
參考計數是資源管理的基石技術,理解其內部機制有助於正確選擇與使用智慧指標。但在多線程高併發場景下,務必權衡同步開銷與共享收益;對於簡單類別,移動語義與 std::unique_ptr 往往是更輕量、更安全的選擇。