前置基础:智能指针的核心使命

C++ 三大智能指针(std::unique_ptrstd::shared_ptrstd::weak_ptr,均位于 <memory> 头文件)。C++ 没有自动垃圾回收(GC),原生指针(raw pointer)的最大问题是手动管理内存容易导致泄漏、重复释放、悬垂指针。智能指针的核心使命是:封装原生指针,利用「RAII(资源获取即初始化)」机制,在对象析构时自动释放所管理的内存,无需手动调用 delete

三大智能指针的底层都基于 RAII,但所有权管理方式不同,这也决定了它们的底层结构和性能差异。

一、std::unique_ptr:独占式智能指针(最简单、最高效)

1. 核心设计思想

unique_ptr 实现独占所有权:一个 unique_ptr 独占它所管理的原生指针,不允许其他智能指针共享该指针,也不允许拷贝(C++11 后支持移动,转移所有权)。正因为独占,它的底层结构最简单,无额外开销(与原生指针几乎等价)。

2. 底层核心数据结构(简化版)

unique_ptr 是一个模板类,底层仅封装两个核心成员(无额外堆内存开销,属于「栈上智能指针」):

template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
private:
    T* ptr_; // 管理的原生指针(指向堆内存对象/数组)
    Deleter del_; // 删除器(默认 std::default_delete,负责释放内存)

    // 关键:禁用拷贝构造和拷贝赋值(保证独占性)
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

public:
    // 移动构造和移动赋值(允许转移所有权)
    unique_ptr(unique_ptr&& other) noexcept;
    unique_ptr& operator=(unique_ptr&& other) noexcept;

    // 其他核心接口...
};
关键细节拆解:
  1. 独占性的实现:通过显式删除拷贝构造和拷贝赋值运算符= delete),禁止将一个 unique_ptr 拷贝给另一个,从语法上保证 “独占”。
  2. 删除器(Deleter)
    • 默认删除器 std::default_delete<T>:底层调用 delete ptr_(针对单个对象)或 delete[] ptr_(针对数组,unique_ptr<T[]> 特化版本)。
    • 自定义删除器:支持传入函数指针、函数对象、lambda 表达式,用于特殊场景(如释放共享内存、文件句柄)。
    • 删除器是模板参数,而非成员变量(C++11 优化):若使用默认删除器,del_ 不会占用额外内存(空基类优化,EBO),保证 unique_ptr 的大小与原生指针一致。
  3. 无额外堆内存:所有数据(ptr_del_)都存储在栈上,析构时直接释放堆内存,无额外开销,是三大智能指针中性能最优的。

3. 核心接口底层实现

(1)构造函数
// 1. 默认构造:管理空指针
constexpr unique_ptr() noexcept : ptr_(nullptr), del_() {}

// 2. 原生指针构造(explicit 禁止隐式转换,避免意外)
explicit unique_ptr(T* ptr) noexcept : ptr_(ptr), del_() {}

// 3. 移动构造(转移所有权,other 变为空)
unique_ptr(unique_ptr&& other) noexcept 
    : ptr_(other.ptr_), del_(std::move(other.del_)) {
    other.ptr_ = nullptr; // 关键:将源对象的 ptr_ 置空,避免重复释放
}
(2)析构函数(核心:自动释放内存)
~unique_ptr() noexcept {
    if (ptr_ != nullptr) {
        del_(ptr_); // 调用删除器释放内存,默认是 delete ptr_
    }
}
  • 析构时仅判断 ptr_ 非空,然后调用删除器,无其他额外操作,效率极高。
  • 若管理的是数组(unique_ptr<T[]>),默认删除器会调用 delete[] ptr_,特化实现如下:
    template <typename T, typename Deleter>
    class unique_ptr<T[], Deleter> {
    private:
        T* ptr_;
        Deleter del_;
    public:
        ~unique_ptr() noexcept {
            if (ptr_ != nullptr) {
                del_(ptr_); // 默认 delete[] ptr_
            }
        }
    };
    
(3)移动赋值运算符
unique_ptr& operator=(unique_ptr&& other) noexcept {
    if (this != &other) { // 避免自赋值
        // 1. 先释放当前对象管理的内存(防止内存泄漏)
        if (ptr_ != nullptr) {
            del_(ptr_);
        }
        // 2. 转移 other 的所有权
        ptr_ = other.ptr_;
        del_ = std::move(other.del_);
        // 3. other 置空
        other.ptr_ = nullptr;
    }
    return *this;
}
(4)释放所有权(release ())
T* release() noexcept {
    T* temp = ptr_;
    ptr_ = nullptr; // 释放所有权,不释放内存
    return temp; // 返回原生指针,由调用者手动管理
}
  • 注意:release() 仅转移原生指针的所有权,不释放内存,与析构函数的核心区别。
