C++ 资源管理(RAII)--智能指针

1. 智能指针(Smart Pointer)

          i. 是存储指向动态分配()对象指针的类

          ii. 在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象

          iii. RAII类模拟智能指针,见备注

2. C++11提供了以下几种智能指针,位于头文件<memory>,它们都是模板类

          i. std::auto_ptr(复制/赋值)

          ii. std::unique_ptr  c++11

          iii.std::shared_ptr  c++11

          iv.std::weak_ptr    c++11

          g++ -std=c++11 xx.cc

3. std::auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象

4. std::auto_ptr要求其对“裸”指针的完全占有性 -> 在拷贝构造或赋值操作时,会发生所有权的转移

5. 本身存在缺陷

6. std::unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:

           i. 拥有它所指向的对象

           ii. 无法进行复制、赋值操作(例题)

           iii.保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象

           iv.具有移动(std::move)语义,可做为容器元素

7. std::shared_ptr是一个引用计数智能指针,用于共享对象的所有权

           i. 引进了一个计数器shared_count,用来表示当前有多少个智能指针对象共享指针指向的内存块

           ii. 析构函数中不是直接释放指针对应的内存块,如果shared_count大于0则不释放内存只是将引用计数减1,只是计数等于0时释放内存

           iii. 复制构造与赋值操作符只是提供一般意义上的复制功能,并且将引用计数加1.

           iv. 在堆内存中只有一份申请的资源,但是有好几个对象都是指向这个内存的资源的。

           v. Shared_ptr的功能就是让不具有值语义的对象拥有值语义,假如说一个对象本身是不希望被复制的,但是要是把这个对象交给shared_ptr管理的时候,使用这个指针的时候就可以对对象进行复制,实际不是真实的对象复制,而是通过一个引用计数的方式去使用

8. 问题:会有一个问题就是循环引用,会导致内存泄漏。

9. std::shared_ptr是强引用智能指针

10. std::weak_ptr 是弱引用智能指针

11. 强引用,只要有一个引用存在,对象就不能被释放

12. 弱引用,并不增加对象的引用计数,但它知道对象是否存在。如果存在,提升为shared_ptr成功;否则,提升失败

