一句话归纳:当类中有指针成员时,可使用智能指针实现对象共享;智能指针经过引用计数实现,即对指向同一对象的指针计数;智能指针的使用能够方便/安全地控制对象的生命周期,对指针进行自动销毁。ios
当类中有指针成员时,通常有两种方式来管理指针成员:一是采用值型的方式管理,每一个类对象都保留一份指针指向的对象的拷贝;另外一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享。程序员
智能指针(smart pointer)的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。编程
智能指针和普通指针的区别在于智能指针其实是对普通指针加了一层封装机制,这样的一层封装机制的目的是为了使得智能指针能够方便的管理一个对象的生命期。安全
在C++中,咱们知道,若是使用普通指针来建立一个指向某个对象的指针,那么在使用完这个对象以后咱们须要本身删除它,例如:函数
ObjectType* temp_ptr = new ObjectType();测试
temp_ptr->foo();this
delete temp_ptr;spa
①若是忘记在调用完以后删除temp_ptr,那么会形成一个悬挂指针(dangling pointer),也就是说这个指针如今指向的内存区域其内容程序员没法把握和控制,也可能很是容易形成内存泄漏。设计
②但是事实上,不止是“忘记”,在上述的这一段程序中,若是foo()在运行时抛出异常,那么temp_ptr所指向的对象仍然不会被安全删除。指针
在这个时候,智能指针的出现实际上就是为了能够方便的控制对象的生命期,在智能指针中,一个对象何时和在什么条件下要被析构或者是删除是受智能指针自己决定的,用户并不须要管理。
有四种类型的智能指针:引用自https://www.zhihu.com/question/20368881/answer/14918675
1) scoped_ptr: 这是比较简单的一种智能指针,正如其名字所述,scoped_ptr所指向的对象在做用域以外会自动获得析构,一个例子是:https://www.boost.org/doc/libs/1_50_0/libs/smart_ptr/scoped_ptr.htm
此外,scoped_ptr是non-copyable的,也就是说你不能去尝试复制一个scoped_ptr的内容到另一个scoped_ptr中,这也是为了防止错误的屡次析构同一个指针所指向的对象。
2) shared_ptr: 不少人理解的智能指针实际上是shared_ptr这个范畴。shared_ptr中实现的本质是引用计数(reference counting),也就是说shared_ptr是支持复制的,复制一个shared_ptr的本质是对这个智能指针的引用次数加1,而当这个智能指针的引用次数下降到0的时候,该对象自动被析构。须要特别指出的是,若是shared_ptr所表征的引用关系中出现一个环,那么环上所述对象的引用次数都确定不可能减为0那么也就不会被删除,为了解决这个问题引入了weak_ptr。
3) weak_ptr: 对weak_ptr起的做用,不少人有本身不一样的理解,我理解的weak_ptr和shared_ptr的最大区别在于weak_ptr在指向一个对象的时候不会增长其引用计数,所以你能够用weak_ptr去指向一个对象而且在weak_ptr仍然指向这个对象的时候析构它,此时你再访问weak_ptr的时候,weak_ptr其实返回的会是一个空的shared_ptr。实际上,一般shared_ptr内部实现的时候维护的就不是一个引用计数,而是两个引用计数,一个表示strong reference,也就是用shared_ptr进行复制的时候进行的计数,一个是weak reference,也就是用weak_ptr进行复制的时候的计数。weak_ptr自己并不会增长strong reference的值,而strong reference下降到0,对象被自动析构。为何要采起weak_ptr来解决刚才所述的环状引用的问题呢?须要注意的是环状引用的本质矛盾是不能经过任何程序设计语言的方式来打破的,为了解决环状引用,第一步首先得打破环,也就是得告诉C++,这个环上哪个引用是最弱的,是能够被打破的,所以在一个环上只要把原来的某一个shared_ptr改为weak_ptr,实质上这个环就能够被打破了,原有的环状引用带来的没法析构的问题也就随之获得了解决。
4) intrusive_ptr: 简单的说,intrusive_ptr和shared_ptr的区别在于intrusive_ptr要求其所指向的对象自己实现一个引用计数机制,也就是说当对象自己包含一个reference counter的时候,可使用intrusive_ptr。在实际使用中我几乎历来没有见到过intrusive_ptr...
传统指针的问题:
当你在堆上建立了一个对象时,系统就把这个对象的生命期彻底交给了你,当用完以后,系统并不会回收资源,而是须要你来释放它。
那么,既然要负责对象的释放问题,就要知道何时释放和在哪里释放。若是你没有处理好这两个问题,就会形成内存泄漏或程序崩溃的问题。
//1 内存泄漏 str1所指的资源没有被释放 { string* str1 = new string("hello"); string* str2 = new string("world"); } //2 多重释放,引发程序崩溃 { string* str1 = new string("hello"); delete str1; //... delete str1;//引发程序崩溃 }
在上述的例子中,指针的释放是在同一做用域中进行的,你们在编程中也能很容易避免上述代码中的问题。
可是,对于一个大型项目,在某一处建立的对象,可能并不会在对应做用域中释放,而是等到某些事件发生,异常处理等状况下才会去销毁对象,对于这样的问题每每是很难排查出来的。
因此,有必要引用一种机制来负责指针的自动销毁。而不是由程序员自己去手动销毁。智能指针偏偏就是这样的一种机制。
引用计数的智能指针原理:(来自:知乎:小豆君的干货铺)
1. 当从堆上申请了一个资源时,咱们就建立一个智能指针对象,使它指向这个资源,同时,在堆上申请一个用于计数的资源,让后来全部的指向该资源的对象都共享这个计数资源,这样,引用计数的个数就只有一份。
2. 当将ptr1对象赋值给对象ptr2时,其共享的引用计数变为2。
3. 删除ptr2对象时,其对应的引用计数减为1。
4. 删除ptr1对象时,引用计数变为0,则释放资源。
下面这段代码来自:知乎:小豆君的干货铺
#pragma once template<class T> class SharedPointer { public: //默认构造函数,内部指针,未指向任何资源,引用计数为0,由于它未与任何资源绑定 SharedPointer() :m_refCount(nullptr), m_pointer(nullptr){} //构造函数,初始化时,指向一个已经分配好的资源 SharedPointer(T* adoptTarget) :m_refCount(nullptr), m_pointer(adoptTarget) { addReference(); } //构造函数,使用其它对象建立新对象 SharedPointer(const SharedPointer<T>& copy) :m_refCount(copy.m_refCount), m_pointer(copy.m_pointer) { addReference(); } //析构函数,引用计数递减,当为0时,释放资源 virtual ~SharedPointer() { removeReference(); } //赋值操做 //当左值被赋值时,代表它再也不指向所指的资源,故引用计数减一 //以后,它指向了新的资源,因此对应这个资源的引用计数加一 SharedPointer<T>& operator=(const SharedPointer<T>& that) { if (this != &that) { removeReference(); this->m_pointer = that.m_pointer; this->m_refCount = that.m_refCount; addReference(); } return *this; } //判断是否指向同一个资源 bool operator==(const SharedPointer<T>& other) { return m_pointer == other.m_pointer; } bool operator!=(const SharedPointer<T>& other) { return !operator==(other); } //指针解引用 T& operator*() const { return *m_pointer; } //调用所知对象的公共成员 T* operator->() const { return m_pointer; } //获取引用计数个数 int GetReferenceCount() const { if (m_refCount) { return *m_refCount; } else { return -1; } } protected: //当为nullpter时,建立引用计数资源,并初始化为1 //不然,引用计数加1。 void addReference() { if (m_refCount) { (*m_refCount)++; } else { m_refCount = new int(0); *m_refCount = 1; } } //引用计数减一,当变为0时,释放全部资源 void removeReference() { if (m_refCount) { (*m_refCount)--; if (*m_refCount == 0) { delete m_refCount; delete m_pointer; m_refCount = 0; m_pointer = 0; } } } private: int * m_refCount; T * m_pointer; };
对上述代码进行测试:
#include <iostream> #include <memory> #include <string> #include <vector> #include "SharedPointer.h" using namespace std; class MyClass { public: ~MyClass() { cout << "释放MyClass(" << _id << ")\n"; } MyClass(int i) :_id(i) { } void Print() const { cout << "MyClass(" << _id << ")" << endl; } private: int _id; }; int main() { { MyClass* px = new MyClass(1); SharedPointer<MyClass> ap(px); SharedPointer<MyClass> bp = ap; SharedPointer<MyClass> cp; cout << "ap的引用计数(2): " << ap.GetReferenceCount() << endl; cout << "将ap赋值给cp\n"; cp = ap; cout << "ap的引用计数(3): " << ap.GetReferenceCount() << endl; MyClass* qx = new MyClass(5); SharedPointer<MyClass> dp(qx); ap = dp; cout << "ap的引用计数(2): " << ap.GetReferenceCount() << endl; cout << "dp的引用计数(2): " << dp.GetReferenceCount() << endl; //"像指针同样使用智能指针" dp->Print(); (*cp).Print(); } cin.get(); }
参考:
https://baike.baidu.com/item/%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88/10784135?fr=aladdin