网站主页用ps做,nike定制在哪个app,泰安房产网数据中心,怎么在主机上的建设网站一、引言 为什么需要智能指针#xff1f; 在上一篇异常中#xff0c;关于内存释放#xff0c;我们提到过一个问题---当我们申请资源之后#xff0c;由于异常的执行#xff0c;代码可能直接跳过资源的释放语句到达catch#xff0c;从而造成内存的泄露#xff0c;对于这种… 一、引言 为什么需要智能指针 在上一篇异常中关于内存释放我们提到过一个问题---当我们申请资源之后由于异常的执行代码可能直接跳过资源的释放语句到达catch从而造成内存的泄露对于这种情况我们当时的解决方案是在抛出异常后我们先对异常进行捕获将资源释放再将异常抛出但这样做会使得代码变得很冗长那有没有什么办法能让它自动释放内存资源呢用智能指针 什么是智能指针 说到自动释放资源是不是有点熟悉我们在学习创建类对象时就知道当类对象的生命周期结束后系统会自动调用它的析构函数完成资源的释放那么我将指针放入这样一个类对象中将释放资源的工作交给析构函数只要该对象生命周期结束那么就释放该资源如此就不用在关心资源的释放问题只要函数栈帧销毁即该对象被销毁资源就会自动释放这就叫智能指针。 智能指针的使用和原理 1.RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源如内存、文件句柄、网络连接、互斥量等等的简单技术。 在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源。借此我们实际上把管理一份资源的责任托管给了一个对象 不需要显式地释放资源。 采用这种方式对象所需的资源在其生命期内始终保持有效 2.具有指针的行为可以解引用也可以通过-去访问所指空间中的内容 下面写一个简单的智能指针 namespace zxws
{templateclass Tclass smart_ptr{public:smart_ptr(T* ptr nullptr):_ptr(ptr){}~smart_ptr(){cout delete _ptr endl;delete _ptr;_ptr nullptr;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
} 但是上面这个智能指针有个严重的问题一旦有两个对象同时指向同一个资源那么析构函数就会被调用两次即资源要被释放两次会报错如下 二、库中的智能指针
C官方给出了3个智能指针 1.auto_ptr
auto_ptr管理权转移的思想即一个资源只能有一个指针能对它进行管理其他的指向这一资源的指针均为空实现如下
namespace zxws
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr nullptr):_ptr(ptr){}//管理权限的转移auto_ptr(const auto_ptr tmp):_ptr(tmp._ptr){tmp._ptr nullptr;}auto_ptr operator(const auto_ptr tmp){if (this ! tmp)//注意自己给自己赋值的情况不需要处理否则会出问题{if (_ptr)//释放当前对象中资源delete _ptr;//管理权限转移_ptr tmp._ptr;tmp._ptr nullptr;}return *this;}~auto_ptr(){if (_ptr){delete _ptr;_ptr nullptr;}}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
} 2.unique_ptr
unique_ptr简单粗暴的防拷贝即一个指针只能被初始化一次且只能用不同的资源初始化
实现如下
namespace zxws
{templateclass Tclass unique_ptr{public:unique_ptr(T* ptr nullptr):_ptr(ptr){}//将拷贝构造和赋值重载直接ban掉unique_ptr(const unique_ptr tmp) delete;unique_ptr operator(const unique_ptr tmp) delete;~unique_ptr(){if (_ptr){delete _ptr;_ptr nullptr;}}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
}
3.shared_ptr shared_ptr是通过引用计数的方式来实现多个shared_ptr对象之间共享资源 具体原理如下 1. shared_ptr在其内部给每个资源都维护了着一份计数用来记录该份资源被几个对象共享。 2. 在对象被销毁时(也就是析构函数调用)就说明自己不使用该资源了对象的引用计数减一。 3. 如果引用计数是0就说明自己是最后一个使用该资源的对象必须释放该资源 4. 如果不是0就说明除了自己还有其他对象在使用该份资源不能释放该资源否则其他对象就成野指针了。 实现如下 namespace zxws
{ templateclass Tclass shared_ptr{public:shared_ptr(T* ptr nullptr):_ptr(ptr),_pcount(new int(1)){}shared_ptr(const shared_ptr tmp):_ptr(tmp._ptr),_pcount(tmp._pcount){(*_pcount);}shared_ptr operator(const shared_ptr tmp){//这里注意自己给自己赋值的情况//当引用计数为1时就会出现将资源释放后在赋值的尴尬情况//用this!tmp也没用可能出现两个不同对象指向同一块资源的情况//所以用资源的地址来判断最准确if (_ptr ! tmp._ptr){release();_ptr tmp._ptr;_pcount tmp._pcount;(*_pcount);}return *this;}void release(){if (--(*_pcount)0){delete _ptr;delete _pcount;_pcount nullptr;_ptr nullptr;}}~shared_ptr(){release();}T operator*(){return *_ptr;}T* operator-(){return _ptr;}T* get() const{return _ptr;}int use_count() const{return *_pcount;}private:T* _ptr;int* _pcount;};
} 那么引用计数为什么要用指针开辟的空间而不是成员变量或者静态成员变量 1、如果是成员变量那么每一个shared_ptr对象都会有一个_pcount 2、如果是静态成员变量那么_pcount将属于一个类 两者都不能满足我们的需求 关于shared_ptr还存在一个循环引用的问题场景如下 当我们将循环链表的两个结点连接起来的时候就不会释放结点空间但是只要有一条边没链接就都能释放为什么 而只连接一条边这个闭环就不复存在所以两个结点都能释放那如何解决这种情况
针对这种情况C官方设计出了weak_ptr来和shared_ptr搭配使用也就是说weak_ptr不增加shared_ptr的引用计数且不参与资源的释放
实现如下
namespace zxws
{ templateclass Tclass weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT tmp):_ptr(tmp.get()){}weak_ptr operator(const shared_ptrT tmp){_ptr tmp.get();return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
} 上面三个智能指针的模拟实现是被简化过的功能不全但是核心就是这些
其中auto_ptr这个智能指针基本不用
上面写的三个智能指针还有一个缺陷就是释放资源的delete写死了如果我们开的是一个数组就需要用delete[]否则资源的释放就会出现问题所以就需要我们定制化它们的释放资源的方式根据前面的知识我们可以给它传一个释放资源的仿函数如下
templateclass T
struct Destroy {void operator()(T*_ptr){delete[] _ptr;}
};
templateclass T, class D
class shared_ptr
{//....
};
shared_ptrint, Destroyintp;
但是库中只写了一个模板参数 我们如果想实现和库中一样的效果该怎么写
既然传模板参数不行我们只能传函数对象了用function包装器和lambda表达式实现如下
namespace zxws
{templateclass Tclass shared_ptr{public:shared_ptr(T* ptr nullptr):_ptr(ptr),_pcount(new int(1)){}shared_ptr(T* ptr,functionvoid(T*) del):_ptr(ptr),_pcount(new int(1)),_del(del){}shared_ptr(const shared_ptr tmp):_ptr(tmp._ptr),_pcount(tmp._pcount),_del(tmp._del){(*_pcount);}shared_ptr operator(const shared_ptr tmp){//这里注意自己给自己赋值的情况//当引用计数为1时就会出现将资源释放后在赋值的尴尬情况//用this!tmp也没用可能出现两个不同对象指向同一块资源的情况//所以用资源的地址来判断最准确if (_ptr ! tmp._ptr){release();_ptr tmp._ptr;_pcount tmp._pcount;_del tmp._del;(*_pcount);}return *this;}void release(){if (--(*_pcount)0){_del(_ptr);delete _pcount;_ptr nullptr;_pcount nullptr;}}~shared_ptr(){release();}T operator*(){return *_ptr;}T* operator-(){return _ptr;}T* get() const{return _ptr;}int use_count() const{return *_pcount;}private:T* _ptr;int* _pcount;functionvoid(T*)_del [](T* ptr) {delete ptr; };};
} 其他几个智能指针写法类似就不写了。