指针、智能指针学习说明
每一个变量都有一个内存位置,每一个内存位置都定义了可使用(&)运算符访问的地址,它表示了在内存中的一个地址。
堆指针/栈指针
堆指针:
申请动态分配,它的值放到堆中。
int p; p=new int;
new返回新分配的内存单元的地址。
因为其地址已经动态分配,所以可以p=22,这样直接赋值。
栈指针:
没有申请动态分配,它的值放到栈中。
int p; int a=22; p=&a;
指针p获取了栈中a的地址,属于静态分配,对p的操作等于对a的操作。
堆和栈的区别:
- 堆由程序员控制(malloc/free)(new/delete)
栈由编译器自动管理 - 堆 不连续 大 灵活
栈 连续 大小由操作系统预定号 - 堆 频繁的new/delete会造成大量内存碎片,降低了程序效率。
栈 先进后出的结构,进出一一对应,不会产生碎片。 - 堆 向上 向高地址方向增长
栈 向下 向低地址方向增长
内存泄漏:
是指程序申请内存后,无法释放已申请的内存空间,内存泄漏多了会导致可用内存空间变小,进一步造成内存溢出。
空指针:
NULL指针是一个定义在标准库中的值为零的常量。
ptr=0表示该指针不指向一个可访问的内存位置。
各类型变量所占字节数
类型 | 字节数 |
---|---|
char | 1 |
short int | 2 |
int | 4 |
long int | 8 |
long long int | 8 |
char * | 8 |
float | 4 |
double | 8 |
指针的算术运算:
- 加减:若pr指向一个整数指针,pr++会让地址加4。
- 比较:如果pr1和pr2指向的两个相关的变量,比如同一个数组中的不同元素,则可对p1和p2进行大小比较。
指针与数组:
var[3]={1,2,3};
*(var+2)=300;
相当于var[2]=300;
char*是指向字符串的指针(指向字符串的首个字母),可以指向一串常量字符串。
例:
char a[3]={‘a’,‘b’,‘c’};
char * a[3]={“aa”,“bb”,“cc”};
a[0]=“a” 因为a[0]代表指向字符串的首地址(首个字母)
- char* const[指向字符的静态指针]
const修饰指针,代表不能改变指针 - const char * 指向静态字符的指针
const修饰char,代表字符不能改变,但是指针可以变,也就是说该指针可以指向其他const char。
char[]和char *
char * a= “string”
char b[]=“stirng”
a是一个char型指针变量,其值(指向可以改变)
b是一个char型数组的名字,也是该数组首元素的地址,其值不可变
a=b; //a,b指向同一区域
char * 本身是一个字符指针变量,但是它既可以指向字符串常量,又可以指向字符串变量,指向的类型决定了对应字符能不能改变,所以若没有对a进行操作的意向,尽量定义为const char *a =“string”;
强指针与弱指针
强指针/强引用:A指向B, A可以决定B的生死(就是会触发incStrong()/decStrong()被调用)
弱指针/弱引用:A指向B, 但是A不可以决定B的生死(弱指针的引用不会修改mCount引用计数,因此也不能通过弱指针引用对象成员)
使用范围:
强:局部变量,子对象指针,可影响其生命周期的指针
弱:父对象指针,某些不能影响其生命周期的对象
作用:
强:利用强指针进行资源回收,不需要再人工计算new和delete,一个对象在何处(父类或函数中)创建,就在何处(父类或函数结束)回收
弱:一是避免强指针循环依赖,二是保障线程安全,其他对象成员不是在该类中创建,则不需要该类负责回收,因此使用弱指针。
参考:深入实践C++11智能指针:
https://blog.csdn.net/code_peak/article/details/119722167?spm=1001.2014.3001.5501
智能指针:
C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用。
unique_ptr:
(替换auto_ptr)unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。
unique_ptr<string> str1 (new string ("auto"));
unique_ptr<string> str2;
str1 = str2;//此时会报错!!
当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:
unique_ptr<string> str1(new string ("hello world"));
unique_ptr<string> str2;
str1= str2; //不允许
unique_ptr<string> str3;
str3= unique_ptr<string>(new string ("test")); //允许
shared_ptr
shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。
成员函数:
- use_count 返回引用计数的个数
- unique 返回是否是独占所有权( use_count 为 1)
- swap 交换两个 shared_ptr 对象(即交换所拥有的对象)
- reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少
- get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的
weak_ptr:
share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。