Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别_创想鸟

C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别

c++++智能指针通过raii机制实现自动内存管理,有效避免内存泄漏和悬空指针。1. unique_ptr以独占所有权确保资源安全,不可复制只能移动,适用于单一所有权场景;2. shared_ptr采用引用计数实现共享所有权,适用于多模块共享资源但需警惕循环引用;3. weak_ptr作为观察者不增加引用计数,用于打破循环引用并安全观察对象生命周期。这三种智能指针各具特性,满足不同内存管理需求,是现代c++编程的核心工具

C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别

C++智能指针的核心在于它们通过RAII(资源获取即初始化)原则,为动态分配的内存提供自动化的生命周期管理,从而有效避免了内存泄漏和悬空指针等常见问题。它们本质上是封装了裸指针的类模板,并在对象析构时自动释放所管理的资源。其中,unique_ptrshared_ptrweak_ptr标准库中最常用的三种基本类型,各自以不同的所有权语义满足了多样化的内存管理需求。

C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别

智能指针的出现,无疑是C++现代编程实践中一个里程碑式的进步。回想过去,我们手动管理内存,newdelete 的配对就像一场永无止境的华尔兹,稍不留神,舞步就乱了,内存泄漏、重复释放,各种运行时错误接踵而至。那感觉,就像你手里拿着一把钥匙,却不知道哪把锁是它的归宿,或者更糟,你把钥匙扔了,但门还没关。智能指针的出现,就是把这把钥匙和锁绑定在一起,当锁不再需要时,钥匙自动消失。这背后,是RAII(Resource Acquisition Is Initialization)这个强大理念在支撑。简单来说,就是把资源的生命周期绑定到一个对象的生命周期上,当对象被创建时获取资源,当对象被销毁时释放资源。

unique_ptr:独占所有权的智能指针如何确保资源安全?

在我看来,unique_ptr 是智能指针家族中最直接、最干净利落的存在。它的名字已经说明了一切:独占所有权。这意味着一个 unique_ptr 实例独一无二地拥有它所指向的资源,任何时候都只有一个 unique_ptr 管理着那块内存。这种独占性带来了极大的安全性:你不用担心资源被多个地方误删,也不用担心它在某个角落被遗忘。

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

C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别

unique_ptr 的一个显著特点是它不能被复制,只能被移动。这就像你拥有一件独一无二的艺术品,你可以把它从一个房间搬到另一个房间(移动),但你不能同时拥有两件一模一样的(复制)。这种移动语义确保了所有权的清晰转移,一旦所有权从一个 unique_ptr 转移到另一个,原来的 unique_ptr 就不再指向任何资源了。这在函数返回动态分配的对象时特别有用,或者在将所有权从一个容器转移到另一个容器时。

#include #include class MyResource {public:    MyResource(int id) : id_(id) {        std::cout << "MyResource " << id_ << " created." << std::endl;    }    ~MyResource() {        std::cout << "MyResource " << id_ << " destroyed." << std::endl;    }    void doSomething() {        std::cout << "MyResource " << id_ << " doing something." << std::endl;    }private:    int id_;};// 假设一个函数返回一个unique_ptrstd::unique_ptr createResource(int id) {    return std::make_unique(id); // 返回一个unique_ptr}int main() {    std::unique_ptr ptr1 = std::make_unique(1);    ptr1->doSomething();    // 移动所有权    std::unique_ptr ptr2 = std::move(ptr1);     if (!ptr1) { // ptr1现在是空的        std::cout << "ptr1 is now null." <doSomething();    // 函数返回的unique_ptr    std::unique_ptr ptr3 = createResource(3);    ptr3->doSomething();    // unique_ptr也可以管理数组    std::unique_ptr arrPtr = std::make_unique(5);    arrPtr[0] = 10;    std::cout << "arrPtr[0]: " << arrPtr[0] << std::endl;    // 当unique_ptr离开作用域时,资源自动释放    return 0; }

这段代码清晰地展示了 unique_ptr 的独占性和移动语义。当 ptr2 = std::move(ptr1) 执行后,ptr1 就失去了对资源的控制,变成了空指针。而 ptr2 接管了所有权。当 ptr2ptr3arrPtr 离开 main 函数的作用域时,它们所管理的资源会自动被销毁,无需手动调用 delete。这种机制极大地简化了资源管理,降低了出错的概率。

C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别

shared_ptr:共享所有权模型在复杂对象管理中的应用与挑战

