C++ 手動管理堆記憶體 (new/delete) 易導致洩漏、雙重釋放、懸空指針。現代 C++ (C++11/14/17/20) 以 RAII、智能指針、標準容器、自定義分配器構建安全、高效、無洩漏的記憶體模型。
int* p = new int(42); // 單對象
int* arr = new int[10]{}; // 陣列 (值初始化)
delete p; // 單對象釋放
delete[] arr; // 陣列釋放 (必須配對 [])
::operator new(size_t); // 全域分配函數 (可替換)
::operator delete(void*); // 全域釋放函數
// 類專屬:static void* operator new(size_t); static void operator delete(void*);
陷阱:new T[3] 返回 T*,無長度資訊,delete[] 依賴編譯器內部元數據 (通常存塊頭)。混用 delete/delete[] 未定義行為。
| 類型 | 所有權語義 | 拷貝/移動 | 適用場景 | 開銷 |
|---|---|---|---|---|
unique_ptr<T> | 獨占所有權 | 不可拷貝、可移動 | 單一擁有者、工廠返回、Pimpl、RAII 包裝 | 零 (等同裸指針) |
shared_ptr<T> | 共享所有權 (引用計數) | 可拷貝、可移動 | 多擁有者、觀察者模式、緩存、跨線程共享 | 控制塊 (2指針+計數)、原子操作 |
weak_ptr<T> | 非所有權觀察者 | 可拷貝、可移動 | 打破循環引用、緩存、臨時訪問 | 共享控制塊、無原子增減 |
auto p = std::make_unique<Widget>(args); // C++14 偏好,異常安全、單次分配
std::unique_ptr<Widget[]> arr(new Widget[10]); // 陣列特化,自動 delete[]
// 自定義刪除器
auto fp = std::unique_ptr<FILE, decltype(&fclose)>(fopen("a.txt","w"), fclose);
auto cuda = std::unique_ptr<int, CudaDeleter>(ptr); // lambda/function object
shared_ptr,B 持有 A 的 shared_ptr → 永不釋放。解決:一方改用 weak_ptr。shared_ptr<T>(new T) 兩次分配 (對象+控制塊)。解決:std::make_shared<T>() 單次分配 (但無法自定義刪除器/分配器)。shared_ptr<T>(this) 導致雙重控制塊。解決:繼承 std::enable_shared_from_this<T>,用 shared_from_this()。class FileHandle {
FILE* fp_;
public:
FileHandle(const char* path, const char* mode) : fp_(fopen(path, mode)) {
if (!fp_) throw std::runtime_error("open failed");
}
~FileHandle() { if (fp_) fclose(fp_); }
// 禁用拷貝,支援移動
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(FileHandle&& other) noexcept : fp_(other.fp_) { other.fp_ = nullptr; }
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) { if (fp_) fclose(fp_); fp_ = other.fp_; other.fp_ = nullptr; }
return *this;
}
FILE* get() const { return fp_; }
};
// 使用
{ FileHandle f("data.bin", "rb"); /* 自動關閉 */ }
// C++17 std::allocator 概念簡化
template <typename T>
struct PoolAllocator {
using value_type = T;
PoolAllocator() = default;
template <typename U> PoolAllocator(const PoolAllocator<U>&) {}
T* allocate(std::size_t n) {
// 從記憶體池取塊 (略)
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) { ::operator delete(p); }
template <typename U> bool operator==(const PoolAllocator<U>&) const { return true; }
};
// 使用
std::vector<int, PoolAllocator<int>> vec;
std::allocate_shared<Widget>(PoolAllocator<Widget>(), args);
// 對齊需求
alignas(64) int cache_line_aligned[16]; // 64 字節對齊,避免偽共享
struct alignas(32) AlignedStruct { double x, y, z; }; // 32 字節對齊
// aligned_alloc / std::aligned_alloc (C++17)
void* ptr = std::aligned_alloc(64, 1024); // 64 字節對齊分配 1KB
std::free(ptr); // 必須用 free 釋放
// new (C++17) 支援對齊
struct Aligned { alignas(128) char data[128]; };
Aligned* p = new Aligned; // 自動滿足 128 字節對齊
// std::assume_aligned (C++20) 提示編譯器
void process(int* p) {
p = std::assume_aligned<64>(p); // 編譯器假設 p 64 字節對齊,生成向量化指令
}
unique_ptr/shared_ptr/容器管理。std::make_unique 避免洩漏。vector, string, array。-fsanitize=address)、Valgrind、Dr. Memory、Visual Studio 診斷工具。