(5)重置(reset ())
void reset(T* new_ptr = nullptr) noexcept {
    T* old_ptr = ptr_;
    ptr_ = new_ptr;
    if (old_ptr != nullptr) {
        del_(old_ptr); // 释放旧内存,管理新内存
    }
}

4. 底层内存管理流程(示例)

{
    unique_ptr<int> up(new int(10)); // 构造:ptr_ 指向堆内存的 10
    unique_ptr<int> up2 = std::move(up); // 移动构造:up2 接管所有权,up.ptr_ = nullptr
    up2.reset(); // 重置:调用 del_(ptr_),释放堆内存,up2.ptr_ = nullptr
} // 离开作用域,up2 析构:ptr_ 已为空,无操作;up 析构:ptr_ 为空,无操作
  • 全程无手动 delete,内存自动释放,且无额外开销。

5. 底层坑点与细节

  1. 禁止管理栈内存unique_ptr 的删除器默认调用 delete,若管理栈内存(unique_ptr<int> up(&a)),析构时会尝试释放栈内存,导致未定义行为。
  2. 不支持数组的下标访问(非特化版本)unique_ptr<int> up(new int[5]) 是错误的,析构时会调用 delete 而非 delete[],应使用 unique_ptr<int[]> up(new int[5]),特化版本支持 [] 下标访问。
  3. 移动后源对象为空:移动后的 unique_ptrptr_nullptr,不可再解引用,否则会触发空指针异常。

二、std::shared_ptr:共享式智能指针(最常用、有额外开销)

1. 核心设计思想

shared_ptr 实现共享所有权:多个 shared_ptr 可以共享同一个原生指针,当最后一个 shared_ptr 析构时,才会释放所管理的内存。其底层核心是引用计数(Reference Count),通过引用计数来跟踪当前有多少个 shared_ptr 共享该原生指针。

2. 底层核心数据结构(简化版)

shared_ptr 也是一个模板类,但底层结构比 unique_ptr 复杂,包含两个核心指针一个引用计数块(控制块),存在额外堆内存开销(控制块)。

(1)整体结构(模板类)
template <typename T, typename Deleter = std::default_delete<T>, typename Allocator = std::allocator<T>>
class shared_ptr {
private:
    T* ptr_; // 管理的原生指针(指向堆内存对象/数组,称为“对象指针”)
    ControlBlock* ctrl_block_; // 指向控制块的指针(称为“控制块指针”)

public:
    // 核心接口...
};
(2)控制块(Control Block):核心中的核心

控制块是 shared_ptr 底层的关键,每次创建一个新的 shared_ptr(非拷贝 / 移动)时,都会在堆上分配一个控制块,用于存储引用计数、删除器、分配器等信息。控制块的简化结构如下:

struct ControlBlock {
    size_t use_count_; // 强引用计数:当前共享该原生指针的 shared_ptr 个数
    size_t weak_count_; // 弱引用计数:当前观察该原生指针的 weak_ptr 个数
    std::default_delete<T> del_; // 删除器(存储在控制块,而非 shared_ptr 本身)
    std::allocator<T> alloc_; // 分配器(存储在控制块,用于释放控制块自身)

