一般new出来的对象会用普通指针引用,此时申请的堆上的资源需要我们手动使用delete进行资源释放。如果忘记delete或者delete之前程序出现未可预知的意外,之前申请的资源就不能正常释放,造成内存泄露;
智能指针是对裸指针(普通指针)的封装,智能指针利用了栈上对象在跳出作用域会自动析构的原理,让智能指针去管理资源。所以智能指针对象一般不能new。
auto_ptr只能管理一个对象,如果使用拷贝构造函数进行auto_ptr初始化,
#include "iostream"
#include "string"
using namespace std;
int main() {
auto_ptr<int> p1(new int(10));
auto_ptr<int> p2(p1);
*p1 = 20; //此处会出现问题,因为把对象的控制权转让给了p2,之前的p1底层会自动指向nullptr
return 0;
}
auto_ptr已经过期,如上的这种问题导致C++并不建议使用auto_ptr。
基于以上auto_ptr的问题,scoped_ptr直接禁止使用拷贝构造函数和赋值重载运算符函数。scoped_ptr部分源码如下所示:
template<class T> class scoped_ptr // noncopyable
{
private:
T * px;
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
typedef scoped_ptr<T> this_type;
//以下两个函数直接被删除,禁止使用
void operator==( scoped_ptr const& ) const;
void operator!=( scoped_ptr const& ) const;
从名字就可以看出来unique_ptr只能管理一个资源。
而且unique_ptr也删除了拷贝构造函数和赋值运算符重载函数。
但是unique_ptr提供了一个带右值引用的拷贝构造函数和赋值运算符重载函数,使用方法如下所示:
// 示例1
unique_ptr<int> ptr(new int);
//如下相当于把对象的控制权从ptr转给了ptr2
unique_ptr<int> ptr2 = std::move(ptr); // 使用了右值引用的拷贝构造
ptr2 = std::move(ptr); // 使用了右值引用的operator=赋值重载函数
如名字所示,多个智能指针对象指向同一个内存资源,智能指针对象里维护了一个引用计数器对象,表示当前指针指向的对象被多少个智能指针引用。当引用计数减到0的时候才真正的进行对象的析构。
shared_ptr维护的计数器对象是在new在堆上的,
shared_ptr又叫做强智能指针。
如上的情况叫做交叉引用,A对象和B对象的引用计数都是2,如果使用shared_ptr,如果ptrA或者ptrB不再引用对象,对象本应该被释放,但是实际上引用计数不为0,对象不能被释放,这种情况就要使用weak_ptr。
弱智能指针weak_ptr区别于shared_ptr之处在于:
1、weak_ptr不会改变资源的引用计数,只是一个观察者的角色,通过观察shared_ptr来判定资源是否存在
2、weak_ptr持有的引用计数,不是资源的引用计数,而是同一个资源的观察者的计数
3、weak_ptr没有提供常用的指针操作,无法直接访问资源,需要先通过lock方法提升为shared_ptr强智能指针,才能访问资源。
从shared_ptr和weak_ptr之间的区别可以看出,在对象内部引用其他对象的时候使用weak_ptr,外部的时候使用shared_ptr。