• STL标准库之智能指针


    RAII机制

    RAII即Resource Acquisition Is Initialization(资源获取即初始化),RAII是C++语法体系中的一种常用的合理管理资源避免出现内存泄漏的常用方法。用对象管理资源,利用C++构造的对象最终会被对象的析构函数销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。

    unique_ptr

    注意事项

    1. 使用智能指针时避免手动释放资源,否则自动释放资源时候会出现重复释放问题
    2. 如果使用了release()接口获取原始指针,注意需要自己手动释放资源
    3. 作为函数参数按值传递时,注意所有权的转移
    4. 不支持拷贝构造和拷贝赋值,可以使用移动构造和移动赋值
    5. 当使用const修饰时,独占的资源不可以修改

    基本用法

    接口返回值说明
    get()原始指针类型获取智能指针管理的原始指针类型
    reset()释放资源并将指针置空,或释放原始资源然后接管新资源
    release()原始指针类型返回原始指针类型,然后释放对原始指针的所有权
    swap()交换两个指针的所有权
    //测试类
    class  MyClass
    {
    public:
        MyClass()
        {
            std::cout<<"structure pointer." << std::endl;
        };
        
        MyClass(const std::string &str)
        : m_strName(str)
        {
            std::cout<<"structure " << m_strName << " pointer." << std::endl;
        };
        ~MyClass()
        {
            std::cout<<"destructed " << m_strName << " pointer." << std::endl;
        };
        void doSomething()
        {
            std::cout << m_strName << " doSomething"<<std::endl;
        }
    
        void setName(const std::string& strName)
        {
            m_strName = strName;
        }
    	void setFriend(std::shared_ptr<MyClass> pFriend)
        {
            m_friend = pFriend;
        }
    
    private:
        std::string m_strName;
        //如果此处为shared_ptr则两个MyClass对象循环引用的话则无法正常释放资源
        //如果为weak_ptr则可以正常释放资源
        std::weak_ptr<MyClass> m_friend;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    //支持移动构造和移动赋值,但是不支持拷贝构造和赋值构造函数
    //1、使用make_unique创建对象
    std::unique_ptr<MyClass> pUniquePtr1 = std::make_unique<MyClass>("class01");
    
    //2、使用new创建对象
    std::unique_ptr<MyClass> pUniquePtr2(new MyClass("class02"));
    
    MyClass *pTr = new MyClass("class03");
    std::unique_ptr<MyClass> pUniquePtr3(pTr);
    
    //3、使用移动语句构造对象后会将原有指针的所有权转交给新指针,原指针变为空指针
    std::unique_ptr<MyClass> pUniquePtr4(std::move(pUniquePtr2));
    pUniquePtr4->setName("class04");
    //puniquePtr2所有权已经转交给pUniquePtr4,自己已经为空指针,再调用其接口会异常
    std::cout << "pUniquePtr2=" << std::hex << pUniquePtr2.get() << std::endl;
    //pUniquePtr2->setName("class02");
    
    //4、使用移动赋值语句
    std::unique_ptr<MyClass> pUniquePtr5;
    std::cout << "pUniquePtr1=" << std::hex << pUniquePtr1.get() << std::endl; //指针不为空,可以打印出其地址
    std::cout << "pUniquePtr5=" << std::hex << pUniquePtr5.get() << std::endl; //指针为空,地址为0
    //执行此语句后,pUniquePtr1的所有权会转交给pUniquePtr5,自身变为空指针   
    pUniquePtr5 = std::move(pUniquePtr1);
    std::cout << "pUniquePtr1=" << std::hex << pUniquePtr1.get() << std::endl; //指针所有权转移,其变为空指针地址为0
    std::cout << "pUniquePtr5=" << std::hex << pUniquePtr5.get() << std::endl; //指针地址变为pUniquePtr1的地址。
    
    //标准库直接将以下两种函数删除了
    //5、不能使用拷贝构造函数,会报错
    //std::unique_ptr pUniquePtr3(pUniquePtr1);
    
    //6、不能使用赋值构造函数,会报错
    //std::unique_ptr pUniquePtr4 = nullptr;
    //pUniquePtr4 = pUniquePtr2;
    
    //7、使用get()接口可以获取原始指针类型。
    std::cout << typeid((pUniquePtr3.get())).name() << std::endl;
    
    //8、使用release()接口可以获取原始指针类型,但同时会放弃对原始指针的所有权,且原始资源需要自己主动释放,智能指针不会自动释放资源。
    std::cout << "pUniquePtr3 = " << std::hex << pUniquePtr3.release() << std::endl;
    std::cout << "after call release:" << std::hex << pUniquePtr3.get() << std::endl;
    
    //9、智能指针可以像原始指针一样使用*解引用,使用->访问
    (*pUniquePtr5).doSomething();
    pUniquePtr5->doSomething();
    
    //10、使用reset()接口替换原始资源,会先释放原始资源,然后接管新资源的所有权
    pUniquePtr5.reset(new MyClass("class06"));
    
    //11、 分配数组
    std::unique_ptr<MyClass[]> pUniquePtr6(new MyClass[3]{{"class07"}, {"class08"}, {"class09"}});
    //使用方括号访问资源
    pUniquePtr6[0].doSomething();
    
    //12、自定义删除器
    auto funcDel = [](MyClass *ptr){
        std::cout << "customer delete." << std::endl;
        delete []ptr;
    };
    std::unique_ptr<MyClass, void(*)(MyClass *ptr)> pUniquePtr7(new MyClass[2]{{"class10"}, {"class11"}}, funcDel);
    
    //13、使用swap交换两个智能指针中的原始资源
    std::unique_ptr<MyClass> pUniquePtr8(new MyClass("class swap"));
    pUniquePtr8.swap(pUniquePtr5);
    pUniquePtr8->doSomething();
    pUniquePtr5->doSomething();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    使用场景

    1. 作为函数局部变量(最常用)
      函数内或局部作用域中使用,在离开作用域后会自动释放资源,有效避免资源泄露
    int main()
    {		
    	//离开大括号时资源会被自动释放掉
    	{
    		std::unique_ptr<MyClass> pTemp = std::make_unique<MyClass>("temp pointer");
    		pTemp->doSomething();
    	}
    	std::cout<< "pTemp have been released" << std::endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 作为函数参数按值传递
    //按值方式传递指针,此时形参会拥有实参的所有权,在函数外再次调研会出现异常
    void passByValue(std::unique_ptr<MyClass> ptr)
    {
        ptr->doSomething();
        ptr->setName("pass by value");
        //函数执行完ptr接管的资源会被自动释放掉
    }
    
    int main()
    {
    	//按值传递智能指针参数
        std::unique_ptr<MyClass> pUniquePtr9 = std::make_unique<MyClass>("create pass by value pointer");
        passByValue(std::move(pUniquePtr9));
        //此处会崩溃,指针所有权已经转移到函数内并被释放了
        //pUniquePtr9->doSomething();
       
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 作为函数参数按引用传递
    //按引用方式传递指针,
    void passByReference(std::unique_ptr<MyClass> &ptr)
    {
        ptr->doSomething();
        ptr->setName("pass by reference");
        //此处不会对资源进行释放
    }
    
    int main()
    {
    	//按引用传递智能指针参数
        std::unique_ptr<MyClass> pUniquePtr10 = std::make_unique<MyClass>("create pass by reference pointer");
        passByReference(pUniquePtr10);
        pUniquePtr10->doSomething();
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 函数返回智能指针对象(不常用)
    //函数返回智能指针
    std::unique_ptr<MyClass> getUniquePtr()
    {
        std::unique_ptr<MyClass> tmpPtr = std::make_unique<MyClass>("local unique pointer");
        return tmpPtr;
    }
    
    int main()
    {
    	//函数内创建智能指针并将其作为函数返回值
        getUniquePtr()->doSomething();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    shared_ptr

    注意事项

    1. 避免循环引用的现象出现。可使用weak_ptr来进行解决。

    基本用法

    接口返回值说明
    get()原始指针类型获取智能指针管理的原始指针类型
    reset()释放资源并将指针置空,或释放原始资源(引用计数-1)然后接管新资源
    use_count()整形值返回原始指针的引用计数
    swap()交换两个指针的所有权
    //1、创建空的指针
    std::shared_ptr<MyClass> pSharedPtr1;
    std::cout << "pSharedPtr1 address = " << std::hex << pSharedPtr1.get() << std::endl;
    
    //2、使用make_shared创建指针
    std::shared_ptr<MyClass> pSharedPtr2 = std::make_shared<MyClass>("make_shared pointer");
    std::cout << "pSharedPtr2 address = " << std::hex << pSharedPtr2.get() << std::endl;
    
    //3、使用new创建指针
    std::shared_ptr<MyClass> pSharedPtr3(new MyClass("use new create pointer"));
    std::cout << "pSharedPtr3 address = " << std::hex << pSharedPtr3.get() << std::endl;
    std::cout << "pSharedPtr3 use_count = " << pSharedPtr3.use_count() << std::endl;
    
    //4、使用拷贝构造
    std::shared_ptr<MyClass> pSharedPtr4(pSharedPtr3);
    std::cout << "pSharedPtr4 address = " << std::hex << pSharedPtr4.get() << std::endl;
    std::cout << "pSharedPtr4 use_count = " << pSharedPtr4.use_count() << std::endl;
    
    //5、使用拷贝赋值
    std::shared_ptr<MyClass> pSharedPtr5 = pSharedPtr4;
    std::cout << "pSharedPtr5 address = " << std::hex << pSharedPtr5.get() << std::endl;
    std::cout << "pSharedPtr5 use_count = " << pSharedPtr5.use_count() << std::endl;
    
    //6、移动构造,此时pSharedPtr5失去控制权,并转移到pSharedPtr6中引用计数不会增加
    std::shared_ptr<MyClass> pSharedPtr6(std::move(pSharedPtr5));
    std::cout << "pSharedPtr6 address = " << std::hex << pSharedPtr6.get() << std::endl;
    std::cout << "pSharedPtr6 use_count = " << pSharedPtr6.use_count() << std::endl;
    
    //7、移动赋值,此时pSharedPtr6失去控制权,并转移到pSharedPtr7中,引用计数不会增加
    std::shared_ptr<MyClass> pSharedPtr7 = std::move(pSharedPtr6);
    std::cout << "pSharedPtr7 address = " << std::hex << pSharedPtr7.get() << std::endl;
    std::cout << "pSharedPtr7 use_count = " << pSharedPtr7.use_count() << std::endl;
    
    //8、使用*进行解引用和使用->访问成员函数
    (*pSharedPtr7).setName("use * dereference");
    pSharedPtr7->doSomething();
    
    //9、检查指针是否为空
    if (pSharedPtr1)
    {
        std::cout << "pSharedPtr1 is not null!" << std::endl;
    }
    else
    {
        std::cout << "pSharedPtr1 is null!" << std::endl;
    }
    
    //10、使用swap()接口交换两个指针的管理
    pSharedPtr2.swap(pSharedPtr7);
    std::cout << "pSharedPtr2 address = " << std::hex << pSharedPtr2.get() << std::endl;
    std::cout << "pSharedPtr2 use_count = " << pSharedPtr2.use_count() << std::endl;
    std::cout << "pSharedPtr7 address = " << std::hex << pSharedPtr7.get() << std::endl;
    std::cout << "pSharedPtr7 use_count = " << pSharedPtr7.use_count() << std::endl;
    
    //11、使用reset()接口接管新对象, 此时pSharedPtr7的引用计数为1, 原管理对象的引用计数-1
    pSharedPtr7.reset(new MyClass("swap"));
    std::cout << "pSharedPtr7 address = " << std::hex << pSharedPtr7.get() << std::endl;
    std::cout << "pSharedPtr7 use_count = " << pSharedPtr7.use_count() << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    使用场景

    1. 作为函数局部变量(最常用)
      函数内或局部作用域中使用,在离开作用域后会自动释放资源,有效避免资源泄露
    int main()
    {		
    	//离开大括号时资源会被自动释放掉
    	{
    		std::shared_ptr<MyClass> pTemp = std::make_shared<MyClass>("temp pointer");
    		pTemp->doSomething();
    	}
    	std::cout<< "pTemp have been released" << std::endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 作为函数参数按值传递
    //按值方式传递指针
    void passByValue(std::shared_ptr<MyClass> ptr)
    {
        std::cout << "ptr address = " << std::hex << ptr.get() << std::endl;
        std::cout << "ptr use_count = " << ptr.use_count() << std::endl;
        ptr->doSomething();
        ptr->setName("func pass by value");
    }
    int main()
    {
        //按值传递到函数引用计数会+1
        std::shared_ptr<MyClass> pSharedPtr(new MyClass("pass by value"));
        std::cout << "pSharedPtr address = " << std::hex << pSharedPtr.get() << std::endl;
        std::cout << "pSharedPtr use_count = " << pSharedPtr.use_count() << std::endl;
        passByValue(pSharedPtr);
        std::cout << "pSharedPtr address = " << std::hex << pSharedPtr.get() << std::endl;
        std::cout << "pSharedPtr use_count = " << pSharedPtr.use_count() << std::endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 作为函数参数按引用传递
    //按引用方式传递指针,
    void passByReference(std::shared_ptr<MyClass> &ptr)
    {
        std::cout << "ptr address = " << std::hex << ptr.get() << std::endl;
        std::cout << "ptr use_count = " << ptr.use_count() << std::endl;
        ptr->doSomething();
        ptr->setName("func pass by reference");
    }
    int main()
    {
        //按引用传递到函数内引用计数不会+1
        std::shared_ptr<MyClass> pSharedPtr(new MyClass("pass by reference"));
        std::cout << "pSharedPtr address = " << std::hex << pSharedPtr.get() << std::endl;
        std::cout << "pSharedPtr use_count = " << pSharedPtr.use_count() << std::endl;
        passByReference(pSharedPtr);
        std::cout << "pSharedPtr address = " << std::hex << pSharedPtr.get() << std::endl;
        std::cout << "pSharedPtr use_count = " << pSharedPtr.use_count() << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 作为函数返回值
    //函数返回智能指针
    std::shared_ptr<MyClass> getSharedPtr()
    {
        std::shared_ptr<MyClass> tmpPtr = std::make_shared<MyClass>("local shared pointer");
        std::cout << "tmpPtr address = " << std::hex << tmpPtr.get() << std::endl;
        std::cout << "tmpPtr use_count = " << tmpPtr.use_count() << std::endl;
        return tmpPtr;
    }
    int main()
    {
        //函数返回值是智能指针,每次调用结束都会释放一次
        getSharedPtr()->doSomething();
        std::cout << "getSharedPtr() address = " << std::hex << getSharedPtr().get() << std::endl;
        std::cout << "getSharedPtr() use_count = " << getSharedPtr().use_count() << std::endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    weak_ptr

    注意事项

    1. 不可以单独存在
    2. 不具有原始指针的内存所有权
    3. 不能使用->和*进行访问和解引用
    4. 主要应用于解决shared_ptr循环引用问题

    基本用法

    接口返回值说明
    reset()释放资源并将指针置空
    use_count()整形值返回原始指针的引用计数
    swap()交换两个指针的所有权
    expired()布尔值判断指针是否有效
    lock()shared_ptr对象将weak_ptr变化为shared_ptr
    //1、创建空的指针
    std::weak_ptr<MyClass> pWeakPtr1;
    std::cout << "pWeakPtr1 use_count = " << pWeakPtr1.use_count() << std::endl;
    
    //2、使用shared_ptr构造
    std::shared_ptr<MyClass> pSharedPtr1 = std::make_shared<MyClass>("shared");
    std::weak_ptr<MyClass> pWeakPtr2(pSharedPtr1);
    std::cout << "pWeakPtr2 use_count = " << pWeakPtr2.use_count() << std::endl;
    
    //3、拷贝构造
    std::weak_ptr<MyClass> pWeakPtr3(pWeakPtr2);
    std::cout << "pWeakPtr3 use_count = " << pWeakPtr3.use_count() << std::endl;
    
    //4、拷贝赋值
    std::weak_ptr<MyClass> pWeakPtr4 = pWeakPtr3;
    std::cout << "pWeakPtr4 use_count = " << pWeakPtr4.use_count() << std::endl;
    
    //5、移动构造
    std::weak_ptr<MyClass> pWeakPtr5(std::move(pWeakPtr4));
    std::cout << "pWeakPtr4 use_count = " << pWeakPtr4.use_count() << std::endl;
    std::cout << "pWeakPtr5 use_count = " << pWeakPtr5.use_count() << std::endl;
    
    //6、移动赋值
    std::weak_ptr<MyClass> pWeakPtr6 = std::move(pWeakPtr5);
    std::cout << "pWeakPtr5 use_count = " << pWeakPtr5.use_count() << std::endl;
    std::cout << "pWeakPtr6 use_count = " << pWeakPtr6.use_count() << std::endl;
    
    //7、不允许使用->和*,编译器会报错
    //pWeakPtr6->doSomething();
    //(*pWeekPtr6).doSomething();
    
    //8、使用lock()接口将其转化为shared_ptr
    pWeakPtr6.lock()->setName("lock()");
    pWeakPtr6.lock()->doSomething();
    
    //9、使用reset()接口释放所有权
    pWeakPtr6.reset();
    std::cout << "pWeakPtr6 use_count = " << pWeakPtr6.use_count() << std::endl;
    
    //10、使用expired()检查原始指针是否被销毁
    if(pWeakPtr6.expired())
    {
        std::cout << "pWeakPtr6 is invalid!" << std::endl;
    }
    
    //12、交换两个指针所有权
    std::shared_ptr<MyClass> pSharedPtr2 = std::make_shared<MyClass>("swap");
    std::weak_ptr<MyClass> pWeakPtr7(pSharedPtr2);
    pWeakPtr7.swap(pWeakPtr2);
    pWeakPtr7.lock()->doSomething();
    pWeakPtr2.lock()->doSomething();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    使用场景

    1. 解决shared_ptr循环引用问题
    int main()
    {
    	//13、循环引用问题,如果MyClass中的指针为shared_ptr的话会出现最后资源无法释放,使用weak_ptr则不会有此问题
        std::shared_ptr<MyClass> pSharedPtr3 = std::make_shared<MyClass>("object03");
        std::shared_ptr<MyClass> pSharedPtr4 = std::make_shared<MyClass>("object04");
        pSharedPtr3->setFriend(pSharedPtr4);
        pSharedPtr4->setFriend(pSharedPtr3);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    JAVA IDEA 下载
    laravel的默认首页怎么改-laravel框架默认欢迎页面如何修改
    无敌了!Redis进军磁盘存储!
    VSCode学习笔记一:添加代码模板
    Awake()、OnEnable()、Start()、Reset()、OnDisable()、OnDestroy()、OnValidate()
    SpringMVC入门案例和@RequestMapping注解
    深入理解死锁问题
    ElasticSearch - 分词器介绍及中文分词器es-ik安装
    官宣|Apache Flink 1.16 发布公告
    Delphi中的匿名方法
  • 原文地址:https://blog.csdn.net/qq_35699583/article/details/132896198