    // 构造函数
    ControlBlock() : use_count_(1), weak_count_(0), del_(), alloc_() {}
    ControlBlock(Deleter del, Allocator alloc) : use_count_(1), weak_count_(0), del_(del), alloc_(alloc) {}
};
关键细节拆解:
  1. 两个指针的作用
    • ptr_:直接指向管理的对象(如 new int(10)),用于解引用(*up)、下标访问等操作。
    • ctrl_block_:指向堆上的控制块,用于访问引用计数、删除器等信息,所有共享同一个对象的 shared_ptr,其 ctrl_block_ 都指向同一个控制块。
  2. 控制块的创建时机(必须牢记,避免引用计数错误):
    • 直接用原生指针构造 shared_ptr 时(shared_ptr<int> sp(new int(10))),创建新控制块。
    • std::make_shared 构造时(推荐),一次性分配 “对象内存 + 控制块内存”,效率更高(减少一次堆分配)。
    • 拷贝 / 移动 shared_ptr 时,不创建新控制块,仅复制 ctrl_block_ 指针,并递增强引用计数。
    • weak_ptr 构造 shared_ptr 时,不创建新控制块,仅递增强引用计数。
  3. 引用计数的含义
    • 强引用计数(use_count_):跟踪当前有效的 shared_ptr 个数,当 use_count_ 减至 0 时,销毁管理的对象(调用删除器),但不销毁控制块。
    • 弱引用计数(weak_count_):跟踪当前有效的 weak_ptr 个数,当 weak_count_ 减至 0 时,销毁控制块(释放控制块的堆内存)
  4. 删除器 / 分配器存储在控制块shared_ptr 的删除器和分配器不是模板参数的一部分(与 unique_ptr 不同),而是存储在控制块中,这使得 shared_ptr 的拷贝 / 移动更灵活,但控制块占用更多内存。
  5. 额外堆内存开销:控制块需要在堆上分配,这是 shared_ptr 相比 unique_ptr 的性能损耗点之一(还有引用计数的原子操作开销)。

3. 核心接口底层实现

(1)构造函数(两种核心场景)
场景 1:原生指针构造(创建新控制块)
explicit shared_ptr(T* ptr) : ptr_(ptr), ctrl_block_(nullptr) {
    if (ptr != nullptr) {
        // 1. 分配并构造控制块(堆上)
        using CtrlBlockAlloc = std::allocator<ControlBlock>;
        CtrlBlockAlloc alloc;
        ctrl_block_ = alloc.allocate(1);
        alloc.construct(ctrl_block_, ControlBlock()); // use_count_ = 1, weak_count_ = 0
    }
}
  • ptr 非空,在堆上分配控制块,并初始化强引用计数为 1,弱引用计数为 0。
场景 2:拷贝构造(共享控制块,递增强引用计数)
shared_ptr(const shared_ptr& other) noexcept : ptr_(other.ptr_), ctrl_block_(other.ctrl_block_) {
    if (ctrl_block_ != nullptr) {
        // 关键:原子递增强引用计数(线程安全)
        std::atomic_fetch_add(&ctrl_block_->use_count_, 1);
    }
}
  • 复制 otherptr_ctrl_block_,然后原子递增强引用计数(atomic_fetch_add 保证多线程环境下引用计数的正确性)。
  • 引用计数的操作必须是原子操作,这是 shared_ptr 线程安全的基础(但 shared_ptr 管理的对象本身不是线程安全的)。
场景 3:std::make_shared 构造(优化堆分配)

std::make_shared 是创建 shared_ptr 的推荐方式,底层一次性分配 “对象内存 + 控制块内存”,减少一次堆分配,效率更高:

template <typename T, typename... Args>
shared_ptr<T> make_shared(Args&&... args) {
    // 1. 一次性分配对象内存 + 控制块内存(连续堆内存)
    using Alloc = std::allocator<std::pair<ControlBlock, T>>;
    Alloc alloc;
    auto* mem = alloc.allocate(1); // 分配连续内存

    // 2. 构造控制块(use_count_ = 1)
    ControlBlock* ctrl_block = &mem->first;
    new (ctrl_block) ControlBlock();

    // 3. 构造对象(完美转发参数)
    T* ptr = &mem->second;
    new (ptr) T(std::forward<Args>(args)...);

    // 4. 构造 shared_ptr 并返回
    return shared_ptr<T>(ptr, ctrl_block);
}
  • 优势:减少一次堆分配和一次堆释放,控制块和对象在连续内存中,缓存友好性更好。
  • 劣势:对象内存和控制块内存绑定,即使 use_count_ 减至 0(对象销毁),控制块也需等待 weak_count_ 减至 0 才会释放,可能导致 “内存延迟释放”。
(2)析构函数(核心:递减强引用计数,释放对象和控制块)
~shared_ptr() noexcept {
    if (ctrl_block_ != nullptr) {
        // 1. 原子递减强引用计数
        size_t new_use_count = std::atomic_fetch_sub(&ctrl_block_->use_count_, 1);
        if (new_use_count == 1) { // 递减后 use_count_ = 0(当前是最后一个 shared_ptr)
            // 2. 销毁管理的对象(调用删除器)
            if (ptr_ != nullptr) {
                ctrl_block_->del_(ptr_);
            }
            // 3. 检查弱引用计数,若为 0,销毁控制块
            if (ctrl_block_->weak_count_ == 0) {
                // 释放控制块内存(使用控制块中的分配器)
                using CtrlBlockAlloc = std::allocator<ControlBlock>;
                CtrlBlockAlloc alloc = ctrl_block_->alloc_;
                alloc.destroy(ctrl_block_);
                alloc.deallocate(ctrl_block_, 1);
            }
        }
    }
}
  • 核心逻辑:只有当强引用计数减至 0 时,才销毁管理的对象;只有当弱引用计数也减至 0 时,才销毁控制块。
  • 原子操作保证多线程环境下引用计数的正确性,避免竞态条件。
