• C++ 4种智能指针的定义与使用——学习记录008


    1.智能指针

    1.1作用

    智能管理动态分配的内存,自动释放程序员new出来的内存,从而避免内存泄漏。

    1.2原理

    动态分配的内存交给有生命周期的对象处理,在对象过期时,内存的释放交给对象来处理。

    1.3使用方法

    1. #include
    2. auto_ptr<类型> 变量名称(new 类型);
    3. auto_ptr str(new string("string型智能指针测试"));
    4. auto_ptrint>> v(new vecotr<int>);
    5. auto_ptr<int> array(new int[10]);

    智能指针内部实现了*,->运算符重载,可以像普通指针一样使用。

    1.4常用函数

    1) get()函数:获取指针返回

    1. auto_ptr test(new Test);
    2. Test *tmp = test.get(); // 获取指针返回

    2) release()函数:取消智能指针对动态内存的托管,会将内存的地址返回

    1. auto_ptr test(new Test);
    2. Test *tmp = test.release(); // 取消智能指针对动态内存的托管
    3. delete tmp; // 手动释放

    3) reset()函数:释放智能指针托管的指针内存

    1. autp_ptr test(new Test);
    2. test.reset(); // 释放智能指针托管的指针内存,将其置为NULL
    3. test.reset(new Test()); // 释放智能指针托管的指针内存,并指向形式参数中的内存地址

    存在的问题:

    1.复制或者赋值都会改变资源的所有权

    2.在STL容器中使用auto_ptr存在风险,因为容器中元素必须可复制和可赋值

    3.不支持对象数组管理

    1)复制和赋值的资源所有权问题

    1. // auto_ptr 被C++11抛弃的主要原因
    2. auto_ptr p1(new string("I'm Li Ming!"));
    3. auto_ptr p2(new string("I'm age 22."));
    4. cout << "p1:" << p1.get() << endl;
    5. cout << "p2:" << p2.get() << endl;
    6. /*
    7. TODO p2赋值给p1后,首先p1会先将自己原先托管的内存释放掉,然后接收托管p2所托管的内存,
    8. 然后p2所托管的指针为NULL,也就是p1托管了p2托管的内存,而p2放弃了托管。
    9. */
    10. p1 = p2;
    11. cout << "p1 = p2 赋值后:" << endl;
    12. // 结构托管
    13. cout << "p1:" << p1.get() << endl; // 输出p1托管的地址
    14. cout << "p2:" << p2.get() << endl;
    15. // 输出为null空地址
    16. cout << *p2.get() << endl; // 出错——空地址取值运算

    2)STL容器中使用auto_ptr的风险,首先需要对auto_ptr进行move修饰

    1. vector> vec;
    2. auto_ptr p3(new string("I'm P3"));
    3. auto_ptr p4(new string("I'm P4"));
    4. // 必须使用std::move修饰成右值,才可以进行插入到容器中
    5. vec.push_back(std::move(p3));
    6. vec.push_back(std::move(p4));
    7. cout << "vec.at(0):" << *vec.at(0) << endl;
    8. cout << "vec[1]:" << *vec[1] << endl;
    9. // 出错
    10. // 在这里进行赋值,赋值成功后,vec[1]智能指针将被释放,输出将会出错
    11. vec[0] = vec[1]; // 如果进行赋值,问题又回到了上面一个问题中。
    12. cout << "vec.at(0):" << *vec.at(0) << endl;
    13. cout << "vec[1]:" << *vec[1] << endl;

    3)不支持对象数组管理

    auto_ptr<int[]> array(new int[5]);    // 出错

    2. C++11 智能指针新特性

    1. unique_ptr独占对象所有权,没有使用引用计数,整体性能良好

    2. shared_ptr共享对象的所有权,使用引用计数,性能较差

    3. weak_ptr配合shared_ptr,解决循环引用问题

    unique_ptr指针替换了auto_ptr智能指针,特性:

    1.基于排他所有权模式,两个指针不能指向同一个资源。

    2.无法进行左值unique_ptr赋值构造,也无法进行左值复制赋值操作,但允许临时右赋值和构造赋值。

    3.在容器中保存指针是安全的

    4.unique_ptr可以指向一个数组

    5.unique_ptr需要指定删除器的类型

    shared_ptr特性

    使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构时,被托管的内存才会释放。

    shared_ptr共享管理的对象,同一时刻可以有多个shared_ptr拥有对象的所有权。

    shared_ptr 的小问题:当有两个对象中成员变量都使用了一个shared_ptr指针,并将shared_ptr指针都指向了对方,会造成循环引用,使引用计数失效,从而导致内存泄漏

    1. /*
    2. 1. 不能使用原始指针初始化多个shared_ptr
    3. 2. 函数实参中不要创建shared_ptr指针 // 因为函数参数的计算顺序不一定每次相同,先定义后使用
    4. 3. 不要通过shared_from_this()返回this指针 // 可能导致重复析构
    5. */
    6. int *ptr = new int;
    7. shared_ptr<int> p1(ptr);
    8. shared_ptr<int> p2(ptr); // 出错
    9. /// 可能顺序:先new一个int,然后去调用g()函数,shared_ptr指针则不能实现对int的托管,造成了内存泄露
    10. func(shared_ptr<int>(new int), g()); // 可能会出问题

    weak_ptr特性

    1. waek_ptr 设计的目的是为了协助shared_ptr工作

    2. weak_ptr 可以从一个shared_ptr或weak_ptr对象构造

    3. weak_ptr 构造和析构不会引起计数的增加或减少

    1. // use_count()函数引用计数问题
    2. shared_ptr<int> sp(new int(10));
    3. weak_ptr<int> wp(sp); // 定义weak_ptr,并用sp进行初始化
    4. cout << wp.use_count() << endl; // 输出结果为 1
    5. // expired()指针有效性问题
    6. shared_ptr<int> sp(new int(10));
    7. weak_ptr<int> wp(sp);
    8. if (wp.expired()) {
    9. cout << "weak_ptr无效,内存已经释放" << endl;
    10. }
    11. else {
    12. cout << "weak_ptr有效,内存未释放" << endl;
    13. }
    14. // lock()获取托管内存
    15. auto sp = make_shared<int>(42);
    16. weak_ptr<int> wp = sp;
    17. auto spt = wp.lock(); // 获取shared_ptr
    18. if (wp.expired()) {
    19. cout << "wp有效" << *spt << endl; // 输出内容
    20. }

    weak_ptr解决shared_ptr中的循环引用问题

    shared_ptr在两个对象中的同一成员变量同时使用了一个shared_ptr,并将shared_ptr指向对方时,会造成循环引用,使引用计数失效,从而造成内存泄漏。

    只需要将两个对象中的其中一个使用的shared_ptr换成weak_ptr就可解决问题

    1. #include
    2. #include
    3. using namespace std;
    4. class A;
    5. class B;
    6. class A {
    7. public:
    8. std::weak_ptr bptr; // 修改为weak_ptr
    9. ~A() {
    10. cout << "A is deleted" << endl;
    11. }
    12. };
    13. class B {
    14. public:
    15. std::shared_ptr aptr;

  • 相关阅读:
    VSCODE 打开多个文件夹
    Python 序列化与反序列化(pickle 标准库的使用)
    Java语法之多态
    Linux中修改环境变量的几种方法比较分析
    【Vuex+ElementUI】Vuex中取值存值以及异步加载的使用
    RabbitMQ实战宝典:从新手到专家的全面探索
    怎样使用 NFTScan Solana API 快速创建 NFT 应用程序?
    时间复杂度吐血总结
    Github每日精选(第71期):自动网页抓取和浏览crawlee
    LeetCode每日一题(2216. Minimum Deletions to Make Array Beautiful)
  • 原文地址:https://blog.csdn.net/qq_43598960/article/details/132889116