C++11 智能指针详解
C++11 智能指针详解

1. 智能指针的引入:告别原始指针的困境

在 C++ 编程中,动态内存管理是一项核心但极具挑战性的任务。传统上,程序员使用原始指针(raw pointers)结合 new 和 delete(或 new 和 delete)操作符来手动分配和释放堆内存 1。尽管这种方式提供了对内存的精细控制,但也极易引入难以追踪和调试的错误 2。

1.1 手动内存管理的风险

手动管理内存的主要风险包括:

  • 内存泄漏 (Memory Leaks):当程序分配了内存但忘记释放(delete 或 delete),或者在释放前因为异常、提前返回等原因跳过了释放代码,就会导致内存泄漏 2。泄漏的内存无法被再次使用,持续累积可能耗尽系统资源,导致程序性能下降甚至崩溃 2。
    C++
    // 示例:异常可能导致的内存泄漏
    void processData() {
    MyResource* resource = new MyResource();
    if (resource->checkStatus() == Status::Error) {
    throw std::runtime_error(“Resource error”); // 如果抛出异常,delete 不会被执行
    }
    resource->use();
    delete resource; // 只有正常执行路径才会释放
    }

    4

  • 悬挂指针 (Dangling Pointers) / 使用已释放内存 (Use-After-Free):当一个指针指向的内存已经被释放,但该指针仍然被使用时,就产生了悬挂指针 1。对悬挂指针进行解引用或操作会导致未定义行为(Undefined Behavior, UB),轻则程序崩溃,重则可能引发严重的安全漏洞 6。
    C++
    // 示例:悬挂指针
    MyResource* ptr1 = new MyResource();
    MyResource* ptr2 = ptr1;
    delete ptr1; // ptr1 指向的内存被释放
    ptr1 = nullptr; // ptr1 置空,但 ptr2 仍然指向已释放的内存
    // ptr2->access(); // 危险!ptr2 是悬挂指针,访问已释放内存导致 UB

    6

  • 重复释放 (Double Deletion):对同一块内存执行多次 delete 或 delete 会破坏内存管理结构,通常导致程序立即崩溃 1。这通常发生在所有权不明确的情况下,多个部分都认为自己负责释放资源 8。
    C++
    // 示例:重复释放
    MyResource* ptr = new MyResource();
    //… 在程序的某个地方 delete ptr;…
    //… 在程序的另一个地方,由于逻辑错误或所有权不清,再次 delete ptr;… // 崩溃!

    6

  • 所有权语义模糊 (Ownership Ambiguity):原始指针本身不携带任何关于“谁负责释放内存”的信息 9。所有权完全依赖于程序员的约定和注释,这在复杂的代码库中极易出错,是导致内存泄漏和重复释放的根本原因之一 5。

这些问题的核心在于,手动内存管理将资源(内存)的生命周期与其管理责任分离开来,并且缺乏明确的所有权表达机制 12。当程序的控制流变得复杂(如多重返回路径、异常处理)时,确保在正确的时机、以正确的方式、不多不少地释放一次资源,对程序员来说是一个沉重的负担,且极易出错 4。

1.2 RAII 原则:自动化资源管理

为了解决这些问题,C++ 推崇一种强大的编程范式:资源获取即初始化 (Resource Acquisition Is Initialization, RAII) 1。RAII 的核心思想是将资源的生命周期与一个栈上对象的生命周期绑定 15。

具体做法是:在对象的构造函数中获取资源(如分配内存、打开文件、获取锁等),并在对象的析构函数中释放资源(如释放内存、关闭文件、释放锁等)15。由于 C++ 保证栈上对象的析构函数在其生命周期结束时(即离开作用域时)自动被调用,即使发生异常导致栈回溯 (stack unwinding),析构函数也总能被执行,从而确保资源被可靠地释放 15。

RAII 极大地简化了资源管理,提高了代码的健壮性和异常安全性 1。标准库中的许多类,如文件流 (std::fstream)、锁 (std::lock_guard, std::unique_lock) 等,都遵循 RAII 原则 20。

C++

#include <fstream>
#include <stdexcept>

// 示例:使用 std::fstream (RAII) 管理文件
void processFileRAII(const char* filename) {
std::ofstream file(filename); // 构造时打开文件 (获取资源)
if (!file.is_open()) {
throw std::runtime_error(“无法打开文件”);
}
file << “使用 RAII 写入数据。\n”;
if (/* 发生某种错误 */ false) {
throw std::runtime_error(“处理文件时出错”);
}
// 当 file 对象离开作用域时 (无论是正常结束还是因异常),
// 其析构函数会自动调用,关闭文件 (释放资源)。
} // file 在这里被销毁,文件自动关闭

20

1.3 智能指针:堆内存的 RAII 封装

智能指针正是将 RAII 原则应用于动态分配内存(堆内存)的解决方案 1。它们是 C++ 标准库(在 <memory> 头文件中定义)提供的类模板,其行为类似于原始指针,但增加了自动管理所指向对象生命周期的能力 2。

智能指针通过在其析构函数中自动调用 delete 或 delete,确保了即使在复杂的控制流或异常发生的情况下,动态分配的内存也能被正确释放,从而有效防止内存泄漏 1。

更重要的是,不同的智能指针类型(如 std::unique_ptr, std::shared_ptr)还显式地定义了所有权模型(独占所有权、共享所有权),使得代码意图更加清晰,有助于从根本上避免重复释放和悬挂指针等问题 1。

C++11 标准引入了三种主要的智能指针类型:

  • std::unique_ptr:用于独占所有权的场景。
  • std::shared_ptr:用于共享所有权的场景。
  • std::weak_ptr:shared_ptr 的辅助类,用于打破循环引用和观察对象。

还有一个较早的 std::auto_ptr,但由于设计缺陷(主要是其复制行为会意外转移所有权),已在 C++11 中被弃用,并在 C++17 中被移除,不应在现代 C++ 代码中使用 4。

可以说,智能指针是 C++ 解决原始指针管理难题、实现安全高效的动态内存管理的基石,是现代 C++ 编程不可或缺的一部分 1。它们将 RAII 的威力带到了堆内存管理领域,让程序员能够编写更简洁、更安全、更易于维护的代码。

2. std::unique_ptr - 管理独占所有权

std::unique_ptr 是 C++11 引入的一种智能指针,它实现独占所有权 (exclusive ownership) 语义 1。这意味着在任何时刻,最多只能有一个 std::unique_ptr 指向并拥有特定的动态分配资源 27。它是现代 C++ 中管理动态内存的首选和默认选择,特别是替代已被废弃的 std::auto_ptr 10。

2.1 核心概念:独占所有权

std::unique_ptr<T> 的核心在于它保证了对其管理的资源(通常是通过 new 分配的 T 类型对象或 T 类型数组)的唯一所有权 35。当 unique_ptr 对象本身被销毁时(例如,离开其定义的作用域、被 reset()、或被赋值覆盖),它会自动调用其关联的删除器 (deleter) 来释放所管理的资源 1。默认的删除器对单个对象使用 delete,对数组使用 delete 35。

为了严格执行独占所有权,unique_ptr 被设计为不可复制 (non-copyable)。它的拷贝构造函数和拷贝赋值运算符都被显式删除(或声明为私有)10。尝试复制 unique_ptr 会导致编译错误,这从根本上防止了多个 unique_ptr 实例意外地指向并试图管理同一资源,从而避免了重复释放的风险 28。

C++

#include <memory>
#include <iostream>

struct Widget {
Widget() { std::cout << “Widget 创建\n”; }
~Widget() { std::cout << “Widget 销毁\n”; }
};

int main() {
std::unique_ptr<Widget> ptr1 = std::make_unique<Widget>();
// std::unique_ptr<Widget> ptr2 = ptr1; // 编译错误!unique_ptr 不可复制
// ptr1 = ptr2; // 编译错误!unique_ptr 不可复制赋值
return 0; // ptr1 在此处离开作用域,自动销毁 Widget
}

29

2.2 移动语义:转移所有权

虽然 unique_ptr 不可复制,但它支持移动语义 (move semantics),允许将资源的所有权从一个 unique_ptr 转移 (transfer) 到另一个 10。转移后,源 unique_ptr 不再拥有资源,其内部指针变为空 (nullptr) 36。移动语义是 unique_ptr 能够实用化的关键,使得独占所有权的资源可以在不同作用域或对象间安全传递 42。

所有权转移主要发生在以下场景:

  1. 从函数返回值 (Return from Function):当函数按值返回一个 unique_ptr 时,所有权会自动(通过返回值优化 RVO/NRVO 或隐式移动)转移给调用者 35。这是工厂函数 (Factory Function) 模式的理想实现方式,工厂创建资源并安全地将所有权交给调用方 22。
    C++
    #include <memory>
    #include <iostream>

    struct Product {
    Product(int id) : id_(id) { std::cout << “Product " << id_ << " 创建\n”; }
    ~Product() { std::cout << “Product " << id_ << " 销毁\n”; }
    void use() { std::cout << “使用 Product " << id_ << “\n”; }
    private:
    int id_;
    };

    // 工厂函数,创建并返回 Product 的 unique_ptr
    std::unique_ptr<Product> createProduct(int id) {
    std::cout << “在 createProduct 中创建 Product " << id << “\n”;
    return std::make_unique<Product>(id); // 所有权通过返回值移动
    }

    int main() {
    std::cout << “调用 createProduct 前\n”;
    std::unique_ptr<Product> p = createProduct(1); // 所有权转移给 p
    std::cout << “调用 createProduct 后\n”;
    if (p) {
    p->use();
    }
    return 0; // p 离开作用域,销毁 Product 1
    }

    35

  2. 作为函数参数(接收所有权):函数可以通过按值接收 unique_ptr 参数来显式地获得资源的所有权 10。调用者必须使用 std::move 来转移所有权 10。
    C++
    #include <memory>
    #include <utility> // for std::move
    #include <iostream>

    struct Gadget {
    Gadget() { std::cout << “Gadget 创建\n”; }
    ~Gadget() { std::cout << “Gadget 销毁\n”; }
    void operate() { std::cout << “操作 Gadget\n”; }
    };

    // 函数接收 Gadget 的所有权
    void takeOwnership(std::unique_ptr<Gadget> g) {
    std::cout << “进入 takeOwnership\n”;
    if (g) {
    g->operate();
    }
    std::cout << “离开 takeOwnership\n”;
    // g 在此离开作用域,销毁 Gadget
    }

    int main() {
    auto myGadget = std::make_unique<Gadget>();
    std::cout << “调用 takeOwnership 前\n”;
    takeOwnership(std::move(myGadget)); // 必须使用 std::move 转移所有权
    std::cout << “调用 takeOwnership 后\n”;
    // myGadget 现在是空的 (nullptr)
    if (!myGadget) {
    std::cout << “myGadget 现在为空\n”;
    }
    return 0;
    }

    10

  3. 存入容器 (Storing in Containers):unique_ptr 可以存储在支持移动语义的容器中,如 std::vector 35。向容器中添加 unique_ptr(如使用 push_back 或 emplace_back)通常需要 std::move 40。
    C++
    #include <vector>
    #include <memory>
    #include <iostream>
    #include <string>

    struct Item {
    std::string name;
    Item(std::string n) : name(std::move(n)) { std::cout << “Item ‘” << name << “’ 创建\n”; }
    ~Item() { std::cout << “Item ‘” << name << “’ 销毁\n”; }
    };

    int main() {
    std::vector<std::unique_ptr<Item>> items;

    // 使用 make\_unique 直接 emplace\_back (隐式移动)  
    items.emplace\_back(std::make\_unique\<Item\>("Apple"));
    
    // 创建后使用 std::move 添加  
    auto orangePtr \= std::make\_unique\<Item\>("Orange");  
    items.push\_back(std::move(orangePtr));
    
    std::cout \<\< "容器内容:\\n";  
    for (const auto& itemPtr : items) {  
        std::cout \<\< "- " \<\< itemPtr-\>name \<\< "\\n";  
    }
    
    return 0; // vector 销毁,其包含的所有 unique\_ptr 被销毁,进而销毁 Items  
    

    }

    35

没有移动语义,unique_ptr 的独占性会使其难以在实际场景中使用,因为它无法将资源的所有权安全地传递出创建它的作用域。std::auto_ptr 试图通过重载复制操作来模拟移动,但这导致了混乱和危险的行为,最终被 unique_ptr 和 C++11 的移动语义所取代 23。

2.3 创建 unique_ptr

创建 unique_ptr 主要有两种方式:

  1. 使用 new (不推荐)
    • 语法:std::unique_ptr<T> ptr(new T(args…)); 4
    • 缺点:
      • 异常安全问题:在 C++17 之前,函数参数的求值顺序是未指定的。对于像 func(std::unique_ptr<X>(new X), std::unique_ptr<Y>(new Y)) 这样的调用,编译器可能先执行 new X,然后执行 new Y,如果 new Y 抛出异常,那么 new X 分配的内存就会泄漏,因为它还没来得及被 unique_ptr 接管 62。
      • 冗余和易错:需要写 new,且类型名 T 需要写两次,增加了代码量和出错的可能性 64。

C++
// 示例:潜在的异常安全问题 (C++17 前)
void process(std::unique_ptr<Widget> w1, std::unique_ptr<Gadget> g1);

void unsafe_call() {
// 编译器可能执行顺序:
// 1. new Widget() // 成功
// 2. new Gadget() // 抛出异常!
// 3. unique_ptr<Widget> 构造 (永远不会执行)
// 4. unique_ptr<Gadget> 构造 (永远不会执行)
// 结果:Widget 内存泄漏
process(std::unique_ptr<Widget>(new Widget()), std::unique_ptr<Gadget>(new Gadget()));
}
62

  1. 使用 std::make_unique<T>(args…) (推荐, C++14 及以后)
    • 语法:auto ptr = std::make_unique<T>(args…); 35
    • 优点:
      • 异常安全:make_unique 将内存分配和 unique_ptr 的构造绑定在一起,避免了上述的异常安全问题 62。
      • 简洁:无需显式使用 new,类型名只需写一次 62。
    • 注意:std::make_unique 是 C++14 标准库的一部分。在 C++11 中,需要自行实现或使用库提供的版本 62。

C++
#include <memory>
#include <utility> // for std::forward (in make_unique impl)

// C++11 环境下的 make_unique 简易实现 (非数组版本)
template<typename T, typename… Args>
std::unique_ptr<T> make_unique_cpp11(Args&&… args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)…));
}

