c++11 之 智能指针

一. 为什么会有智能指针

c++ 引入智能指针(Smart Pointers)的核心目的,是 解决传统 raw pointer(裸指针)的三大核心问题,同时在不丧失 C++ 底层控制能力的前提下,引入更安全、更符合现代编程范式的内存管理机制。

1. 内存泄漏(Memory Leak)

当动态分配的内存(new 出来的)没有被手动 delete 时,这块内存会一直占用,直到程序结束。

  • 函数提前返回(比如中间有 if 分支跳过 delete);
  • 异常抛出(try 块中 newcatch 块中未处理 delete);
  • 逻辑疏忽(忘记写 delete)。

示例:

c++ 复制代码
void func() {
    int* p = new int(10); // 动态分配内存
    if (some_condition) {
        return; // 提前返回,p 指向的内存未释放 → 内存泄漏
    }
    delete p; // 正常路径才释放,异常/提前返回时失效
}

2. 野指针(Dangling Pointer)

当指针指向的内存被释放(delete)后,指针本身没有被置为 nullptr,后续仍可能被访问 / 操作,导致:

  • 程序崩溃(访问已释放的内存);
  • 数据错乱(内存被重新分配给其他对象,旧指针访问到无效数据)。
c++ 复制代码
int* p = new int(10);
delete p; // 内存释放,但 p 仍指向原地址(野指针)
cout << *p; // 未定义行为(UB),可能崩溃或输出垃圾值

3. 二次释放(Double Free)

同一内存被 delete 两次,会导致程序崩溃(内存管理机制检测到非法释放).

示例 :

c++ 复制代码
int* p = new int(10);
delete p; 
delete p; // 二次释放 → 程序崩溃

二. 智能指针的核心设计理念

RAII + 所有权语义

智能指针本质是 "封装裸指针的类模板" ,其核心机制是:

  1. RAII(资源获取即初始化) :利用类的构造函数获取资源(如动态内存),析构函数自动释放资源(无需手动 delete)。因为 C++ 保证:对象生命周期结束时(如离开作用域、函数返回),其析构函数一定会被调用。

  2. 所有权语义(Ownership Semantics) :明确指针指向的内存 "归谁所有",避免多指针混乱操作(比如 std::unique_ptr 独占所有权,std::shared_ptr 共享所有权)

RAII 理解可以继续深入理解一下,这是一种编程范式

三. 共享智能指帧 std::shared_ptr

核心特点是允许多个 shared_ptr 指向同一块动态内存,通过引用计数(Reference Counting) 管理内存生命周期:当最后一个指向该内存的 shared_ptr 被销毁 / 重置时,才会自动释放内存。

(1)引用计数机制

  • shared_ptr 内部维护两个指针:
    • 指向实际数据的裸指针(data pointer);
    • 指向控制块(control block)的指针(存储引用计数、析构函数、分配器等)
  • 每新增一个 shared_ptr 指向同一资源,引用计数 +1;
  • 每销毁 / 重置一个 shared_ptr,引用计数 -1;
  • 当引用计数降至 0 时,自动调用 delete(或自定义删除器)释放资源,并销毁控制块。
c++ 复制代码
struct MyInt {
    int val;
    MyInt(int v) : val(v) {
        // std::cout << "MyInt 构造:" << val << std::endl;
    }
    ~MyInt() {
        std::cout << "MyInt 析构:" << val << std::endl;
    }
};

void func(std::shared_ptr<MyInt>& ptr) {
    std::cout << "func 内引用计数:" << ptr.use_count() << std::endl;
    // 赋值新的 shared_ptr
    ptr = std::make_shared<MyInt>(20);

    std::shared_ptr<MyInt> ptr2 = ptr;

    std::cout << "func 内引用计数:" << ptr.use_count() << std::endl;
}

int main() {
    std::shared_ptr<MyInt> sp = std::make_shared<MyInt>(10);
    std::cout << "调用前引用计数:" << sp.use_count() << std::endl;
    func(sp);
    std::cout << "调用后引用计数:" << sp.use_count() << std::endl;
    std::cout << "sp 指向的值:" << sp->val << std::endl;
    return 0;
}

执行结果:

(2)独占性(相对)与拷贝 / 赋值

  • 支持拷贝构造赋值运算符:拷贝 / 赋值时仅增加引用计数,不深拷贝数据;
  • 不同于 unique_ptr(不可拷贝),shared_ptr 可自由拷贝、作为函数参数传递(值传递)、存入容器(如 std::vector<std::shared_ptr<T>>)。

