免费销售网站模板下载安装,品牌注册查询官网入口,网站建设需求分析酒类,伊春市网站建设为什么要有智能指针#xff1f;
1.什么是智能指针#xff1f; 智能指针是一个类#xff0c;这个类的构造函数中传入一个普通指针#xff0c;析构函数中释放传入的指针。智能指针的类都是栈上的对象#xff0c;所以当函数#xff08;或程序#xff09;结束时会自动被释放…为什么要有智能指针
1.什么是智能指针 智能指针是一个类这个类的构造函数中传入一个普通指针析构函数中释放传入的指针。智能指针的类都是栈上的对象所以当函数或程序结束时会自动被释放。
2.为什么需要智能指针 指针在C的学习和使用中是必不可少的重要性可想而知如果对指针的理解不是很深入很容易产生野指针造成内存泄漏等问题。这时有人就会想到用一个“有思想”的指针知道自己什么时候该释放问题不就解决了吗。智能指针正好是针对这些问题所存在的因为它是存放在栈上的模板对象在栈内部包了一层指针。栈的生命周期结束时它里面的指针自然也就释放了。这样一个“有思想”的指针就实现了。
3.智能指针和普通指针的区别在哪里 智能指针实际是利用RAll(资源获取及初始化的技术对普通指针加了一层封装机制目的是为了使智能指针可以方便的管理一个对象的生命期。如果是普通指针使用这个对象之后我们需要删除它。如果忘记删除会造成一个野指针、内存泄漏等问题在调用运行这个函数的时候就会抛出异常。
智能指针的实现。
1.基于RAII思想的SmartPtr类 RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源的简单技术是C语言的一种管理资源、避免泄漏的惯用法。 这种思想是在对象构造的时候获取资源然后控制对资源的访问使之在对象生命周期始终保持有效最有在对象析构的时候释放资源。这种做法不需要显式地释放资源并且对象所需要的资源在其生命周期内始终保持有效。
templateclass T
class SmartPtr{
public:SmartPtr(T* ptr nullptr) :_ptr(ptr){}~SmartPtr(){if (_ptr)delete _ptr;}//SmartPtr写到这里还不能算是一个智能指针这个指针现在是符合了RAII机制//但是还不具有指针的行为需要将*和-重载才可以T operator*(){return *_ptr;}T operator-(){return _ptr}
};2.auto_ptr 在C98中就提出了智能指针auto_ptr auto_ptr的实现原理管理权转移的思想。 接下来通过简单地模拟实现auto_ptr来了解它的原理命名为Auto_Ptr
templateclass T
class Auto_Ptr
{
public:AutoPtr(T* ptr nullptr) :_ptr(ptr){}~AutoPtr(){if (_ptr)delete _ptr;}//一旦发生了拷贝就将sp中的资源转移到当前对象中然后sp与它所管理的资源断开练习//这样就解决了一块空间被多个对象使用造成的程序崩溃问题。AutoPtr(AutoPtrT sp){//检查是不是自己给自己赋值if (this ! sp){//释放当前对象中的资源if (_ptr)delete _ptr;//转移sp中资源到当前对象_ptr sp._ptr;sp._ptr nullptr;}return *this;}private:T* _ptr;
};auto_ptr存在的问题 当对象拷贝或者赋值后前面的对象就悬空了再使用前面对象访问资源就会出现问题 C98中设计的auto_ptr问题是非常明显的所以在实际工作中很多公司直接明确规定了不能使用auto_ptr这个智能指针已被C11明确声明不再支持使用。
3. unique_ptr C11中开始提供更靠谱的智能指针unique_ptr unique的实现原理防止被拷贝 接下来通过简单地模拟实现auto_ptr来了解它的原理命名为Unique_Ptr
templateclass T
class Unique_Ptr
{
public:Unique_Ptr(T* ptr nullptr) : _ptr(ptr){}~Unique_Ptr(){if (_ptr)delete _ptr;}Unique_Ptr operator*(){return *_ptr;}Unique_Ptr operator-(){return _ptr;}
private:Unique_Ptr(Unique_PtrT const );Unique_Ptr operator(Unique_PtrT const );//上述方法是C98中给出的防止被使用方法只声明不实现并将其生命为私有//下面是C11中给出的防止被使用的方法deleteUnique_Ptr(Unique_PtrT const )delete;Unique_Ptr operator(Unique_PtrT const )delete;T* _ptr;
};4.shared_ptr 基于前面智能指针不能拷贝的缺陷C11提供了更为可靠并且可以拷贝的shared_ptr shared_ptr的实现原理是通过引用计数的方式来实现多个shared_prt对象之间共享资源这就和一个宿舍都要去上课每个人走的时候都会通知让最后一个走的把门锁了。 shared_prt在内部给每个资源都维护者一份计数用来记录这份资源被多少个对象所共享。在对象调用析构函数的时候说明这个对象不在使用这份资源了他的引用计数要减一。如果他是最后一个使用者引用计数减一后为零他就得释放这块资源。如果不是最后一个使用者引用计数减一后不为零就不能释放这块资源。 接下来通过简单地模拟实现auto_ptr来了解它的原理命名为Shared_Ptr
#include thread
#include mutextemplateclass T
class Shared_Ptr
{
public:Shread_Ptr(T* ptr nullptr): _ptr(ptr), _pCount(new int(1)), _pMutex(new mutex){if (_ptr nullptr)*_ptrCount 0;}~Shread_Ptr(){Release();}//拷贝构造函数Shared_Ptr(Shared_PtrT sp):_ptr(sp._ptr), _pCount(sp._pCount), _pMutex(sp._pMutex){if (_ptr) //如果不是空指针增加引用计数AddCount();}//重载运算符Shared_PtrT operator(const Shared_PtrT sp){if (_prt ! sp._ptr) //检查是不是自己给自己赋值{Release(); //释放所管理的旧资源_ptr sp._ptr;_pCount sp._pCount;_pMutex sp._pMutex;if (_ptr) //如果不是空指针增加引用计数AddCount();}return *this;}Shared_PtrT operator*(){return *_ptr;}Shread_PtrT operator-(){return _ptr;}T* Get(){return _ptr;}
private://加减引用计数使用锁对操作进行加锁或者使用原子操作int AddCount(){_pMutex-lock();(*_pCount);_pMutex-unlock();return *_pCount;}int SubCount(){_pMutex-lock();--(*_pCount);_pMutex-unlock();return *_pCount;}void Release(){//如果指向的指针不为空并且这个对象是最后一个使用这个指针的人开始释放这份资源if (_ptr 0 SubCount()){delete _ptr;delete _pCount;}}private:T* _ptr; //指向管理资源的指针int *_pCount; //引用计数mutex* _pMutex; //互斥锁
};4.1 shared_ptr的线程安全问题。 假设在上面代码中对引用计数进行加减时没有加锁或者进行原子操作由于智能指针对象中引用计数是多个智能指针对象共享的两个线程中智能指针的引用计数同时进行加或减操作时这 个操作不是原子的引用计数原来是1加了两次可能还是2。这样引用计数就错乱了会导致资源没有按照约定释放或者程序崩溃的问题。所以智能指针中对引用计数的加减操作是必须要要加锁的也就是说引用计数的操作是线程安全的。
4.2 shared_ptr的循环引用问题。
先实现一个循环引用的场景
#include memory
#include iostreamusing namespace std;struct ListNode
{int _data;shared_ptrListNode _prev;shared_ptrListNode _next;~ListNode(){cout ~ListNode() endl;}
};int main()
{shared_ptrListNode node1(new ListNode);shared_ptrListNode node2(new ListNode);//打印这两个节点的引用计数cout node1.use_count() endl;cout node2.use_count() endl;node1-_next node2;node2-_prev node1;//打印这两个节点的引用计数cout node1.use_count() endl;cout node2.use_count() endl;return 0;
}打印结果如下 很明显没有调用析构函数画图解析
分析
a.node1和node2两个智能指针对象指向两个节点引用计数变为1。node1的_next指向node2node2的_prev指向node1,引用计数变为2。当两个智能指针释放空间时引用计数减到1但是_next还指向着下一个节点_prev还指向着上一个节点。也就是说如果想释放node1中的_next就先得释放它指向的node2但是如果想释放node2就得将node2的_next和_prev先释放而_prev指向了node1得先将node1释放了才能释放_prev。这样一来如果想释放node1就得先释放node2要释放node2就得先释放node1。这样就构成了循环引用谁也不会释放 针对这一问题解决方案是在引用计数的场景下把节点中的_prev和_next换成weak_ptr就可以了。其中的原理是当node1-_next node2;和node2-_prev node1时weak_ptr的_next和_prev不会增加 node1和node2的引用计数。