struct MyData {
MyData(int, double) {}
};

int main() {
// C++14 及以后推荐方式
auto ptr1 = std::make_unique<MyData>(1, 2.0);

// C++11 中使用自定义实现  
auto ptr2 \= make\_unique\_cpp11\<MyData\>(3, 4.0);

return 0;  

}
35

因此,除非有特殊需求(如使用自定义删除器,见后文),总是优先使用 std::make_unique 来创建 std::unique_ptr 64。

2.4 使用 unique_ptr

unique_ptr 的使用方式与原始指针非常相似:

  • 访问成员:使用 -> 操作符访问对象的成员,或使用 * 操作符解引用获得对象的引用后再用 . 访问 15。
    C++
    auto ptr = std::make_unique<Widget>();
    ptr->someMethod();
    (*ptr).someOtherMethod();

    29

  • 获取原始指针 (get):ptr.get() 返回 unique_ptr 内部管理的原始指针 15。这主要用于与不接受智能指针的 C 风格 API 或旧代码交互 15。极其重要:通过 get() 获取的原始指针不具有所有权,绝不能对其调用 delete 77。同时,避免用 get() 返回的指针再创建另一个智能指针,这会导致双重管理重复释放 77。
    C++
    void legacy_api(Widget* raw_widget_ptr);
    auto u_ptr = std::make_unique<Widget>();
    legacy_api(u_ptr.get()); // 安全传递原始指针
    // delete u_ptr.get(); // 错误!会导致重复释放
    // auto another_smart_ptr = std::shared_ptr<Widget>(u_ptr.get()); // 错误!双重管理

    15

  • 检查是否为空 (operator bool):可以将 unique_ptr 用在布尔上下文中(如 if 语句)来检查它是否管理着一个对象(即内部指针是否非空)39。
    C++
    std::unique_ptr<Widget> maybe_ptr = createOptionalWidget(/*…*/);
    if (maybe_ptr) { // 检查指针是否有效
    maybe_ptr->use();
    }

  • 释放所有权 (release):ptr.release() 会放弃 unique_ptr 对资源的所有权,返回原始指针,并将 unique_ptr 内部指针设为 nullptr 35。调用者必须接管返回的原始指针的生命周期管理责任,并在适当时候手动 delete 它,否则会造成内存泄漏 35。
    C++
    auto u_ptr = std::make_unique<Widget>();
    Widget* raw_widget = u_ptr.release(); // u_ptr 放弃所有权,变为 nullptr
    // 现在必须手动管理 raw_widget 的生命周期
    //… 使用 raw_widget…
    delete raw_widget; // 必须手动删除

    35

  • 重置指针 (reset)

    • ptr.reset(new_raw_ptr):首先销毁 ptr 当前管理的对象(如果有),然后接管 new_raw_ptr 的所有权 29。
    • ptr.reset():销毁 ptr 当前管理的对象(如果有),并将 ptr 置为空 15。

C++
auto u_ptr = std::make_unique<Widget>(); // 管理 Widget A
u_ptr.reset(new Widget()); // 销毁 Widget A,接管 Widget B
u_ptr.reset(); // 销毁 Widget B,u_ptr 变为空
29

2.5 处理数组 unique_ptr<T>

std::unique_ptr 对动态分配的数组提供了特化版本 std::unique_ptr<T> 35。

  • 创建

    • 使用 new T[N]:std::unique_ptr<int> arr_ptr(new int); 57。
    • 使用 std::make_unique<T>(N) (C++14+):auto arr_ptr = std::make_unique<int>(10); 这会创建包含 10 个值初始化(对于 int 来说是零初始化)元素的数组 40。
    • 使用 std::make_unique_for_overwrite<T>(N) (C++20+):auto arr_ptr = std::make_unique_for_overwrite<int>(10); 这会创建包含 10 个默认初始化元素的数组。对于 POD 类型(如 int, char),这意味着内存内容是未定义的,可能比值初始化更快 67。
  • 销毁:unique_ptr<T> 的析构函数会自动调用 delete 来释放数组内存,确保正确销毁数组中的每个对象(如果它们有析构函数)并释放整个内存块 57。

  • 访问:数组特化版本提供了 operator 来访问数组元素,但不支持指针算术(如 arr_ptr + 5)35。
    C++
    #include <memory>
    #include <iostream>

    int main() {
    // 创建并初始化一个 int 数组
    auto numbers = std::make_unique<int>(5);
    for (int i = 0; i < 5; ++i) {
    numbers[i] = i * 10; // 使用 operator 访问
    }

    std::cout \<\< "数组第三个元素: " \<\< numbers \<\< std::endl; // 输出 20
    
    // numbers \= 50; // 错误:越界访问 (未定义行为)  
    // auto ptr\_to\_third \= numbers \+ 2; // 编译错误:不支持指针算术
    
    return 0; // numbers 在此销毁,自动调用 delete  
    

    }

    35

  • 注意:unique_ptr<T> 不能直接用于管理多维数组(如 new int)。需要使用 std::vector<std::vector<T>> 或 std::vector<std::unique_ptr<T>>,或者手动管理一维数组并计算索引 87。

  • 与 std::vector 的比较:std::vector<T> 通常是管理动态数组更好的选择,因为它提供了更丰富的功能(如 size(), push_back(), 迭代器等)并自动处理内存 57。unique_ptr<T> 主要适用于需要固定大小数组、与 C API 交互(需要传递原始数组指针)或追求极致轻量级(vector 有额外的容量和大小成员)的场景 57。

2.6 自定义删除器 (Custom Deleters)

unique_ptr 不仅能管理通过 new 分配的内存,还能管理任何需要特定清理操作的资源,例如 C 库分配的内存 (malloc/free)、文件句柄 (fopen/fclose)、网络套接字、图形库资源等 35。这是通过自定义删除器实现的。

  • 机制:删除器的类型是 unique_ptr 类型的一部分:std::unique_ptr<T, DeleterType> 35。这意味着不同删除器类型的 unique_ptr 是不同的类型。

  • 删除器形式

    • 函数指针:可以直接使用函数指针作为删除器类型。这会增加 unique_ptr 的大小,因为它需要存储指向删除函数的指针 35。
    • 无状态函数对象(Functor)或 Lambda:如果删除器是一个没有成员变量的类(或 struct)重载了 operator(),或者是一个不捕获任何变量的 Lambda 表达式,那么通常可以通过空基类优化 (Empty Base Optimization, EBO) 将删除器“嵌入”到 unique_ptr 中,而不增加其大小 35。这是推荐的方式。
    • 有状态函数对象或 Lambda:如果删除器需要存储状态(例如,捕获了变量的 Lambda),那么这些状态会存储在 unique_ptr 对象中,增加其大小 27。
  • 示例:管理 FILE*
    C++
    #include <cstdio>
    #include <memory>
    #include <iostream>

    // 方式一:使用函数指针
    void file_closer_func(FILE* fp) {
    if (fp) {
    std::cout << “通过函数指针关闭文件…\n”;
    fclose(fp);
    }
    }

    // 方式二:使用无状态函数对象 (推荐)
    struct FileCloserFunctor {
    void operator()(FILE* fp) const {
    if (fp) {
    std::cout << “通过函数对象关闭文件…\n”;
    fclose(fp);
    }
    }
    };

    int main() {
    // 使用函数指针作为删除器
    std::unique_ptr<FILE, decltype(&file_closer_func)> filePtr1(fopen(“test1.txt”, “w”), file_closer_func);
    if (filePtr1) {
    fprintf(filePtr1.get(), “写入文件 1\n”);
    }
    std::cout << “sizeof(filePtr1): " << sizeof(filePtr1) << std::endl; // 通常 > sizeof(FILE*)

    // 使用无状态函数对象作为删除器  
    std::unique\_ptr\<FILE, FileCloserFunctor\> filePtr2(fopen("test2.txt", "w"));  
    if (filePtr2) {  
        fprintf(filePtr2.get(), "写入文件 2\\n");  
    }  
    std::cout \<\< "sizeof(filePtr2): " \<\< sizeof(filePtr2) \<\< std::endl; // 通常 \== sizeof(FILE\*)
    
    // 使用 Lambda 作为删除器 (无捕获,通常无大小开销)  
    auto file\_closer\_lambda \=(FILE\* fp) {  
        if (fp) {  
            std::cout \<\< "通过 Lambda 关闭文件...\\n";  
            fclose(fp);  
        }  
    };  
    std::unique\_ptr\<FILE, decltype(file\_closer\_lambda)\> filePtr3(fopen("test3.txt", "w"), file\_closer\_lambda);  
     if (filePtr3) {  
        fprintf(filePtr3.get(), "写入文件 3\\n");  
    }  
    

    std::cout << “sizeof(filePtr3): " << sizeof(filePtr3) << std::endl; // 通常 == sizeof(FILE*)

    return 0; // 所有文件在此处自动关闭  
    

    }

    35

  • 示例:管理 malloc 分配的内存
    C++
    #include <cstdlib> // for malloc, free
    #include <memory>
    #include <iostream>

    struct MallocDeleter {
    void operator()(void* ptr) const {
    if (ptr) {
    std::cout << “使用 free() 释放内存…\n”;
    free(ptr);
    }
    }
    };

    int main() {
    // 分配内存
    void* mem = malloc(100);
    if (!mem) return 1;

    // 使用 unique\_ptr 管理  
    std::unique\_ptr\<void, MallocDeleter\> memPtr(mem);
    
    //... 使用内存...  
    int\* int\_array \= static\_cast\<int\*\>(memPtr.get());  
    int\_array \= 123;
    
    return 0; // memPtr 离开作用域,自动调用 MallocDeleter::operator() 释放内存  
    

    }

    90

  • 注意:std::make_unique 不支持直接指定自定义删除器。必须使用 unique_ptr 的构造函数来创建带有自定义删除器的实例 64。

