讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

Seko
Seko

AI短剧一键生成成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

AniShort
AniShort

AI短剧协同制作平台

什么是C++中的智能指针 详解unique_ptr shared_ptr weak_ptr用法

老辰酱_5158

老辰酱_5158

发布时间:2025-07-25 17:20:02

|

700人浏览过

|

来源于php中文网

原创

c++中的智能指针通过raii机制自动管理内存,避免内存泄漏等问题。1. unique_ptr强调独占所有权,不可复制,支持移动语义,适用于对象仅由一处管理的场景;2. shared_ptr通过引用计数实现共享所有权,多个指针可同时管理同一资源,但需注意循环引用和性能开销;3. weak_ptr作为弱引用不增加引用计数,用于打破shared_ptr的循环引用,适用于观察者或缓存机制。它们共同构成现代c++内存管理的核心工具。

什么是C++中的智能指针 详解unique_ptr shared_ptr weak_ptr用法

C++中的智能指针,简单来说,就是一种行为类似于指针,但能自动管理内存的类模板。它们的核心价值在于通过RAII(资源获取即初始化)原则,帮助我们避免手动管理内存时常遇到的内存泄漏、野指针和二次释放等问题。这玩意儿简直是现代C++编程的基石,少了它,很多代码写起来都提心吊胆的。

什么是C++中的智能指针 详解unique_ptr shared_ptr weak_ptr用法

智能指针的出现,彻底改变了我们处理动态内存的方式。它不是什么魔法,本质上就是个封装了原始指针的类,在对象生命周期结束时,自动调用析构函数来释放其管理的内存。这听起来可能有点抽象,但想想看,每次new一个对象后,你是不是总得记得delete它?如果中间抛了异常,或者逻辑分支复杂了,很容易就忘了delete,内存泄漏就发生了。智能指针就是来解决这个痛点的,它确保了资源(这里主要是内存)在不再需要时,会被妥善地释放掉。这就像你租了个房子,智能指针就是那个自动帮你退房、打扫干净的管家,你只管住,不用操心后续。

unique_ptr 到底“独”在哪里?它有哪些典型的使用场景?

unique_ptr,顾名思义,它强调的是“独占”所有权。这意味着一个unique_ptr对象拥有它所指向资源(比如一块内存)的唯一所有权。这东西是不能被复制的,你不能有两个unique_ptr同时指向同一块内存,因为那样就乱套了,谁来负责释放呢?它只支持移动语义,也就是说,所有权可以从一个unique_ptr转移给另一个,但转移之后,原来的那个unique_ptr就不再拥有资源了,它会变成空的。

立即学习“C++免费学习笔记(深入)”;

什么是C++中的智能指针 详解unique_ptr shared_ptr weak_ptr用法

这种独占性让unique_ptr在很多场景下都显得非常有用。比如说,当你需要一个对象,并且明确知道这个对象只会被一个地方拥有和管理时,unique_ptr就是最佳选择。比如一个函数创建了一个新对象,然后把这个对象的管理权“移交”给调用者。

#include <iostream>
#include <memory>
#include <vector>

class MyClass {
public:
    MyClass() { std::cout << "MyClass created!\n"; }
    ~MyClass() { std::cout << "MyClass destroyed!\n"; }
    void doSomething() { std::cout << "Doing something...\n"; }
};

// 函数返回一个独占所有权的MyClass对象
std::unique_ptr<MyClass> createMyClass() {
    return std::make_unique<MyClass>(); // 推荐使用make_unique
}

int main() {
    // 独占所有权,不能被复制
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
    ptr1->doSomething();

    // 所有权转移:ptr1的资源转移给ptr2,ptr1变为空
    std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
    if (ptr1 == nullptr) {
        std::cout << "ptr1 is now null after move.\n";
    }
    ptr2->doSomething();

    // 作为函数返回值,实现所有权转移
    std::unique_ptr<MyClass> ptr3 = createMyClass();
    ptr3->doSomething();

    // 将unique_ptr存储在容器中
    std::vector<std::unique_ptr<MyClass>> objects;
    objects.push_back(std::make_unique<MyClass>());
    objects.push_back(std::make_unique<MyClass>());
    // 当objects超出作用域时,其中的MyClass对象都会被自动销毁

    // ptr2和ptr3在main函数结束时会自动销毁其管理的MyClass对象
    return 0;
}

