文档目录

一、物理大小:和裸指针一样大

unique_ptr<int> up(new int(42));
栈上 (up 对象本身,8 字节):
+────────────────────+
| ptr (8字节,64位)   │  ← 就是指向堆上 int 的指针
+────────────────────+

堆上:
+────────────────────+
| 42                  │
+────────────────────+

关键:默认情况下(deleter 是无状态的函数对象/函数指针),unique_ptr 就是一个指针的大小,没有任何额外成员。它的析构函数在编译期被展开为 delete ptr,和裸指针手动 delete 完全一样。

// 汇编层面完全等价:
delete raw_ptr;          // mov rdi, [raw_ptr]; call operator delete
up.reset();              // 同上,mov rdi, [up.ptr]; call operator delete

这就是"零运行时开销"的含义:unique_ptr 在 Release 编译下和裸指针一样快。

二、为什么只能 std::move 不能拷贝?

unique_ptr<int> a(new int(42));
unique_ptr<int> b = a;          // ❌ 编译错误:拷贝构造被 delete
unique_ptr<int> c = std::move(a); // ✅ 移动:a 的指针置空,c 接管所有权

物理上的原因:

移动前:
a.ptr → [42] (堆)
b = a (如果允许):
a.ptr → [42] ← b.ptr  → 两个 unique_ptr 指向同一块内存 → 双重 delete 💥

移动后:
a.ptr → nullptr
c.ptr → [42] (堆) → 所有权唯一

面试追问:“如果 unique_ptr 的 deleter 是有状态的,size 会变大吗?”

  • 会!如果 deleter 是有状态的函数对象(比如存储了一个 FILE* 关闭函数),unique_ptr 的大小 = sizeof(ptr) + sizeof(deleter)。
  • 但如果是无状态的 deleter(如 default_delete<T>),通过 空基类优化(EBO) 将大小压缩回 8 字节。