2.7 用例:Pimpl (Pointer to Implementation) Idiom

Pimpl Idiom 是一种常用的 C++ 设计模式,旨在将类的实现细节与其接口分离,从而减少头文件依赖,加快编译速度 102。std::unique_ptr 是实现 Pimpl 的现代、安全的方式 26。

  • 机制:公共类(如 Widget)在头文件中只包含一个指向其实现类(如 Widget::Impl)的 std::unique_ptr 成员。实现类 Impl 在头文件中仅做前向声明,其完整定义则放在源文件 (.cpp) 中 102。公共类的所有实现都委托给 Impl 对象。

  • 关键点:析构函数:为了让 std::unique_ptr<Impl> 能够正确销毁 Impl 对象,公共类 (Widget) 的析构函数必须在头文件中声明,但在源文件中定义(即使是 = default)103。这是因为 unique_ptr 的析构函数需要看到 Impl 的完整定义才能调用 delete,而 Impl 的完整定义只在源文件中可见。如果析构函数在头文件中内联定义(默认行为),编译器在处理包含该头文件的其他文件时,由于 Impl 是不完整类型,会导致编译错误 104。

  • 示例
    C++
    // widget.h
    #pragma once
    #include <memory>
    #include <string>

    class Widget {
    public:
    Widget(const std::string& name);
    ~Widget(); // 必须声明析构函数

    Widget(Widget&& other) noexcept; // 支持移动构造  
    Widget& operator\=(Widget&& other) noexcept; // 支持移动赋值
    
    // 禁用复制  
    Widget(const Widget&) \= delete;  
    Widget& operator\=(const Widget&) \= delete;
    
    void doSomething();  
    std::string getName() const;
    

    private:
    class Impl; // 前向声明实现类
    std::unique_ptr<Impl> pImpl; // 指向实现的指针
    };

    C++
    // widget.cpp
    #include “widget.h”
    #include <iostream>
    #include <vector> // 假设实现需要 vector

    // 实现类的完整定义
    class Widget::Impl {
    public:
    Impl(const std::string& name) : name_(name) {
    std::cout << “Widget::Impl 创建: " << name_ << std::endl;
    }
    ~Impl() {
    std::cout << “Widget::Impl 销毁: " << name_ << std::endl;
    }

    void doSomething() {  
        std::cout \<\< name\_ \<\< " 正在做某事,数据大小: " \<\< data\_.size() \<\< std::endl;  
    }
    
    std::string getName() const {  
        return name\_;  
    }
    

    private:
    std::string name_;
    std::vector<int> data_; // 实现细节,对外部隐藏
    };

    // 公共类构造函数委托给实现类
    Widget::Widget(const std::string& name)
    : pImpl(std::make_unique<Impl>(name)) {}

    // 公共类析构函数定义 (必须在 Impl 完整定义之后)
    Widget::~Widget() = default;

    // 移动构造函数
    Widget::Widget(Widget&& other) noexcept = default;
    // 移动赋值运算符
    Widget& Widget::operator=(Widget&& other) noexcept = default;

    // 公共类方法委托给实现类
    void Widget::doSomething() {
    pImpl->doSomething();
    }

    std::string Widget::getName() const {
    return pImpl->getName();
    }

    102

2.8 性能与大小

std::unique_ptr 在设计上追求效率:

  • 大小 (Size):当使用默认删除器或无状态自定义删除器时,std::unique_ptr<T> 的大小通常与原始指针 T* 完全相同(例如,在 64 位系统上是 8 字节)15。如果使用函数指针或有状态删除器,大小会相应增加以存储删除器信息 27。
  • 速度 (Speed)
    • 访问:通过 -> 和 * 访问被管理对象的操作,其速度通常与直接访问原始指针没有显著差异 15。
    • 创建/销毁:开销主要来自底层的 new/delete,unique_ptr 本身的构造和析构开销极小 27。
    • 移动:移动操作非常快,本质上只是指针的赋值和将源指针置空 27。

这种高效率使得 unique_ptr 成为 C++ 中实现自动内存管理和异常安全的首选工具,它在提供显著安全性的同时,几乎不引入任何性能损失。这体现了 C++ 的一个核心设计哲学:零成本抽象 (zero-overhead abstraction)。当只需要独占所有权时,没有理由不使用 unique_ptr 来替代原始指针 27。

3. std::shared_ptr - 管理共享所有权

与 std::unique_ptr 的独占所有权不同,std::shared_ptr 允许多个智能指针实例共同拥有 (share ownership) 同一个动态分配的资源 1。这在资源需要在程序的多个部分之间共享,且无法明确哪个部分应该最后负责释放资源时非常有用 4。

3.1 核心概念:共享所有权与引用计数

std::shared_ptr<T> 通过引用计数 (reference counting) 机制来管理共享资源的生命周期 1。

  • 机制
    • 每个由 shared_ptr 管理的资源都有一个关联的控制块 (control block) 76。这个控制块通常独立于资源对象本身分配,并包含(至少)一个强引用计数 (strong reference count) 76。
    • 当创建一个新的 shared_ptr 指向某个资源(通过构造函数或 make_shared)时,如果该资源尚未被管理,则会创建一个控制块,并将强引用计数初始化为 1 27。
    • 当一个 shared_ptr 被复制(通过拷贝构造或拷贝赋值)时,指向同一个资源的新的 shared_ptr 会共享同一个控制块,并且控制块中的强引用计数会原子地增加 1 10。
    • 当一个 shared_ptr 被销毁(离开作用域)、重置 (reset)赋值给它另一个 shared_ptr 时,它所管理的资源的强引用计数会原子地减少 1 10。
    • 当强引用计数减至 0 时,表明没有任何 shared_ptr 再拥有该资源,此时最后一个 shared_ptr 负责销毁资源(通过调用存储在控制块中的删除器,默认为 delete)1。
  • 控制块:除了强引用计数,控制块通常还包含:
    • 指向被管理对象的指针(或对象本身,如果使用 make_shared)。
    • 一个弱引用计数 (weak reference count),用于 std::weak_ptr(详见后文)。
    • 自定义删除器(如果提供了)。
    • 自定义分配器信息(如果提供了)。 76 控制块本身也是动态分配的,只有当强引用计数和弱引用计数都变为 0 时,控制块才会被销毁 116。

3.2 创建 shared_ptr

创建 shared_ptr 有几种方式:

  1. 使用 new (不推荐)
    • 语法:std::shared_ptr<T> ptr(new T(args…)); 76
    • 缺点:
      • 性能:需要进行两次堆分配:一次为对象 T 分配(通过 new),一次为控制块分配(由 shared_ptr 构造函数内部完成)27。
      • 异常安全:与 unique_ptr 类似,在复杂表达式中可能因异常导致 new 分配的对象泄漏 65。
      • 双重管理风险:严禁使用同一个原始指针初始化多个独立的 shared_ptr 实例。这样做会导致每个 shared_ptr 都创建自己的控制块,它们都认为自己拥有资源,当各自引用计数归零时都会尝试删除资源,导致重复释放 77。

C++
Widget* raw_ptr = new Widget();
std::shared_ptr<Widget> sp1(raw_ptr); // 控制块 1,引用计数 1
std::shared_ptr<Widget> sp2(raw_ptr); // 错误!控制块 2,引用计数 1
// sp1 和 sp2 都认为自己拥有 raw_ptr
// 当 sp1 和 sp2 销毁时,会尝试 delete raw_ptr 两次!
77

  1. 使用 std::make_shared<T>(args…) (推荐)
    • 语法:auto ptr = std::make_shared<T>(args…); 63
    • 优点:
      • 性能:通常只需要进行一次堆分配,将对象 T 和控制块分配在相邻的内存区域,减少了内存碎片和分配开销,提高了缓存局部性 27。
      • 异常安全:避免了 new 和 shared_ptr 构造函数分离可能导致的泄漏问题 65。
      • 简洁:语法更简单,无需显式使用 new 53。

C++
auto sp1 = std::make_shared<Widget>(arg1, arg2); // 推荐
auto sp2 = sp1; // 正确的共享方式
76

  1. 从 std::unique_ptr 转换

    • 机制:shared_ptr 提供了接受 unique_ptr 右值引用的构造函数和赋值运算符,通过 std::move 实现所有权从 unique_ptr 到 shared_ptr 的转移 48。
    • 语法:std::shared_ptr<T> sPtr = std::move(uPtr); 或 std::shared_ptr<T> sPtr{std::move(uPtr)}; 48。
    • 用途:允许工厂函数返回开销最小的 unique_ptr,而调用者可以根据需要决定是否将其转换为 shared_ptr 以共享所有权 27。

    C++
    std::unique_ptr<Base> createDerived() {
    return std::make_unique<Derived>();
    }

    int main() {
    std::unique_ptr<Base> unique_obj = createDerived();
    //… 可能只需要独占所有权…

      // 如果需要共享,可以转移给 shared\_ptr  
      std::shared\_ptr\<Base\> shared\_obj \= std::move(unique\_obj);  
      // unique\_obj 现在为空
    
      return 0;  
    

    }
    48

    • 注意:无法将 shared_ptr 安全地转换回 unique_ptr,因为 shared_ptr 可能已经被共享,转换回 unique_ptr 会破坏独占所有权语义 48。

3.3 使用 shared_ptr

shared_ptr 的基本使用与 unique_ptr 类似:

  • 访问成员:使用 -> 和 * 76。
  • 获取原始指针 (get):ptr.get() 返回原始指针,同样需要注意不能 delete,也不能用它创建新的智能指针 76。
  • 检查是否为空 (operator bool):if (ptr) 76。
  • 获取引用计数 (use_count):ptr.use_count() 返回当前共享所有者的数量(强引用计数)76。注意,这个值在多线程环境下可能瞬间变化,主要用于调试和测试,不应用于程序的同步逻辑 120。
    C++
    auto sp1 = std::make_shared<int>(10);
    std::cout << “Count 1: " << sp1.use_count() << std::endl; // 输出 1
    auto sp2 = sp1;
    std::cout << “Count 2: " << sp1.use_count() << std::endl; // 输出 2
    std::cout << “Count 3: " << sp2.use_count() << std::endl; // 输出 2
    {
    auto sp3 = sp1;
    std::cout << “Count 4: " << sp1.use_count() << std::endl; // 输出 3
    } // sp3 销毁
    std::cout << “Count 5: " << sp1.use_count() << std::endl; // 输出 2
    76
  • 重置指针 (reset)
    • ptr.reset(new_raw_ptr):使 ptr 放弃当前管理的对象(引用计数减 1,如果归零则删除对象),然后开始管理 new_raw_ptr(为其创建新的控制块,引用计数为 1)76。
    • ptr.reset():使 ptr 放弃当前管理的对象(引用计数减 1,如果归零则删除对象),并将 ptr 置为空 76。

C++
auto sp1 = std::make_shared<Widget>(); // Widget A, count = 1
auto sp2 = sp1; // Widget A, count = 2
sp1.reset(new Widget()); // Widget A count = 1, sp1 管理 Widget B (count = 1)
sp2.reset(); // Widget A count = 0 (销毁), sp2 为空
sp1.reset(); // Widget B count = 0 (销毁), sp1 为空
76

3.4 自定义删除器