你看,unique_ptr的开销几乎为零,因为它不需要维护引用计数什么的,和原始指针一样高效。它在编译期就能保证内存安全,一旦所有权转移,原指针就失效了,这在逻辑上非常清晰。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
什么是C++中的智能指针 详解unique_ptr shared_ptr weak_ptr用法

shared_ptr 是如何实现共享所有权的?它又带来哪些潜在的问题?

shared_ptr则完全是另一种哲学,它实现了“共享”所有权。这意味着多个shared_ptr对象可以同时指向并管理同一块内存。它通过一个内部的引用计数(reference count)机制来工作:每当一个新的shared_ptr指向同一个资源时,引用计数就加一;每当一个shared_ptr离开作用域或者不再指向该资源时,引用计数就减一。只有当引用计数归零时,shared_ptr才会释放它所管理的内存。这就像一个公共图书馆的书,大家都可以借阅,只要还有人在借,书就不会被扔掉。

#include <iostream>
#include <memory>

class Resource {
public:
    Resource() { std::cout << "Resource created!\n"; }
    ~Resource() { std::cout << "Resource destroyed!\n"; }
    void use() { std::cout << "Using resource...\n"; }
};

int main() {
    std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>(); // 引用计数为1
    std::cout << "ptr1 use_count: " << ptr1.use_count() << "\n";

    std::shared_ptr<Resource> ptr2 = ptr1; // 引用计数为2
    std::cout << "ptr1 use_count: " << ptr1.use_count() << "\n";
    std::cout << "ptr2 use_count: " << ptr2.use_count() << "\n";

    {
        std::shared_ptr<Resource> ptr3 = ptr1; // 引用计数为3
        std::cout << "ptr1 use_count (inside scope): " << ptr1.use_count() << "\n";
    } // ptr3离开作用域,引用计数减1,变为2

    std::cout << "ptr1 use_count (after ptr3 scope): " << ptr1.use_count() << "\n";

    ptr1.reset(); // ptr1不再指向资源,引用计数减1,变为1
    std::cout << "ptr2 use_count (after ptr1 reset): " << ptr2.use_count() << "\n";

    // 当ptr2离开作用域时,引用计数变为0,Resource被销毁
    return 0;
}

shared_ptr的便利性毋庸置疑,尤其是在对象生命周期复杂,有多个地方需要访问同一个对象时。但它也带来了几个潜在的问题,其中最臭名昭著的就是“循环引用”(circular reference)。如果两个或多个shared_ptr相互持有对方的shared_ptr,就会形成一个闭环。在这种情况下,即使外部已经没有其他shared_ptr指向它们了,它们的引用计数也永远不会降到零,导致它们所管理的内存永远不会被释放,这就造成了内存泄漏。

此外,shared_ptr相比unique_ptr会带来一些性能开销。因为它需要维护一个引用计数,这个计数器通常是原子操作,以保证多线程环境下的正确性。原子操作比普通操作要慢,虽然在大多数情况下这点开销可以忽略不计,但在性能敏感的场景下,也需要考虑。

weak_ptr 的存在意义是什么?它如何解决 shared_ptr 的循环引用问题?

weak_ptr就是为了解决shared_ptr的循环引用问题而诞生的。它是一种“弱引用”或者说“非拥有”的智能指针。weak_ptr可以指向一个由shared_ptr管理的对象,但它不会增加对象的引用计数。这就像一个观察者,它知道有这本书在那里,但它并没有借阅这本书,所以它不会影响书的借阅计数。