如果说 unique_ptr 是独行侠,那 shared_ptr 就是一个团队合作者。它允许多个 shared_ptr 实例共同拥有和管理同一块资源。这在很多场景下非常有用,比如一个数据对象需要被多个模块同时访问,并且这些模块的生命周期可能不同步。shared_ptr 通过内部的引用计数机制来实现这一点:每当一个新的 shared_ptr 共享同一资源时,引用计数就增加;当一个 shared_ptr 离开作用域或被重置时,引用计数就减少。当引用计数降到零时,表示没有 shared_ptr 再指向该资源了,资源便会自动释放。

它的应用场景很广,比如在图形渲染中,一个纹理可能被多个模型共享;或者在一个缓存系统中,同一个数据项可能被多个请求引用。

#include #include class DataObject {public:    DataObject(const std::string& name) : name_(name) {        std::cout << "DataObject " << name_ << " created." << std::endl;    }    ~DataObject() {        std::cout << "DataObject " << name_ << " destroyed." << std::endl;    }    void showName() {        std::cout << "My name is " << name_ << std::endl;    }private:    std::string name_;};int main() {    std::shared_ptr s_ptr1 = std::make_shared("SharedData");    std::cout << "s_ptr1 use_count: " << s_ptr1.use_count() << std::endl;    {        std::shared_ptr s_ptr2 = s_ptr1; // 复制,增加引用计数        std::cout << "s_ptr1 use_count: " << s_ptr1.use_count() <showName();    } // s_ptr2 离开作用域,引用计数减少    std::cout << "s_ptr1 use_count after s_ptr2 scope: " << s_ptr1.use_count() << std::endl;    // 当所有shared_ptr都失效时,DataObject才会被销毁    return 0;}

这段代码展示了 shared_ptr 的引用计数如何工作。s_ptr2 的创建和销毁都影响了 s_ptr1 所指向对象的引用计数。只有当 s_ptr1 也离开作用域时,DataObject 才会被销毁。

然而,shared_ptr 并非没有缺点。最大的挑战,也是它在使用时最需要警惕的问题,就是循环引用。如果两个或多个 shared_ptr 相互持有对方的 shared_ptr,就会形成一个闭环,导致它们的引用计数永远不会降到零,即使它们在逻辑上已经不再需要了,所管理的资源也永远不会被释放,这实际上就是一种内存泄漏。这种问题在设计复杂的图结构或双向关联时尤其容易发生。解决这个问题,就需要引入 weak_ptr

weak_ptr:如何巧妙解决shared_ptr的循环引用问题并观察对象生命周期?

weak_ptrshared_ptr 的一个“伴侣”,它的设计初衷就是为了解决 shared_ptr 的循环引用问题,同时它也提供了一种非侵入式地观察对象生命周期的方式。weak_ptr 不拥有它所指向的资源,它只是一个“观察者”,因此它不会增加资源的引用计数。这就像你给一个朋友发了一张名片,上面写着他的住址,但你并没有拥有他的房子。

当你想通过 weak_ptr 访问资源时,你需要先调用它的 lock() 方法。lock() 会尝试返回一个 shared_ptr。如果资源仍然存在(即有至少一个 shared_ptr 还在管理它),lock() 就会成功返回一个有效的 shared_ptr;如果资源已经被销毁(所有 shared_ptr 都已失效),lock() 就会返回一个空的 shared_ptr。这种机制使得 weak_ptr 成为检查资源是否仍然存活的理想工具。

我们来看一个典型的循环引用场景以及 weak_ptr 如何打破它:

#include #include #include class B; // 前向声明class A {public:    std::shared_ptr b_ptr; // 强引用    std::string name;    A(const std::string& n) : name(n) {        std::cout << "A " << name << " created." << std::endl;    }    ~A() {        std::cout << "A " << name << " destroyed." << std::endl;    }};class B {public:    // 方案一:使用shared_ptr,会造成循环引用    // std::shared_ptr a_ptr;     // 方案二:使用weak_ptr,打破循环引用    std::weak_ptr a_ptr; // 弱引用    std::string name;    B(const std::string& n) : name(n) {        std::cout << "B " << name << " created." << std.endl;    }    ~B() {        std::cout << "B " << name << " destroyed." << std::endl;    }    void showAPtr() {        if (auto sp = a_ptr.lock()) { // 尝试获取shared_ptr            std::cout << "B " << name << " sees A " <name << std::endl;        } else {            std::cout << "B " << name << " cannot see A (A has been destroyed)." << std::endl;        }    }};int main() {    std::cout << "--- 场景一:模拟循环引用 (如果B使用shared_ptr) ---" << std::endl;    // 如果B::a_ptr是shared_ptr,这里A和B都不会被销毁    // {    //     std::shared_ptr a = std::make_shared("ObjA");    //     std::shared_ptr b = std::make_shared("ObjB");    //     a->b_ptr = b;    //     b->a_ptr = a; // 循环引用形成    // } // A和B的引用计数永远不会降到0,导致内存泄漏    // std::cout << "A and B should have been destroyed, but might not be due to circular reference." << std::endl;    std::cout << "n--- 场景二:使用weak_ptr解决循环引用 ---" << std::endl;    {        std::shared_ptr a = std::make_shared("ObjA_Weak");        std::shared_ptr b = std::make_shared("ObjB_Weak");        a->b_ptr = b;       // A强引用B        b->a_ptr = a;       // B弱引用A        std::cout << "A use_count: " << a.use_count() << std::endl; // 1 (来自a) + 1 (来自b_ptr) = 2        std::cout << "B use_count: " << b.use_count() <b_ptr是强引用,a->b_ptr会增加b的引用计数,但是b->a_ptr是弱引用,不会增加a的引用计数)        // 修正:A use_count: 1 (来自a)        // B use_count: 1 (来自b) + 1 (来自a->b_ptr) = 2        std::cout << "A use_count (after assignment): " << a.use_count() << std::endl;        std::cout << "B use_count (after assignment): " << b.use_count() <showAPtr(); // B可以访问A    } // a 和 b 离开作用域,引用计数降为0,A和B都被销毁    std::cout << "A and B should be destroyed now." << std::endl;    std::cout << "n--- 场景三:weak_ptr作为观察者 ---" << std::endl;    std::shared_ptr data = std::make_shared("ObservedData");    std::weak_ptr observer = data;    if (auto sp = observer.lock()) {        std::cout <showName();    } else {        std::cout << "Observer cannot see data." << std::endl;    }    data.reset(); // 销毁data指向的对象    if (auto sp = observer.lock()) {        std::cout <showName();    } else {        std::cout << "Observer cannot see data (it's gone)." << std::endl;    }    return 0;}

场景二 中,当 ab 离开作用域时,a 的引用计数降为0(因为它只被 main 函数中的 ab->a_ptr 弱引用,而弱引用不计入),b 的引用计数也降为0(因为它只被 main 函数中的 ba->b_ptr 强引用)。这样,AB 对象都能被正确销毁,避免了内存泄漏。

场景三 则展示了 weak_ptr 作为纯粹观察者的能力。它不延长对象的生命周期,只是在对象存在时提供访问途径,在对象被销毁后安全地返回空指针,这在缓存管理、事件监听器等场景中非常有用。

总的来说,unique_ptr 适用于单一所有权、清晰生命周期的资源;shared_ptr 适用于多所有权、复杂生命周期的资源,但需要警惕循环引用;而 weak_ptr 则是 shared_ptr 的补充,用于打破循环引用和安全地观察对象生命周期。理解它们的区别和适用场景,是写出健壮、高效C++代码的关键。

以上就是C++智能指针有哪些基本类型 解析unique_ptr shared_ptr weak_ptr核心区别的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1468750.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
C++怎样处理损坏的文件数据 错误检测和恢复机制
上一篇 2025年12月18日 17:42:46
C++异常与返回错误码如何选择 不同场景下的适用性对比
下一篇 2025年12月18日 17:42:57