与 unique_ptr 类似,shared_ptr 也支持自定义删除器,用于管理非 new 分配或需要特殊清理的资源 76。

  • 机制:删除器(函数指针、Lambda、函数对象)在构造 shared_ptr 时作为参数传入 76。与 unique_ptr 不同,删除器的类型不是 shared_ptr 类型的一部分,而是通过类型擦除 (type erasure) 存储在控制块中 76。这意味着不同删除器的 shared_ptr<T> 仍然是相同的类型,可以相互赋值和存储在同一容器中。

  • 示例:管理 FILE*
    C++
    #include <memory>
    #include <cstdio>
    #include <iostream>

    int main() {
    FILE* f = fopen(“shared_example.txt”, “w”);
    if (!f) return 1;

    // 使用 Lambda 作为自定义删除器  
    std::shared\_ptr\<FILE\> filePtr(f,(FILE\* fp) {  
        if (fp) {  
            std::cout \<\< "Lambda 删除器:关闭文件。\\n";  
            fclose(fp);  
        }  
    });
    
    if (filePtr) {  
        fprintf(filePtr.get(), "通过 shared\_ptr 写入。\\n");  
        std::cout \<\< "文件指针引用计数: " \<\< filePtr.use\_count() \<\< std::endl; // 输出 1
    
        auto filePtrCopy \= filePtr; // 复制 shared\_ptr  
        std::cout \<\< "文件指针引用计数: " \<\< filePtr.use\_count() \<\< std::endl; // 输出 2  
    } // filePtrCopy 在此销毁,引用计数减为 1
    
    return 0; // filePtr 在此销毁,引用计数减为 0,Lambda 删除器被调用,文件关闭  
    

    }

    100

  • 注意:由于自定义删除器需要通过构造函数传递,而 std::make_shared 无法直接接受自定义删除器(C++20 之前的版本),因此使用自定义删除器时通常需要直接调用 shared_ptr 的构造函数并配合 new 27。这会失去 make_shared 的单次分配优化。

3.5 线程安全考量

shared_ptr 的线程安全是一个容易混淆的概念,需要区分三个层面:

  1. 控制块(引用计数)的线程安全:shared_ptr 保证其内部引用计数的增减操作是原子的、线程安全的 76。这意味着来自不同线程的多个 shared_ptr 实例可以安全地复制、赋值和销毁,而无需外部锁来保护引用计数本身 76。这是 shared_ptr 能够在多线程环境中安全共享所有权的基础。
  2. shared_ptr 对象本身的访问安全:对同一个 shared_ptr 实例(例如一个全局变量或共享的类成员)进行并发访问不是线程安全的,如果至少有一个线程在修改它(如赋值、reset)76。这种情况下,需要使用外部同步机制,如 std::mutex 或 C++20 引入的 std::atomic<std::shared_ptr<T>> 148。
  3. 被管理对象 (T) 的访问安全:shared_ptr 不提供对其管理的对象的线程安全访问保证 122。如果多个线程通过不同的 shared_ptr 实例访问同一个被管理对象 T,并且至少有一个线程修改 T 的状态,那么程序员必须负责对 T 的访问进行同步(例如,在 T 的成员函数中使用 std::mutex,或者在访问 T 的代码块周围加锁)122。

C++

#include <memory>
#include <thread>
#include <vector>
#include <mutex>
#include <iostream>

struct Counter {
int value = 0;
std::mutex mtx; // 用于保护 value 的互斥锁

void increment() {  
    std::lock\_guard\<std::mutex\> lock(mtx); // 访问前加锁  
    \++value;  
}

int get() {  
    std::lock\_guard\<std::mutex\> lock(mtx); // 访问前加锁  
    return value;  
}  

};

std::shared_ptr<Counter> global_counter = std::make_shared<Counter>(); // 全局共享指针

void worker_thread() {
std::shared_ptr<Counter> local_counter = global_counter; // 线程安全的复制

for (int i \= 0; i \< 10000; \++i) {  
    // 错误:直接访问 \*local\_counter.get() 或 local\_counter-\>value 是非线程安全的  
    // 正确:通过 Counter 类提供的线程安全方法访问  
    local\_counter-\>increment();  
}  

}

int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back(worker_thread);
}

for (auto& t : threads) {  
    t.join();  
}

std::cout \<\< "最终计数值: " \<\< global\_counter-\>get() \<\< std::endl; // 应为 40000

// 错误示例:并发修改同一个 shared\_ptr 实例 (需要 std::atomic 或 mutex)  
// std::thread t1(\[&\](){ global\_counter \= std::make\_shared\<Counter\>(); });  
// std::thread t2(\[&\](){ global\_counter \= std::make\_shared\<Counter\>(); });  
// t1.join(); t2.join();

return 0;  

}

122

必须清楚地区分这三个层面的线程安全。shared_ptr 本身只解决了引用计数的同步问题,并未解决并发访问被管理对象或 shared_ptr 实例本身的数据竞争问题。

3.6 性能与大小

shared_ptr 的共享所有权能力是有代价的:

  • 大小 (Size):通常是原始指针大小的两倍,因为它内部需要存储指向被管理对象的指针和指向控制块的指针 15。
  • 速度 (Speed)
    • 创建:使用 new 创建比 unique_ptr 慢(两次分配),使用 make_shared 通常更快(一次分配),但仍比 make_unique 或原始 new 有额外开销(需要初始化控制块)27。
    • 复制/赋值/销毁:涉及原子操作来修改引用计数,这比非原子操作慢,尤其是在多核处理器上且存在争用时,开销可能变得显著 27。移动 shared_ptr 则相对较快,因为它不涉及原子操作 158。
    • 解引用 (->, *):访问被管理对象的速度与原始指针相同 115。

shared_ptr 提供的便利性和安全性是以一定的性能和内存开销为代价的。make_shared 是减轻这些开销的重要手段,但它并不能完全消除与 unique_ptr 的性能差距。因此,只有在确实需要共享所有权时才应使用 shared_ptr 27。

3.7 处理数组 shared_ptr<T> (C++17 及以后)

C++17 标准扩展了 shared_ptr,使其能够原生支持管理动态分配的数组 76。

  • 类型:使用 std::shared_ptr<T> 或 std::shared_ptr<T[N]>(对于已知大小的数组)。
  • 销毁:默认情况下,shared_ptr<T> 会使用 delete 来释放内存 128。
  • 访问:提供了 operator 用于访问数组元素 76。
  • 创建 (C++11):在 C++17 之前,管理数组需要显式提供自定义删除器(如 std::default_delete<T> 或 Lambda)来调用 delete 160。
    C++
    // C++11 方式创建 shared_ptr 管理数组
    std::shared_ptr<int> sp_arr_cpp11(new int, std::default_delete<int>());
    // 或者使用 Lambda
    // std::shared_ptr<int> sp_arr_lambda(new int,(int* p){ delete p; });
    160
  • 创建 (C++17 及以后):可以直接使用 new T 初始化 shared_ptr<T> 128。
    C++
    // C++17 方式
    std::shared_ptr<int> sp_arr_cpp17(new int);
    sp_arr_cpp17 = 100; // 使用 operator
    76
  • 创建 (C++20 及以后):可以使用 std::make_shared<T>(N) 65。
    C++
    // C++20 方式 (假设编译器支持)
    // auto sp_arr_cpp20 = std::make_shared<int>(5);
    // sp_arr_cpp20 = 200;
    84

4. std::weak_ptr - 安全地观察共享对象

std::weak_ptr 是 C++11 引入的一种特殊的智能指针,它本身不拥有所指向的对象,而是作为 std::shared_ptr 的“观察者” 1。它主要用于解决 shared_ptr 可能引发的两个问题:循环引用和需要临时访问共享对象而又不影响其生命周期的情况。

4.1 核心概念:非拥有式观察

  • 定义:std::weak_ptr<T> 持有一个对其所观察对象(该对象必须由至少一个 std::shared_ptr 管理)的弱引用 (weak reference) 163。
  • 不参与引用计数:weak_ptr 的创建、复制或销毁不会改变其所观察对象的强引用计数 2。这意味着 weak_ptr 的存在与否不会阻止被观察对象在其最后一个 shared_ptr 拥有者被销毁时释放 163。
  • 用途 1: 打破循环引用 (Break Circular References):当两个或多个对象通过 shared_ptr 相互引用时,会形成一个引用循环。即使外部不再有指向这些对象的 shared_ptr,循环内部的 shared_ptr 也会使它们的引用计数永远无法降到 0,导致内存泄漏 2。将循环中的一个或多个 shared_ptr 替换为 weak_ptr 可以打破这种所有权循环,允许对象在不再被外部强引用时正确销毁 163。
  • 用途 2: 缓存与观察 (Caching/Observation):有时需要检查一个由 shared_ptr 管理的对象是否存在并可能访问它,但又不希望仅仅因为这个检查或临时访问就延长该对象的生命周期 38。例如,在一个缓存系统中,缓存可能持有对象的弱引用,这样当对象不再被其他地方使用时可以被销毁并从缓存中移除。或者在观察者模式中,观察者持有对主题的弱引用,避免主题因为观察者的存在而无法销毁 155。
  • 与控制块的关系:weak_ptr 仍然需要访问 shared_ptr 的控制块,以检查对象是否仍然存在(通过检查强引用计数是否为 0)并获取指向对象的指针 76。控制块本身会维护一个弱引用计数,只有当强引用计数和弱引用计数都为 0 时,控制块才会被释放 76。

4.2 创建与使用 weak_ptr

  • 创建:weak_ptr 通常由一个 shared_ptr 初始化 163。
    C++
    std::shared_ptr<Data> sp = std::make_shared<Data>();
    std::weak_ptr<Data> wp = sp; // 从 shared_ptr 创建 weak_ptr
    std::weak_ptr<Data> wp_empty; // 默认构造为空

    168

  • 检查对象是否存在 (expired):wp.expired() 返回 true 如果被观察的对象已经被销毁(即强引用计数为 0),否则返回 false 163。注意:在多线程环境中,expired() 返回 false 并不保证对象在下一刻仍然存在,因为其他线程可能同时销毁了最后一个 shared_ptr。因此,expired() 主要用于判断对象是否确定已销毁,或者作为 lock() 的前置优化检查(但不能替代 lock() 的检查)123。
    C++
    if (wp.expired()) {
    std::cout << “对象已被销毁。\n”;
    } else {
    std::cout << “对象可能仍然存在。\n”;
    }

    168

  • 获取临时所有权 (lock):这是安全访问 weak_ptr 所观察对象的唯一方法 163。wp.lock() 会尝试创建一个指向被观察对象的 shared_ptr。

    • 如果对象仍然存在(强引用计数 > 0),lock() 返回一个有效的 shared_ptr,该 shared_ptr 会增加对象的强引用计数,确保在返回的 shared_ptr 存在期间对象不会被销毁 163。
    • 如果对象已经被销毁(强引用计数 == 0),lock() 返回一个空的(持有 nullptr)的 shared_ptr 163。

C++
std::weak_ptr<Data> wp = /*… */;
if (std::shared_ptr<Data> sp_temp = wp.lock()) {
// 成功获取临时 shared_ptr,对象在此作用域内保证存活
sp_temp->use();
std::cout << “当前共享引用计数: " << sp_temp.use_count() << std::endl;
} else {
// 对象已被销毁
std::cout << “无法锁定 weak_ptr,对象已销毁。\n”;
}
165

  • 重置 (reset):wp.reset() 使 weak_ptr 不再观察任何对象,将其置空 163。这会减少弱引用计数。
  • 获取共享计数 (use_count):wp.use_count() 返回当前有多少个 shared_ptr 正在共享对象的所有权(即强引用计数),与从对应的 shared_ptr 调用 use_count() 结果相同 163。

4.3 示例:打破循环引用

考虑一个简化的父子关系场景,父节点和子节点都使用 shared_ptr 相互引用:

C++

#include <memory>
#include <iostream>
#include <string>

struct Child; // 前向声明

struct Parent {
std::string name;
std::shared_ptr<Child> child; // Parent 拥有 Child

Parent(std::string n) : name(std::move(n)) { std::cout \<\< "Parent " \<\< name \<\< " 创建\\n"; }  
\~Parent() { std::cout \<\< "Parent " \<\< name \<\< " 销毁\\n"; }  

};

