c/c++语言开发共享c++中的智能指针

内存泄漏、指针操作符重载、类模板技术、auto_ptr 指针 …


目录

    2.2 stl 中的智能指针应用

    2.3 qt  中的智能指针应用

 

初识智能指针

  在c++语言中没有垃圾回收机制,内存泄漏这个问题就不得不让程序员自己来解决,稍有不慎就会给软件带来bug,幸运的是我们可以使用智能指针去解决内存泄漏的问题

1、 内存泄漏的原因分析

  (1)动态申请堆空间,用完后不归还;

  (2)c++ 语言中没有垃圾回收的机制;

         1)java、c# 语言中都引入了垃圾回收机制,定期检测内存,若发现没有使用,则回收;

    2)垃圾回收机制可以很好的避免内存泄漏;

    3)c++ 中的动态内存申请和归还完全依赖开发者,稍有不慎就会出错;

  (3)指针无法控制所指堆空间的生命周期;(根本原因

    1)通过指针可以指向动态内存空间,但是却不能够控制动态内存空间的生命周期;

    2)也就是指针和动态内存空间没有必然联系,即使指针变量销毁了,动态内存空间还可以存在;

  补充:多次释放多个指针指向的同一块堆空间也会使软件出现bug;

 1 #include <iostream>  2 #include <string>  3   4 using namespace std;  5   6 class test  7 {  8     int i;  9 public: 10     test(int i) 11     { 12         this->i = i; 13     } 14     int value() 15     { 16         return i; 17     } 18     ~test() 19     { 20     } 21 }; 22  23 int main() 24 { 25     for(int i=0; i<5; i++) 26     {   27          // 指针p指向所申请的堆空间,但是并没有手动归还这块内存;当进行下一次循环时,指针p又指向了一块新的堆空间,这样前一次的堆空间就永远无法归还了, 28          // 同时,指针p是一个局部变量,for循环结束后指针p就销毁了,这就意味着这片空间永远无法归还了; 29         test* p = new test(i);  30          31         cout << p->value() << endl; 32  33         // delete p; // 正确做法:每次用完之后记得归还所申请的堆空间,否则就会造成内存泄漏 34     } 35      36     return 0; 37 }