相关推荐

  • mysql添加索引命令 mysql创建普通唯一复合索引教程

    mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程mysql添加索引命令 mysql创建普通唯一复合索引教程

    在mysql中给表加索引的核心目的是提升查询效率。解决方案是通过create index或alter table语句创建不同类型的索引:1. 普通索引用于加快非唯一列的查询;2. 唯一索引确保列值唯一性并提升性能;3. 复合索引支持多列组合查询,遵循最左前缀原则;4. 复合唯一索引结合复合和唯一特性…

    2026年9月23日 用户投稿
    000
  • VSCode快速配置React:JSX支持、中文文档、组件调试

    首先确保安装es7 react/redux/graphql/react-native snippets扩展并配置jsconfig.json文件以启用jsx语法高亮;通过安装中文语言包汉化界面,并结合浏览器书签、翻译工具及笔记软件高效查阅react中文文档;调试react组件时需安装debugger …

    2026年9月23日
    100
  • PaintToolSAI的AI功能如何裁剪图片?教你精准实现裁剪方法

    PaintToolSAI的AI功能如何裁剪图片?教你精准实现裁剪方法PaintToolSAI的AI功能如何裁剪图片?教你精准实现裁剪方法PaintToolSAI的AI功能如何裁剪图片?教你精准实现裁剪方法PaintToolSAI的AI功能如何裁剪图片?教你精准实现裁剪方法

    PaintToolSAI没有AI裁剪功能,其裁剪依赖手动操作。用户需使用选区工具框选区域,再通过“画布→裁剪”完成,过程基础但精确,适合专注绘画而非复杂图像处理的场景。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ PaintToolSAI…

    2026年9月23日 用户投稿
    100
  • D盘和C盘有什么区别_D盘与C盘功能及用途对比解析

    C盘是系统核心,负责电脑启动和运行,存放操作系统及关键文件,需谨慎操作并预留至少20%空间;D盘为数据存储区,用于保存个人文件和软件,可自由管理但需定期整理与备份。 电脑里的C盘和D盘虽然都是用来存东西的,但它们的角色完全不同。简单说,C盘是系统的大本营,管着电脑能不能正常开机运行;D盘是你的私人仓…

    2026年9月23日
    000
  • X 旗下 Grok 推出即时语音搜索功能 向 Google 发起挑战?

    近日,X 平台的人工智能助手 Grok 正式上线“即时语音搜索”功能,用户现在可通过语音直接提问,触发实时网络检索,并迅速获得整合后的精准答复。这项新能力意在优化信息查询流程,推动人机互动向更自然、高效的形态发展。 根据官方说明与实际用户体验,Grok 的语音搜索实现了“说话即搜、即时回应”的流畅体…

    2026年9月23日
    100
  • Java JUnit assertThrows 与异常消息比对:避免常见陷阱

    本教程深入探讨在 Java JUnit 中使用 assertThrows 进行异常测试时常见的 assertEquals 误用问题。它详细解释了为何不能直接将期望的字符串消息与捕获到的异常对象进行比对,并提供了正确的解决方案:通过 e.getMessage() 获取异常消息进行精确断言,确保测试的准…

    2026年9月23日
    000
  • win11怎么看电脑配置信息_win11查看电脑配置信息操作教程

    通过系统设置可快速查看处理器、内存等核心配置;使用“msinfo32”获取BIOS、主板等详细信息;运行“systeminfo”命令导出完整配置清单;借助设备管理器检查硬件设备及驱动状态。 如果您想了解自己电脑的硬件和系统详情,Windows 11 提供了多种内置方法来查看配置信息,无需安装额外软件…

    2026年9月23日
    000
  • 为什么iPhoneSE2022收款语音不响?教你配置微信语音播报的方法

    最常见的原因是iPhone系统或微信设置问题,如静音、勿扰模式开启或通知权限关闭。需检查手机音量、专注模式,并确保微信通知和收款语音提醒已开启。 最常见的原因是iPhone的系统通知设置、微信应用内部设置,或简单地开启了“勿扰模式”或“专注模式”导致。这通常不是手机硬件故障,而是软件配置上的疏忽。 …

    2026年9月23日
    200
  • safari浏览器如何导入Netscape格式的书签文件_safari浏览器导入Netscape书签方法

    Safari无法直接读取Netscape格式书签时,可通过HTML文件转换、第三方工具修复或借助Chrome/Firefox中转导入。首先确认书签文件含标准结构,尝试直接导入Safari;若失败,使用Linkman Lite等工具修复格式后导入;或通过Chrome先行导入再导出标准HTML文件,最后…

    2026年9月23日
    000
  • VSCode如何通过AI优化内存使用 VSCode智能内存分析调试插件

    ai并不能直接优化vscode的内存占用,而是通过赋能智能化的内存分析调试插件,间接提升vscode的运行效率和开发体验;2. 这类插件通过采集堆快照、cpu性能数据、gc日志和扩展资源消耗等运行时信息,结合ai的模式识别与异常检测能力,精准识别内存泄漏、未清理的事件监听器、循环引用等问题;3. a…

    2026年9月23日
    200
  • PHP容器与依赖注入怎么理解_PHP中DI容器概念与Pimple等库应用

    依赖注入通过外部传入依赖解耦代码,容器如Pimple集中管理对象创建与依赖解析,提升可测试性与维护性,适用于中小型项目。 PHP中的容器和依赖注入(Dependency Injection,简称DI)是现代开发中解耦代码、提升可测试性和可维护性的核心技术。理解它们的关键在于掌握“控制反转”(Inve…

    2026年9月23日
    100
  • 如何在iPhone13设置密码?为你的手机添加安全保护的实用方法

    设置iPhone 13密码需进入“设置”→“面容ID与密码”,选择六位或自定义字母数字密码,配合面容ID实现双重安全;推荐使用复杂密码或密码管理器提升安全性,同时开启“查找我的iPhone”和数据加密功能,防止信息泄露;若忘记密码,可通过恢复模式或iCloud远程抹除并从备份恢复数据,前提是提前开启…

    2026年9月23日
    900
  • VS Code算法实战:竞赛编程与调试环境搭建

    首先安装编程语言环境及VS Code扩展,如C/C++、Code Runner和LeetCode;接着配置Code Runner支持编译运行与输入重定向;最后通过代码片段提升编码速度,形成高效竞赛开发环境。 在竞赛编程中,高效的开发环境能大幅提升编码速度与调试效率。VS Code凭借轻量、可扩展和强…

    2026年9月23日
    000
  • win8任务计划程序怎么用_Win8任务计划程序使用教程

    win8任务计划程序怎么用_Win8任务计划程序使用教程win8任务计划程序怎么用_Win8任务计划程序使用教程win8任务计划程序怎么用_Win8任务计划程序使用教程win8任务计划程序怎么用_Win8任务计划程序使用教程

    答案:可通过任务计划程序实现。在Windows 8中,使用任务计划程序可创建基本或高级任务,设置触发器(如定时或开机启动)并指定操作(如运行程序、脚本或显示消息),通过向导或高级配置完成自动化。 如果您需要在Windows 8系统中自动执行特定程序、脚本或操作,可以通过任务计划程序来实现定时或触发式…

    2026年9月23日 用户投稿
    100
  • 字节入局,AR眼镜掀起新“风口”?

    近日,关于老凤祥与字节跳动合作推出AI眼镜的消息在网络上引发热议。据相关媒体报道,老凤祥计划联合字节跳动旗下的火山引擎共同开发多款AI眼镜,并由豆包大模型提供技术支持,预计将在今年7月正式发布。 对此,6月12日,火山引擎方面进行了澄清。其负责人表示,并未有与老凤祥合作研发AI智能眼镜的计划。而豆包…

    2026年9月23日
    000
  • 在无Maven或Eclipse环境下手动构建Java Web应用WAR包的教程

    本教程详细介绍了如何在不依赖Maven或Eclipse等集成开发环境的情况下,为Java Web应用程序手动生成WAR(Web Application Archive)文件。文章首先阐述了WAR文件的基本结构,随后通过一个Ant构建脚本的实例,指导读者完成从源代码编译、文件组织到最终WAR包生成的全…

    2026年9月23日
    000
  • AdobePhotoshop的AI混合工具怎么用?掌握智能图像编辑的教程

    Photoshop的AI混合工具以生成式填充和神经网络滤镜为代表,通过语义理解实现智能图像融合。生成式填充可依据文本提示添加、移除或扩展内容,自动匹配光影与纹理;神经网络滤镜如和谐化则优化颜色与光照匹配。与传统基于像素计算的混合模式不同,AI工具理解图像内容,实现“生成并融合”。使用时需精准输入英文…

    2026年9月23日
    100
  • windows10怎么把任务栏设置成透明_windows10任务栏透明效果设置方法

    可通过系统设置、注册表或第三方工具实现Windows 10任务栏透明。一、在“个性化-颜色”中开启“透明效果”;二、在注册表新建TaskbarAcrylicOpacity值并设为0-100调节透明度,重启生效;三、使用TranslucentTB软件实现多模式透明控制,支持常透明或悬停显示。 如果您希…

    2026年9月23日
    000
  • PHP多维数组重构:按指定键值分组数据

    本文将详细介绍如何在PHP中将扁平化的关联数组列表重构为多维数组,核心思路是根据数组中某个特定键(例如 object_type)的值进行分组,将具有相同键值的所有子数组归集到同一个父级键下,从而实现数据的层次化组织,提高数据的可读性和管理效率。 引言:数据重构的需求 在PHP开发中,我们经常会遇到需…

    2026年9月23日
    000
  • 解决 Conda 环境中 Java 版本冲突的策略

    本文旨在解决 Conda 环境中 Java 版本激活不正确的问题。当用户尝试在 Conda 环境中指定特定 Java 版本(如 OpenJDK 8)时,系统可能仍激活旧的或错误的 Java 版本。教程将详细分析问题根源,并提供一种通过精确指定 Java 包名来确保 Conda 环境正确管理 Java…

    2026年9月23日
    000

发表回复

登录后才能评论
关注微信