0

0

C++如何使用智能指针管理动态资源

P粉602998670

P粉602998670

发布时间:2025-09-07 09:40:01

|

422人浏览过

|

来源于php中文网

原创

C++智能指针通过RAII管理动态内存,避免泄漏与野指针。std::unique_ptr独占所有权,高效无开销,适用于单一所有者场景;std::shared_ptr共享所有权,用引用计数控制生命周期,适合多所有者共享资源;std::weak_ptr作为弱引用不增计数,解决shared_ptr循环引用问题,常用于观察者或缓存。三者结合可安全高效管理堆资源。

c++如何使用智能指针管理动态资源

C++中,智能指针是管理动态资源(主要是堆内存)的强大工具,它们通过RAII(资源获取即初始化)原则,自动化了内存的生命周期管理,从而有效避免了内存泄漏、野指针和重复释放等常见问题。在我看来,它们是现代C++编程中不可或缺的基石,极大提升了代码的健壮性和可维护性。

解决方案

要有效管理C++中的动态资源,核心在于使用标准库提供的三种智能指针:

std::unique_ptr
std::shared_ptr
std::weak_ptr
。它们各自拥有不同的所有权语义,适用于不同的场景。简单来说,
unique_ptr
强调独占,资源只能有一个所有者;
shared_ptr
允许多个所有者共享资源,通过引用计数来管理生命周期;而
weak_ptr
则是一种不拥有资源的观察者,主要用于解决
shared_ptr
的循环引用问题。理解并合理运用这三者,几乎可以解决所有基于堆内存的资源管理难题。

std::unique_ptr
:独占所有权的效率之选?

std::unique_ptr
,顾名思义,它代表的是对资源的独占所有权。这意味着一个
unique_ptr
实例是它所管理资源的唯一所有者。一旦
unique_ptr
超出作用域,它所指向的资源就会被自动释放。我个人觉得,这种设计简洁而高效,因为它不需要维护引用计数,几乎没有运行时开销,性能上与裸指针无异。

它的一个显著特点是不可复制,只能通过

std::move
进行所有权的转移。这非常符合“独占”的语义。比如,你有一个函数需要创建一个对象并返回给调用者,但这个对象的所有权应该转移给调用者,这时
unique_ptr
就派上用场了。

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

#include 
#include 
#include 

class MyResource {
public:
    MyResource(const std::string& name) : name_(name) {
        std::cout << "MyResource " << name_ << " created." << std::endl;
    }
    ~MyResource() {
        std::cout << "MyResource " << name_ << " destroyed." << std::endl;
    }
    void doSomething() {
        std::cout << "MyResource " << name_ << " is doing something." << std::endl;
    }
private:
    std::string name_;
};

// 函数返回一个unique_ptr,所有权转移给调用方
std::unique_ptr createResource(const std::string& name) {
    return std::make_unique(name); // 推荐使用make_unique
}

int main() {
    std::cout << "--- unique_ptr example ---" << std::endl;
    std::unique_ptr res1 = createResource("A");
    res1->doSomething();

    // 尝试复制会编译错误:std::unique_ptr res2 = res1;

    // 所有权转移
    std::unique_ptr res2 = std::move(res1);
    if (res1 == nullptr) { // res1现在为空
        std::cout << "res1 is now empty after move." << std::endl;
    }
    res2->doSomething();

    // 当res2超出作用域时,MyResource "A" 将被销毁
    std::cout << "--- unique_ptr example end ---" << std::endl;
    return 0;
}

在我看来,

std::make_unique
是创建
unique_ptr
的最佳实践,它能保证异常安全,并且通常比直接
new
然后包装更高效。
unique_ptr
特别适合那些明确知道资源只有一个所有者的场景,比如文件句柄、网络连接或者一些工厂模式的返回值。

std::shared_ptr
:共享资源的协作模式?

聊完了独占的