struct Child {
std::string name;
std::shared_ptr<Parent> parent; // Child 也拥有 Parent (导致循环引用)

Child(std::string n) : name(std::move(n)) { std::cout \<\< "Child " \<\< name \<\< " 创建\\n"; }  
\~Child() { std::cout \<\< "Child " \<\< name \<\< " 销毁\\n"; }  

};

int main() {
{
auto p = std::make_shared<Parent>(“P”);
auto c = std::make_shared<Child>(“C”);
p->child = c;
c->parent = p; // 形成 shared_ptr 循环
std::cout << “Parent use_count: " << p.use_count() << std::endl; // 输出 2 (p 和 c->parent)
std::cout << “Child use_count: " << c.use_count() << std::endl; // 输出 2 (c 和 p->child)
} // p 和 c 离开作用域,但引用计数都 > 0,对象不会被销毁

std::cout \<\< "main 结束,但对象未销毁 (内存泄漏)\\n";  
return 0;  

}

27

要解决这个问题,我们需要打破所有权循环。通常,子节点不应该拥有父节点,它只需要能够观察或访问父节点。因此,将 Child 中的 parent 成员改为 weak_ptr:

C++

#include <memory>
#include <iostream>
#include <string>

struct Child; // 前向声明
struct Parent; // 前向声明

struct Parent {
std::string name;
std::shared_ptr<Child> child; // Parent 仍然拥有 Child

Parent(std::string n) : name(std::move(n)) { std::cout \<\< "Parent " \<\< name \<\< " 创建\\n"; }  
\~Parent() { std::cout \<\< "Parent " \<\< name \<\< " 销毁\\n"; }  

};

struct Child {
std::string name;
std::weak_ptr<Parent> parent; // Child 只观察 Parent,不参与所有权

Child(std::string n) : name(std::move(n)) { std::cout \<\< "Child " \<\< name \<\< " 创建\\n"; }  
\~Child() { std::cout \<\< "Child " \<\< name \<\< " 销毁\\n"; }

void printParentName() {  
    if (auto locked\_parent \= parent.lock()) { // 尝试获取临时 shared\_ptr  
        std::cout \<\< "Child " \<\< name \<\< " 的 Parent 是 " \<\< locked\_parent-\>name \<\< std::endl;  
    } else {  
        std::cout \<\< "Child " \<\< name \<\< " 的 Parent 已销毁\\n";  
    }  
}  

};

int main() {
std::shared_ptr<Parent> p;
std::shared_ptr<Child> c;
{
p = std::make_shared<Parent>(“P”);
c = std::make_shared<Child>(“C”);
p->child = c;
c->parent = p; // 用 weak_ptr 赋值,不增加 p 的引用计数
std::cout << “Parent use_count: " << p.use_count() << std::endl; // 输出 1 (只有 p)
std::cout << “Child use_count: " << c.use_count() << std::endl; // 输出 1 (只有 c)
c->printParentName(); // 可以通过 lock() 访问 Parent
} // p 和 c 离开作用域,引用计数降为 0,对象被正确销毁

std::cout \<\< "main 结束,对象已销毁\\n";  
// 尝试访问已销毁的 Parent  
c-\>printParentName(); // 输出 "Child C 的 Parent 已销毁" (假设 c 仍然存在,但其 parent weak\_ptr 已失效)

return 0;  

}

10

通过将循环中的一个强引用 (shared_ptr) 替换为弱引用 (weak_ptr),我们打破了所有权的循环依赖,使得引用计数可以正常归零,从而避免了内存泄漏。weak_ptr 的设计体现了所有权和访问权的分离:shared_ptr 代表所有权和生命周期控制,而 weak_ptr 代表临时的、可能失效的访问权 165。

5. 高级用法、最佳实践与指南

掌握了 unique_ptr, shared_ptr, weak_ptr 的基本概念和用法后,还需要了解一些更高级的技术和普遍接受的最佳实践,以充分发挥智能指针的优势并避免潜在的陷阱。

5.1 使用 std::enable_shared_from_this

有时,一个已经被 shared_ptr 管理的对象需要在其成员函数内部获取一个指向自身的 shared_ptr 117。一个常见的场景是在异步操作中,需要将指向当前对象的 shared_ptr 传递给回调函数或捕获到 Lambda 中,以确保在异步操作完成之前该对象不会被销毁 191。

直接在成员函数中使用 std::shared_ptr<T>(this) 来创建指向自身的 shared_ptr 是极其危险的 189。这样做会创建一个全新的控制块,独立于管理该对象的现有 shared_ptr 群体。这会导致至少两个独立的 shared_ptr 组都认为自己拥有该对象,最终导致重复释放 189。

C++11 提供了 std::enable_shared_from_this<T> 来安全地解决这个问题 76。

  • 用法:让需要此功能的类 T 公开继承自 std::enable_shared_from_this<T> 117。

  • 机制

    • enable_shared_from_this 内部通常包含一个 std::weak_ptr<T> 成员(我们称之为 weak_this)117。
    • 第一个 shared_ptr 通过 new T(…) 或 make_shared<T>(…) 创建并管理这个 T 对象时,shared_ptr 的构造函数会检测到 T 继承自 enable_shared_from_this,并将新创建的 shared_ptr(或其控制块信息)赋给 weak_this 117。
    • 之后,在 T 的成员函数中调用继承来的 shared_from_this() 方法时,该方法会尝试 lock() 内部的 weak_this 117。如果成功(意味着对象仍被至少一个 shared_ptr 管理),它会返回一个新的 shared_ptr,这个新的 shared_ptr 与外部管理该对象的 shared_ptr 共享同一个控制块,从而安全地增加了引用计数 117。
  • 前提条件:调用 shared_from_this() 时,必须已经存在至少一个 shared_ptr 实例在管理当前对象 (this) 189。如果在对象被 shared_ptr 管理之前调用 shared_from_this()(例如,在构造函数中,或者对象是在栈上创建的),会导致未定义行为(C++17 前)或抛出 std::bad_weak_ptr 异常(C++17 及以后)189。

  • 设计模式:为了确保对象总是在 shared_from_this() 被调用前由 shared_ptr 管理,通常将类的构造函数设为私有或保护,并提供一个公共的静态工厂方法(如 create())来返回 std::shared_ptr<T> 117。

  • 示例:异步回调
    C++
    #include <memory>
    #include <thread>
    #include <iostream>
    #include <chrono>

    // 模拟一个异步执行器
    void execute_async(std::function<void()> task) {
    std::thread(task).detach();
    }

    class TaskProcessor : public std::enable_shared_from_this<TaskProcessor> {
    public:
    // 工厂方法确保对象由 shared_ptr 管理
    static std::shared_ptr<TaskProcessor> create(int id) {
    // 不能使用 make_shared,因为它无法访问私有构造函数
    // (除非使用特殊技巧,此处简化处理)
    return std::shared_ptr<TaskProcessor>(new TaskProcessor(id));
    }

    void startAsyncTask() {  
        std::cout \<\< "TaskProcessor " \<\< id\_ \<\< ": 启动异步任务...\\n";  
        // 捕获 shared\_ptr 到 Lambda 中,确保对象在回调执行前存活  
        execute\_async(\[self \= shared\_from\_this()\]() {  
            self-\>onAsyncTaskComplete();  
        });  
    }
    
    void onAsyncTaskComplete() {  
        // 模拟耗时操作  
        std::this\_thread::sleep\_for(std::chrono::milliseconds(100));  
        std::cout \<\< "TaskProcessor " \<\< id\_ \<\< ": 异步任务完成。\\n";  
    }
    
    \~TaskProcessor() {  
        std::cout \<\< "TaskProcessor " \<\< id\_ \<\< " 销毁。\\n";  
    }
    

    private:
    // 构造函数私有,强制使用工厂方法
    TaskProcessor(int id) : id_(id) {
    std::cout << “TaskProcessor " << id_ << " 创建。\n”;
    }
    int id_;
    };

    int main() {
    std::cout << “进入 main\n”;
    {
    auto processor = TaskProcessor::create(1);
    processor->startAsyncTask();
    // processor 即将离开作用域,但由于 Lambda 中捕获了 shared_ptr (self),
    // 对象会存活直到异步任务完成并释放 self。
    std::cout << “processor 即将离开作用域\n”;
    } // processor shared_ptr 销毁,但对象可能仍然存活

    std::cout \<\< "main 等待异步任务...\\n";  
    std::this\_thread::sleep\_for(std::chrono::seconds(1)); // 等待异步任务完成  
    std::cout \<\< "main 结束\\n";  
    return 0;  
    

    }

    117

5.2 在标准容器中使用智能指针

智能指针可以作为元素存储在标准库容器中,这对于管理一组动态分配的对象特别有用,尤其是处理多态对象集合时。

  • std::vector<std::unique_ptr<T>>
    • 用途:存储一组独占所有权的对象,通常用于基类指针 T 指向不同的派生类对象,实现多态集合 35。
    • 操作:由于 unique_ptr 不可复制,向 vector 中添加元素必须使用移动语义,例如 vec.push_back(std::move(ptr)) 或 vec.emplace_back(std::make_unique<Derived>(…)) 40。
    • 所有权:vector 拥有所有 unique_ptr,当 vector 被销毁时,它包含的所有 unique_ptr 也会被销毁,进而释放它们管理的资源 58。

C++
#include <vector>
#include <memory>
#include <iostream>

struct Base { virtual ~Base() = default; virtual void print() = 0; };
struct DerivedA : Base { void print() override { std::cout << “DerivedA\n”; } };
struct DerivedB : Base { void print() override { std::cout << “DerivedB\n”; } };

int main() {
std::vector<std::unique_ptr<Base>> objects;
objects.push_back(std::make_unique<DerivedA>()); // 使用 make_unique
auto ptrB = std::make_unique<DerivedB>();
objects.push_back(std::move(ptrB)); // 使用 move

for (const auto& objPtr : objects) {  
    objPtr-\>print(); // 多态调用  
}  
return 0; // vector 销毁,所有对象被正确删除  

}
35

  • std::vector<std::shared_ptr<T>>
    • 用途:存储一组共享所有权的对象。vector 本身是所有权共享者之一,对象可能同时被容器外部的其他 shared_ptr 拥有 209。
    • 操作:shared_ptr 是可复制的,可以直接 push_back 或 emplace_back。复制 shared_ptr 会增加引用计数 76。
    • 所有权:当 vector 被销毁时,它包含的 shared_ptr 被销毁,对应资源的引用计数减 1。只有当引用计数归零时,资源才会被释放 126。

C++
#include <vector>
#include <memory>
#include <iostream>

struct Resource { int id; Resource(int i): id(i) {} ~Resource() {std::cout << “Resource " << id << " 销毁\n”;} };