13. 通过weak_ptr访问对象的成员的时候,要提升为shared_ptr

 

 1 auto_ptr.cc
 2 
 3 #include<iostream>
 4 #include<memory>
 5 
 6 int main(void)
 7 {
 8     double *pd = new double(7.77);
 9     std::auto_ptr<double> apd(pd);  //apd本身是个对象,因为它重载了星号访问运算符,所以可以加* 用。
10     //或者std::auto_ptr<double> apd(new double(7.77));
11 
12     std::cout << “*apd=” << *apd <<std::endl;
13     //通过* 去访问的时候,就相当于对他所托管的指针(pd)所指向的对象进行访问 .
14     
15     //std::cout << “apd = ” << apd << std::endl;  //会出错,对象不能这样直接打印
16     
17     std::cout << “apd.get() = ” << reinterpret_cast<long>(apd.get()) << std::endl;    
18 
19     double *pd = new double(8.88);
20     std::auto_ptr<double> apd2(pd);  
21     std::cout << “pd = ” << reinterpret_cast<long>(pd) << std::endl;
22 std::cout << “apd2.get() = ” << reinterpret_cast<long>(apd2.get()) << std::endl;    //通过get()可以获得原生的指针所在的地址。
23 
24     int *pi = new int(7);
25     std::auto_ptr<int> api1(pi);
26     std::auto_ptr<int> api2(api1);  //复制, 发生了所有权的转移。首先把api1裸指针所指向的值交给了api2, 同时又把api1所指向的值设为了空。相当于api1对pi的所有权完全交给了api2。发生所有权的转移,与常规的认识矛盾。本身有缺陷,不推荐使用。
27 // std::auto_ptr要求其对“裸”指针的完全占有性在拷贝构造或赋值操作时,会发生所有权的转移
28 
29 std::cout << “*api1= ” << *api1 <<std::endl;  //现在访问api1指向的值,发现没有了,发生一个段错误。
30     std::cout << “*api2 = ” << *api2 <<std::endl;
31     
32 
33     return 0;
34 }
 1 unique_ptr.cc
 2 
 3 #include<iostream>
 4 #include<memory>
 5 #include<vector>
 6 
 7 
 8 std::unique_ptr<int> getVal()   //这里返回一个unique_ptr对象,这个对象是个右值。
 9 { 
10     std::unique_ptr<int> up(new int(66));
11     return up;
12 }
13 
14 int main(void)
15 {
16     // 无法进行复制、赋值操作
17     std::unique_ptr<int> ap(new int(99));
18     //std::unique_ptr<int> one(ap);   //编译出错,不能够进行复制。
19 
20     std::unique_ptr<int> two;
21     //two = ap;  //编译出错,不能进行赋值。
22 
23     std::cout << “*ap = ” << *ap << std::endl;
24     std::cout << “ap.get() = ” << reinterpret_cast<long>(ap.get())  << std::endl;  //获取指针的值
25     
26     //可以进行移动构造和移动赋值操作
27     std::unique_ptr<int> up = getVal();   //getVal()函数返回一个右值,这个右值会优先绑定到右值引用上去。unique_ptr是具有移动语义的,意思就是说它提供了一个移动构造函数和一个移动赋值函数。而这里就优先调用了移动赋值函数。并没有调用复制构造函数。
28     std::cout << “*up = ” << *up << std::endl;
29 
30 
31     //实际上上面的操作有点类似于如下操作
32     Unique_ptr<int> up(int new int(99));
33     Unique_ptr<int> uPtr2 = std::move(up);  //这里是显式的所有权转移。把up所指的内存转给uPtr2了,而up不再拥有该内存。
34 
35 
36     
37     //可以作为容器的元素(就是因为具有移动语义)
38     std::unique_ptr<int> sp(new int(55));  //sp现在是一个左值,就要绑定到复制构造函数上面去,因为复制构造函数的参数是一个左值引用。
39     std::vector<std::unique_ptr<int> vec;
40     //vec.push_back(sp);   //会出错,因为会调用复制构造函数
41     vec.puch_back(std::move(sp));  //将左值转成右值引用,这时就会调用 移动构造函数,而不是 复制构造函数。
42     std::cout << *vec[0] << std::endl;  //打印刚添加的值。
43 
44     return 0;
45 }
 1 Shared_ptr.cc
 2 
 3     #include<iostream>
 4     #include<memory>
 5     
 6     class Child;
 7     class Parent;
 8 
 9     typedef std::shared_ptr<Child> Child_ptr;
10     typedef std::shared_ptr<Parent> Parent_ptr;
11 
12     class Child
13     {
14     public:
15         Child()
16          {
17             std::cout << “Child()” << std::endl;
18 }
19 ~Child()
20          {
21             std::cout << “~Child()” << std::endl;
22 }
23      //private:    
24         Parent_ptr parent_;
25 };
26     
27     class Parent
28     {
29     public:
30         Parent()
31         {    
32             std::cout << “Parent()” << std::endl;
33 }
34 ~Parent()
35         {    
36             std::cout << “~Parent()” << std::endl;
37 }
38 
39     //private:
40         Child_ptr child_;
41 };
42 
43 int main(void)
44 {
45     Parent_ptr parent(new Parent);  //交给shared_ptr进行管理
46     Child_ptr child(new Child);    
47 
48     std::cout << “parent’s count = ”  << parent.use_count() << std::endl;
49     std::cout << “child’s count = ” << child.use_count() << std::endl;
50 
51     std::cout << “进行复制之后:” << std::endl;
52     parent->child_  = child;      //shared_ptr复制操作
53     std::cout << “child’s count = ” << child.use_count() << std::endl;  //打印出引用计数为2.
54     child->parent_ = parent;
55     std::cout << “parent’s count = ”  << parent.use_count() << std::endl;   //打印出引用计数为2.
56     
57     return 0;
58 }
59     //因为相互引用,当程序结束时,引用计数都减一,都成为1。内存中还有这两个对象的存在,就不会调用析构函数。这样就带来了内存泄漏,是循环引用存在的问题。

 

 1 weak_ptr1.cc
 2 
 3     #include<iostream>
 4     #include<memory>
 5     
 6     class Child;
 7     class Parent;
 8 
 9     typedef std::shared_ptr<Child> Child_ptr;
