讲师中心 微信公众号

工具推荐

视频效率加速
立刻MV
立刻MV

低成本歌曲 MV、视频生成

updream

一键生成高质量推广成片

剧云
剧云

中文短剧剧本制作平台

AniShort
AniShort

AI短剧协同制作平台

如何避免智能指针的误用导致资源泄漏 常见陷阱与最佳实践

老瑶吖_1201

老瑶吖_1201

发布时间:2025-08-02 10:33:01

|

861人浏览过

|

来源于php中文网

原创

智能指针可能导致资源泄漏的三个主要原因是循环引用、unique_ptr所有权转移失败和自定义删除器使用不当。1. 避免shared_ptr循环引用的方法是使用weak_ptr打破循环,使其不增加引用计数;2. unique_ptr所有权转移失败常见于复制尝试、未使用std::move或返回局部unique_ptr引用,应避免这些操作;3. 使用自定义删除器时需确保其与分配方式匹配、不抛异常且线程安全,如用free_deleter释放malloc内存。

如何避免智能指针的误用导致资源泄漏 常见陷阱与最佳实践

智能指针旨在自动管理动态分配的内存,但如果使用不当,仍然可能导致资源泄漏。核心在于理解智能指针的所有权语义,并避免循环引用。

如何避免智能指针的误用导致资源泄漏 常见陷阱与最佳实践

循环引用会导致

shared_ptr
无法正确释放资源,而
unique_ptr
的不当使用则可能导致所有权转移失败。

如何避免智能指针的误用导致资源泄漏 常见陷阱与最佳实践

如何避免 shared_ptr 循环引用?

循环引用通常发生在两个或多个对象彼此持有

shared_ptr
的情况。当这些对象超出作用域时,它们的引用计数不会降为零,导致内存泄漏。

如何避免智能指针的误用导致资源泄漏 常见陷阱与最佳实践

解决方案是使用

weak_ptr
。
weak_ptr
是一种不增加引用计数的智能指针,它指向由
shared_ptr
管理的对象。当需要访问该对象时,可以尝试从
weak_ptr
创建一个
shared_ptr
。如果原始对象已被销毁,则创建会失败,从而避免悬挂指针。

例如:

#include <iostream>
#include <memory>

class B; // 前向声明

class A {
public:
    std::weak_ptr<B> b_ptr; // 使用 weak_ptr
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::shared_ptr<A> a_ptr;
    ~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->b_ptr = b;
    b->a_ptr = a;

    // 没有循环引用,A 和 B 都会被销毁
    return 0;
}

在这个例子中,

A
类使用
weak_ptr
指向
B
类的实例,避免了循环引用。当
A
和
B
超出作用域时,它们都会被正确销毁。如果
A
类也使用
shared_ptr
指向
B
类的实例,就会发生内存泄漏。

unique_ptr 所有权转移失败的场景有哪些?

unique_ptr
具有独占所有权,这意味着只有一个
unique_ptr
可以指向给定的资源。所有权转移必须显式进行,否则会导致编译错误或运行时错误。

常见的错误包括:

  1. 尝试复制

    unique_ptr
    :
    unique_ptr
    不支持复制构造函数或赋值运算符。尝试复制会导致编译错误。

    AI舌诊面诊问诊医疗系统+诊疗商城系统源码
    AI舌诊面诊问诊医疗系统+诊疗商城系统源码

    AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

    下载
  2. 忘记使用

    std::move
    转移所有权: 在函数调用或赋值时,如果需要转移
    unique_ptr
    的所有权,必须使用
    std::move
    。否则,编译器会报错。

  3. 返回局部

    unique_ptr
    的引用: 返回局部
    unique_ptr
    的引用会导致悬挂引用,因为
    unique_ptr
    在函数返回时会被销毁,它所管理的资源也会被释放。

例如:

#include <iostream>
#include <memory>

std::unique_ptr<int> create_int() {
    std::unique_ptr<int> ptr(new int(10));
    return ptr; // 隐式使用 move 语义
}

int main() {
    std::unique_ptr<int> my_ptr = create_int();
    if (my_ptr) {
        std::cout << *my_ptr << std::endl;
    }

    // std::unique_ptr<int> another_ptr = my_ptr; // 错误:尝试复制 unique_ptr
    std::unique_ptr<int> another_ptr = std::move(my_ptr); // 正确:转移所有权

    if (my_ptr) {
        std::cout << *my_ptr << std::endl; // 不会执行,因为所有权已转移
    }

    if (another_ptr) {
        std::cout << *another_ptr << std::endl; // 输出 10
    }
    return 0;
}

如何正确使用自定义删除器避免资源泄漏?

unique_ptr
和
shared_ptr
都允许指定自定义删除器,用于在资源释放时执行特定的操作。如果资源不是通过
new
分配的,或者需要执行额外的清理工作,自定义删除器就非常有用。

使用自定义删除器时,需要注意以下几点:

  1. 确保删除器与资源的分配方式匹配: 如果资源是通过

    malloc
    分配的,则删除器应该使用
    free
    释放资源。如果资源是通过其他方式分配的,则删除器应该执行相应的清理操作。

  2. 避免删除器抛出异常: 如果删除器抛出异常,会导致程序终止。应该在删除器中捕获并处理任何可能发生的异常。

  3. 确保删除器是线程安全的: 如果多个线程可能同时访问同一个资源,则删除器应该是线程安全的。

例如:

#include <iostream>
#include <memory>

// 自定义删除器,使用 free 释放资源
void free_deleter(void* ptr) {
    std::cout << "free_deleter called\n";
    free(ptr);
}

int main() {
    void* ptr = malloc(1024);
    std::unique_ptr<void, decltype(&free_deleter)> my_ptr(ptr, &free_deleter);

    // 资源会被 free_deleter 正确释放
    return 0;
}

在这个例子中,

free_deleter
函数被用作
unique_ptr
的自定义删除器。当
my_ptr
超出作用域时,
free_deleter
会被调用,使用
free
释放
malloc
分配的内存。 如果没有使用自定义删除器,或者使用了错误的删除器(例如,使用
delete
释放
malloc
分配的内存),就会导致内存泄漏或程序崩溃。

热门AI工具

更多
函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

WorkBuddy

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

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

Flowith
Flowith Hot

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

超级麦吉

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

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

豆包大模型

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

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

DeepSeek

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

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6404

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3564

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

1952

2025.10.17

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

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

399

2026.04.23

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

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

4898

2023.08.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

3611

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1085

2023.12.29

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

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

100

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

60

2026.11.12

热门下载

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

精品课程

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

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