2、内存泄漏的解决方案

  需求分析 -> 解决方案:(结合案列更容易理解)

  (1)需要一个特殊的指针,即智能指针对象(普通类对象,通过重载指针操作符就可以使对象指向堆空间),通过类的构造函数完成;

  (2)指针生命周期结束时主动释放堆空间,可以通过类的析构函数完成;

  (3)一片堆空间最多只能由一个指针标识,为的是避免多次释放内存,通过拷贝构造函数和赋值操作符完成;

  (4)杜绝指针运算和指针比较

    1) 杜绝指针运算可以避免指针越界和野指针;

    2)上面的第三个需求满足了,指针比较就没有意义了;

    3)不重载类的运算符(算术运算符、关系运算符、++、–),当进行指针(类对象)运算与比较时,程序会编译失败。

  (5)重载指针特征操作符(-> 和 *);

            1)通过重载指针操作符使得类对象具备了指针的行为;

            2)创建一个类对象,让这个对象通过操作符重载模拟真正的指针行为;

    注:只能通过类的成员函数重载指针操作符,且该重载函数不能使用参数;

  通过类的成员函数重载指针特征操作符,从而使得类对象可以模拟指针的行为,这个类对象称为智能指针

  使用智能指针的注意事项:只能指向堆空间的对象或变量,不允许指向栈对象。

  智能指针的表现形象:使用类对象来取代指针。

  1 #include <iostream>   2    3 using namespace std;   4    5 class test   6 {   7     int i;   8 public:   9     test(int i)  10     {  11         cout << "test(int i)::" << i  << endl;  12         this->i = i;  13     }  14     int value()  15     {  16         return i;  17     }  18     ~test()  19     {  20         cout << "~test()::" << i << endl;  21     }  22 };  23   24 class pointer  25 {  26 private:  27     test *mp;  28 public:  29     pointer(test *p = null)  30     {  31         mp = p;  32     }  33     pointer(const pointer& obj)  34     {  35         mp = obj.mp;  36         const_cast<pointer&>(obj).mp = null;  37     }  38     pointer& operator=(const pointer& obj)  39     {  40         if(this != &obj)  41         {  42             if(mp != null)  43             {  44                 delete mp;  45             }  46             mp = obj.mp;  47             const_cast<pointer&>(obj).mp = null;  48         }  49   50         return *this;  51     }  52     test* operator->()  53     {  54         return mp;  55     }  56     test& operator*()  57     {  58         return *mp;  59     }  60     bool isnull()  61     {  62         return (mp == null);  63     }  64     ~pointer()  65     {  66         delete mp;  67     }         68   69 };  70   71 int main(int argc, char const *argv[])  72 {  73     cout << "-------1-------------" << endl;  74     pointer pt1 = new test(10);     // test(int i)::10  75     cout << pt1->value() << endl;   // 10  76     cout << (*pt1).value() << endl; // 10  77   78     cout << "-------2-------------" << endl;  79     pointer pt2 = new test(5);      // test(int i)::5  80     cout << pt2->value() << endl;   // 5  81       82     cout << "-------3-------------" << endl;  83     pointer pt3 = pt2;              // 将指针pt2的使用权交给指针pt3  84     cout << pt2.isnull() << endl;   // 1  85     cout << pt3->value() << endl;   // 5  86   87     cout << "-------4-------------" << endl;  88     pt3 = pt1;                      // 将指针pt1的使用权交给指针pt3   // ~test()::5  89     cout << pt1.isnull() << endl;   // 1  90   91     cout << "-------5-------------" << endl;  92     pointer pt4;  93     pt4 = pt3;                      // 将指针pt3的使用权交给指针pt4   // ~test()::10  94     cout << pt3.isnull() << endl;   // 1  95   96     return 0;  97 }  98   99 /** 100  *  智能指针的需求: 101  *  指针的生命周期结束时,主动的释放堆空间 102  *  一片堆空间最多只能由一个指针标识  103  *  杜绝指针运算和指针比较 104  *  105  *  使用智能指针的注意事项:只能指向堆空间的对象或变量,不允许指向栈变量 106  *  智能指针的表现形象:使用类对象来取代指针 107  *   108  */

  注:这个案列只实现了一个类的内存回收,关于任意类的内存回收,会在后续的模板技术中介绍。

 智能指针类模板

  知识回顾

  由于智能指针相关的类重载了指针操作符 ,所以其对象可以像原生的指针一样使用,本质上智能指针对象就是类对象。但是,此时的智能指针对象有很大的局限性,不能灵活的指向任意的类对象。为了解决这个问题,智能指针类模板就出现了。

1、智能指针的意义

  (1)现代 c++ 开发库中最重要的类模板之一;(如 stl 标准库、qt )

  (2)是c++开发中自动内存管理的主要手段;

  (3)能够在很大程度上避开内存相关的问题。

2、stl中的智能指针应用

  (1) auto_ptr 

    1)生命周期结束时,销毁指向的内存空间;(避免只借不还的现象出现)

    2)不能指向堆数组,只能指向堆对象(变量);(若需要使用堆数组,我们可以自己实现内存回收机制)

    3)一片堆空间只属于一个智能指针对象;或者,多个智能指针对象不能指向同一片堆空间;(避免多次释放同一个指针;)

  (2)shared_ptr

         带有引用计数机制,支持多个指针对象指向同一片内存;

  (3)weak_ptr

    配合 shared_ptr 而引入的一种智能指针;

  (4)unique_ptr

    一个指针对象指向一片内存空间,不能拷贝构造和赋值(auto_ptr 的进化版,没有使用权的转移); 

 1 #include <iostream>  2 #include <string>  3 #include <memory>  // 智能指针类模板的头文件  4   5 using namespace std;  6   7 class test  8 {  9     string m_name; 10 public: 11     test(const char* name) 12     { 13         cout << "construct @" << name << endl; 14          15         m_name = name; 16     } 17      18     void print() 19     { 20         cout << "member @" << m_name << endl; 21     } 22      23     ~test() 24     { 25         cout << "destruct @" << m_name << endl; 26     } 27 }; 28  29 int main() 30 { 31     auto_ptr<test> pt(new test("smartpoint")); 32      33     cout << "pt = " << pt.get() << endl;    // pt.get() 返回指针所指向数组的首地址 34      35     pt->print(); 36      37     cout << endl; 38      39     auto_ptr<test> pt1(pt); // pt 转移了对堆空间的控制权,指向 null; 40      41     cout << "pt = " << pt.get() << endl;   42     cout << "pt1 = " << pt1.get() << endl; 43      44     pt1->print(); 45      46     return 0; 47 } 48 /** 49  * 运行结果: 50  * construct @smartpoint 51  * pt = 0x1329c20 52  * member @smartpoint 53  *  54  * pt = 0 55  * pt1 = 0x1329c20 56  * member @smartpoint 57  * destruct @smartpoint 58  * /