//详细解释,结合传参

(3)自定义删除器

shared_ptr 支持自定义删除器(Deletor),用于替代默认的 delete,适配特殊场景(如数组、文件句柄、内存池分配的内存等)。

在计数到0的时候,就会执行删除器,- 如果只是"复制"或"赋值"产生新的 shared_ptr,引用计数仍 >0,删除器不会被执行。

场景 A:数组

c++ 复制代码
std::shared_ptr<int> sp(new int[100],          // 裸指针
                       [](int* p){ delete[] p; } // 自定义删除器
);

场景 B:文件句柄

c++ 复制代码
auto file_deleter = [](FILE* fp){
    if(fp) fclose(fp);
};
std::shared_ptr<FILE> sp(fopen("log.txt","w"), file_deleter);

场景 C:内存池

arduino 复制代码
MemoryPool pool;
std::shared_ptr<Foo> sp(
    static_cast<Foo*>(pool.allocate()),
    [&pool](Foo* p){ pool.deallocate(p); }
);

场景 D:函数对象当删除器

c++ 复制代码
struct VerboseDeleter{
    std::string name;
    void operator()(Widget* w) const {
        std::cout << "destroying " << name << '\n';
        delete w;
    }
};
std::shared_ptr<Widget> pw(new Widget, VerboseDeleter{"widget#1"});

(4)空指针安全

  • 空的 shared_ptr(未指向任何资源)引用计数为 0,操作(如 reset()use_count())不会触发未定义行为;
  • 可通过 operator bool() 判断是否指向有效资源:
cpp 复制代码
std::shared_ptr<int> p;
if (p) { // 等价于 p != nullptr
    std::cout << *p << std::endl;
} else {
    std::cout << "p 为空" << std::endl; // 输出该行
}

(5)大小与性能

  • shared_ptr 大小通常是裸指针的 2 倍(存储数据指针 + 控制块指针);
  • 引用计数的增减是原子操作 (线程安全),但存在轻微的性能开销(相比裸指针 /unique_ptr);
  • 注意:引用计数线程安全 ≠ 数据访问线程安全:多个线程同时修改 shared_ptr 指向的数据,仍需加锁。

(6)避免循环引用(核心坑点)

shared_ptr 的最大问题是循环引用 :两个(或多个)shared_ptr 互相指向对方,导致引用计数无法降至 0,内存泄漏。

c++ 复制代码
struct Node {
    std::shared_ptr<Node> next; // 共享所有权
    ~Node() { std::cout << "Node 析构" << std::endl; }
};

int main() {
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);
    n1->next = n2; // n1 引用 n2
    n2->next = n1; // n2 引用 n1 → 循环引用
    // 函数结束时,n1/n2 销毁,但引用计数仍为1,内存泄漏(析构函数不执行)
    return 0;
}

(7)避免从裸指针多次构造 shared_ptr

同一裸指针多次构造 shared_ptr,会导致多个独立的控制块,最终触发二次释放

c 复制代码
int* raw = new int(10);
std::shared_ptr<int> p1(raw);
std::shared_ptr<int> p2(raw); // 错误:p1和p2各有独立控制块,析构时两次delete raw

(8)优先使用 std::make_shared

  • 更高效:一次性分配数据内存 + 控制块内存(裸指针构造需两次分配:数据 + 控制块);
  • 更安全:避免异常安全问题(如 func(shared_ptr<int>(new int), other_func()) 可能因编译器优化导致内存泄漏);
  • 语法简洁:无需手动写 new

限制:make_shared 无法直接指定自定义删除器(需通过 shared_ptr 构造函数配合)。

(9)不要将 shared_ptr 存储在全局 / 静态变量中(谨慎)

全局 shared_ptr 的析构顺序不确定,可能导致资源释放时依赖的对象已销毁,引发未定义行为。而且这个对象死的可能比main还晚

(10)线程安全说明

  • 引用计数操作shared_ptr 的引用计数增减是原子操作,多线程拷贝 / 重置 shared_ptr 本身是线程安全的;
  • 数据访问 :多线程同时读写 shared_ptr 指向的数据,不是线程安全的 ,需通过互斥锁(如 std::mutex)保护;
  • 同一 shared_ptr 对象的修改 :多线程同时修改同一个 shared_ptr 对象(如 p.reset()),不是线程安全的,需加锁。