(3)移动构造(转移所有权,无引用计数操作)
shared_ptr(shared_ptr&& other) noexcept : ptr_(other.ptr_), ctrl_block_(other.ctrl_block_) {
    // 关键:将 other 的指针置空,避免其析构时操作控制块
    other.ptr_ = nullptr;
    other.ctrl_block_ = nullptr;
}
  • 移动构造不修改引用计数,仅转移 ptr_ctrl_block_ 的所有权,效率与 unique_ptr 相当。
(4)重置(reset ())
void reset(T* new_ptr = nullptr) noexcept {
    // 1. 保存当前控制块(用于后续析构)
    ControlBlock* old_ctrl_block = ctrl_block_;
    T* old_ptr = ptr_;

    // 2. 重置当前指针
    ptr_ = new_ptr;
    ctrl_block_ = nullptr;

    // 3. 若有旧控制块,递减强引用计数(逻辑同析构函数)
    if (old_ctrl_block != nullptr) {
        size_t new_use_count = std::atomic_fetch_sub(&old_ctrl_block_->use_count_, 1);
        if (new_use_count == 1) {
            if (old_ptr != nullptr) {
                old_ctrl_block_->del_(old_ptr);
            }
            if (old_ctrl_block_->weak_count_ == 0) {
                using CtrlBlockAlloc = std::allocator<ControlBlock>;
                CtrlBlockAlloc alloc = old_ctrl_block_->alloc_;
                alloc.destroy(old_ctrl_block_);
                alloc.deallocate(old_ctrl_block_, 1);
            }
        }
    }

    // 4. 若有新指针,创建新控制块
    if (new_ptr != nullptr) {
        using CtrlBlockAlloc = std::allocator<ControlBlock>;
        CtrlBlockAlloc alloc;
        ctrl_block_ = alloc.allocate(1);
        alloc.construct(ctrl_block_, ControlBlock());
    }
}

4. 底层内存管理流程(示例)

{
    // 1. 创建 sp1,分配控制块(use_count_=1,weak_count_=0)
    shared_ptr<int> sp1(new int(10));
    // 2. 拷贝 sp1 到 sp2,共享控制块,use_count_ 原子递增为 2
    shared_ptr<int> sp2 = sp1;
    // 3. 移动 sp1 到 sp3,sp1 置空,use_count_ 仍为 2
    shared_ptr<int> sp3 = std::move(sp1);
    // 4. sp2 重置,use_count_ 原子递减为 1
    sp2.reset();
} // 离开作用域:
  // - sp3 析构:use_count_ 原子递减为 0,销毁对象(int(10)),weak_count_=0,销毁控制块
  // - sp2 析构:ctrl_block_ 已为空,无操作
  // - sp1 析构:ctrl_block_ 已为空,无操作
  • 只有最后一个 shared_ptr(sp3)析构时,才释放对象和控制块,实现共享所有权。

5. 底层坑点与细节

  1. 循环引用问题:这是 shared_ptr 最经典的底层坑,两个 shared_ptr 互相引用,会导致强引用计数无法减至 0,内存泄漏。例如:
    struct Node {
        shared_ptr<Node> next;
    };
    shared_ptr<Node> n1(new Node());
    shared_ptr<Node> n2(new Node());
    n1->next = n2;
    n2->next = n1; // 循环引用,n1 和 n2 的 use_count_ 均为 2,析构时无法减至 0
    
    • 解决方案:使用 weak_ptr 打破循环引用(见下文)。
  2. 避免多个 shared_ptr 管理同一个原生指针(非拷贝):这会创建多个独立的控制块,导致重复释放内存。例如:
    int* p = new int(10);
    shared_ptr<int> sp1(p);
    shared_ptr<int> sp2(p); // 错误:创建第二个控制块,sp1 和 sp2 析构时都会释放 p,重复释放
    
  3. 引用计数的原子操作仅保证计数本身安全shared_ptr 的引用计数操作是线程安全的,但对管理的对象的读写操作不是线程安全的,需要手动加锁。
  4. std::make_shared 无法指定自定义删除器:若需要使用自定义删除器,只能用原生指针构造 shared_ptr

