• 12、使用智能指针注意3个问题


    一、不能使用一个原始地址初始化多个共享智能指针

      如下代码

    #include "stdafx.h"
    #include <iostream>
    #include <windows.h>
    #include <memory>
    
    using namespace std;
    
    struct Test
    {
        shared_ptr<Test> getSharedPtr()
        {
            return shared_ptr<Test>(this);
        }
    
        ~Test()
        {
            cout << "class Test is disstruct ..." << endl;
        }
    
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
        Test* pTest = new Test;
    
        shared_ptr<Test> sp1(pTest);
        shared_ptr<Test> sp2(pTest);
    }

    以上代码编译时不会出现错误,但是运行的时候会出错,运行结果:

      

     注意,我们只在堆上申请了一块内存,但是却释放了2次,所以报错。

    二、函数不能返回管理了this的共享智能指针对象

    如下代码

    #include <iostream>
    #include <memory>
    using namespace std;
    
    struct Test
    {
        shared_ptr<Test> getSharedPtr()
        {
            return shared_ptr<Test>(this);
        }
        
        ~Test()
        {
            cout << "class Test is disstruct ..." << endl;
        }
    
    };
    
    int main() 
    {
        shared_ptr<Test> sp1(new Test);
        cout << "use_count: " << sp1.use_count() << endl;
        shared_ptr<Test> sp2 = sp1->getSharedPtr();
        cout << "use_count: " << sp1.use_count() << endl;
        return 0;
    }

     这种写法其实和第一种是一样的,用new Test匿名对象初始化了sp1,然后调用了getSharedPtr()方法初始化sp2,此时sp1管理的是this,sp2也管理的是this,

    相当于1中的用1块原始指针初始化了2个智能指针, 这时程序释放这块堆空间时,就会报错。此时可以使用weak_ptr来解决此问题。

      通过输出的结果可以看到一个对象被析构了两次,其原因是这样的:在这个例子中使用同一个指针 this 构造了两个智能指针对象 sp1 和 sp2,这二者之间是没有任何关系的,因为 sp2 并不是通过 sp1 初始化得到的实例对象。在离开作用域之后 this 将被构造的两个智能指针各自析构,导致重复析构的错误。

    这个问题可以通过 weak_ptr 来解决,通过 wek_ptr 返回管理 this 资源的共享智能指针对象 shared_ptr。C++11 中为我们提供了一个模板类叫做 std::enable_shared_from_this<T>,这个类中有一个方法叫做 shared_from_this(),通过这个方法可以返回一个共享智能指针,在函数的内部就是使用 weak_ptr 来监测 this 对象,并通过调用 weak_ptr 的 lock() 方法返回一个 shared_ptr 对象。

    代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <windows.h>
    #include <memory>
    
    using namespace std;
    
    struct Test : public enable_shared_from_this<Test>
    {
        shared_ptr<Test> getSharedPtr()
        {
            return shared_from_this();
        }
    
        ~Test()
        {
            cout << "class Test is disstruct ..." << endl;
        }
    
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
        Test* pTest = new Test;
    
        shared_ptr<Test> sp1(pTest);
        shared_ptr<Test> sp2 = sp1->getSharedPtr();
    }

    三、共享指针不能循环引用

     智能指针如果循环引用会导致内存泄露,比如下面的例子:

    #include "stdafx.h"
    #include <iostream>
    #include <windows.h>
    #include <memory>
    
    using namespace std;
    
    struct TA;
    struct TB;
    
    struct TA
    {
        shared_ptr<TB> bptr;
        ~TA()
        {
            cout << "class TA is disstruct ..." << endl;
        }
    };
    
    struct TB
    {
        shared_ptr<TA> aptr;
        ~TB()
        {
            cout << "class TB is disstruct ..." << endl;
        }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        shared_ptr<TA> ap(new TA);
        shared_ptr<TB> bp(new TB);
    
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
    
        //当加上这2句,ap和bp的引用计数就一直是2,TA和TB就不会析构
        ap->bptr = bp;
        bp->aptr = ap;
    
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
    }

    结果:

      在测试程序中,共享智能指针 ap、bp 对 TA、TB 实例对象的引用计数变为 2,在共享智能指针离开作用域之后引用计数只能减为1,这种情况下不会去删除智能指针管理的内存,导致类 TA、TB 的实例对象不能被析构,最终造成内存泄露。通过使用 weak_ptr 可以解决这个问题,只要将类 TA 或者 TB 的任意一个成员改为 weak_ptr,修改之后的代码如下:

    #include "stdafx.h"
    #include <iostream>
    #include <windows.h>
    #include <memory>
    
    using namespace std;
    
    struct TA;
    struct TB;
    
    struct TA
    {
        weak_ptr<TB> bptr;
        ~TA()
        {
            cout << "class TA is disstruct ..." << endl;
        }
    };
    
    struct TB
    {
        shared_ptr<TA> aptr;
        ~TB()
        {
            cout << "class TB is disstruct ..." << endl;
        }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        shared_ptr<TA> ap(new TA);
        shared_ptr<TB> bp(new TB);
    
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
    
        //当加上这2句,ap和bp的引用计数就一直是2,TA和TB就不会析构
        ap->bptr = bp;
        bp->aptr = ap;
    
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
    }

    结果:

     通过输出的结果可以看到类 TA 或者 TB 的对象被成功析构了。

      上面程序中,在对类 TA 成员赋值时 ap->bptr = bp; 由于 bptr 是 weak_ptr 类型,这个赋值操作并不会增加引用计数,所以 bp 的引用计数仍然为 1,在离开作用域之后 bp 的引用计数减为 0,类 TB 的实例对象被析构。

    在类 TB 的实例对象被析构的时候,内部的 aptr 也被析构,其对 TA 对象的管理解除,内存的引用计数减为 1,当共享智能指针 ap 离开作用域之后,对 TA 对象的管理也解除了,内存的引用计数减为 0,类 TA 的实例对象被析构。

    111
  • 相关阅读:
    测量MySQL的表达式和函数的速度
    MySQL中的比较操作符<=>
    Python中的args和kwargs
    MySQL8新特性(2)--mysql的升级过程
    MySQL8新特性(1)--原子DDL
    PostgreSQL中的一些日志
    PostgreSQL的表空间
    [九]基础数据类型之Boolean详解
    [八]基础数据类型之Double详解
    [七]基础数据类型之Float详解
  • 原文地址:https://www.cnblogs.com/zwj-199306231519/p/15144204.html
Copyright © 2020-2023  润新知