unique_ptr
,自然就得说说那更复杂、但同样不可或缺的
std::shared_ptr
了。当多个对象需要共享同一份资源时,
shared_ptr
就是你的不二选择。它通过内部维护一个引用计数器来追踪有多少个
shared_ptr
实例正在指向同一个资源。只有当最后一个
shared_ptr
实例被销毁或重置时,它所管理的资源才会被释放。

这种共享所有权模型非常灵活,尤其在实现一些复杂的数据结构或设计模式时显得尤为重要,比如图结构中的节点、观察者模式中的主题等。我常遇到需要多个模块共同持有某个配置对象或数据缓存的情况,这时候

shared_ptr
就显得游刃有余。

AOXO_CMS建站系统企业通用版1.0
AOXO_CMS建站系统企业通用版1.0

一个功能强大、性能卓越的企业建站系统。使用静态网页技术大大减轻了服务器负担、加快网页的显示速度、提高搜索引擎推广效果。本系统的特点自定义模块多样化、速度快、占用服务器资源小、扩展性强,能方便快捷地建立您的企业展示平台。简便高效的管理操作从用户使用的角度考虑,对功能的操作方便性进行了设计改造。使用户管理的工作量减小。网站互动数据可导出Word文档,邮件同步发送功能可将互动信息推送到指定邮箱,加快企业

下载
#include 
#include 
#include 
#include 

class SharedResource {
public:
    SharedResource(const std::string& id) : id_(id) {
        std::cout << "SharedResource " << id_ << " created." << std::endl;
    }
    ~SharedResource() {
        std::cout << "SharedResource " << id_ << " destroyed." << std::endl;
    }
    void report() {
        std::cout << "SharedResource " << id_ << " is active." << std::endl;
    }
private:
    std::string id_;
};

int main() {
    std::cout << "--- shared_ptr example ---" << std::endl;
    std::shared_ptr s_res1 = std::make_shared("X");
    s_res1->report();
    std::cout << "Reference count for X: " << s_res1.use_count() << std::endl;

    // 复制shared_ptr,引用计数增加
    std::shared_ptr s_res2 = s_res1;
    std::cout << "Reference count for X: " << s_res1.use_count() << std::endl;

    // 放入容器中,引用计数再次增加
    std::vector> resources;
    resources.push_back(s_res1);
    std::cout << "Reference count for X: " << s_res1.use_count() << std::endl;

    // s_res2超出作用域,引用计数减少
    {
        std::shared_ptr s_res3 = s_res1;
        std::cout << "Reference count for X: " << s_res1.use_count() << std::endl;
    } // s_res3销毁
    std::cout << "Reference count for X: " << s_res1.use_count() << std::endl;

    // 当所有shared_ptr实例都销毁后,SharedResource "X" 才会被销毁
    std::cout << "--- shared_ptr example end ---" << std::endl;
    return 0;
}

unique_ptr
类似,
std::make_shared
是创建
shared_ptr
的首选方式。它不仅能提供异常安全,还能优化内存分配,将对象本身和其管理块(包含引用计数等信息)一次性分配,减少了内存碎片。不过,
shared_ptr
并非没有缺点,它的主要开销在于需要维护引用计数,这会带来一些性能损耗。更重要的是,它可能会引入一个非常棘手的问题:循环引用。

std::weak_ptr
:打破循环引用的观察者?

这玩意儿,

std::weak_ptr
,我觉得是智能指针家族里最“低调”但又最关键的一员。它不拥有资源,仅仅是
std::shared_ptr
的一个“观察者”或者说“弱引用”。
weak_ptr
不会增加资源的引用计数,因此它的存在不会阻止资源被释放。这使得它成为解决
shared_ptr
循环引用问题的完美方案。

什么是循环引用?想象一下,对象A有一个

shared_ptr
指向对象B,同时对象B也有一个
shared_ptr
指向对象A。这样一来,即使外部已经没有其他
shared_ptr
指向A或B,它们的引用计数永远不会降到零,导致它们永远不会被销毁,造成内存泄漏。这简直是C++程序员的噩梦,但好在标准库给了我们解药。

