当前位置: 首页 > news >正文

C++——智能指针详解及实现

1. 概述

在 C++ 的早期版本中,程序员主要使用原生指针来管理动态内存。例如,使用 new 关键字分配内存,使用 delete 释放内存。然而,这种方式很容易出错。例如,当程序员忘记释放内存时,就会导致内存泄漏;当释放内存后仍然使用指向该内存的指针时,就会产生悬空指针问题。智能指针的出现就是为了解决这些问题,它通过自动管理内存,让程序员可以更安全、更方便地使用动态内存。

所谓的智能指针本质就是一个类模板,它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。

下面是智能指针的基本框架,所有的智能指针类模板中都需要包含一个指针对象,构造函数和析构函数。
在这里插入图片描述

C++ 标准库提供了几种智能指针类型,主要包括 std::unique_ptr、std::shared_ptr和std::weak_ptr。它们各自有不同的用途和特性。

2. std::unique_ptr

std::unique_ptr 是 C++11 引入的智能指针类型,属于标准库 memory 中的内容。

特点:

  • 独占所有权:std::unique_ptr 表示对动态分配对象的独占所有权。也就是说,一个 std::unique_ptr 对象在任何时刻只能拥有一个所有者。
  • 它不允许复制(拷贝构造函数和拷贝赋值运算符被删除),但可通过 std::move 转移所有权(移动构造函数和移动赋值运算符被定义)。
std::unique_ptr<int> p1 = std::make_unique<int>(10);  // 创建并拥有一个 int
std::unique_ptr<int> p2 = p1;  // ❌ 错误:不能拷贝
std::unique_ptr<int> p3 = std::move(p1);  // ✅ 正确:通过 move 转移所有权
  • 当 unique_ptr 离开作用域,它所拥有的对象会被自动释放,不需要手动 delete。
{
    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    // 离开这个作用域后,内存会自动被释放
}
  • 适用于一个资源只被一个对象拥有的情况。
  • 性能接近原始指针。

unique_ptr 的拷贝构造函数和赋值重载函数
在这里插入图片描述
适用场景: 单一所有权的资源管理。
常见方法:

p.get();        // 返回原始指针
p.release();    // 释放所有权,不删除对象
p.reset();      // 删除当前对象并可指向新对象
p.swap(p2);     // 交换两个 unique_ptr

示例:

#include <iostream>
#include <memory>

int main()
{
    // 创建一个unique_ptr,指向一个动态分配的int对象
    std::unique_ptr<int> ptr(new int(10));
    std::cout << *ptr << std::endl; // 输出10

    // unique_ptr不允许复制
    // std::unique_ptr<int> ptr2 = ptr; // 错误

    // 但可以移动
    std::unique_ptr<int> ptr2 = std::move(ptr);
    std::cout << *ptr2 << std::endl; // 输出10
    // 此时ptr不再拥有所有权,ptr.get()返回nullptr

    return 0;
}

3. std::shared_ptr

特点:

  • 共享所有权:std::shared_ptr 允许多个 shared_ptr 对象共享同一个动态分配的对象。它通过内部的引用计数机制来管理对象的生命周期。当最后一个 shared_ptr 被销毁时,它所管理的内存才会被释放。
  • 线程安全:引用计数操作是线程安全的,这使得 shared_ptr 可以在多线程环境中安全地使用。
  • 会造成循环引用,需要搭配 weak_ptr 使用

适用场景: 需要共享所有权的复杂场景。

#include <memory>
#include <iostream>

struct Test {
    Test() { std::cout << "Test created\n"; }
    ~Test() { std::cout << "Test destroyed\n"; }
};

int main() {
    std::shared_ptr<Test> p1 = std::make_shared<Test>();
    {
        std::shared_ptr<Test> p2 = p1; // 引用计数 +1
        std::cout << p1.use_count() << "\n"; // 输出 2
    } // p2 析构,计数 -1
    std::cout << p1.use_count() << "\n"; // 输出 1
}

