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

C++11引入了智能指针,用于自动管理动态内存的释放,避免内存泄漏。常用的智能指针有 unique_ptr、shared_ptr 和 weak_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,不增加引用计数,避免循环引用,适用于观察者模式等场景。

相关推荐
小小龙学IT4 小时前
Boost.Interprocess 开源 C++ 进程间通信库深度解析
c++·开源
倒头就睡的小比特6 天前
算法竞赛C++常用的STL
c++·算法
weilx12346 天前
C++笔记-文件IO-<fcntl.h>
c++
Smileyqp沛沛6 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车6 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光6 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·6 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁6 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven6 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
程序猿编码6 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