当前位置: 首页 > news >正文

智能指针C++11

1-定义

在C++中,智能指针是一种用于自动管理动态分配内存的抽象机制。它们通过RAII(资源获取即初始化,Resource Acquisition Is Initialization)原则,帮助开发者自动管理资源生命周期,避免内存泄漏、双重释放等问题。

定义我们需了解,具体应用我会展示。 

2-智能指针的类型

C++标准库中主要的智能指针包括:

1.unique_ptr:独占所有权的智能指针。

2.shared_ptr:共享所有权的智能指针。

3.weak_ptr:配合shared_ptr使用的非拥有型智能指针,用于解决循环引用问题。

    其中包括auto_ptr 但是在C++17中已被移除,基本功能已被 unique_ptr所替代。

    3-各指针用法

    3.1 auto_ptr(选读)

    虽然被取代但是 我们还需了解。

    auto_ptr 的基本用法:

    定义与特点
    • 独占所有权auto_ptr 拥有对动态分配对象的唯一所有权。
    • 拷贝语义:拷贝一个 auto_ptr 会转移所有权,而不是进行深拷贝。这意味着拷贝后,原来的 auto_ptr 将不再拥有对象的所有权。
    • 不可拷贝赋值:不能进行拷贝赋值操作,但可以通过拷贝构造函数进行所有权转移。

    举例

    
    
    int main() {
        auto_ptr<int> ptr1(new int(10));
        cout << "*ptr1 = " << *ptr1 << endl;
        // 拷贝构造,转移所有权
        auto_ptr<int> ptr2(ptr1);
        if(ptr1.get() == nullptr) {
            cout << "ptr1 is now empty after copy." << endl;
        }
    
        cout << "*ptr2 = " << *ptr2 << endl;
    
        return 0;
    }
    //输出
    //*ptr1 = 10
    //ptr1 is now empty after copy.
    //*ptr2 = 10

    实现的内容:

    • ptr1 通过 new int(10) 分配了一个整数对象。
    • ptr2(ptr1) 通过拷贝构造转移了 ptr1 的所有权,导致 ptr1 不再拥有对象的所有权,其内部指针被置为 nullptr
    • 这种所有权转移机制避免了双重释放的问题,但同时也带来了其他问题。

     存在的问题:

    1.不安全的拷贝语义

    • auto_ptr 的拷贝操作会转移所有权,这可能导致意外的资源所有权转移,进而引发未定义行为或程序崩溃。
    • 例如,在函数参数传递或返回时,拷贝操作会转移所有权,可能导致原指针变为空。

    2.不支持移动语义

    • auto_ptr 在C++11之前没有移动语义的概念,这使得它在需要明确所有权转移的场景下不够灵活和安全。

    3.不支持数组

    • auto_ptr 只能管理单个对象,不能管理动态分配的数组。这限制了它的使用范围。

    4.缺乏删除器支持

    • auto_ptr 不支持自定义删除器,这使得它无法管理需要特殊释放逻辑的资源,如文件句柄、数据库连接等。

    后续为何被取代

    • 明确的移动语义unique_ptr 支持移动语义,明确了所有权转移的方式,避免了 auto_ptr 的不安全拷贝问题。
    • 支持数组std::unique_ptr 可以管理动态分配的数组,通过指定删除器可以正确释放数组内存。
    • 支持自定义删除器unique_ptr 支持自定义删除器,可以管理不同类型的资源。
    • 更好的性能和安全性unique_ptr 提供了更好的性能和类型安全性。

     3.2 unique_ptr

    定义与特点
    • 独占所有权unique_ptr 拥有对动态分配对象的唯一所有权,不允许拷贝,只允许移动。
    • 轻量级:由于不需要引用计数,unique_ptr 的开销非常小。
    • 不可拷贝:无法进行拷贝构造或拷贝赋值,但可以移动。
    使用场景
    • 用于管理独占资源的生命周期。
    • 当不需要多个指针共享同一个对象时。

    举例:

    
    
    int main() {
        // 使用 unique_ptr 管理动态分配的整数
        unique_ptr<int> ptr1 = make_unique<int>(10);
        cout << "*ptr1 = " << *ptr1 << endl;
    
        // 移动所有权
        unique_ptr<int> ptr2 = move(ptr1);
        if(!ptr1) {
            cout << "ptr1 is now empty." << endl;
        }
    
        cout << "*ptr2 = " << *ptr2 << endl;
    
        return 0;
    }
    //输出
    //*ptr1 = 10
    //ptr1 is now empty.
    //*ptr2 = 10

     3.3shared_ptr

    定义与特点
    • 共享所有权shared_ptr 允许多个指针共享同一个对象,内部使用引用计数来管理对象的生命周期。
    • 引用计数:每增加一个shared_ptr指向对象,引用计数加1;每销毁一个shared_ptr,引用计数减1。当引用计数为0时,自动释放对象。
    • 拷贝与赋值:可以拷贝和赋值,但需要注意循环引用问题。
    使用场景
    • 当多个指针需要共享同一个对象时。
    • 需要在多个地方引用同一个动态分配的对象。

    举例:

    
    int main() {
        // 使用 shared_ptr 管理动态分配的整数
        shared_ptr<int> ptr1 = make_shared<int>(20);
        {
            shared_ptr<int> ptr2 = ptr1;
            cout << "*ptr1 = " << *ptr1 << endl;
            cout << "*ptr2 = " << *ptr2 << endl;
            cout << "Reference count: " << ptr1.use_count() << endl; // 输出: 2
        }
        cout << "After inner scope, reference count: " << ptr1.use_count() << endl; // 输出: 1
        cout << "*ptr1 = " << *ptr1 << endl;
    
        return 0;
    }
    //输出
    //*ptr1 = 20
    //*ptr2 = 20
    //Reference count: 2
    //After inner scope, reference count: 1
    //*ptr1 = 20

     3.4 weak_ptr

    定义与特点
    • 非拥有型weak_ptr 不拥有对象的所有权,不增加引用计数。
    • 配合 shared_ptr 使用:通常由shared_ptr创建,用于解决循环引用问题。
    • 访问对象:需要通过lock()方法获取一个shared_ptr,以访问对象。
    使用场景
    • 当需要引用一个对象但不希望影响其生命周期时。
    • 解决shared_ptr之间的循环引用问题。

    举例:

    //修改前的样例
    class B;
    
    class A {
    public:
        shared_ptr<B> ptr;
        ~A() { cout << "A destroyed" << endl; }
    };
    
    class B {
    public:
        weak_ptr<A> ptr; // 使用 weak_ptr 避免循环引用
        ~B() { cout << "B destroyed" << endl; }
    };
    
    int main() {
        {
            shared_ptr<A> a = make_shared<A>();
            shared_ptr<B> b = make_shared<B>();
            a->ptr = b;
            b->ptr = a;
            // 此时引用计数为2
        }
        // 离开作用域后,引用计数减为1,A 和 B 都不会被销毁
        cout << "Objects still alive." << endl;
    
        return 0;
    }
    //输出:Objects still alive.

     

    解释:

    • 这个例子中,A 和 B 互相持有shared_ptr,导致引用计数无法降为0,从而产生内存泄漏。
    • 如果将 B 中的 shared_ptr<A> 改为 weak_ptr<A>,则可以避免循环引用

    修改后:

    
    class B;
    
    class A {
    public:
        shared_ptr<B> ptr;
        ~A() { cout << "A destroyed" << endl; }
    };
    
    class B {
    public:
        weak_ptr<A> ptr; // 使用 weak_ptr 避免循环引用
        ~B() { cout << "B destroyed" << endl; }
    };
    
    int main() {
        {
            shared_ptr<A> a = make_shared<A>();
            shared_ptr<B> b = make_shared<B>();
            a->ptr = b;
            b->ptr = a;
            // 此时引用计数为2
        }
        // 离开作用域后,引用计数减为1,A 被销毁,B 也被销毁
        cout << "After scope, objects destroyed." << endl;
    
        return 0;
    }
    //输出:
    //A destroyed
    //B destroyed
    //After scope, objects destroyed.

     

    4 智能指针实现原理

     

    1. unique_ptr

    • 内部实现:通常包含一个指向对象的指针和一个删除器(deleter)。
    • 移动语义:通过移动构造函数和移动赋值运算符转移所有权。
    • 删除器:默认情况下,使用 delete 操作符释放资源,也可以自定义删除器以管理不同类型的资源。

    2. shared_ptr

    • 内部实现:包含一个指向对象的指针和一个指向控制块的指针。控制块包含引用计数和一个弱引用计数。
    • 引用计数:每增加一个shared_ptr指向对象,引用计数加1;每销毁一个shared_ptr,引用计数减1。
    • 控制块:控制块在第一次创建shared_ptr时分配,包含引用计数和弱引用计数。
    • 线程安全:引用计数和弱引用计数的操作是线程安全的。

    3. weak_ptr

    • 内部实现:包含一个指向控制块的指针,但不拥有对象的所有权。
    • 弱引用计数:用于跟踪shared_ptr的引用计数,不影响对象的生命周期。
    • 访问对象:需要通过lock()方法获取一个shared_ptr,以访问对象。

     

    5-使用及其注意事项

    1.避免循环引用

    • shared_ptr 之间互相持有会导致引用计数无法降为0,从而产生内存泄漏。
    • 使用weak_ptr可以解决循环引用问题。

    2.选择合适的智能指针

    • unique_ptr 用于独占所有权场景。
    • shared_ptr 用于共享所有权场景。
    • weak_ptr 用于需要引用对象但不影响其生命周期的情况。

    3.自定义删除器

    • 可以为智能指针指定自定义删除器,以管理不同类型的资源,如文件句柄、数据库连接等。

    4.避免不必要的拷贝

    • unique_ptr 不支持拷贝,只能移动。
    • shared_ptr 支持拷贝,但会增加引用计数,带来一定的开销。

    5.性能考虑

    • shared_ptr 的引用计数操作会带来一定的性能开销,应根据实际需求合理使用。

    希望这篇文章有助于了解智能指针,如果有错误还请纠正 

       

       

         

        相关文章:

      1. Spring Boot 通过全局配置去除字符串类型参数的前后空格
      2. 遵循IEC62304YY/T0664:确保医疗器械软件生命周期合规性
      3. 【Hadoop入门】Hadoop生态之MapReduce简介
      4. 25.4.8学习总结
      5. 自定义实现C++拓展pytorch功能
      6. 无标记点动作捕捉方案:偃动坊无标记动作捕捉系统,解锁无穿戴动捕体验
      7. 浅谈类的复制构造函数和赋值运算符
      8. 今日行情明日机会——20250408
      9. Java 设计模式:工厂模式详解
      10. 前端实现docx格式word文件预览,可以兼容原生、vue2、以及uni-app 项目,详细步骤。
      11. 3月AI论文精选十篇
      12. 遍历集合list工具
      13. C++学习day8
      14. AI-人工智能-基于LC-MS/MS分子网络深度分析的天然产物成分解析的新策略
      15. LeetCode541反转字符串②
      16. 最简CNN based RNN源码
      17. Vue.js 中 v-model 的使用及其原理
      18. 注意力机制 Attention
      19. 第九章:前沿 RAG 技术探索
      20. 数字内容体验驱动用户参与增效
      21. 英国和美国就关税贸易协议条款达成一致
      22. 国家主席习近平同普京总统出席签字和合作文本交换仪式
      23. 常州市委原常委、组织部部长陈翔调任江苏省民宗委副主任
      24. 暴雨及强对流天气黄色预警已发布!南方进入本轮降雨最强时段
      25. 上海:5月8日起5年以上首套个人住房公积金贷款利率下调至2.6%
      26. 数据中心业务今年预增50%,丹佛斯:中国是全球最重要的市场