(11)补充:shared_ptr 作为函数参数时的引用计数行为

  • 值传递:引用计数 +1,函数结束 -1

    shared_ptr值传递 方式作为函数参数时,会触发 shared_ptr拷贝构造

    1. 函数调用时,实参的 shared_ptr 拷贝给形参,引用计数 +1
    2. 函数执行结束后,形参出作用域被销毁,引用计数 -1
    3. 最终引用计数回到调用前的状态。
    c++ 复制代码
    #include <iostream>
    #include <memory>
    
    void func(std::shared_ptr<int> ptr) { // 值传递,拷贝构造
        std::cout << "func 内引用计数:" << ptr.use_count() << std::endl; // 输出 2
    }
    
    int main() {
        std::shared_ptr<int> sp = std::make_shared<int>(10);
        std::cout << "调用前引用计数:" << sp.use_count() << std::endl; // 输出 1
        func(sp);
        std::cout << "调用后引用计数:" << sp.use_count() << std::endl; // 输出 1
        return 0;
    }

    优缺点

    • 优点 :函数内对形参的修改(如 ptr = nullptr)不会影响实参,逻辑隔离;

    • 缺点

      1. 拷贝 shared_ptr 有开销(原子操作 + 指针拷贝),高频调用时性能损耗明显;
      2. 若函数内长期持有形参(如存储到全局容器),可能导致引用计数长期不为 0,内存无法及时释放。
  • 非 const 引用传递:引用计数不变

    shared_ptr非 const 引用(shared_ptr<T>& 传递时,形参是实参的别名,不会触发拷贝构造,因此引用计数始终不变

    c++ 复制代码
    #include <iostream>
    #include <memory>
    
    struct MyInt {
        int val;
        MyInt(int v) : val(v) {
            std::cout << "MyInt 构造:" << val << std::endl;
        }
        ~MyInt() {
            std::cout << "MyInt 析构:" << val << std::endl;
        }
    };
    
    void func(std::shared_ptr<MyInt>& ptr) {
        std::cout << "func 内引用计数:" << ptr.use_count() << std::endl;
        // 赋值新的 shared_ptr
        ptr = std::make_shared<MyInt>(20);
    }
    
    int main() {
        std::shared_ptr<MyInt> sp = std::make_shared<MyInt>(10);
        std::cout << "调用前引用计数:" << sp.use_count() << std::endl;
        func(sp);
        std::cout << "调用后引用计数:" << sp.use_count() << std::endl;
        std::cout << "sp 指向的值:" << sp->val << std::endl;
        return 0;
    }

    注意这里方法执行,重新创建一个对象的时候,会析构原来的

    优缺点

    • 优点 :函数内对形参的修改(如 ptr = nullptr)不会影响实参,逻辑隔离;

    • 缺点

      1. 拷贝 shared_ptr 有开销(原子操作 + 指针拷贝),高频调用时性能损耗明显;
      2. 若函数内长期持有形参(如存储到全局容器),可能导致引用计数长期不为 0,内存无法及时释放。
  • const 引用传递:引用计数不变

    shared_ptrconst 引用(const shared_ptr<T>& 传递时,形参是实参的只读别名,同样不触发拷贝构造,引用计数不变;且函数内无法修改形参(如赋值、置空),避免了非 const 引用的风险。

    c++ 复制代码
    #include <iostream>
    #include <memory>
    
    void func(const std::shared_ptr<int>& ptr) { // const 引用传递
        std::cout << "func 内引用计数:" << ptr.use_count() << std::endl; // 输出 1
        // ptr = std::make_shared<int>(20); // 编译错误:const 引用不可修改
        *ptr = 20; // 允许修改指向的资源(仅限制 shared_ptr 本身,不限制资源)
    }
    
    int main() {
        std::shared_ptr<int> sp = std::make_shared<int>(10);
        std::cout << "调用前引用计数:" << sp.use_count() << std::endl; // 输出 1
        func(sp);
        std::cout << "调用后引用计数:" << sp.use_count() << std::endl; // 输出 1
        std::cout << "sp 指向的值:" << *sp << std::endl; // 输出 20
        return 0;
    }

    优缺点

    • 优点:无拷贝开销,性能优;只读特性避免了意外修改实参的风险,是最安全的传参方式;
    • 缺点 :若函数内需要长期持有该 shared_ptr(如存储到容器),const 引用可能悬空(实参销毁后引用失效),需手动拷贝(此时引用计数会 +1)。
  • 移动语义(std::move)传递

    若通过 std::moveshared_ptr 以值传递方式传入函数,会触发移动构造

    1. 实参的 shared_ptr 所有权转移给形参,引用计数 保持不变(而非 +1);
    2. 实参变为 "空" 状态(use_count() == 0),不再管理资源;
    3. 函数结束后形参析构,引用计数 -1,若此时无其他持有者,资源被释放。
    C++ 复制代码
    #include <iostream>
    #include <memory>
    
    void func(std::shared_ptr<int> ptr) { // 值传递 + std::move
        std::cout << "func 内引用计数:" << ptr.use_count() << std::endl; // 输出 1
    }
    
    int main() {
        std::shared_ptr<int> sp = std::make_shared<int>(10);
        std::cout << "move 前引用计数:" << sp.use_count() << std::endl; // 输出 1
        func(std::move(sp));
        std::cout << "move 后实参引用计数:" << sp.use_count() << std::endl; // 输出 0
        return 0;
    }

    移动构造会 "窃取" 实参的资源指针和引用计数,实参的内部指针被置空,因此引用计数不会增加(仅所有权转移);适用于不再使用实参的场景,避免拷贝开销。

四. 独享智能指帧 std::unique_ptr

std::unique_ptr 是 C++11 引入的独占式智能指针 ,用于管理动态分配的内存,核心特性是所有权唯一 ------ 同一时间只有一个 unique_ptr 指向某个对象,销毁时自动释放内存,彻底避免内存泄漏和手动 delete 的风险。使用相对共享指针要简单,但却是最常用的智能指针。

  1. 独占所有权:不可拷贝(C++11),仅可移动(move),确保对象只有一个管理者;
  2. 自动释放unique_ptr 析构时(如离开作用域、被重置),自动调用 delete 释放所管理的内存;
  3. 轻量级:无额外开销(大小等同于原始指针),支持自定义删除器;
  4. 支持数组 :C++11 原生支持管理动态数组(unique_ptr<T[]>)。

简单用法,单个对象:

c++ 复制代码
// 方式1:直接构造(C++11 推荐)
unique_ptr<int> ptr1(new int(10));

// 方式2:make_unique(C++14 引入,更安全,避免内存泄漏)
// C++11 无 make_unique,可自行实现
unique_ptr<int> ptr2 = make_unique<int>(20);

// 空 unique_ptr
unique_ptr<int> ptr3; // 初始化为 nullptr

管理动态数组:

c++ 复制代码
// 管理 int 数组,析构时自动调用 delete[]
unique_ptr<int[]> arr_ptr(new int[5]{1,2,3,4,5});
// 访问数组元素
cout << arr_ptr[0] << endl; // 输出 1

通过 * 解引用(单个对象)或 ->(成员访问),或 [](数组):

c++ 复制代码
struct Person {
    string name;
    int age;
    void show() { cout << name << ", " << age << endl; }
};

// 单个对象
unique_ptr<Person> p(new Person{"Alice", 20});
cout << (*p).name << endl; // 解引用访问成员
cout << p->age << endl;    // -> 访问成员
p->show();                 // 调用成员函数

// 数组
unique_ptr<int[]> arr(new int[3]{10,20,30});
cout << arr[1] << endl; // 输出 20

重置与释放

  • reset():释放当前管理的对象,可选传入新指针;
  • release():释放所有权(返回原始指针),但不释放内存(需手动管理);
  • nullptr 赋值:等价于 reset()
c++ 复制代码
unique_ptr<int> ptr(new int(50));

// 重置(释放原内存,指向新对象)
ptr.reset(new int(60));
cout << *ptr << endl; // 输出 60

// 重置为空(释放原内存)
ptr.reset();
cout << (ptr ? "非空" : "空") << endl; // 输出 空

// release():释放所有权,返回原始指针
unique_ptr<int> ptr2(new int(70));
int* raw_ptr = ptr2.release(); 
cout << *raw_ptr << endl; // 输出 70
delete raw_ptr; // 必须手动释放,否则内存泄漏
ptr2.reset(); // ptr2 已空,重置无影响

自定义删除器

默认删除器调用 delete/delete[],但可自定义删除器(如管理 FILE*、动态库句柄等)。

c++ 复制代码
// 自定义删除器:关闭文件
void close_file(FILE* fp) {
    if (fp) {
        fclose(fp);
        cout << "文件已关闭" << endl;
    }
}

// 创建 unique_ptr,指定删除器类型
unique_ptr<FILE, decltype(&close_file)> file_ptr(fopen("test.txt", "w"), close_file);
if (file_ptr) {
    fputs("hello unique_ptr", file_ptr.get());
}
// 析构时自动调用 close_file

Lambda 作为删除器(更简洁)

c++ 复制代码
// 管理动态数组,自定义删除器(示例,实际 delete[] 已足够)
auto deleter = [](int* arr) {
    cout << "释放数组内存" << endl;
    delete[] arr;
};
unique_ptr<int[], decltype(deleter)> arr_ptr(new int[3], deleter);

其他常用成员函数

get() 返回原始指针(仅访问,不释放所有权)
swap() 交换两个 unique_ptr 的所有权
operator bool() 判断是否指向有效对象(非空)

场景使用场景

  • 替代原始指针,避免内存泄漏
csharp 复制代码
void func() {
    int* ptr = new int(10);
    // 若中间抛出异常,delete 不会执行,内存泄漏
    delete ptr;
}

// unique_ptr:自动释放
void func_safe() {
    unique_ptr<int> ptr(new int(10));
    // 即使抛出异常,析构时自动释放
}
  • 作为函数返回值(移动语义自动生效)
c++ 复制代码
unique_ptr<Person> create_person(string name, int age) {
    // C++11 中,返回值会自动移动(无需显式 std::move)
    return unique_ptr<Person>(new Person{name, age});
}

// 调用
auto p = create_person("Bob", 25);
p->show(); // 输出 Bob, 25
  • 存储在容器中
arduino 复制代码
vector<unique_ptr<int>> vec;
// 方式1:emplace_back 直接构造
vec.emplace_back(new int(10));
// 方式2:move 插入
unique_ptr<int> ptr(new int(20));
vec.push_back(move(ptr));

// 遍历容器
for (const auto& p : vec) {
    cout << *p << " "; // 输出 10 20
}

其他事项

  • 禁止拷贝 :C++11 中 unique_ptr 的拷贝构造和拷贝赋值被 delete
  • 避免裸指针与智能指针混用 :不要用 get() 返回的指针创建新的 unique_ptr,否则会重复释放;
c++ 复制代码
unique_ptr<int> ptr(new int(10));
// 错误:两个 unique_ptr 管理同一内存,析构时双重释放
unique_ptr<int> ptr2(ptr.get()); 
  • make_unique 更安全 :C++14 引入 make_unique,避免 "new 表达式" 和 "智能指针构造" 之间的异常安全问题,C++11 可自行实现:
arduino 复制代码
template<typename T, typename... Args>
unique_ptr<T> make_unique(Args&&... args) {
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

-数组管理unique_ptr<T[]> 不支持 *->,仅支持 [] 访问;

  • 不要管理栈内存unique_ptr 仅用于管理堆内存(new/new[] 分配),若指向栈对象,析构时会调用 delete,导致未定义行为:
arduino 复制代码
int a = 10;
unique_ptr<int> ptr(&a); // 错误:栈内存不能被 delete

五. 弱引用智能指正 std::weak_ptr

std::weak_ptr 是 C++11 引入的智能指针,专门用于解决 std::shared_ptr 循环引用导致的内存泄漏问题 ,它本身不管理对象的生命周期,仅作为 shared_ptr 的 "弱引用"------ 既可以访问共享对象,又不会增加引用计数。

核心特性

  1. 不增加引用计数 :绑定到 shared_ptr 时,不会改变其引用计数,因此不会阻止对象被销毁
  2. 可检测对象是否存活 :通过 expired()lock() 检查所指向的对象是否已被销毁
  3. 不能直接解引用 :必须先通过 lock() 转换为 shared_ptr,才能安全访问对象(避免访问已销毁的对象)
  4. 线程安全 :与 shared_ptr 类似,读操作线程安全,写操作(如 lock())需加锁

lock() 是特殊的 "写操作"------ 它会先检查 use_count(读),若对象存活则创建 shared_ptr,此时会原子性增加 use_count(写)

基本用法 std::weak_ptr 不能直接指向裸指针,必须通过 shared_ptr 初始化或赋值:

c++ 复制代码
#include <memory>
#include <iostream>

int main() {
    // 1. 通过 shared_ptr 初始化
    std::shared_ptr<int> sp = std::make_shared<int>(10);
    std::weak_ptr<int> wp(sp); // 引用计数仍为 1

    // 2. 空 weak_ptr
    std::weak_ptr<int> wp_empty;

    // 3. 赋值操作
    std::weak_ptr<int> wp2;
    wp2 = sp; // 引用计数仍为 1

    return 0;
}

核心成员函数

函数 功能
lock() 转换为 shared_ptr:若对象存活,返回指向该对象的 shared_ptr;否则返回空 shared_ptr
expired() 判断对象是否已销毁(引用计数为 0),返回 bool
reset() 清空 weak_ptr,不再引用任何对象
use_count() 返回当前 shared_ptr 的引用计数(慎用,仅用于调试)
swap() 交换两个 weak_ptr 的引用对象

安全访问对象(lock () 核心用法)

weak_ptr 不能直接解引用,必须通过 lock() 获取 shared_ptr 后访问:

c++ 复制代码
// 主函数:程序执行的起点(必须有且仅有一个)
#include <iostream>

#include <iostream>
#include <memory>


int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(100);
    std::weak_ptr<int> wp(sp);

    // 安全访问:先 lock() 转换为 shared_ptr
    
    if (std::shared_ptr<int> temp = wp.lock()) {
        std::cout << "对象值:" << *temp << std::endl; // 输出 100
        std::cout << "引用计数:" << temp.use_count() << std::endl; // 2
    } else {
        std::cout << "对象已销毁" << std::endl;
    }

    std::cout << "sp 引用计数:" << sp.use_count() << std::endl; // 2

    // 销毁 shared_ptr,对象被释放
    sp.reset();

    // 再次尝试访问
    auto temp2 = wp.lock();
    if (temp2) {
        std::cout << "对象值:" << *temp2 << std::endl; // 输出 100 std::cout << *temp2 << std::endl;
        std::cout << "引用计数:" << temp2.use_count() << std::endl; // 2
    } else {
        std::cout << "对象已销毁" << std::endl; // 输出此行
    }

    return 0;
}

expired () 检查对象存活

c++ 复制代码
int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(200);
    std::weak_ptr<int> wp(sp);

    if (!wp.expired()) {
        std::cout << "对象存活" << std::endl; // 输出
    }

    sp.reset();
    if (wp.expired()) {
        std::cout << "对象已销毁" << std::endl; // 输出
    }

    return 0;
}

解决循环引用问题(核心场景)

shared_ptr 循环引用会导致引用计数无法归 0,对象无法析构,最终内存泄漏。weak_ptr 因不增加引用计数,可打破循环。

c++ 复制代码
struct B; // 前向声明

struct A {
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A 析构" << std::endl; }
};

struct B {
    std::shared_ptr<A> a_ptr; // 循环引用
    ~B() { std::cout << "B 析构" << std::endl; }
};

int main() {
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
        a->b_ptr = b;
        b->a_ptr = a;
        // 离开作用域时,a 和 b 的引用计数均为 2,无法析构
    }
    std::cout << "作用域结束" << std::endl; // 输出,但 A/B 未析构
    return 0;
}