3、qt 中的智能指针应用

  (1)qpointer

         1)当其指向的对象被销毁(释放)时,它会被自动置空;

               可以使用多个 qpointer 智能指针指向同一个对象,当这个对象被销毁的时候,所有的智能指针对象都变为空,这可以避免多次释放和野指针的问题。

         2)析构时不会自动销毁所指向的对象;(!!!

             也就是当 qpointer 对象生命周期完结的时候,不会自动销毁堆空间中的对象,需要手动销毁;

  (2)qsharedpointer(和 stl中shared_ptr 相似)

           1)引用计数型智能指针(引用计数的对象是堆空间申请的对象);

      2)可以被自由地拷贝和赋值;

        3)当引用计数为 0 时,才删除指向的对象;(这个智能指针对象生命周期结束后,引用计数减一)

  (3)其它的智能指针(qweakpointer;qscopedpointer;qscopedarraypointer;qshareddatapointer;qexplicitlyshareddatapointer;)

  为什么qt要重新开发自己的内存管理机制,而不直接使用已有的stl中智能指针?

  这个和它的架构开发思想相关,因为 qt 有自己的内存管理思想,但是这些思想并没有在 stl 中实现,为了将这种内存管理思想贯彻到 qt 中的方方面面,所以qt 才开发自己的智能指针类模板。  

 1 #include <qpointer>  2 #include <qsharedpointer>  3 #include <qdebug>  4   5 class test : public qobject  // qt 开发中都要将类继承自 qobject  6 {  7     qstring m_name;  8 public:  9     test(const char* name) 10     { 11         qdebug() << "construct @" << name; 12  13         m_name = name; 14     } 15  16     void print() 17     { 18         qdebug() << "member @" << m_name; 19     } 20  21     ~test() 22     { 23         qdebug() << "destruct @" << m_name ; 24     } 25 }; 26  27 int main() 28 { 29     qpointer<test> pt(new test("smartpoint")); 30     qpointer<test> pt1(pt); 31     qpointer<test> pt2(pt); 32  33     pt->print(); 34     pt1->print(); 35     pt2->print(); 36  37     delete pt;  // 手工删除,这里只用删除一次就可,上述三个指针都指向null; 38  39     qdebug() << "pt = " << pt;   40     qdebug() << "pt1 = " << pt1;  41     qdebug() << "pt2 = " << pt2; 42  43     qdebug() << "qpointer 与 qsharedpointer 的区别" << endl; 44  45     qsharedpointer<test> spt(new test("smartpoint")); // 引用计数是相对于 test("smartpoint") 对象而言; 46     qsharedpointer<test> spt1(spt); 47     qsharedpointer<test> spt2(spt); 48  49     spt->print(); 50     spt1->print(); 51     spt2->print(); 52  53     return 0; 54 } 55  56 /** 57  * 运行结果: 58  * construct @ smartpoint 59  * member @ "smartpoint" 60  * member @ "smartpoint" 61  * member @ "smartpoint" 62  * destruct @ "smartpoint" 63  * pt =  qobject(0x0) 64  * pt1 =  qobject(0x0) 65  * pt2 =  qobject(0x0) 66  *  67  * qpointer 与 qsharedpointer 的区别  68  *  69  * construct @ smartpoint 70  * member @ "smartpoint" 71  * member @ "smartpoint" 72  * member @ "smartpoint" 73  * destruct @ "smartpoint" 74  * /

 4、智能指针模板类的实现

  参照 auto_ptr 的设计,同样会在拷贝构造函数和赋值操作符中发生堆空间控制权的转移。

  1 // smartpointer.hpp 智能指针模板类   2 #ifndef smartpointer_h   3 #define smartpointer_h   4    5 template   6 <typename t>   7 class smartpointer   8 {   9 private:  10     t *mp;  11 public:  12     smartpointer(t *p = 0);  13     smartpointer(const smartpointer<t>& obj);  14     smartpointer<t>& operator=(const smartpointer<t>& obj);  15     t* operator->();  16     t& operator*();  17     bool isnull();  18     t* get();  19     ~smartpointer();  20       21 };  22   23 template  24 <typename t>  25 smartpointer<t>::smartpointer(t *p)  26 {  27     mp = p;  28 }  29   30 template  31 <typename t>  32 smartpointer<t>::smartpointer(const smartpointer<t>& obj)  33 {  34     mp = obj.mp;  35     const_cast<smartpointer&>(obj).mp = 0;  36 }  37   38 template  39 <typename t>  40 smartpointer<t>& smartpointer<t>::operator=(const smartpointer<t>& obj)  41 {  42     if(this != &obj)  43     {  44         if(mp != 0)  45         {  46             delete mp;  47         }  48         mp = obj.mp;  49         const_cast<smartpointer<t>&>(obj).mp = 0;  50     }  51   52     return *this;  53 }  54   55 template  56 <typename t>  57 t* smartpointer<t>::operator->()  58 {  59     return mp;  60 }  61   62 template  63 <typename t>  64 t& smartpointer<t>::operator*()  65 {  66     return *mp;  67 }  68   69 template  70 <typename t>  71 bool smartpointer<t>::isnull()  72 {  73     return (mp == 0);  74 }  75   76 template  77 <typename t>  78 t* smartpointer<t>::get()  79 {  80     return mp;  81 }  82   83 template  84 <typename t>  85 smartpointer<t>::~smartpointer()  86 {  87     delete mp;  88 }         89   90 #endif  91   92 // main.cpp 测试文件  93   94 #include <iostream>  95 #include "test.hpp"  96   97 using namespace std;  98   99 class test 100 { 101     string m_name; 102 public: 103     test(const char* name) 104     { 105         cout << "construct @" << name << endl; 106          107         m_name = name; 108     } 109      110     void print() 111     { 112         cout << "member @" << m_name << endl; 113     } 114      115     ~test() 116     { 117         cout << "destruct @" << m_name << endl; 118     } 119 }; 120  121 class demo 122 { 123     string m_name; 124 public: 125     demo(const char* name) 126     { 127         cout << "construct @" << name << endl; 128          129         m_name = name; 130     } 131      132     void print() 133     { 134         cout << "member @" << m_name << endl; 135     } 136      137     ~demo() 138     { 139         cout << "destruct @" << m_name << endl; 140     } 141 }; 142  143  144  145 int main(int argc, char const *argv[]) 146 { 147     smartpointer<test> pt(new test("smartpointer template <test>")); 148      149     cout << "pt = " << pt.get() << endl; 150      151     pt->print(); 152      153     cout << endl; 154      155     smartpointer<test> pt1(pt); 156      157     cout << "pt = " << pt.get() << endl; 158     cout << "pt1 = " << pt1.get() << endl; 159      160     pt1->print(); 161      162     //--------------------------------------------------------------- 163     cout << "--------------------------------------------" << endl; 164      165     smartpointer<demo> spt(new demo("smartpointer template <demo>")); 166      167     cout << "spt = " << spt.get() << endl; 168      169     spt->print(); 170      171     cout << endl; 172      173     smartpointer<demo> spt1(spt); 174      175     cout << "spt = " << spt.get() << endl; 176     cout << "spt1 = " << spt1.get() << endl; 177      178     spt1->print(); 179      180     return 0; 181 } 182 /** 183  * 运行结果: 184  * construct @smartpointer template <test> 185  * pt = 0x17bcc20 186  * member @smartpointer template <test> 187  *  188  * pt = 0 189  * pt1 = 0x17bcc20 190  * member @smartpointer template <test> 191  * ----------------------------------------- 192  * construct @smartpointer template <demo> 193  * spt = 0x17bd090 194  * member @smartpointer template <demo> 195  *  196  * spt = 0 197  * spt1 = 0x17bd090 198  * member @smartpointer template <demo> 199  * destruct @smartpointer template <demo> 200  * destruct @smartpointer template <test> 201  */

  

本节总结:

智能指针能够尽可能的避开内存相关的问题,主要表现在:

(1)内存泄漏

(2)多次释放

 

 

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/600224.html

(0)
上一篇 2021年5月9日
下一篇 2021年5月9日

精彩推荐