10     typedef std::shared_ptr<Parent> Parent_ptr;
11 
12     class Child
13     {
14     public:
15         Child()
16          {
17             std::cout << “Child()” << std::endl;
18 }
19 ~Child()
20          {
21             std::cout << “~Child()” << std::endl;
22 }
23      //private:    
24         Parent_ptr parent_;
25 };
26     
27     class Parent
28     {
29     public:
30         Parent()
31         {    
32             std::cout << “Parent()” << std::endl;
33 }
34 ~Parent()
35         {    
36             std::cout << “~Parent()” << std::endl;
37 }
38     
39 std::weak_ptr<Child> child_;  //弱引用
40 };
41 
42 int main(void)
43 {
44     Parent_ptr parent(new Parent);  //交给shared_ptr进行管理
45     Child_ptr child(new Child);    
46 
47     std::cout << “parent’s count = ”  << parent.use_count() << std::endl;
48     std::cout << “child’s count = ” << child.use_count() << std::endl;
49 
50     std::cout << “进行复制之后:” << std::endl;
51     parent->child_  = child;      //child_是weak_ptr,引用计数并没有加1
52     std::cout << “child’s count = ” << child.use_count() << std::endl;  //打印出引用计数为2.
53     child->parent_ = parent;
54     std::cout << “parent’s count = ”  << parent.use_count() << std::endl;   //打印出引用计数为2.
55     
56     return 0;
57 }
58     //因为使用的是弱引用,复制的时候引用计数不会加1. 程序结束时会调用析构函数。
 1 Weak_ptr.cc
2 #include<iostream> 3 #include<memory> 4 5 class X 6 { 7 public: 8 X() {std::cout << “X()” << std::endl;} 9 ~ X() {std::cout << “~X()” << std::endl;} 10 11 void fun() 12 { 13 std::cout << “fun()” << std::endl; 14 } 15 }; 16 17 int main(void) 18 { 19 std::weak_ptr<X> p; 20 { 21 std::shared_ptr<X> p2(new X); //所有new出来的东西都放到这个栈对象p2里面,因为当这个语句块结束的时候p2会调用析构函数,我们在析构函数中加上delete来释放托管过来的指针。 22 std::cout << “p2’s count = ” << p2.use_count() <<std::endl; 23 24 p = p2; //std::weak_ptr 不会增加引用计数 25 std::cout << “after p = p2 ” <<std::endl; 26 std::cout << “p2’s count = ” << p2.use_count() <<std::endl; 27 28 std::shared_ptr<X> p3 = p.lock(); //lock()函数就是用来提升weak_ptr为shared_ptr的函数。 29 30 if(p3) //提升成功 31 { 32 p3->fun(); 33 std::cout << “p3’s count = ” << p3.use_count() <<std::endl; 34 } 35 else //提升失败 36 { 37 std::cout << “object has been destroied” << std::endl; 38 } 39 } 40 41 //new X 已经被释放了 42 //通过weak_ptr访问对象的成员的时候,要提升为shared_ptr 43 std::shared_ptr<X> p4 = p.lock(); 44 if(p4) //提升成功 45 { 46 P4->fun(); 47 std::cout << “p4’s count = ” << p4.use_count() <<std::endl; 48 } 49 else //提升失败 50 { 51 std::cout << “object has been destroied” << std::endl; 52 } 53 54 //智能指针作为栈对象来使用,不要采用堆对象的方式来使用。因为智能指针作为栈对象来使用,它可以具有自动管理、自动回收的功能。 55 //智能指针的实现原理:栈对象生命周期结束的时候,会自动调用析构函数。 56 //std::shared_ptr<X> *pthis = new std::shared_ptr<X>(new X); 57 //上述方式就是生成堆对象,不推荐这么做。而且这时候只能通过显示调用delete函数,这样就跟我们的初衷不符。 58 59 return 0; 60 }

 

posted @ 2015-07-21 20:29  Jianhui_Ethan  阅读(628)  评论(0编辑  收藏  举报