一、物理大小:和裸指针一样大
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 字节。