C++ 原始指針易導致記憶體洩漏、重複釋放、懸空指針。智能指針通過 RAII + 引用計數自動管理對象生命周期。本文從零手寫一個引用計數智能指針 SmartPoint,深度解析核心機制。
SmartPoint (用戶層) UPoint (控制塊,內部) +-------------------+ +-------------------+ | UPoint* m_UPoint | ───→ | Point m_Point | ← 被管理對象 (值語義) | | | int m_iUse | ← 引用計數 +-------------------+ +-------------------+
UPoint 獨立分配,存儲對象副本 + 計數器SmartPoint 複製共享同一 UPoint*SmartPoint 析構時 delete m_UPoint
class Point {
public:
Point() : m_x(0), m_y(0) {}
Point(int x, int y) : m_x(x), m_y(y) {}
~Point() { std::cout << "Point destructed\n"; }
void SetX(int x) { m_x = x; }
void SetY(int y) { m_y = y; }
int GetX() const { return m_x; }
int GetY() const { return m_y; }
void SetName(const std::string& n) { m_name = n; }
void ShowInfo() const {
std::cout << "Point[" << m_name << "] = (" << m_x << ", " << m_y << ")\n";
}
private:
int m_x, m_y;
std::string m_name;
};
class UPoint {
friend class SmartPoint; // 關鍵:授予 SmartPoint 完全訪問權
private:
UPoint() : m_iUse(1) {}
UPoint(int x, int y) : m_Point(x, y), m_iUse(1) {}
UPoint(const Point& p) : m_Point(p), m_iUse(1) {}
Point m_Point; // 值語義存儲被管理對象
int m_iUse; // 引用計數
};
class SmartPoint {
public:
// 構造函數族
SmartPoint() : m_UPoint(new UPoint()) {}
SmartPoint(int x, int y) : m_UPoint(new UPoint(x, y)) {}
SmartPoint(const Point& p) : m_UPoint(new UPoint(p)) {}
// 拷貝構造:淺拷貝指標,計數+1
SmartPoint(const SmartPoint& other) : m_UPoint(other.m_UPoint) {
++m_UPoint->m_iUse;
std::cout << "Copy ctor, use=" << m_UPoint->m_iUse << "\n";
}
// 複製賦值:處理自賦值、舊計數-1、新計數+1
SmartPoint& operator=(const SmartPoint& other) {
std::cout << "Assignment operator\n";
if (this != &other) { // 自賦值檢查
++other.m_UPoint->m_iUse; // 先增新,防異常
// 釋放舊資源
if (--m_UPoint->m_iUse == 0) {
std::cout << "Deleting UPoint in assignment\n";
delete m_UPoint;
}
m_UPoint = other.m_UPoint; // 指向新控制塊
}
return *this;
}
// 析構:計數-1,歸零刪除
~SmartPoint() {
std::cout << "Destructor, use=" << m_UPoint->m_iUse - 1 << "\n";
if (--m_UPoint->m_iUse == 0) {
std::cout << "Deleting UPoint\n";
delete m_UPoint;
}
}
// 代理轉發接口
void SetX(int x) { m_UPoint->m_Point.SetX(x); }
void SetY(int y) { m_UPoint->m_Point.SetY(y); }
int GetX() const { return m_UPoint->m_Point.GetX(); }
int GetY() const { return m_UPoint->m_Point.GetY(); }
void SetName(const std::string& n) { m_UPoint->m_Point.SetName(n); }
void ShowInfo() const { m_UPoint->m_Point.ShowInfo(); }
private:
UPoint* m_UPoint; // 指向控制塊
};
int main() {
std::cout << "=== 創建 A ===\n";
SmartPoint* A = new SmartPoint();
A->SetName("Class A");
A->ShowInfo(); // Point[Class A] = (0, 0)
std::cout << "\n=== 拷貝構造 B = A ===\n";
SmartPoint* B = new SmartPoint(*A);
B->ShowInfo(); // 共享同一對象
std::cout << "\n=== 創建 C 並賦值 C = B ===\n";
SmartPoint* C = new SmartPoint();
*C = *B; // 複製賦值
A->ShowInfo();
std::cout << "\n=== 刪除 A ===\n";
delete A; // 計數 3→2,不刪除 UPoint
B->ShowInfo();
std::cout << "\n=== 刪除 B ===\n";
delete B; // 計數 2→1,不刪除
C->ShowInfo();
std::cout << "\n=== 刪除 C ===\n";
delete C; // 計數 1→0,刪除 UPoint → Point 析構
std::cin.get();
return 0;
}
=== 創建 A === Point[Class A] = (0, 0) === 拷貝構造 B = A === Copy ctor, use=2 Point[Class A] = (0, 0) === 創建 C 並賦值 C = B === Assignment operator Point[Class A] = (0, 0) === 刪除 A === Destructor, use=2 Point[Class A] = (0, 0) === 刪除 B === Destructor, use=1 Point[Class A] = (0, 0) === 刪除 C === Destructor, use=0 Deleting UPoint Point destructed
引用計數變化流程: 創建 A : UPoint(use=1) 拷貝 B=A : UPoint(use=2) ← 共享 賦值 C=B : UPoint(use=3) ← 共享 delete A : UPoint(use=2) ← 仍有 B,C delete B : UPoint(use=1) ← 仍有 C delete C : UPoint(use=0) ← 刪除 UPoint + Point
| 特性 | 手寫 SmartPoint | std::shared_ptr |
|---|---|---|
| 控制塊分配 | 單獨 new UPoint | 通常與對象同塊分配 (make_shared 優化) |
| 線程安全 | 無 (計數非原子) | 控制塊計數原子操作 |
| 弱引用 | 不支援 | std::weak_ptr 打破循環引用 |
| 刪除器 | 固定 delete | 可自定義 Deleter |
| 數組支援 | 無 | shared_ptr |
智能指針核心三件套:RAII 封裝 + 引用計數共享 + 歸零自動刪除。手寫實現能深度理解:控制塊分離設計、拷貝/賦值/析構的計數邏輯、自賦值檢查、異常安全 (先增新後減舊)。實際項目請直接使用 std::shared_ptr/std::unique_ptr,配合 std::make_shared 獲得最佳性能與安全性。