修改后

c 复制代码
struct B;
struct A {
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A 析构" << std::endl; }
};

struct B {
    std::weak_ptr<A> a_ptr; // 改为 weak_ptr,不增加引用计数
    ~B() { std::cout << "B 析构" << std::endl; }
};

int main() {
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();
        a->b_ptr = b;
        b->a_ptr = a;
        // a 的引用计数:1(b->a_ptr 是 weak_ptr,不计数)
        // b 的引用计数:1(a->b_ptr 是 shared_ptr,计数+1)
    }
    std::cout << "作用域结束" << std::endl;
    return 0;
}
相关推荐
deng-c-f6 小时前
C/C++内置库函数(2):智能指针
java·c语言·c++
yuhaiqun19896 小时前
新手练 C++ HTTP 服务实操:从 “拆请求头” 到 “发 HTML 响应”
c语言·c++·程序人生·http·html·学习方法·改行学it
小年糕是糕手6 小时前
【C/C++刷题集】类和对象算法题(一)
数据结构·c++·程序人生·考研·算法·leetcode·改行学it
博语小屋6 小时前
Socket UDP 网络编程V2 版本- 简单聊天室
linux·网络·c++·网络协议·udp
一叶之秋14127 小时前
从零掌握 List:高效操作与性能优化实战
数据结构·c++·list
fie88897 小时前
C++实现D星 Lite算法
c++·算法·哈希算法
筏.k7 小时前
C++ 设计模式系列:单例模式
c++·单例模式·设计模式
liu****7 小时前
二.protobuf的使用
c++·c·protobuf·企业级组件
Arciab7 小时前
C++ 学习_基础知识
c++·学习