c++primer智能指针部分
1 内存篇 2 静态内存、栈内存、动态内存 3 静态内存:用来保存局部static对象,类static数据成员、以及定义在 任何函数外的变量 4 栈内存:用来保存定义在函数内的非static对象。 5 动态内存:new出来的内存 6 7 分配在静态内存和栈内存的对象由编译器自动创建和销毁,对于栈内存,仅在其定义的程序块运行时才存在 8 static对象在使用之前分配,程序结束时销毁。 9 10 除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称作自由空间或堆。 11 程序用堆来存储动态分配的对象; 12 13 动态内存与智能指针 14 动态内存是通过一对运算符来完成的; 15 new:在动态内存中为对象分配空间并返回一个指向该对象的指针 16 delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存 17 18 19 智能指针: 20 shared_ptr:允许多个指针指向同一个对象 21 unique_ptr:独占所指对象 22 weak_ptr伴随类,它是一种弱引用,指向shared_pr所管理的对象 23 24 25 类似vector,智能指针也是模板 26 shared_ptr<string> p;//指向p是string类型的智能指针 27 28 29 它们支持的操作: 30 shared_ptr<T> p;//空指针,可以指向T类型 31 unique_ptr<T> p;//空指针,可以指向T类型 32 p;//判断p是否为空 33 *p;//解引用,获得它指向的对象 34 35 p->mem;//等价于(*p).mem; 36 swap(p,q);//交换两个指针 37 38 make_shared<T>(args);//返回一个shared_ptr,指向一个动态分配的类型为T的对象。并使用args初始化此对象 39 shared_ptr<T>p(q);//此操作会递增q中的计时器,q必须能转换成T* 40 41 p.unique();//若p.use_count() == 1返回true,否则返回false 42 p.use_count();//返回与p共享对象的智能指针数量,可能很慢,主要用于调试 43 44 用法: 45 shared_ptr<int> p = make_shared<int>(42);//指向一个值为42的int的shared_ptr 46 shared_ptr<int> p = make_shared<int>();//右边是默认构造所以p指向一个值为42的int的shared_ptr; 47 48 make_shared和emplace的()内都是构造函数的参数,args 49 50 auto p = make_shared<vector<string>>();//p指向一个动态分配空的vector<string>; 51 52 关联计时器 53 auto p = make_shared<int>(42);//p指向的对象只有一个引用者,计数为1; 54 auto q(p);//p,q,指向相同对象,此对象有两个引用者,关联容器为2,而不管谁复制谁 55 56 当用一个shared_ptr初始化另一个shared_ptr时(本质还是构造形参复制实参) 57 或将它作为参数传递给一个函数以及作为函数返回值时,它关联的计数器就会+1; 58 同时我们给shared_ptr赋予一个新值(改变指向)或shared_ptr被销毁,计数器-1; 59 一旦计数器为0,它会自动释放自己所管理的对象; 60 销毁是通过析构函数完成销毁的,析构函数控制此类对象在销毁时做什么操作 61 62 析构函数对智能指针的两步操作,释放p指向的内存,并销毁p对象,防止p成为野指针 63 64 65 #include<bits/stdc++.h> 66 using namespace std; 67 68 69 class type 70 { 71 72 public: 73 int c; 74 type () = default; 75 type(int b) : c(b){cout << 1 << \n;} 76 type(const type &obj){cout << 3 << \n;} 77 type operator= (const type &b){ 78 cout << 2 << \n; 79 this->c = b.c; 80 return *this; 81 } 82 }; 83 type test(type a) 84 { 85 cout << ******* << \n; 86 return a; 87 } 88 int main() 89 { 90 91 type a(1);//调用普通构造 92 type c; 93 test(a);//调用实参转形参调用一次拷贝构造,return又调用一次 拷贝构造,不管有没有值相等都会调用(type obj = test(a)) 94 cout <<\n\n\n\n; 95 c = a;//这里会调用重载=,return会调用拷贝构造,如果不return就不会调用拷贝构造 96 return 0; 97 } 98 99 new 和 delete 100 new 分配失败时,会抛出bad_alloc的异常 101 可以改变new的方式阻止它抛异常 102 int *p = new int; //如果分配失败就会抛出std::bad_alloc 103 int *p = new (nothrow) int ;//如果分配失败,new返回一个空指针 104 105 delete 106 传递给delete的指针,必须是指向动态分配的内存或是一个空指针 107 delete空指针是不会产生错误的 108 const int *p = new const int (1024) 109 delete p;//释放一个const对象 110 111 112 delete一个指针后,指针值已经无效,但是很多机器上的指针仍然保存着已经释放了的动态内存地址,在delte后,指针变成人们所说的空悬指针 113 114 shared_ptr和new结合使用 115 shared_ptr<double> p;//p是一个空指针 116 shared_ptr<intl> p(new int(42));//p指向一个值为4的int 117 118 由于智能指针的构造函数是explicit的,因此我们不能将一个内置指针隐式转换为一个智能指针,必须直接初始化形参 119 shared_ptr<int> p = new int(1024);//错误:必须要使用直接初始化 120 shared_ptr<int> p(new int(1024));// 正确,使用了直接初始化 121 122 explicit 从来都不是阻止double转换为int的,不知道你从哪里听来的? 123 124 explicit 阻止的是隐式的从int构造C,比如一个函数要传一个const C&参数,而你传进去一个23。 125 他阻止的是参数类型到类的对象类型的隐式转换。并不是别的什么比如你调用构造函数时传的参数类型之间的隐式转换(比如int到double) 126 127 shared_ptr<T> p(q);//q是new出来的指针,如:shared_ptr<int>p(new int(10)); 128 shared_ptr<T> p(u);//u是unique_ptr,将p置空,让q接管q指向的地方 129 130 p.reset();//若p是唯一指向其对象的shared_ptr,reset会释放此对象; 131 132 不要混用普通指针和智能指针 133 134 如: 135 void test(shared_ptr<int> obj){} 136 int *p(new int(1)); 137 test(p);//错误,不能将Int*转换成shared_ptr 138 test(shared_ptr<int>(p));//合法,传临时变量 139 int j = *p;//p是空悬指针 140 上述程序shared_ptr的临时变量使得它的计时器为1;复制一份到形参计时器=2 141 程序结束后形参和实参临时变量消亡计时器为0,此时p指向的内存被释放,还用*p赋值会产生严重后果; 142 143 shared_ptr<int>p(new int(1)); 144 p.get();//返回一个内置指针,指向p管理的内存 145 int *q = p.get(); 146 147 148 reset操作经常与unique_ptr使用 149 虽然说unique_ptr不能同时指向多个对象; 150 if(!p.unique())//如果不是独享一个对象,则返回0; 151 { 152 p.reset(new int(1));//p释放自己指向的内存,然后指向new int(1)的内存 区域; 153 } 154 *p += 10; 155 156 157 与shared_ptr相同,必须采用直接初始化 158 unique_ptr没有make_shared返回一个unique_ptr的标准库,所以要用内置指针初始化 159 unique_ptr<int> p;//可以指向int类型的空指针 160 unique_ptr<int> p(new int(42));//p是一个指向值为42的int 161 162 由于一个对象只能被一个unique_ptr管理 163 所以unique_ptr不支持普通的拷贝或赋值 164 unique_ptr<int> p(new int(1)); 165 unique_ptr<int> p(q);//错误,不支持拷贝 166 unique_ptr<int> q; 167 q = p;//错误,不支持赋值 168 169 u.release();//u放弃对指针的控制权,返回指针,并将u置为空 170 u.reset();//释放u指向的对象 171 如: 172 unique_ptr<int> p1(new int(1)); 173 unique_ptr<int> p2(p1.release());//返回的匿名int * ,并将p1置为空;即让p2接管匿名int *的内存内容 174 unique_ptr<int> p3(new int(1)); 175 p3.reset(p2.release());//p3释放自己指向对象的内存,并接管p2的内存; 176 177 上面两个函数要配套使用 178 如果只是p1.release();//P1指向的内存会得不到释放,造成内存泄漏 179 180 181 weak_ptr 182 weak_ptr是一种弱指针,它与shared_ptr绑定,但是不会增加shared_ptr的计时器 183 184 auto p = make_shared<int>(42); 185 weak_ptr<int> wp(p);//wp弱共享,p的计数器未改变 186 上面p有可能被释放,而wp指向的内存可能会无效,于是使用前要进行判断 187 188 if(shared_ptr<int> np = wp.lock()){ 189 } //如果wp共享的对象那片内存有效,则返回一个指向共享对象的shred_ptr; 190 否则返回空的shared_ptr指针; 191 192 193 new动态分配数组 194 int *p = new int[3];//new返回的是一个数组元素的指针,也就是说new的返回值是T*,不能返回整个数组的指针,即不能返回二级指针 195 196 197 int *p = new int[2];//2个未初始化的int 198 int *p = new int[2]();//2个初始值为0的int 199 200 上面的方法可以对数组元素进行值初始化 201 如int *p = new int[2](5);//2个初始值为5的int 202 int *p = new int [2]{1,2;};//初始值为1,2 203 204 释放动态数组del1ete [] p;//数组中的元素逆序销毁 205 206 智能指针与动态数组 207 标准库提供了一个可以管理new分配数组的unique_ptr版本 208 209 unique<int[]> p(new int[2]);//p指向2个未初始化的int数组的开头 210 p.release();//自动用delete[]销毁其指针 211 这里与指向单个int的有点不同,这里release是直接delete掉,而不会考虑转移控制权 212 213 与unique_ptr不同,share_ptr不直接管理动态数组, 214 如果希望shared_ptr管理一个动态数组,必须提供自定义的删除其器 215 share_ptr<int>p(new int[2],[](int *p1){delete [] p1;}) ; 216 //用lambda作为删除器,删除器里的参数其实是函数指针; 217 p.reset();//这样就能使用我们提供的lambda释放数组内存了 218 如果未提供删除器,则只会delete p 而不是[] p; 219 220 shared_ptr未定义下标运算符,所以要访问数组元素必须用get获取内置指针 221 for( int i = 0; i <2; i++) 222 { 223 *(p.get() + i) = i; 224 } 225 但是对于Unique_ptr来说,它对数组内置了[]可以直接访问 226 如: 227 unique_ptr<int[]> p(new int[2]); 228 for(int i = 0; i < 2; i++) 229 p[i] = i; 230 231 232 233