int main() {
std::vector<std::shared_ptr<Resource>> resources;
auto res1 = std::make_shared<Resource>(1);
auto res2 = std::make_shared<Resource>(2);
resources.push_back(res1);
resources.push_back(res2);
resources.push_back(res1); // res1 被共享两次

std::cout \<\< "res1 use\_count: " \<\< res1.use\_count() \<\< std::endl; // 输出 3  
std::cout \<\< "res2 use\_count: " \<\< res2.use\_count() \<\< std::endl; // 输出 2

auto external\_ref \= res1; // 外部也持有 res1  
std::cout \<\< "res1 use\_count: " \<\< res1.use\_count() \<\< std::endl; // 输出 4

resources.clear(); // 清空 vector,内部 shared\_ptr 被销毁  
std::cout \<\< "Vector 清空后\\n";  
std::cout \<\< "res1 use\_count: " \<\< res1.use\_count() \<\< std::endl; // 输出 1 (只有 external\_ref 持有)  
std::cout \<\< "res2 use\_count: " \<\< res2.use\_count() \<\< std::endl; // 输出 0 (res2 已销毁)

return 0; // external\_ref 销毁,res1 销毁  

}
76

  • std::map<Key, std::unique_ptr<Value>>
    • 用途:将键映射到独占所有的值对象。常用于缓存或资源管理,其中每个键对应一个唯一资源 214。
    • 操作:插入时需要移动 unique_ptr,例如使用 map.emplace(key, std::move(ptr)) 或 map.emplace(key, std::make_unique<Value>(…)) [214, S_

引用的著作

  1. Smart Pointers In C++ - GeeksProgramming, 访问时间为 五月 6, 2025, https://geeksprogramming.com/smart-pointers-in-cpp/
  2. Smart Pointers in C++ | GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/smart-pointers-cpp/
  3. Why are C++ pointers portrayed as a crazy hard topic? They seem really simple to me., 访问时间为 五月 6, 2025, https://www.reddit.com/r/learnprogramming/comments/18x7xa8/why_are_c_pointers_portrayed_as_a_crazy_hard/
  4. c++ - What is a smart pointer and when should I use one? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one
  5. Why are pointers not recommended when coding with C++?, 访问时间为 五月 6, 2025, https://softwareengineering.stackexchange.com/questions/56935/why-are-pointers-not-recommended-when-coding-with-c
  6. The problem with raw pointers and manual memory management - StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/12309/397155/the-problem-with-raw-pointers-and-manual-memory-management
  7. C++ Memory Management - The Chromium Projects, 访问时间为 五月 6, 2025, https://www.chromium.org/chromium-os/developer-library/reference/cpp/cpp-memory-management/
  8. Understanding Memory Management, Part 3: C++ Smart Pointers - Educated Guesswork, 访问时间为 五月 6, 2025, https://educatedguesswork.org/posts/memory-management-3/
  9. How do you decide when to use smart pointers vs raw pointers in modern C++? - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/1ibxuyn/how_do_you_decide_when_to_use_smart_pointers_vs/
  10. When to use a smart pointer, a raw pointer or a reference? And what are they actually? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/8d5ory/when_to_use_a_smart_pointer_a_raw_pointer_or_a/
  11. 啥时候用智能指针,啥时候用原始指针,啥时候用引用?它们到底是个啥? - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/8d5ory/when_to_use_a_smart_pointer_a_raw_pointer_or_a/?tl=zh-hans
  12. When to Use Raw Pointers - Hitchcock Codes, 访问时间为 五月 6, 2025, https://hitchcock.codes/blog/when-to-use-raw-pointers
  13. C++ pointers and memory leak - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/32653090/c-pointers-and-memory-leak
  14. Modern C++ Memory Management With unique_ptr (stop using delete) - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/programming/comments/44qa2b/modern_c_memory_management_with_unique_ptr_stop/
  15. Smart pointers (Modern C++) | Microsoft Learn, 访问时间为 五月 6, 2025, https://learn.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170
  16. C++11中智能指针的原理、使用、实现- wxquare - 博客园, 访问时间为 五月 6, 2025, https://www.cnblogs.com/wxquare/p/4759020.html
  17. 为C编程语言实现智能指针 - 齐思, 访问时间为 五月 6, 2025, https://news.miracleplus.com/share_link/26824
  18. Memory Management and RAII - DEV Community, 访问时间为 五月 6, 2025, https://dev.to/10xlearner/memory-management-and-raii-4f20
  19. C++11智能指针(auto_ptr,unique_ptr,shared_ptr,weak_ptr的详解与模拟实现) - 支付宝开放平台, 访问时间为 五月 6, 2025, https://open.alipay.com/portal/forum/post/129201021
  20. RAII and smart pointers in C++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/395123/raii-and-smart-pointers-in-c
  21. C++ RAII, Shared Pointers, Unique Pointers Tutorial - /src$ make, 访问时间为 五月 6, 2025, https://www.srcmake.com/home/raii
  22. Smart pointer - Wikipedia, 访问时间为 五月 6, 2025, https://en.wikipedia.org/wiki/Smart_pointer
  23. Quick Q: What is a smart pointer and when should I use one? - Standard C++, 访问时间为 五月 6, 2025, https://isocpp.org/blog/2015/09/quick-q-what-is-a-smart-pointer-and-when-should-i-use-one
  24. RAII and Smart Pointers, 访问时间为 五月 6, 2025, https://web.stanford.edu/class/archive/cs/cs106l/cs106l.1192/lectures/lecture15/15_RAII.pdf
  25. The benefits of using RAII in C++ [duplicate] - Software Engineering Stack Exchange, 访问时间为 五月 6, 2025, https://softwareengineering.stackexchange.com/questions/155927/the-benefits-of-using-raii-in-c
  26. c++ - Which kind of pointer do I use when? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/8706192/which-kind-of-pointer-do-i-use-when
  27. 4. Smart Pointers - Effective Modern C++ [Book] - O’Reilly Media, 访问时间为 五月 6, 2025, https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch04.html
  28. C++ 智能指针 - 阿里云天池, 访问时间为 五月 6, 2025, https://tianchi.aliyun.com/forum/post/4344
  29. smart pointers - cppreference.com - C++ Reference, 访问时间为 五月 6, 2025, https://en.cppreference.com/book/intro/smart_pointers
  30. 20. Smart Pointers — Programming for Financial Technology, 访问时间为 五月 6, 2025, https://fintechpython.pages.oit.duke.edu/jupyternotebooks/3-CPlusCPlus/20-SmartPointers/20-SmartPointers.html
  31. auto_ptr vs unique_ptr vs shared_ptr vs weak_ptr in C++ | GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-in-cpp/
  32. What is the difference between Boost smart pointers and std smart pointers? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/13267908/what-is-the-difference-between-boost-smart-pointers-and-std-smart-pointers
  33. A beginner’s look at smart pointers in modern C++, 访问时间为 五月 6, 2025, https://isocpp.org/blog/2018/11/a-beginners-look-at-smart-pointers-in-modern-cpp
  34. 智能指针-使用、避坑和实现- 高性能架构探索 - 博客园, 访问时间为 五月 6, 2025, https://www.cnblogs.com/gaoxingnjiagoutansuo/p/15870166.html
  35. std::unique_ptr - cppreference.com, 访问时间为 五月 6, 2025, https://en.cppreference.com/w/cpp/memory/unique_ptr
  36. Unique_ptr in C++ | GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/unique_ptr-in-cpp/
  37. C++11: Using std::unique_ptr as a class member: initialization, move semantics and custom deleters | Katy’s Code, 访问时间为 五月 6, 2025, https://katyscode.wordpress.com/2012/10/04/c11-using-stdunique_ptr-as-a-class-member-initialization-move-semantics-and-custom-deleters/
  38. unique_ptr, shared_ptr, weak_ptr, scoped_ptr, raw pointers - Knowing your smart pointers (2/7) - Fluent C++, 访问时间为 五月 6, 2025, https://www.fluentcpp.com/2017/08/25/knowing-your-smart-pointers/
  39. std::unique_ptr | C++ Programming Language, 访问时间为 五月 6, 2025, https://cpp-lang.net/docs/std/memory/unique_ptr/
  40. How to: Create and use unique_ptr instances | Microsoft Learn, 访问时间为 五月 6, 2025, https://learn.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-170
  41. std::unique_ptr - Cppreference, 访问时间为 五月 6, 2025, https://www.inf.pucrs.br/~flash/lapro2ec/cppreference/w/cpp/memory/unique_ptr.html
  42. unique_ptr or move semantic? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/1i186zn/unique_ptr_or_move_semantic/
  43. Transferring the ownership of object from one unique_ptr to another unique_ptr in C++11?, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/26318506/transferring-the-ownership-of-object-from-one-unique-ptr-to-another-unique-ptr-i
  44. std::unique_ptr - cppreference.com, 访问时间为 五月 6, 2025, http://ld2015.scusa.lsu.edu/cppreference/en/cpp/memory/unique_ptr.html
  45. std::unique_ptr, move constructors, etc and you’re pretty safe, but it’s still n… | Hacker News, 访问时间为 五月 6, 2025, https://news.ycombinator.com/item?id=11722635
  46. Returning unique_ptr from functions - Modern C++ Memory Management with unique_ptr | StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/12308/397140/returning-uniqueptr-from-functions
  47. Return std::make_unique from function? - The First Cry of Atom, 访问时间为 五月 6, 2025, https://www.lewuathe.com/2020-10-29-return-std-make_unique-from-function/
  48. Does C++11 unique_ptr and shared_ptr able to convert to each other’s type?, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/37884728/does-c11-unique-ptr-and-shared-ptr-able-to-convert-to-each-others-type
  49. return unique_ptr
  50. Converting unique_ptr to shared_ptr: Factory function example - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/question/qa1257940863/converting-unique_ptr-to-shared_ptr-factory-function-example
  51. Smart pointer confusion (std::unique_ptr - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/general/284342/
  52. C++ Core Guidelines: Rules for Smart Pointers – MC++ BLOG, 访问时间为 五月 6, 2025, https://www.modernescpp.com/index.php/c-core-guidelines-rules-to-smart-pointers/
  53. The Pitfalls of Aliasing Pointers in Modern C++, 访问时间为 五月 6, 2025, https://www.fluentcpp.com/2019/01/22/pitfalls-of-aliasing-a-pointer-in-modern-cpp/
  54. unique_ptr in C++, I end up using std::move everywhere. Am I doing something wrong, or is this how you are supposed to use them? - Quora, 访问时间为 五月 6, 2025, https://www.quora.com/When-I-use-std-unique_ptr-in-C-I-end-up-using-std-move-everywhere-Am-I-doing-something-wrong-or-is-this-how-you-are-supposed-to-use-them
  55. Understanding std::move and Ownership Transfer with std::unique_ptr in C++, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/76552262/understanding-stdmove-and-ownership-transfer-with-stdunique-ptr-in-c
  56. std::unique_ptr - cppreference.com, 访问时间为 五月 6, 2025, https://cppreference-45864d.gitlab-pages.liu.se/en/cpp/memory/unique_ptr.html
  57. C++ | Dynamic array with std::unique_ptr - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/question/qa1348405750/dynamic-array-with-stdunique_ptr
  58. How to Create Vectors of Unique Pointers in C++? - GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/how-to-create-vector-of-unique-pointers-in-cpp/
  59. vector of unique_ptr - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/beginner/258997/
  60. Vectors and unique pointers - Sandor Dargo’s Blog, 访问时间为 五月 6, 2025, https://www.sandordargo.com/blog/2023/04/12/vector-of-unique-pointers
  61. Implementation of Binary Search Trees Via Smart Pointers - The Science and Information (SAI) Organization, 访问时间为 五月 6, 2025, https://thesai.org/Downloads/Volume6No3/Paper_9-Implementation_of_Binary_Search_Trees_Via_Smart_Pointers.pdf
  62. std::unique_ptr vs std::make_unique : r/Cplusplus - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/Cplusplus/comments/1jpzlie/stdunique_ptr_vs_stdmake_unique/
  63. what is difference between std::unique_ptr V/s std::make_unique ? | Qt Forum, 访问时间为 五月 6, 2025, https://forum.qt.io/topic/154869/what-is-difference-between-std-unique_ptr-v-s-std-make_unique
  64. Advantages of using std::make_unique over new operator [duplicate] - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/37514509/advantages-of-using-stdmake-unique-over-new-operator
  65. V824. It is recommended to use the ′make_unique/make_shared′ function to create smart pointers. - PVS-Studio, 访问时间为 五月 6, 2025, https://pvs-studio.com/en/docs/warnings/v824/
  66. Quick Q: Differences between std::make_unique and std::unique_ptr with new, 访问时间为 五月 6, 2025, https://isocpp.org/blog/2019/06/quick-q-differences-between-stdmake-unique-and-stdunique-ptr-with-new
  67. make_unique, std::make_unique_for_overwrite - cppreference.com - C++ Reference, 访问时间为 五月 6, 2025, https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
  68. std::make_unique in C++ 14 - GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/cpp-14-make_unique/
  69. How to implement make_unique function in C++11? [duplicate] - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/17902405/how-to-implement-make-unique-function-in-c11
  70. C++ How to create a std::unique_ptr from a class that takes parameters on constructor, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/31173299/c-how-to-create-a-stdunique-ptr-from-a-class-that-takes-parameters-on-constr
  71. Unique_ptr and make_unique not working well with char arrays : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/8wbuu6/unique_ptr_and_make_unique_not_working_well_with/
  72. C++14 Library Extensions, C++ FAQ, 访问时间为 五月 6, 2025, https://isocpp.org/wiki/faq/cpp14-library
  73. Why does C++11 have `make_shared` but not `make_unique` [duplicate] - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/12580432/why-does-c11-have-make-shared-but-not-make-unique
  74. abseil / Tip of the Week #126: `make_unique` is the new `new`, 访问时间为 五月 6, 2025, https://abseil.io/tips/126
  75. 智能指针(现代C++) - Learn Microsoft, 访问时间为 五月 6, 2025, https://learn.microsoft.com/zh-cn/cpp/cpp/smart-pointers-modern-cpp?view=msvc-170
  76. std::shared_ptr - cppreference.com - C++ Reference, 访问时间为 五月 6, 2025, https://en.cppreference.com/w/cpp/memory/shared_ptr
  77. Top 10 dumb mistakes to avoid with C++ 11 smart pointers - A CODERS JOURNEY, 访问时间为 五月 6, 2025, https://acodersjourney.com/top-10-dumb-mistakes-avoid-c-11-smart-pointers/
  78. Use of raw pointers in modern C++ post C++11 [closed] - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/25705417/use-of-raw-pointers-in-modern-c-post-c11
  79. std::unique_ptr
  80. unique_ptr syntax - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/beginner/127597/
  81. Handling arrays with std::unique_ptr - Mastering std::unique_ptr for Memory Management in C++ | StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/11857/376985/handling-arrays-with-stduniqueptr
  82. What are the different ways to deallocate an array in C++? Are there any alternatives to using the delete [] operator? - Quora, 访问时间为 五月 6, 2025, https://www.quora.com/What-are-the-different-ways-to-deallocate-an-array-in-C-Are-there-any-alternatives-to-using-the-delete-operator
  83. Create a unique_ptr to an array with type double in c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/62751984/create-a-unique-ptr-to-an-array-with-type-double-in-c
  84. C++ Smart Pointers and Arrays - C++ Stories, 访问时间为 五月 6, 2025, https://www.cppstories.com/2021/smartptr-array/
  85. Is there any use for unique_ptr with array? - c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/16711697/is-there-any-use-for-unique-ptr-with-array
  86. How to use delete operator correctly - LabEx, 访问时间为 五月 6, 2025, https://labex.io/tutorials/cpp-how-to-use-delete-operator-correctly-466975
  87. How may I properly declare an object array using unique_ptr along with make_unique, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/57138380/how-may-i-properly-declare-an-object-array-using-unique-ptr-along-with-make-uniq
  88. How may I properly declare an object array using unique_ptr along with make_unique [ Solved ] | Sololearn: Learn to code for FREE!, 访问时间为 五月 6, 2025, https://www.sololearn.com/en/Discuss/1898965/how-may-i-properly-declare-an-object-array-using-unique_ptr-along-with-make_unique-solved-
  89. How do I use a custom deleter with a std::unique_ptr member? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member
  90. Does anybody really use deleters ? : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/rlvsq0/does_anybody_really_use_deleters/
  91. Understanding the inner workings of C++ smart pointers - The unique_ptr with custom deleter - Andreas Fertig, 访问时间为 五月 6, 2025, https://andreasfertig.com/blog/2024/08/understanding-the-inner-workings-of-cpp-smart-pointers-the-unique_ptr-with-custom-deleter/
  92. Best way to use custom deleter with unique_ptr : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/1hoctkx/best_way_to_use_custom_deleter_with_unique_ptr/
  93. How To: Wrap unsigned char* in unique_ptr (with custom deleter) : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/18zyae6/how_to_wrap_unsigned_char_in_unique_ptr_with/
  94. Is the use of custom stateful deleter in std::unique_ptr (C++11) dangerous? - Quora, 访问时间为 五月 6, 2025, https://www.quora.com/Is-the-use-of-custom-stateful-deleter-in-std-unique_ptr-C++11-dangerous
  95. std::unique_ptr w/ custom deleter best practices and syntax questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/xv16r1/stdunique_ptr_w_custom_deleter_best_practices_and/
  96. std::unique_ptr
  97. How to create a unique_ptr ( with custom deleter ) from a raw pointer - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/19385sw/how_to_create_a_unique_ptr_with_custom_deleter/
  98. C++ | unique_ptr with custom deleter - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/question/qa1366990479/unique_ptr-with-custom-deleter
  99. unique_ptr is NOT just for heap allocations | custom deleters - YouTube, 访问时间为 五月 6, 2025, https://www.youtube.com/watch?v=mhCMtNWQ_CQ
  100. C++ Custom Deleters: unique_ptr vs shared_ptr - Martyn Davis | Marengo, 访问时间为 五月 6, 2025, https://www.martyndavis.com/?p=474
  101. make_unique and delete? - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/beginner/228922/
  102. PImpl Idiom in C++ with Examples | GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/pimpl-idiom-in-c-with-examples/
  103. Pimpl idiom with modern C++ - Sangwan’s blog, 访问时间为 五月 6, 2025, https://bitboom.github.io/2019-12-13/pimpl-idiom
  104. How to implement the pimpl idiom by using unique_ptr - Fluent C++, 访问时间为 五月 6, 2025, https://www.fluentcpp.com/2017/09/22/make-pimpl-using-unique_ptr/
  105. C++ PImpl pattern with std::unique_ptr, incomplete types and default constructors | Chrizog, 访问时间为 五月 6, 2025, https://chrizog.com/cpp-pimpl-unique-ptr-incomplete-types-default-constructor
  106. How do I use unique_ptr for pimpl? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/9020372/how-do-i-use-unique-ptr-for-pimpl
  107. How can I use the pimpl idiom with std::unique_ptr with Swift/C++ interop?, 访问时间为 五月 6, 2025, https://forums.swift.org/t/how-can-i-use-the-pimpl-idiom-with-std-unique-ptr-with-swift-c-interop/76606
  108. Memory and Performance Overhead of Smart Pointers – MC++ BLOG - Modernes C++, 访问时间为 五月 6, 2025, https://www.modernescpp.com/index.php/memory-and-performance-overhead-of-smart-pointer/
  109. Smart Pointers, 访问时间为 五月 6, 2025, http://www.mathcs.richmond.edu/~dszajda/classes/cs240/Fall_2022/slides/SmartPointers.pdf
  110. shared_ptr in C++ - GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/shared_ptr-in-cpp/
  111. Shared Pointers using std::shared_ptr - StudyPlan.dev, 访问时间为 五月 6, 2025, https://www.studyplan.dev/pro-cpp/shared-pointers
  112. Exploring std::shared_ptr | Shahar Mike’s Web Spot, 访问时间为 五月 6, 2025, https://shaharmike.com/cpp/shared-ptr/
  113. New to c++11 features, proper use of shared_ptr? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/37801184/new-to-c11-features-proper-use-of-shared-ptr
  114. How shared ownership works with reference counting - Smart Memory Management with std::shared_ptr in Modern C++ | StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/12309/397158/how-shared-ownership-works-with-reference-counting
  115. C++ | shared_ptr - basics and internals with examples - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/tutorial/ta1358374985/shared_ptr-basics-and-internals-with-examples
  116. std::shared_ptr - cppreference.com, 访问时间为 五月 6, 2025, http://ld2015.scusa.lsu.edu/cppreference/en/cpp/memory/shared_ptr.html
  117. C++ | enable_shared_from_this - overview, examples, and internals - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/tutorial/ta1414193955/enable_shared_from_this-overview-examples-and-internals
  118. C++11 Smart Pointers: Shared Pointer - antonym.org, 访问时间为 五月 6, 2025, http://antonym.org/2014/02/c-plus-plus-11-shared-pointer.html
  119. Memory management using Smart Pointers in C++ - Part 2 - DEV Community, 访问时间为 五月 6, 2025, https://dev.to/pratikparvati/memory-management-using-smart-pointers-in-c-part-2-3pp4
  120. shared_ptr - 1.53.0 - Boost C++ Libraries, 访问时间为 五月 6, 2025, https://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm
  121. Smart pointers, shared. - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/general/188409/
  122. C++ Shared Pointer Thread-Safety - Lei Mao’s Log Book, 访问时间为 五月 6, 2025, https://leimao.github.io/blog/CPP-Shared-Ptr-Thread-Safety/
  123. `weak_ptr::expired` behavior in the dtor of the object - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/41851520/weak-ptrexpired-behavior-in-the-dtor-of-the-object
  124. 22.6 — std::shared_ptr – Learn C++, 访问时间为 五月 6, 2025, https://www.learncpp.com/cpp-tutorial/stdshared_ptr/
  125. ’new’ in modern c++ - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/general/179117/
  126. Vector of SharedPointers without C++11 m - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/general/171877/
  127. Blog: Exploring Smart Pointers in C++ : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/1ftbw4o/blog_exploring_smart_pointers_in_c/
  128. std::shared_ptr
  129. What is the benifit of make_unique or make_shared in C++? [duplicate] - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/68794752/what-is-the-benifit-of-make-unique-or-make-shared-in-c
  130. creating a shared_ptr from unique_ptr - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/18889843/creating-a-shared-ptr-from-unique-ptr
  131. shared_ptr Versus unique_ptr in Factory Functions - Simplify C++!, 访问时间为 五月 6, 2025, https://arne-mertz.de/2015/12/shared_ptr-versus-unique_ptr-in-factory-functions/
  132. How can I transfer ownership from a shared_ptr to a unique_ptr ? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/x0jzq3/how_can_i_transfer_ownership_from_a_shared_ptr_to/
  133. std::shared_ptr - Cppreference, 访问时间为 五月 6, 2025, https://www.inf.pucrs.br/~flash/lapro2ec/cppreference/w/cpp/memory/shared_ptr.html
  134. Why does a null but non-empty std::shared_ptr fail an if() statement? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/71877042/why-does-a-null-but-non-empty-stdshared-ptr-fail-an-if-statement
  135. What is the maximum reference count in std::shared_ptr? What happens if you try to exceed it? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/12402116/what-is-the-maximum-reference-count-in-stdshared-ptr-what-happens-if-you-try
  136. Is it safe to rely on “use_count()” to re-use “shared_ptr” memory in a in case the producer is a single thread? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/77286007/is-it-safe-to-rely-on-use-count-to-re-use-shared-ptr-memory-in-a-in-case-t
  137. Woes with std::shared_ptr
  138. Purpose of use_count() with shared_ptr : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/xvh52b/purpose_of_use_count_with_shared_ptr/
  139. C++ Custom Deleters - j2i.net, 访问时间为 五月 6, 2025, https://blog.j2i.net/2024/07/16/c-custom-deleters/
  140. using a custom deleter for std::shared_ptr on a direct3d11 object - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/13633737/using-a-custom-deleter-for-stdshared-ptr-on-a-direct3d11-object
  141. using custom deleter for shared_ptr - C Board, 访问时间为 五月 6, 2025, https://cboard.cprogramming.com/cplusplus-programming/168337-using-custom-deleter-shared_ptr.html
  142. Custom Deleters for C++ Smart Pointers, 访问时间为 五月 6, 2025, https://www.cppstories.com/2016/04/custom-deleters-for-c-smart-pointers/
  143. Shared_ptr custom deleter - c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/60568363/shared-ptr-custom-deleter
  144. Custom Deleter for C++ Smart Pointers - Lei Mao’s Log Book, 访问时间为 五月 6, 2025, https://leimao.github.io/blog/CPP-Smart-Pointer-Custom-Deleter/
  145. c++ - Implementing reference counting from scratch or using shared_ptr for resource?, 访问时间为 五月 6, 2025, https://softwareengineering.stackexchange.com/questions/372925/implementing-reference-counting-from-scratch-or-using-shared-ptr-for-resource
  146. Why prefer new over std::make_shared? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/1fdhbna/why_prefer_new_over_stdmake_shared/
  147. Whether shared_ptr are thread safe is an… interesting question. If you have to… | Hacker News, 访问时间为 五月 6, 2025, https://news.ycombinator.com/item?id=24391765
  148. std::shared_ptr thread safety - c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/14482830/stdshared-ptr-thread-safety
  149. Question about shared_ptr move and thread safety : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/qfhqsr/question_about_shared_ptr_move_and_thread_safety/
  150. Question about the thread-safety of using shared_ptr - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/72326082/question-about-the-thread-safety-of-using-shared-ptr
  151. How to make C++ thread safe? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/150jvqf/how_to_make_c_thread_safe/
  152. Here’s everything I know about shared_ptr : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/5do55l/heres_everything_i_know_about_shared_ptr/
  153. When should I use a mutex with std::shared_ptr in C++ multithreading? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/76585300/when-should-i-use-a-mutex-with-stdshared-ptr-in-c-multithreading
  154. Are smart pointers threadsafe? - Intel Community, 访问时间为 五月 6, 2025, https://community.intel.com/t5/Intel-oneAPI-Threading-Building/Are-smart-pointers-threadsafe/td-p/893565
  155. simplfied observer pattern with std::shared_ptr/weak_ptr - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/63025270/simplfied-observer-pattern-with-stdshared-ptr-weak-ptr
  156. Thread safety considerations in multithreaded environments - Smart Memory Management with std::shared_ptr in Modern C++ | StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/12309/397171/thread-safety-considerations-in-multithreaded-environments
  157. GotW #95 Solution: Thread Safety and Synchronization - Herb Sutter, 访问时间为 五月 6, 2025, https://herbsutter.com/2014/01/13/gotw-95-solution-thread-safety-and-synchronization/
  158. How fast is std::shared_ptr? : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/9byrhy/how_fast_is_stdshared_ptr/
  159. Mistakes to avoid with C++ 11 smart pointers - Hacker News, 访问时间为 五月 6, 2025, https://news.ycombinator.com/item?id=11698784
  160. Can you make a std::shared_ptr manage an array allocated with new T[]? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/13061979/can-you-make-a-stdshared-ptr-manage-an-array-allocated-with-new-t
  161. A beginner’s look at smart pointers in modern C++, 访问时间为 五月 6, 2025, https://www.internalpointers.com/post/beginner-s-look-smart-pointers-modern-c
  162. C++11 : Why array with shared_ptr can not be deleted by default - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/41058824/c11-why-array-with-shared-ptr-can-not-be-deleted-by-default
  163. std::weak_ptr - cppreference.com, 访问时间为 五月 6, 2025, https://en.cppreference.com/w/cpp/memory/weak_ptr
  164. Weak Pointers - CodeX: C++, Introduction for Entry Level Programmers, 访问时间为 五月 6, 2025, https://codexfoundation.net/codex-cpp/memory_management/weak
  165. C++ | Using weak_ptr for circular references - nextptr, 访问时间为 五月 6, 2025, https://www.nextptr.com/tutorial/ta1382183122/using-weak_ptr-for-circular-references
  166. How does a weak_ptr prevent cyclic references? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/4984381/how-does-a-weak-ptr-prevent-cyclic-references
  167. Why use std::weak_ptr and what’s the difference between this and std::shared_ptr? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/qxe9p5/why_use_stdweak_ptr_and_whats_the_difference/
  168. weak_ptr in C++ | GeeksforGeeks, 访问时间为 五月 6, 2025, https://www.geeksforgeeks.org/weak_ptr-in-cpp/
  169. 22.7 — Circular dependency issues with std::shared_ptr, and std::weak_ptr - Learn C++, 访问时间为 五月 6, 2025, https://www.learncpp.com/cpp-tutorial/circular-dependency-issues-with-stdshared_ptr-and-stdweak_ptr/
  170. Of common problems with shared pointers - twdev.blog, 访问时间为 五月 6, 2025, https://twdev.blog/2024/09/sharedptr/
  171. C++11, shared_ptr.reset() and cyclic references - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/27550424/c11-shared-ptr-reset-and-cyclic-references
  172. How to break shared_ptr cyclic reference using weak_ptr - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/27085782/how-to-break-shared-ptr-cyclic-reference-using-weak-ptr
  173. Weak Pointers and Circular References in C++ 11 - Visual Studio Magazine, 访问时间为 五月 6, 2025, https://visualstudiomagazine.com/articles/2012/10/19/circular-references.aspx
  174. shared_ptr helps, but it won’t cure your circular dependency blues. When do you - Hacker News, 访问时间为 五月 6, 2025, https://news.ycombinator.com/item?id=4969999
  175. About “circular reference”, I used weak_ptr but memory leak still happened - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/57851897/about-circular-reference-i-used-weak-ptr-but-memory-leak-still-happened
  176. Smart Pointers in C++ - Part 5 - Codementor, 访问时间为 五月 6, 2025, https://www.codementor.io/@sandesh87/smart-pointers-in-c-part-5-1jdn6o4bcb
  177. std::weak_ptr | cpp, 访问时间为 五月 6, 2025, https://irkos.org/cpp/weak_ptr/
  178. When is std::weak_ptr useful? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful
  179. Should I use shared_ptr or weak_ptr on not mainly containers? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/31778308/should-i-use-shared-ptr-or-weak-ptr-on-not-mainly-containers
  180. smart pointers for modelling a general tree structure & its iterators - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/6679482/smart-pointers-for-modelling-a-general-tree-structure-its-iterators
  181. std::weak_ptr in C++ - TheJat.in, 访问时间为 五月 6, 2025, https://thejat.in/learn/stdweak-ptr-in-c
  182. std::weak_ptr - cppreference.com, 访问时间为 五月 6, 2025, http://ld2015.scusa.lsu.edu/cppreference/en/cpp/memory/weak_ptr.html
  183. std::weak_ptr
  184. std::weak_ptr
  185. How does the ::lock() function work on std::weak_ptr? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/67412380/how-does-the-lock-function-work-on-stdweak-ptr
  186. Why weak_ptr::use_count may return a different count to shared_ptr - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/79080965/why-weak-ptruse-count-may-return-a-different-count-to-shared-ptruse-count
  187. std::weak_ptr::use_count - CPlusPlus.com, 访问时间为 五月 6, 2025, https://cplusplus.com/reference/memory/weak_ptr/use_count/
  188. How to have a class member hold *either* a shared_ptr or a weak_ptr? - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/uu7evo/how_to_have_a_class_member_hold_either_a_shared/
  189. std::enable_shared_from_this - cppreference.com - C++ Reference, 访问时间为 五月 6, 2025, https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
  190. std::shared_ptr and shared_from_this - Embedded Artistry, 访问时间为 五月 6, 2025, https://embeddedartistry.com/blog/2017/01/11/stdshared_ptr-and-shared_from_this/
  191. Understanding completion handlers - Mastering Network Programming with Asio C++: A Comprehensive Guide | StudyRaid, 访问时间为 五月 6, 2025, https://app.studyraid.com/en/read/12426/401292/understanding-completion-handlers
  192. std::enable_shared_from_this - OpenKneeboard, 访问时间为 五月 6, 2025, https://openkneeboard.com/internals/why-std-enable-shared-from-this/
  193. Callbacks (async functions in C++11) - 1.88.0 - Boost C++ Libraries, 访问时间为 五月 6, 2025, https://live.boost.org/doc/libs/1_88_0/libs/mysql/doc/html/mysql/examples/callbacks.html
  194. Safe Asynchronous Calls in C++ - Coding Notes, 访问时间为 五月 6, 2025, https://nixiz.github.io/yazilim-notlari/2023/09/06/secure-callback-usage-en
  195. when to use shared_from_this() - c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/69998629/when-to-use-shared-from-this
  196. shared_from_this() throws bad_weak_ptr exception when called from objects managed by fruit · Issue #90 - GitHub, 访问时间为 五月 6, 2025, https://github.com/google/fruit/issues/90
  197. What are some use cases of using shared_ptr in a single threaded program? : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/uhhx1w/what_are_some_use_cases_of_using_shared_ptr_in_a/
  198. Be careful of using smart pointers and async code (C++) - Sergei Zobov | Blog, 访问时间为 五月 6, 2025, https://blog.szobov.ru/2018/12/23/smart-pointers-and-async-code/
  199. What is the usefulness of `enable_shared_from_this`? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/712279/what-is-the-usefulness-of-enable-shared-from-this
  200. std::enable_shared_from_this - cppreference.com - Mooshak, 访问时间为 五月 6, 2025, https://mooshak.dcc.fc.up.pt/~oni-judge/doc/cppreference/reference/en/cpp/memory/enable_shared_from_this.html
  201. std::enable_shared_from_this - cppreference.com, 访问时间为 五月 6, 2025, https://saco-evaluator.org.za/docs/cppreference/en/cpp/memory/enable_shared_from_this.html
  202. std::enable_shared_from_this - cppreference.com, 访问时间为 五月 6, 2025, https://perveevm.ru/docs/cpp/reference/en/cpp/memory/enable_shared_from_this.html
  203. std::enable_shared_from_this - cppreference.com, 访问时间为 五月 6, 2025, http://naipc.uchicago.edu/2014/ref/cppreference/en/cpp/memory/enable_shared_from_this.html
  204. Boost.SmartPtr: The Smart Pointer Library - master, 访问时间为 五月 6, 2025, https://www.boost.org/doc/libs/master/libs/smart_ptr/
  205. std::enable_shared_from_this: Is public inheritance necessary? - Google Groups, 访问时间为 五月 6, 2025, https://groups.google.com/a/isocpp.org/g/std-discussion/c/ExE27N-uJm8
  206. std::enable_shared_from_this - cppreference.com, 访问时间为 五月 6, 2025, https://cppreference-45864d.gitlab-pages.liu.se/en/cpp/memory/enable_shared_from_this.html
  207. std::enable_shared_from_this::shared_from_this - cppreference.com, 访问时间为 五月 6, 2025, http://ld2015.scusa.lsu.edu/cppreference/en/cpp/memory/enable_shared_from_this/shared_from_this.html
  208. enable_shared_from_this - overview, examples, and internals : r/cpp - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp/comments/gueukq/enable_shared_from_this_overview_examples_and/
  209. Vector of Objects vs Vector of Pointers - C++ Stories, 访问时间为 五月 6, 2025, https://www.cppstories.com/2014/05/vector-of-objects-vs-vector-of-pointers/
  210. How to: Create and use shared_ptr instances | Microsoft Learn, 访问时间为 五月 6, 2025, https://learn.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-170
  211. Help understanding how shared_ptr’s work - C++ Forum, 访问时间为 五月 6, 2025, https://cplusplus.com/forum/general/156462/
  212. How can I create a shared_ptr to a std::vector? - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/26734452/how-can-i-create-a-shared-ptr-to-a-stdvector
  213. How do you push_back a shared_ptr variable to a vector of shared_ptrs in C++?, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/69891999/how-do-you-push-back-a-shared-ptr-variable-to-a-vector-of-shared-ptrs-in-c
  214. std::unique_ptr with std::map - c++ - Stack Overflow, 访问时间为 五月 6, 2025, https://stackoverflow.com/questions/54828756/stdunique-ptr-with-stdmap
  215. How am I supposed to use unique_ptr here? : r/cpp_questions - Reddit, 访问时间为 五月 6, 2025, https://www.reddit.com/r/cpp_questions/comments/1byfygz/how_am_i_supposed_to_use_unique_ptr_here/
  216. How to make your maps, try_emplace and smart pointers play nicely with each others in C++17. - Jean Guegant, 访问时间为 五月 6, 2025, https://jguegant.github.io/blogs/tech/performing-try-emplace.html

最后修改于 2025-05-06 18:25