weak_ptr
通过
lock()
方法可以尝试获取一个
shared_ptr
。如果资源仍然存在(即还有其他
shared_ptr
持有它),
lock()
会返回一个有效的
shared_ptr
;否则,它会返回一个空的
shared_ptr

#include 
#include 
#include 

class B; // 前向声明

class A {
public:
    std::shared_ptr b_ptr;
    A() { std::cout << "A created." << std::endl; }
    ~A() { std::cout << "A destroyed." << std::endl; }
};

class B {
public:
    // 使用weak_ptr打破循环引用
    std::weak_ptr a_ptr;
    B() { std::cout << "B created." << std::endl; }
    ~B() { std::cout << "B destroyed." << std::endl; }
    void checkA() {
        if (auto shared_a = a_ptr.lock()) { // 尝试获取shared_ptr
            std::cout << "B can access A." << std::endl;
        } else {
            std::cout << "A has been destroyed." << std::endl;
        }
    }
};

int main() {
    std::cout << "--- weak_ptr breaking circular reference example ---" << std::endl;
    std::shared_ptr a = std::make_shared();
    std::shared_ptr b = std::make_shared();

    // 建立循环引用
    a->b_ptr = b;
    b->a_ptr = a; // 这里使用weak_ptr

    std::cout << "A's ref count: " << a.use_count() << std::endl; // 1 (b_ptr持有B,B的weak_ptr不影响A的计数)
    std::cout << "B's ref count: " << b.use_count() << std::endl; // 1 (a_ptr持有A)

    b->checkA(); // B可以访问A

    // 当a和b超出作用域时,它们将分别被销毁
    // 如果B中a_ptr也是shared_ptr,这里不会调用析构函数
    std::cout << "--- weak_ptr example end ---" << std::endl;
    return 0;
}

除了解决循环引用,

weak_ptr
在实现缓存、观察者模式或者任何需要“非拥有”地访问某个资源的场景都非常有用。它允许你观察一个对象,而不会影响它的生命周期。说实话,我个人觉得,理解
weak_ptr
的使用场景和机制,是掌握
shared_ptr
高级用法的关键一步。

总而言之,C++智能指针是现代C++内存管理的核心。

unique_ptr
用于独占资源,高效简洁;
shared_ptr
用于共享资源,灵活方便;
weak_ptr
则作为
shared_ptr
的补充,解决循环引用并提供非拥有观察能力。合理搭配使用它们,能让你的C++代码更安全、更健壮。

相关专题

更多
treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

529

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

6

2025.12.22

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

366

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

561

2023.08.10

PHP 命令行脚本与自动化任务开发
PHP 命令行脚本与自动化任务开发

本专题系统讲解 PHP 在命令行环境(CLI)下的开发与应用,内容涵盖 PHP CLI 基础、参数解析、文件与目录操作、日志输出、异常处理,以及与 Linux 定时任务(Cron)的结合使用。通过实战示例,帮助开发者掌握使用 PHP 构建 自动化脚本、批处理工具与后台任务程序 的能力。

21

2025.12.13

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

php网站源码教程大全
php网站源码教程大全

本专题整合了php网站源码相关教程,阅读专题下面的文章了解更多详细内容。

4

2025.12.31

视频文件格式
视频文件格式

本专题整合了视频文件格式相关内容,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

不受国内限制的浏览器大全
不受国内限制的浏览器大全

想找真正自由、无限制的上网体验?本合集精选2025年最开放、隐私强、访问无阻的浏览器App,涵盖Tor、Brave、Via、X浏览器、Mullvad等高自由度工具。支持自定义搜索引擎、广告拦截、隐身模式及全球网站无障碍访问,部分更具备防追踪、去谷歌化、双内核切换等高级功能。无论日常浏览、隐私保护还是突破地域限制,总有一款适合你!

7

2025.12.31

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 5.7万人学习

C 教程
C 教程

共75课时 | 3.8万人学习

C++教程
C++教程

共115课时 | 10.6万人学习

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

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号