普通变量/对象
储存位置:栈
当定义一个变量的时候,计算机会将指定的一块内存空间和变量名进行绑定,如:
int a = 1;
此时便会申请一块内存空间,里面储存着值1,可以看作这块内存空间的名字是a。
- 优点:直接的值传递;
- 缺点:占用大量内存空间,在定义或传递一些内存占用较大的变量(如结构体等)甚至能耗尽栈空间。
在c++中,通过直接定义普通变量或对象并不能满足所有的需求,很多时候我们需要用到指针,引用等其他方式。
指针
储存位置:堆
指针是一种储存地址的变量,其内存空间里存放的不是具体的值,而是某个变量的地址,如:
int a = 1;
int *ptr = &a;
其中 & 是取地址符,取得a变量对应的内存空间的地址,有了这个地址,便可以找到变量a所在的内存空间,进而获取变量a储存的值。
指针的优点
- 通过Object myObject 方式定义对象会消耗大量内存,而用指针做形参。指针的空间占用是与CPU的寻址长度相关的,如果是32位的话就是4个字节,大大减少了内存的占用;
- 可以实现多态;
- 动态内存分配的便利性;
指针的缺点
- 指针涉及到手动的内存管理和释放,通过 new Object() 方式分配对象时,对象的生存期是动态的,这意味着若不显式地 detete 对象,对象将一直存在。
- 潜在的内存冲突(数组越界)和内存泄漏;
- 空指针和野指针的存在可能会导致程序崩溃。
引用
首先明确一点,引用本质上也是指针,属于一种特殊的指针。在c++中,最开始引用的引入是为了支持运算符重载。既然C++引入了引用,那为什么C++不和Java一样抛弃指针呢?答案是为了兼容C语言。
引用就是变量的别名,指向同一个内存地址,直接对引用的修改都会改变引用所指向的对象。
引用的优点:
- 使用引用的时候会自动解引用,而不需要像使用指针一样显式解引用;
- 不存在空引用(保证不操作空指针);
- 必须初始化(保证不是野指针);
- 一个引用永远指向他初始化的那个对象(保证指针值不变);
引用的缺点:
- 指针在使用中可以指向其它对象,但是引用只能是一个对象的引用;
- 指针可以有多级指针(**p),而引用只能一级。
智能指针
储存位置:栈
为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。
智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类是栈上的对象,智能指针指向堆上开辟的空间,函数结束时,栈上的函数会自动被释放,智能指针指向的内存也会随之消失,防止内存泄漏。其包含在头文件<memory>中,其中包括:
- shared_ptr:它采用引用计数的方法,记录当前内存资源被多少个智能指针引用,该引用计数的内存在堆上分配。每新增一个引用计数加1,当过期时引用计数减1。只有引用计数为0时,智能指针才会自动释放引用的内存资源。shared_ptr的一个最大的陷阱是循环引用,会导致堆内存无法正确释放,从而导致内存泄漏。
- weak_ptr:为了解决循环引用导致的内存泄漏,引入了weak_ptr弱指针,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,虽然不指向引用计数的共享内存,但是可以检测所管理的对象是否已经被释放,从而避免非法访问。
- unique_ptr:一种对资源具有独占性的智能指针,即一个对象资源只能同时被一个unique_ptr指向。
总结使用场景
1.引用和指针
- 指针尽可能得以引用代替,除却一些引用无能为力的场景,如指针在使用中可以指向其它对象,而引用不行;
2.智能指针和指针
- 智能指针天生负责对象生命期管理;所以生命期对象全都由unique_ptr和shared_ptr来管理。
- 原始指针天生不负责对象生命周期管理;所以使用动态对象均通过原始指针来使用(参考各种第三方库QT/BCG/OS API等等)。
参考:https://www.zhihu.com/question/319277442
https://www.csdn.net/gather_26/MtzaUgwsMjgtYmxvZwO0O0OO0O0O.html