4. std::weak_ptr

特点:

  • 不拥有资源,不增加引用计数
  • 必须调用 .lock() 转换为 shared_ptr 后使用
  • 用于观察 shared_ptr 管理的对象,解决循环引用问题
  • 和shared_ptr一样,weak_ptr的操作也是线程安全的。

由于 shared_ptr 是通过引用计数来管理对象的生命周期,如果不小心形成了循环引用,就会导致内存泄漏。循环引用(Circular Reference)是指两个或多个智能指针相互引用,形成一个环形结构,导致它们永远无法释放资源,即使已经超出了作用域。例如:

#include <memory>
#include <iostream>

struct B; // 前向声明

struct A {
    std::shared_ptr<B> bptr;
    ~A() { std::cout << "A destroyed\n"; }
};

struct B {
    std::shared_ptr<A> aptr;
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->bptr = b;
    b->aptr = a;

    // main函数结束时,看似 a 和 b 超出作用域会销毁
}

❌ 问题:

  • a 和 b 出了作用域后,它们的引用计数都为 1

  • 所以资源不会释放,析构函数不会被调用,造成内存泄漏

为了解决循环引用问题,可以使用 std::weak_ptr。

struct B;

struct A {
    std::shared_ptr<B> bptr;
    ~A() { std::cout << "A destroyed\n"; }
};

struct B {
    std::weak_ptr<A> aptr; // 改为 weak_ptr
    ~B() { std::cout << "B destroyed\n"; }
};

👍 这样做的效果:

  • shared_ptr 管理资源(增加引用计数)
  • weak_ptr 观察资源(不增加引用计数)
  • 所以当 a 和 b 出了作用域后:
    • a 的 bptr 是 shared_ptr,计数归 0,释放
    • b 的 aptr 是 weak_ptr,不阻止 a 被销毁
    • 成功释放,析构函数被调用

相关文章:

  • 青少年编程与数学 02-015 大学数学知识点 07课题、数值分析
  • 全面支持MCP协议,开启便捷连接之旅,MaxKB知识库问答系统v1.10.3 LTS版本发布
  • 12AI搭建preparationのCIFAR-10数据集分类(论训练的必要性)
  • Redis设计与实现-集群
  • 数字政府政务服务领域智能化应用解决方案
  • 硬件产品经理:智慧标签产品需求说明
  • AI智算-K8s如何利用GPFS分布式并行文件存储加速训练or推理
  • Windows环境下PyCharm 配置miniforge
  • 前端开发vue项目(node-modules 可视化神器 Node Modules Inspector)
  • 【题解-Acwing】798. 差分矩阵
  • java版鸿鹄招采系统源码招投标系统源码 供应商招投标平台源码-数字化浪潮下的招投标管理系统:技术赋能采购全流程
  • 【我赢职场】安克创新自适应能力cata测评全解析
  • Docker学习--网络相关命令
  • Kubernetes 集群搭建(三):使用dashboard用户界面(需要访问外网获取yaml)
  • 2010年-全国大学生数学建模竞赛(CUMCM)试题速浏、分类及浅析
  • 【算法实践】跳跃游戏——计算到达终点的最小跳数
  • 目标跟踪Deepsort算法学习2025.4.7
  • 前端基础总结
  • [数据结构]图krusakl算法实现
  • [蓝桥杯] 求和(C语言)
  • 新闻1+1丨强对流天气频繁组团来袭,该如何更好应对?
  • 国际奥委会举办研讨会,聚焦如何杜绝操纵比赛
  • 创同期历史新高!1至4月全国铁路发送旅客14.6亿人次
  • 5吨煤炭“瞬间蒸发”?掺水炭致企业损失千万,腐败窝案曝光
  • “典孝急乐批麻蚌赢”:互联网“八字真言”与当代赛博赢学
  • 大英博物馆展歌川广重:他是梵高最钟爱的浮世绘名家