1. 永恒的话题:内存泄漏
(1)动态申请堆空间,用完后不归还
(2)C++语言中没有垃圾回收的机制
(3)指针无法控制所指堆空间的生命周期------------指针是变量,可以指向内存堆空间,但是无法控制所指堆空间的生命周期
说明内存泄漏:
1 #include<iostream> 2 #include<string> 3 4 using namespace std; 5 6 class Test 7 { 8 int i; 9 10 public: 11 12 Test(int i) 13 { 14 this->i = i; 15 } 16 17 int value() 18 { 19 return i; 20 } 21 22 ~Test() 23 { 24 } 25 }; 26 27 int main() 28 { 29 for (int i = 0; i < 5; i++) 30 { 31 Test* p = new Test(i); //P指向堆空间的对象 32 //指针是局部变量for()循环之后消失,但是指针开辟的空间没有进行释放 内存泄漏 33 //长时间运行会出现Bug 34 35 cout << p->value() << endl; 36 37 } 38 39 return 0; 40 }
2. 深度的思考:我们需要什么?
(1)需要一个特殊的指针,指针生命期结束时主动释放堆空间
(2)一片堆空间最多只能由一个指针来标识------------避免内存多次释放
(3)杜绝指针运算和指针比较-----------(因为只有一个该类对象的指针能指向堆空间,避免指针越界,野指针)
3. 解决方案
定义一个对象,让对象取模拟真正意思上的指针行为---------对象代替指针,称该对象为智能指针 -------智能指针本质:是个对象
(1)重载指针特征操作符( -> 和 * )
(2)只能通过类的成员函数重载
(3)重载函数不能使用参数
(4)只能定义一个重载函数
(5)注意:智能指针只能用来指向堆空间中的对象或变量(不能指向栈)
实验:(1)实现特殊的指针,指针生命期结束时主动释放堆空间
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 cout <<"Test (int i)"<< endl; 13 14 this->i = i; 15 } 16 int value() 17 { 18 return i; 19 } 20 ~Test() 21 { 22 cout << "~Test ()" << endl; 23 } 24 25 }; 26 27 28 //智能指针 本质是对象--------首先创建智能指针类 29 class Pointer 30 { 31 Test* mp; //指针mp指向对象Test,用于保存要被管理对象的指针 32 33 public: 34 35 //构造函数被堆空间上内存地址初始化 36 37 Pointer(Test* p = NULL) //指针被堆空间上的地址初始化---参数Test*指针 38 { 39 mp = p; 40 } 41 42 //重载->操作符 43 44 Test* operator -> () //不能使用参数,所以也就只能定义一个重载函数 45 { 46 return mp; //返回成员指针 47 } 48 49 //重载*操作符 50 51 Test& operator * () //不能使用参数,所以也就只能定义一个重载函数 52 { 53 return *mp; //*作用指针意义:返回当前指针所指变量或对象 54 } 55 56 ~Pointer() 57 { 58 delete mp; //智能指针被析构时,同时删除其所管理的Test类的对象,释放成员指针指向的堆空间 59 } 60 }; 61 62 int main() 63 { 64 for (int i = 0; i < 5; i++) 65 { 66 // Test* p = new Test(i); //那么就可以用类对象名代替指针 67 68 Pointer p = new Test(i); //可以动态的将申请的空间释放 69 70 cout << p->value() << endl; //01234 71 72 } 73 return 0; 74 }
实现:一片堆空间最多只能由一个指针来标识------------避免内存多次释放
不进行指针运算和比较
方法:重载赋值操作符和拷贝构造
1 #include<iostream> 2 #include<string> 3 4 //智能指针 本质是对象 5 using namespace std; 6 7 class Test 8 { 9 int i; 10 public: 11 Test(int i) 12 { 13 cout <<"Test (int i)"<< endl; 14 this->i = i; 15 } 16 int value() 17 { 18 return i; 19 } 20 ~Test() 21 { 22 cout << "~Test (int i)" << endl; 23 } 24 25 }; 26 27 class Pointer 28 { 29 Test* mp; //数据成员为指针 30 31 public: 32 33 Pointer(Test* p = NULL) //构造函数被堆空间上内存地址初始化 34 { 35 mp = p; 36 } 37 38 39 // 实现一片堆空间最多只能由一个指针标识 杜绝多次释放 40 41 //拷贝构造函数 42 Pointer(const Pointer& obj) //自定义深拷贝构造函数 43 { 44 // delete mp; //拷贝构造阶段,mP野指针不能删除---内存错误 45 46 mp = obj.mp; //当前对象的成员指针mp指向初始化对象的成员指针所对应的堆空间---两个指针指向一片内存空间 47 //初始化对象把他指向的堆空间完全交给当前对象-----------即保证只有一个智能指针指向同一片内存 48 49 const_cast<Pointer&>(obj).mp = NULL; //所有权的传递,自己置空 const_cast去除只读属性 50 } 51 52 53 //赋值操作符重载函数 54 Pointer& operator=(const Pointer& obj) // 1,返回值类型一定是引用Test&为了连续赋值 2,参数是const引用类型 55 { 56 //进行赋值操作 57 if (this != &obj) //3,赋值操作符不是自赋值a=a,要避免赋值,通过This判断,this指向当前对象的地址和参数地址不同才进行赋值操作 58 { 59 delete mp; 60 61 mp = obj.mp; //保证赋值操作时,只能由一个智能指针指向同一堆空间 62 63 const_cast<Pointer&>(obj).mp = NULL; 64 } 65 return *this; //4,返回当前对象地址 66 } 67 68 //重载指针特征操作符 69 Test* operator -> () 70 { 71 return mp; //返回成员指针 72 } 73 Test& operator * () 74 { 75 return *mp; //返回当前指针所指变量或对象 76 } 77 78 bool isNull() //判断当前智能指针是否为空 79 { 80 return(mp == NULL); 81 } 82 83 ~Pointer() 84 { 85 delete mp; 86 } 87 }; 88 89 int main() 90 { 91 Pointer p1 = new Test(0); //定义智能指针P1 92 93 cout << p1->value() << endl; //0 94 95 Pointer p2 = p1; //定义智能指针P2,用p1进行初始化,p1管理的堆空间转给P2 96 97 //不能指针比较的指针运算 98 // p2++; 99 // if(p1==p2) 100 101 cout << p1.isNull() << endl; //1空 p1指向NULL,不再指向原来的堆空间 102 103 cout << p2->value() << endl; //0 p2接管P1指向的堆空间------管理92行堆对象 104
//p2++;不重载++,因为每次只能一个智能指针指向堆空间,这种++操作没意义
//p2智能指针生命期结束,会自动释放接管的堆空间
105 return 0; 106 }
//输出结果:
//Test(int i)
//5
//1
//5
//~Test()
存在问题: 智能指针对应的类只能指向Test固定的类类型,不能指向其他类型,能不能指向各类他能管理的类类型------以后会学到模板实现
4. 小结
掌握智能指针的创建
智能指针在自己生命周期结束,自动释放所指的内存空间
要求c++避免使用原生指针,那么以后就可以使用智能指针
(1)指针特征操作符( -> 和 * )可以被重载
(2)重载指针特征符能够使用对象代替指针
(3)智能指针只能用于指向堆空间中的内存
(4)智能指针的意义在于最大程度的避免内存问题