三、std::weak_ptr:弱引用智能指针(辅助 shared_ptr,无所有权)

1. 核心设计思想

weak_ptr辅助 shared_ptr 的弱引用智能指针,它不拥有对象的所有权,仅能观察 shared_ptr 管理的对象。其核心作用是打破 shared_ptr 的循环引用,同时避免悬垂指针。

weak_ptr 底层也依赖 shared_ptr 的控制块,仅跟踪弱引用计数,不影响强引用计数,因此不会阻止 shared_ptr 释放对象。

2. 底层核心数据结构(简化版)

weak_ptr 也是一个模板类,底层结构比 shared_ptr 简单,仅包含一个控制块指针(无对象指针,因为不拥有所有权):

template <typename T>
class weak_ptr {
private:
    ControlBlock* ctrl_block_; // 指向 shared_ptr 的控制块(与 shared_ptr 共享同一个控制块)

public:
    // 核心接口...
};
关键细节拆解:
  1. 无对象指针(ptr_weak_ptr 不直接管理对象,因此不需要存储对象指针,仅需存储控制块指针,用于访问弱引用计数和检查对象是否存活。
  2. 不影响强引用计数weak_ptr 的创建、拷贝、移动都只会修改弱引用计数(weak_count_),不会修改强引用计数(use_count_),因此不会阻止 shared_ptr 释放对象。
  3. 必须从 shared_ptrweak_ptr 构造weak_ptr 不能直接用原生指针构造,只能从 shared_ptr 或其他 weak_ptr 构造,保证其控制块指针的有效性。
  4. 无解引用操作(*/->:因为 weak_ptr 不拥有对象所有权,对象可能已经被 shared_ptr 释放,因此不支持直接解引用,需要先通过 lock() 方法获取一个 shared_ptr,再解引用。

3. 核心接口底层实现

(1)构造函数(从 shared_ptr 构造)
weak_ptr(const shared_ptr<T>& sp) noexcept : ctrl_block_(sp.ctrl_block_) {
    if (ctrl_block_ != nullptr) {
        // 原子递增弱引用计数
        std::atomic_fetch_add(&ctrl_block_->weak_count_, 1);
    }
}
  • 复制 shared_ptr 的控制块指针,原子递增弱引用计数,不修改强引用计数。
(2)拷贝构造(从其他 weak_ptr 构造)
weak_ptr(const weak_ptr& other) noexcept : ctrl_block_(other.ctrl_block_) {
    if (ctrl_block_ != nullptr) {
        std::atomic_fetch_add(&ctrl_block_->weak_count_, 1);
    }
}
(3)析构函数(递减弱引用计数,销毁控制块)
~weak_ptr() noexcept {
    if (ctrl_block_ != nullptr) {
        // 1. 原子递减弱引用计数
        size_t new_weak_count = std::atomic_fetch_sub(&ctrl_block_->weak_count_, 1);
        if (new_weak_count == 1) { // 递减后 weak_count_ = 0
            // 2. 检查强引用计数,若为 0,销毁控制块(对象已被销毁)
            if (ctrl_block_->use_count_ == 0) {
                using CtrlBlockAlloc = std::allocator<ControlBlock>;
                CtrlBlockAlloc alloc = ctrl_block_->alloc_;
                alloc.destroy(ctrl_block_);
                alloc.deallocate(ctrl_block_, 1);
            }
        }
    }
}
  • 核心逻辑:仅递减弱引用计数,当弱引用计数减至 0 且强引用计数也为 0 时,销毁控制块。
  • 因为 weak_ptr 不拥有对象所有权,所以永远不会销毁管理的对象。
(4)lock() 方法(核心:获取有效的 shared_ptr

lock()weak_ptr 最常用的接口,底层逻辑是:检查对象是否存活(强引用计数 > 0),若存活,创建一个新的 shared_ptr,递增强引用计数;若不存活,返回一个空的 shared_ptr

shared_ptr<T> lock() const noexcept {
    if (ctrl_block_ != nullptr && ctrl_block_->use_count_ > 0) {
        // 1. 原子递增强引用计数
        std::atomic_fetch_add(&ctrl_block_->use_count_, 1);
        // 2. 创建并返回 shared_ptr
        return shared_ptr<T>(ctrl_block_->ptr_, ctrl_block_);
    }
    // 3. 对象已销毁,返回空 shared_ptr
    return shared_ptr<T>();
}
  • lock() 是线程安全的,通过检查强引用计数保证获取的 shared_ptr 管理的对象是存活的。
(5)expired() 方法(检查对象是否存活)
bool expired() const noexcept {
    return (ctrl_block_ == nullptr) || (ctrl_block_->use_count_ == 0);
}
  • 若返回 true,表示对象已被销毁;若返回 false,表示对象仍存活。

4. 底层内存管理流程(打破循环引用示例)

struct Node {
    weak_ptr<Node> next; // 用 weak_ptr 替代 shared_ptr,打破循环引用
};
{
    shared_ptr<Node> n1(new Node());
    shared_ptr<Node> n2(new Node());
    n1->next = n2;
    n2->next = n1; // n1->next 是 weak_ptr,不增加 n2 的 use_count_;n2->next 是 weak_ptr,不增加 n1 的 use_count_
} // 离开作用域:
  // - n1 析构:use_count_ 减至 0,销毁 n1 对象,检查 weak_count_(n2->next 引用),递减为 0,销毁控制块
  // - n2 析构:use_count_ 减至 0,销毁 n2 对象,检查 weak_count_(n1->next 引用),递减为 0,销毁控制块
  • weak_ptr 不增加强引用计数,因此循环引用被打破,内存正常释放。

5. 底层坑点与细节

  1. weak_ptr 不能直接解引用:必须先调用 lock() 获取 shared_ptr,再解引用,否则无法保证对象存活。
  2. lock() 返回的 shared_ptr 为空时,不可解引用:若对象已被销毁,lock() 返回空 shared_ptr,解引用会触发空指针异常。
  3. weak_ptr 也存在控制块开销weak_ptr 指向 shared_ptr 的控制块,因此也存在堆内存开销(控制块)。

四、三大智能指针底层对比(核心差异)

对比维度 std::unique_ptr std::shared_ptr std::weak_ptr
所有权 独占所有权 共享所有权 无所有权(弱引用)
底层核心结构 栈上:对象指针 + 删除器 栈上:对象指针 + 控制块指针;堆上:控制块(引用计数 + 删除器) 栈上:控制块指针(共享 shared_ptr 的控制块)
引用计数 强引用计数(use_count_)+ 弱引用计数(weak_count_) 仅跟踪弱引用计数(weak_count_)
额外开销 无(与原生指针等价) 有(控制块 + 原子操作) 有(控制块)
拷贝 / 移动 禁止拷贝,支持移动 支持拷贝(递增强引用)+ 移动 支持拷贝(递增弱引用)+ 移动
析构逻辑 直接释放对象(若非空) 强引用计数为 0 时释放对象,弱引用计数为 0 时释放控制块 递减弱引用计数,弱引用计数为 0 且强引用计数为 0 时释放控制块
核心用途 单个对象的独占管理,性能优先 多个对象共享同一个资源,灵活优先 打破 shared_ptr 循环引用,观察对象存活状态
循环引用 无此问题 存在循环引用问题,导致内存泄漏 解决 shared_ptr 循环引用问题

五、核心总结

  1. 底层核心思想:三大智能指针均基于 RAII 机制,在析构时自动释放资源,避免手动 delete;差异的核心是所有权管理方式
  2. unique_ptr 底层:独占所有权,栈上封装对象指针 + 删除器,无额外开销,禁用拷贝、支持移动,析构直接释放对象。
  3. shared_ptr 底层:共享所有权,依赖堆上控制块存储引用计数,拷贝递增强引用、析构递减强引用,最后一个释放对象,存在循环引用和额外开销问题。
  4. weak_ptr 底层:无所有权,共享 shared_ptr 的控制块,仅跟踪弱引用计数,通过 lock() 获取有效 shared_ptr,核心用于打破循环引用
  5. 性能优先级unique_ptr > shared_ptr > weak_ptrweak_ptr 通常配合 shared_ptr 使用),实际开发中优先使用 unique_ptr,需要共享时使用 shared_ptr,循环引用时用 weak_ptr
  6. 关键避坑shared_ptr 避免循环引用、避免多个 shared_ptr 管理同一个原生指针;unique_ptr 避免管理栈内存;weak_ptr 解引用前必须 lock() 检查。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