目录

前言

一、智能指针的使用原理

二、RAll原理

四、各智能指针适用场景

五、避免使用智能指针的场景


前言

        智能指针是现代C++管理资源的核心工具,它极大降低内存泄漏和资源管理错误的风险。

        本文将简要介绍其底层实现机制,以及各种智能指针的设计差异、适用场景。


一、智能指针的使用原理

        在C++中,如果用 new 去分配内存,但忘记用 delete 释放,便会导致内存泄漏。

        同时,如果程序异常退出,delete 语句便无法执行。

void Func()
{
    int *a1=new int;
    int *a2=new int;
    cout<<div()<<endl;//若此处抛出异常,a1、a2都会无法释放。
    delete a1;
    delete a2;
}

        所以,C++引用了 智能指针 来解决这一问题,底层思想为· RAll

二、RAll原理

        RAll 是 C++ 中一种将资源的生命周期对象的生命周期绑定的核心技术,指通过对象的构造和析构来管理资源。

  •   构造时,获取资源。(如内存、文件句柄、锁、数据库连接等)
  •   析构时,释放资源。      

        其本质为:只要把资源封装到对象里,就可以自动管理资源的生命周期。

【优势】:

        C++保证,无论函数是正常退出还是因为异常导致栈展开,所有局部对象的析构函数都会被调用。这确保了资源总是能被正确释放,极大地避免了资源泄漏。

特性 unique_ptr shared_ptr weak_ptr
所有权

独占所有权

       一个对象只能同时被一个unique_ptr拥有。

共享所有权

        一个对象可以被多个shared_ptr拥有,采用引用计数。对象在所有拥有它的shared_ptr被销毁后释放。

弱引用

        不拥有对象,不增加引用计数。它指向一个由shared_ptr管理的对象。

实现原理         拷贝构造和拷贝赋值被禁用         控制块存储引用计数(use count)和弱计数(weak count)。拷贝时引用计数+1,析构时-1,减至0时释放对象资源。         内部持有指向shared_ptr控制块的指针。可以通过lock()方法尝试获取一个指向对象的shared_ptr(如果对象还存在的话)。它不改变引用计数,但会影响控制块的弱计数。
性能 零开销,和裸指针几乎一样 有额外开销:需要动态分配控制块,引用计数的增减需要原子操作(线程安全),有一定性能损耗。 同样有控制块开销,lock()也需要原子操作。

【注】:

        1.shared_ptr 弥补了 unique_ptr 的不足,通过引用计数实现多智能指针共享同一资源。

  • 每块被管理的资源都会关联一个引用计数器。

  • 拷贝时引用计数+1,析构时-1,减至0时释放对象资源。

        2.shared_ptr 虽然实现了资源共享,但引用计数本身无法检测对象之间是否形成循环引用,从而导致资源无法释放。

        3.为解决循环引用造成的内存泄露,C++引用了 weak_ptr(解决循环引用)

四、各智能指针适用场景

(一)unique_ptr

  1. 需要多态行为的独占资源:例如,在策略模式中持有具体的策略对象。
  2. 作为类的成员变量(组合关系):表示容器拥有这个成员,生命周期与容器一致。

(二)shared_ptr

  1. 多个所有者:例如,多个线程或对象需要共享同一个资源,只有当最后一个使用者销毁时才释放。
  2. 复杂的数据结构:但要注意,在双向链表或图中要避免循环引用

(三)weak_ptr

  1. 打破循环引用:这是它最主要的设计目的。
  2. 缓存:缓存对象可以用shared_ptr管理,而外部引用可以用weak_ptr。当缓存对象不再被任何地方使用时(引用计数为0),它会被释放,weak_ptr自动悬空。再次使用时,通过lock()尝试提升,如果成功则直接使用,否则重新加载。
  3. 观察者模式:观察者持有被观察者的weak_ptr,可以随时检查被观察者是否还存活,而不会影响其生命周期。

五、避免使用智能指针的场景

  • 为了避免引用循环而引入过于复杂的weak_ptr设计时

        如果一个类的设计一开始就充满了shared_ptr和weak_ptr的互相指涉,代码可读性和维护性会大大降低。有时一个清晰的所有权设计(如明确的父子关系,父亲拥有孩子,孩子持有父亲的裸指针)比复杂的智能指针组合更可取

  • 自定义内存池

        如果使用了特定的内存池(如对象池),资源的分配和释放由池自己管理,再用智能指针去自动释放就会造成冲突(双重释放)。此时可能只需要用裸指针,或者配合自定义删除器来将对象归还给池,而不是直接delete。


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