C++新特性—— 智能指针(shared_ptr/unique_ptr/weak_ptr)

C++11引入了智能指针,用于自动管理动态内存的释放,避免内存泄漏。常用的智能指针有 unique_ptrshared_ptrweak_ptr,每种类型的智能指针有不同的特点和使用场景。

头文件为<memory>

1. unique_ptr 智能指针

unique_ptr 是独占指针,即某个动态分配的内存只能被一个 unique_ptr 所拥有,不支持拷贝和赋值操作。它会在超出作用域时自动释放资源。

示例代码:

cpp 复制代码
#include <iostream>
#include <memory>

using namespace std;

int main()
{
    unique_ptr<int> p1(new int(24));
    cout << "*p1 = " << *p1 << endl << endl;

    unique_ptr<int> p2 = move(p1); // 使用 move 转移所有权
    cout << "*p2 = " << *p2 << endl << endl;

    p2.reset(); // 显式释放内存
    p1.reset(); 

    unique_ptr<int> p3(new int(250));
    p3.reset(new int(666)); // 绑定动态对象
    cout << "*p3 = " << *p3 << endl << endl;

    p3 = nullptr; // 显式销毁指向的对象,同时智能指针变为空,等价于 p3.reset()

    unique_ptr<int> p4(new int(999));
    int* p = p4.release(); // 释放控制权,内存不释放
    cout << "*p = " << *p << endl << endl;

    // p4 已经没有管理任何对象,访问会导致错误
    // cout << "*p4 = " << *p4 << endl;

    delete p; // 手动释放内存
    return 0;
}

输出结果:

bash 复制代码
*p1 = 24

*p2 = 24

*p3 = 666

*p = 999

关键点:

  • unique_ptr 不能拷贝,只能通过 std::move 转移所有权。

  • reset() 用来释放智能指针管理的内存,release() 用来转移控制权但不释放内存。


2. shared_ptr 智能指针

shared_ptr 是共享指针,多个 shared_ptr 可以指向同一块动态内存,并通过引用计数的方式管理内存的生命周期。引用计数为零时,资源会被释放。

shared_ptr 的核心是一个叫 控制块(control block) 的结构体,它记录了以下两个关键信息:

  1. 指向资源的原始指针(void *ptr

  2. 引用计数(ref_count)

可以把它想象成这样(伪结构体):

cpp 复制代码
struct ControlBlock {
    int ref_count; // 当前有多少 shared_ptr 指向这块内存
    void* ptr;     // 指向实际数据的指针
};

示例代码:

cpp 复制代码
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> p1(new int(456));
    shared_ptr<int> p2 = p1; // 共享所有权
    cout << "p2 引用计数: " << p2.use_count() << endl << endl;

    cout << "*p1 = " << *p1 << endl;
    cout << "*p2 = " << *p2 << endl;
    cout << "p2 引用计数: " << p2.use_count() << endl << endl;

    p1.reset(); // p1 不再管理内存
    cout << "p2 引用计数: " << p2.use_count() << endl << endl;

    return 0;
}

代码解释:

  • 每个 shared_ptr 都会共享同一个"引用计数控制块"。

  • 当新的 shared_ptr 被复制时,引用计数 +1;

  • 当某个 shared_ptr 调用 reset() 或超出作用域时,引用计数 -1;

  • 当引用计数减为 0 时,内存会被自动释放。

输出结果:

bash 复制代码
p2 引用计数: 2

*p1 = 456
*p2 = 456
p2 引用计数: 2

p2 引用计数: 1

关键点:

  • shared_ptr 会自动管理内存,通过引用计数来确保内存安全。

  • use_count() 用来获取当前对象的引用计数。

  • reset() 用来重置 shared_ptr,使其释放控制的内存。


3. make_shared 函数

make_shared 是创建 shared_ptr 的推荐方法,它通过单次内存分配实现了更高效的内存管理。它不仅分配内存给对象,还分配了用于引用计数的内存。

示例:

cpp 复制代码
auto p = make_shared<int>(10);

shared_ptr(new int(10)) 的区别在于:

  • make_shared 会一次性分配内存,效率更高。

例如:

(1)shared_ptr

cpp 复制代码
shared_ptr<int> p1(new int(10));

这里是创建一个 shared_ptr,同时通过 new 操作符分配了一个 int 类型的动态内存。这样,shared_ptr 管理的是一个指向动态内存的原始指针。首先,new int(10) 分配了内存给 int 类型的对象,然后 shared_ptr 会为引用计数分配内存。因此总共进行了 两次内存分配 :一次是为对象本身分配内存(即 int 类型的内存),一次是为 shared_ptr 维护的引用计数分配内存

(2)make_shared

cpp 复制代码
auto p2 = make_shared<int>(10);

使用 make_shared,它会一次性分配内存,同时为对象本身和引用计数分配内存。即,make_shared 只需要 一次内存分配,同时包含了对象和引用计数的信息。

  • 它避免了内存泄漏的风险,特别是在异常情况下。

4. weak_ptr 智能指针

weak_ptr 是辅助 shared_ptr 使用的智能指针,它不增加或减少引用计数,因此它并不拥有资源的所有权。weak_ptr 不能直接访问资源,但可以通过 lock() 方法将其转化为 shared_ptr 进行访问。

示例代码:

cpp 复制代码
#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> p1(new int(300));
    shared_ptr<int> p2 = p1;
    weak_ptr<int> wp = p1; // weak_ptr 不增加引用计数

    cout << "wp 引用计数: " << wp.use_count() << endl << endl;
    cout << "*p1 = " << *p1 << endl;
    cout << "*p2 = " << *p2 << endl;
    cout << endl;

    return 0;
}

输出结果:

bash 复制代码
wp 引用计数: 2

*p1 = 300
*p2 = 300

关键点:

  • weak_ptr 不拥有资源的所有权,不能直接使用资源。

  • 使用 lock() 方法可以获得一个 shared_ptr,从而访问资源。


5.总结

  • unique_ptr:独占资源,无法拷贝或赋值,适用于资源的独占管理。

  • shared_ptr:共享资源,通过引用计数管理内存,适用于多个对象需要共享同一资源的场景。

  • weak_ptr:辅助 shared_ptr,不增加引用计数,避免循环引用,适用于观察者模式等场景。

相关推荐
liu****3 小时前
笔试强训(十三)
开发语言·c++·算法·1024程序员节
老王熬夜敲代码4 小时前
ES安装和简单讲解
c++·微服务
m0_748240254 小时前
基于Reactor模式的高性能C++仿Muduo库:Server服务器模块实现
服务器·c++·php
hope_wisdom5 小时前
C/C++数据结构之用链表实现栈
c语言·数据结构·c++·链表·
王老师青少年编程5 小时前
AtCoder真题及详细题解 ABC427C: Bipartize
c++·题解·1024程序员节·atcoder·csp·abc·信奥赛
ceclar1235 小时前
C++容器forward_list
开发语言·c++·list
ceclar1235 小时前
C++容器list
java·c++·list
大肘子咒你5 小时前
数字狂潮来袭
数据结构·c++·1024程序员节
hansang_IR5 小时前
【算法速成课 3】康托展开(Cantor Expansion)/ 题解 P3014 [USACO11FEB] Cow Line S
c++·算法·状态压缩·康托展开·排列映射