因为weak_ptr不拥有资源,所以它不能直接访问资源。要访问它指向的对象,你必须先调用它的lock()方法,lock()会尝试返回一个shared_ptr。如果它指向的对象还存在(即其shared_ptr的引用计数不为零),lock()就会成功返回一个shared_ptr;如果对象已经被销毁了(因为所有shared_ptr都已失效),lock()就会返回一个空的shared_ptr。通过这种方式,weak_ptr提供了一种安全地访问共享对象的方式,同时又不会阻止对象的销毁。

#include <iostream>
#include <memory>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    A() { std::cout << "A created!\n"; }
    ~A() { std::cout << "A destroyed!\n"; }
};

class B {
public:
    // 这里使用weak_ptr来打破循环引用
    std::weak_ptr<A> a_ptr; 
    B() { std::cout << "B created!\n"; }
    ~B() { std::cout << "B destroyed!\n"; }
};

int main() {
    std::shared_ptr<A> sp_a = std::make_shared<A>();
    std::shared_ptr<B> sp_b = std::make_shared<B>();

    std::cout << "A use_count: " << sp_a.use_count() << "\n"; // 1
    std::cout << "B use_count: " << sp_b.use_count() << "\n"; // 1

    sp_a->b_ptr = sp_b; // A持有B的shared_ptr,B的引用计数变为2
    // sp_b->a_ptr = sp_a; // 如果这里也用shared_ptr,就形成循环引用了

    // 使用weak_ptr来持有A,不会增加A的引用计数
    sp_b->a_ptr = sp_a; 

    std::cout << "After linking:\n";
    std::cout << "A use_count: " << sp_a.use_count() << "\n"; // 1 (因为B持有的a_ptr是weak_ptr)
    std::cout << "B use_count: " << sp_b.use_count() << "\n"; // 2 (因为A持有的b_ptr是shared_ptr)

    // 尝试通过weak_ptr访问A
    if (auto sharedA = sp_b->a_ptr.lock()) {
        std::cout << "A is still alive, use_count: " << sharedA.use_count() << "\n";
    } else {
        std::cout << "A is gone.\n";
    }

    // 当sp_a和sp_b离开作用域时,它们各自的引用计数会变为0,对象会被正确销毁
    // 如果sp_b->a_ptr也是shared_ptr,那么sp_a和sp_b的引用计数永远是1,不会销毁
    return 0;
}

在这个例子里,A拥有一个B的shared_ptr,而B则拥有一个A的weak_ptr。当main函数结束,sp_a和sp_b离开作用域时,sp_a的引用计数首先降为0,A对象被销毁。接着,sp_b的引用计数也降为0,B对象被销毁。整个过程没有内存泄漏。如果没有weak_ptr,A和B就会互相牵制,导致内存泄露。

weak_ptr主要用于那些“观察者”模式或者缓存机制中,你希望能够访问一个对象,但又不希望你的存在影响这个对象的生命周期。它提供了一种优雅的方式来处理复杂的对象依赖关系,避免了shared_ptr固有的循环引用陷阱。理解并恰当使用这三种智能指针,是写出健壮、高效C++代码的关键一步。它们虽然不能解决所有内存管理问题(比如循环引用就需要weak_ptr来辅助),但绝对是C++现代编程中不可或缺的利器。

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

SoundView
SoundView Hot

SoundView是一款面向跨境电商的视频翻译、配音和本地化 AI 创作平台。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

相关专题

更多
counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3908

2023.11.20

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

399

2026.04.23

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4858

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1369

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

463

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

599

2026.01.21

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

449

2026.02.06

C++多线程并发控制与线程安全设计实践
C++多线程并发控制与线程安全设计实践

本专题围绕 C++ 在高性能系统开发中的并发控制技术展开,系统讲解多线程编程模型与线程安全设计方法。内容包括互斥锁、读写锁、条件变量、原子操作以及线程池实现机制,同时结合实际案例分析并发竞争、死锁避免与性能优化策略。通过实践讲解,帮助开发者掌握构建稳定高效并发系统的关键技术。

273

2026.03.16

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go-Zero goctl安装与使用手册
Go-Zero goctl安装与使用手册

共0课时 | 0人学习

DBeaver生态工具dbvr官方手册
DBeaver生态工具dbvr官方手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn