C++11智能指针

简单地说,智能指针是用对象去管理一个资源指针,同时用一个计数器计算引用当前指针对象的个数,当管理指针的对象增加或减少时,计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也对指针管理对象所管理的指针进行delete操作。

下面我们介绍两个常用的智能指针std::shared_ptr和std::weak_ptr。

1.1 std::shared_ptr

std::shared_ptr包装了new操作符动态分配的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。

下面是一个代码例子:

 1#include <memory>
 2#include <iostream>
 3class Test
 4{
 5public:
 6    Test()
 7    {
 8        std::cout << "Test()" << std::endl;
 9    }
10    ~Test()
11    {
12        std::cout << "~Test()" << std::endl;
13    }
14};
15int main()
16{
17    std::shared_ptr<Test> p1 = std::make_shared<Test>();
18    std::cout << "1 ref:" << p1.use_count() << std::endl;
19    {
20        std::shared_ptr<Test> p2 = p1;
21        std::cout << "2 ref:" << p1.use_count() << std::endl;
22    }
23    std::cout << "3 ref:" << p1.use_count() << std::endl;
24    return 0;
25}

结果如下:

1Test()
21 ref:1
32 ref:2
43 ref:1
5~Test()

针对代码解读如下:

  • std::make_shared里面调用了new操作符分配内存;

  • 第二个p1.use_count()之所以显示为2,是因为增加了引用对象p2,而随着大括号的结束,p2的作用域结束,所以p1的引用计数变回1,而随着main函数的结束,p1的作用域结束,此时检测到计数为1,那就会在销毁p1的同时,调用p1的析构函数delete掉之前分配的内存空间;

1.2 std::weak_ptr

std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值的时候,不会引起智能指针计数增加。

weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。如要操作资源,则必须使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。

1.2.1 std::shared_ptr相互引用会有什么后果

代码如下:

 1#include <memory>
 2#include <iostream>
 3class TestB;
 4class TestA
 5{
 6public:
 7    TestA()
 8    {
 9        std::cout << "TestA()" << std::endl;
10    }
11    void ReferTestB(std::shared_ptr<TestB> test_ptr)
12    {
13        m_TestB_Ptr = test_ptr;
14    }
15    ~TestA()
16    {
17        std::cout << "~TestA()" << std::endl;
18    }
19private:
20    std::shared_ptr<TestB> m_TestB_Ptr; //TestB的智能指针
21}; 
22class TestB
23{
24public:
25    TestB()
26    {
27        std::cout << "TestB()" << std::endl;
28    }
29    void ReferTestB(std::shared_ptr<TestA> test_ptr)
30    {
31        m_TestA_Ptr = test_ptr;
32    }
33    ~TestB()
34    {
35        std::cout << "~TestB()" << std::endl;
36    }
37    std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
38};
39int main()
40{
41    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
42    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
43    ptr_a->ReferTestB(ptr_b);
44    ptr_b->ReferTestB(ptr_a);
45    return 0;
46}

运行结果:

1TestA()
2TestB()

可以看到,上面代码中,我们创建了一个TestA和一个TestB的对象,但在整个main函数都运行完后,都没看到两个对象被析构,这是为什么呢?

原来,智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。

这等效于说:

  • ptr_a对ptr_b说,哎,我说ptr_b,我现在的条件是,你先释放我,我才能释放你,这是天生的,造物者决定的,改不了;

  • ptr_b也对ptr_a说,我的条件也是一样,你先释放我,我才能释放你,怎么办?

是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。

1.2.2 std::weak_ptr如何解决相互引用的问题

我们在上面的代码基础上使用std::weak_ptr进行修改,如下:

 1#include <iostream>
 2#include <memory>
 3class TestB;
 4class TestA
 5{
 6public:
 7    TestA()
 8    {
 9        std::cout << "TestA()" << std::endl;
10    }
11    void ReferTestB(std::shared_ptr<TestB> test_ptr)
12    {
13        m_TestB_Ptr = test_ptr;
14    }
15    void TestWork()
16    {
17        std::cout << "~TestA::TestWork()" << std::endl;
18    }
19    ~TestA()
20    {
21        std::cout << "~TestA()" << std::endl;
22    }
23private:
24    std::weak_ptr<TestB> m_TestB_Ptr;
25};
26class TestB
27{
28public:
29    TestB()
30    {
31        std::cout << "TestB()" << std::endl;
32    }
33    void ReferTestB(std::shared_ptr<TestA> test_ptr)
34    {
35        m_TestA_Ptr = test_ptr;
36    }
37    void TestWork()
38    {
39        std::cout << "~TestB::TestWork()" << std::endl;
40    }
41    ~TestB()
42    {
43        把std::weak_ptr类型转换成std::shared_ptr类型
44        std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
45        tmp->TestWork();
46        std::cout << "2 ref a:" << tmp.use_count() << std::endl;
47        std::cout << "~TestB()" << std::endl;
48    }
49    std::weak_ptr<TestA> m_TestA_Ptr;
50};
51int main()
52{
53    std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
54    std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
55    ptr_a->ReferTestB(ptr_b);
56    ptr_b->ReferTestB(ptr_a);
57    std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;
58    std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;
59    return 0;
60}

运行结果:

1TestA()
2TestB()
31 ref a:1
41 ref b:1
5~TestA::TestWork()
62 ref a:2
7~TestB()
8~TestA()

由以上代码运行结果我们可以看到:

  • 所有的对象最后都能正常释放,不会存在上一个例子中的内存没有释放的问题;

  • ptr_a和ptr_b在main函数中退出前,引用计数均为1,也就是说,在TestA和TestB中对std::weak_ptr的相互引用,不会导致计数的增加。在TestB析构函数中,调用std::shared_ptr

    tmp = m_TestA_Ptr.lock(),把std::weak_ptr类型转换成std::shared_ptr类型,然后对TestA对象进行调用。

1.2.3 std::weak_ptr支持的调用

1weak_ptr<T> w;    //空weak_ptr可以指向类型为T的对象
2weak_ptr<T> w(shared_ptr sp);    //与sp指向相同对象的weak_ptr, T必须能转换为sp指向的类型
3w = p;    //p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
4w.reset();    //weak_ptr置为空
5w.use_count();    //与w共享对象的shared_ptr的计数
6w.expired();    //w.use_count()为0则返回true,否则返回false
7w.lock();    //w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr

1.3 std::unique_ptr

uniqut_ptr是一种对资源具有排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向。

1.3.1 初始化方式

  • 使用new

1T *pT = new T();
2std::unique_ptr<T> up1(pT);
  • 通过make_unique

1auto pT = make_unique<T>();
  • 通过move()函数

1//up也是一个std::unique_ptr<T>指针
2unique_ptr<T> up1 = std::move(up); 

1.3.2 unique_ptr不能被复制或者拷贝

1unique_ptr<T> up(new T()); //ok
2unique_ptr<T> up1(up); //error, can not be copy
3unique_ptr<T> up2 = up; //error, can not be assigned

1.3.3 unique_ptr可以移动赋值或者移动拷贝

1unique_ptr<T> pT(new T());
2unique_ptr<T> pT2 = std::move(pT);    //移动赋值,此时pT被销毁,为空
3unique_ptr<T> pT3(std::move(pt2)); //移动拷贝,此时pT2被销毁,为空

1.3.4 unique_ptr可以作为函数的返回值

1unique_ptr<T> GetPtr(); //function getthe unique pointer
2unique_ptr<T> pT = GetPtr(); // ok

1.3.5 使用范例

1#include <iostream>
2int main()
3{
4    std::unique_ptr<int> pInt;
5    pInt.reset(new int());
6    int *p = pInt.release(); //释放所有权
7    //由于unique_ptr有std::unique_ptr<T[]>的重载函数,所以它可以用来管理数组资源
8    std::unique_ptr<int[]> pArray(new int[3]{1,3,3}); 
9}

2. 智能指针小结

可以看出,智能指针其实是std::shared_ptr和std::unique_ptr, std::shared_ptr可以有多个引用对象,但不能互相引用,而std::unique_ptr只能有一个引用,不能赋值或者拷贝,但可以移动赋值和移动拷贝,std::weak_ptr实际上是对std::shared_ptr的补充,它并不能对对象进行具体的操作。

注意:shared_ptr,weak_ptr,unique_ptr这些都是模板,并不是指针或者其他的。

转载:公众号 cpp加油站

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值