首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C++ 裸指標 vs unique_ptr vs shared_ptr 效能實測:300 萬次佇列操作、自訂 new/delete 統計、移動語義優化

     編輯:更多程式設計知識

智慧指標引入的執行期開銷常被開發者質疑。本文設計嚴謹微基準:300 萬次 `push`/`pop` 循環,自訂 `operator new/delete` 精確統計目標型別配置/釋放次數,對比 **裸指標**、**unique_ptr**、**shared_ptr** 在 `std::queue` 中的實際耗時,並示範 `std::move` 正確移動語義用法。

測試架構

  • 目標型別:`int` (單一標量,最小化型別本身開銷)
  • 容器:`std::queue` / `std::queue>` / `std::queue>`
  • 迴圈次數:`MAX_LOOP = 3,000,000`
  • 計時:`GetTickCount()` 毫秒級 (Windows)
  • 記憶體統計:重載 `new(size_t, int flag)` / `delete(void*)`,旗標 `MY_INT_FLAG=0x12ff0102` 標記目標配置

核心程式碼片段

自訂 new/delete 統計


static size_t s_my_int_count = 0;
const int MY_INT_FLAG = 0x12ff0102;

void* operator new(size_t size, int flag) throw(std::bad_alloc) {
    int addLen = sizeof(size_t);
    void* p = std::malloc(addLen + 4 + size);
    if (!p) throw std::bad_alloc();
    if (flag == MY_INT_FLAG) ++s_my_int_count;
    memcpy(p, &size, addLen);
    memcpy((char*)p + addLen, &flag, 4);
    return (char*)p + addLen + 4;
}

void operator delete(void* q) throw() {
    int addLen = sizeof(size_t);
    void* p = (char*)q - addLen - 4;
    int flag; memcpy(&flag, (char*)p + addLen, 4);
    if (flag == MY_INT_FLAG) --s_my_int_count;
    std::free(p);
}

裸指標版本 (手動 delete)


queue q;
for (int i=0; i

unique_ptr 版本 (移動語義)


queue> q;
for (int i=0; i p(new(MY_INT_FLAG) int);
    q.push(std::move(p));  // 必須 move,unique_ptr 不可複製
}
while (!q.empty()) {
    unique_ptr p = std::move(q.front());  // front() 回傳左值引用,需 move 取得所有權
    q.pop();
    // 自動 delete
}

shared_ptr 版本 (複製語義、引用計數)


queue> q;
for (int i=0; i(new(MY_INT_FLAG) int));  // 複製構造,原子增減引用計數
}
while (!q.empty()) { q.pop(); }  // 自動釋放

實測結果 (Windows, Release 建置)

結論:`unique_ptr` 開銷僅 **36%**,`shared_ptr` 約 **2 倍**。若所有權單一且不需共享,**優先使用 `unique_ptr`**。

關鍵 C++11/14 技術點解析

指標類型耗時 (ms)相對比率關鍵開銷來源
裸指標 (raw)5,3751.00x (基準)無額外開銷
unique_ptr7,3131.36x移動構造/賦值、空指標檢查、刪除器呼叫
shared_ptr11,1712.08x控制塊配置 (雙重配置)、原子引用計數增減、快取未命中

最佳實務建議

  1. 預設選 `unique_ptr`:單一所有權、零成本抽象、編譯期防止複製錯誤。
  2. 需共享所有權才用 `shared_ptr`:多執行緒共享、觀察者模式、快取。
  3. 避免 `shared_ptr` 循環引用:使用 `weak_ptr` 打破環。
  4. 陣列用 `unique_ptr` 或 `shared_ptr` (C++17):正確呼叫 `delete[]`。
  5. 自訂配置器:大量小物件配置時,搭配物件池/記憶體池 (`operator new` 重載) 可進一步大幅降低配置開銷,效果遠超指標選擇差異。

擴展驗證:編譯器優化影響

  • **Release /O2** 開啟內聯、移除邊界檢查、優化原子操作,上述比率具代表性。
  • **Debug** 模式下智慧指標檢查 (如 `_DEBUG` 疊代器偵錯) 開銷劇增,**不可用 Debug 效能判斷 Release 表現**。
  • 現代編譯器 (MSVC 19.3+, GCC 11+, Clang 13+) 對 `unique_ptr` 幾乎零開銷內聯,`shared_ptr` 原子操作仍為主要成本。

以實測數據為依據,在架構設計時自信選擇智慧指標,兼得安全與效能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性程式碼體現效能影響
移動構造/賦值`unique_ptr` 刪除複製、提供 `move`避免深層複製,僅轉移指標所有權
`std::move``q.push(std::move(p))`將左值轉為右值參考,啟用移動語義
控制塊`shared_ptr` 內部配置控制塊額外堆配置、原子操作、記憶體局部性差
自定義刪除器`unique_ptr`零開銷 (空基類優化 EBO),支援陣列 `unique_ptr`