云南省植保植检站网址,郑州开发公司,网站创意设计方案,可信网站认证是否必须做文章目录 0. 概述智能指针#xff0c;智能在哪儿#xff1f;RAII 的介绍四个智能指针的特点#xff1a; 1. auto_ptr#xff08;C98#xff09;#x1f40e;核心功能的简单实现 2. unique_ptr#xff08;C11#xff09;#x1f40e;核心功能的简单实现 3. shared_ptr智能在哪儿RAII 的介绍四个智能指针的特点 1. auto_ptrC98核心功能的简单实现 2. unique_ptrC11核心功能的简单实现 3. shared_ptrC11核心功能的简单实现 0. 概述
智能指针智能在哪儿
使用了模板类建立的是 智能指针对象自动调用智能指针类型的构造和析构函数。也就是说对于动态开辟的空间如果用智能指针保存就不需要手动释放啦极大程度降低了内存泄漏的风险。 这样利用对象生命周期进行程序资源控制的技术就是 RAII。 对 * 和 - 的重载使 智能指针对象 具有指针的行为能力能让用户像使用指针一样的使用。
RAII 的介绍
RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源如内存、文件句柄、网络连接、互斥量等等的简单技术。
在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源。借此我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处
不需要显式地释放资源采用这种方式对象所需的资源在其生命期内始终保持有效。
四个智能指针的特点
我把四个智能指针的特点介绍在前面你若还有什么细节问题再去具体的栏目下翻找吧~
auto_ptr管理权转移通过拷贝构造函数和赋值重载函数来实现。 原对象拷贝给新对象的时候原对象就会被设置为nullptr此时就只有新对象指向一块资源空间。会出现指针悬空问题。 unique_ptr禁用拷贝构造和赋值构造 unique_ptr(unique_ptr) delete;operator(unique_ptr) delete; share_ptr引用计数 计数的对象在堆上所有线程都能访问因此需要锁保证其安全性会出现循环引用的问题 weak_ptr弱关联性 weak_ptr 类的对象它可以指向 shared_ptr并且不会改变 shared_ptr 的引用计数 1. auto_ptrC98 核心功能管理权转移 管理权转移的同时也会导致 原指针悬空容易造成野指针问题不推荐使用。
核心功能的简单实现
namespace ttang
{templateclass Tclass auto_ptr{public:auto_ptr(T* ptr):_ptr(ptr){}~auto_ptr(){if (_ptr){cout delete: _ptr endl;delete _ptr;}}// 无力吐槽的神拷贝...// 管理权转移导致的是原来的指针悬空很多公司明令禁止使用 auto_ptrauto_ptr(auto_ptrT ap):_ptr(ap._ptr){ap._ptr nullptr;}T* operator(auto_ptrT ap){T* tmp ap._ptr;ap._ptr nullptr;return tmp;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr;};
--------------------------------------------------void test_auto(){auto_ptrint ap1(new int(1));auto_ptrint ap2(ap1);*ap1 1; // err...管理权转移以后导致ap1悬空不能访问*ap2 1;}
}2. unique_ptrC11 核心功能防拷贝 delete 声明拷贝构造和复制重载 unique_ptr 的指针简单粗暴是防了拷贝不过也只解决了不需要拷贝的场景。 ps从 boost 里面吸收来的 pps需要拷贝的场景就需要使用到接下来会介绍的 shared_ptr 和 weak_ptr 了
核心功能的简单实现
namespace ttang
{templateclass Tclass unique_ptr{private:T* _ptr;public:unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout delete: _ptr endl;delete _ptr;}}T operator*(){return *_ptr;}T* operator-(){return _ptr;}// C11思路设置不许再实现了语法直接支持的不需要私有了unique_ptr(const unique_ptrT up) delete;unique_ptrT operator(const unique_ptrT up) delete; // 严格来说赋值也封了更好一点// C98思路只声明不实现但是用的人可能会在外面强行定义所以再加一条声明为私有//private:// unique_ptr(const unique_ptrT up);// unique_ptrT operator(const unique_ptrT up);};
--------------------------------------------------void test_unique(){unique_ptrint up1(new int(1));unique_ptrint up2(up1); // err...}
}3. shared_ptrC11 核心功能引用计数 之前在一开始的概述部分介绍了两个智能指针为什么智能的原因走到了 shared_ptr我们的智能指针就真的更神了他甚至还引申出 智能指针三大件 的说法
RAII想指针一样使用可以拷贝浅拷贝
他可以对同一个对象拷贝的同时还可以在最后一个智能指针使用完毕后调用其析构函数使用的是引用计数的技术。
在具体对 shared_ptr 实现之前对于这里的 引用计数可以稍微探讨一下
1如果要使用引用计数设置一个静态变量 count 行不行呢不行因为静态变量属于所有对象。而 每实例化一个对象都可能多有个资源每个资源应该配对一个引用计数。
2如果是多个线程去调用引用计数还需要保证其线程安全那就加个锁吧。
3计数加锁后shared_ptr 本身就会是线程安全的但是他生成的对象不是线程安全的。
核心功能的简单实现
namespace ttang
{templateclass Tclass shared_ptr{private:T* _ptr;int* _pcount;mutex* _pmtx; // 锁也得是指针因为是多个指针指向同一把锁functionvoid(T*) _del [](T* ptr) { // 解决对 deletor 的保存问题需要一个缺省的cout lambda delete: ptr endl;delete ptr;};public:shared_ptr(T* ptr nullptr):_ptr(ptr), _pcount(new int(1)) // 每个资源都分配一个引用计数count, _pmtx(new mutex) // 每个资源都有一把锁保证自己资源计数安全{}// 定制删除器通过仿函数实现的--是可调用对象所以我们拿的一个function定义_deltemplateclass Dshared_ptr(T* ptr, D del): _ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}~shared_ptr(){Release();}void Release(){_pmtx-lock();bool deleteFlag false;if (--(*_pcount) 0){if (_ptr){//cout delete: _ptr endl;//delete _ptr;_del(_ptr); // 如果_del 不给缺省的话这里默认的构造可能会出问题}delete _pcount;deleteFlag true;// delete _pmtx; 锁也要释放的呀可以下面又要解锁。如何解决}_pmtx-unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx-lock();(*_pcount);_pmtx-unlock();}shared_ptr(const shared_ptrT sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}// 正常赋值// sp1 sp4; 被赋值sp1的肯定要--sp4要// 自己给自己赋值// sp1 sp1; 自己给自己 // sp1 sp2;管理同样资源 也是自己给自己 if(sp ! this)不得行哦防不了这一种shared_ptrT operator(const shared_ptrT sp){if (_ptr ! sp._ptr){Release();_ptr sp._ptr;_pcount sp._pcount;_pmtx sp._pmtx;AddCount();}return *this;}T operator*(){return *_ptr;}T* operator-(){return _ptr;}T* get(){return _ptr;}int use_count(){return *_pcount;}};
}// 跟库里不一样简易版的蛤templateclass Tclass weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptrT sp):_ptr(sp.get()){}T operator*(){return *_ptr;}T* operator-(){return _ptr;}T* get(){return _ptr;}private:T* _ptr;};void test_shared(){shared_ptrint sp1(new int(1));shared_ptrint sp2(sp1);shared_ptrint sp3(sp2);shared_ptrint sp4(new int(10));//sp1 sp4;sp4 sp1;sp1 sp1;sp1 sp2;}// 多线程的问题struct Date{int _year 0;int _month 0;int _day 0;};// shared_ptr本身是线程安全的因为计数是加锁保护// shared_ptr管理的对象是否是线程安全不是void SharePtrFunc(ttang::shared_ptrDate sp, size_t n, mutex mtx){//cout sp.get() endl;//cout sp endl;for (size_t i 0; i n; i){// 这里智能指针拷贝会计数智能指针析构会--计数这里是线程安全的。ttang::shared_ptrDate copy(sp);mtx.lock();sp-_year;sp-_day;sp-_month;mtx.unlock();}}void test_shared_safe(){ttang::shared_ptrDate p(new Date);cout p.get() endl;const size_t n 10000;mutex mtx;thread t1(SharePtrFunc, ref(p), n, ref(mtx)); // 线程中以引用传递对象参数必须加一个ref()是一个库函数否则会认为是传值传参会报错。thread t2(SharePtrFunc, ref(p), n, ref(mtx)); // 13 底下可以检测19 是直接报错//cout p endl; t1.join();t2.join();cout p.use_count() endl;cout p-_year endl;cout p-_month endl;cout p-_day endl;}// 循环引用的问题struct ListNode{/*ListNode* _next;ListNode* _prev;*///ttang::shared_ptrListNode _next;//ttang::shared_ptrListNode _prev;ttang::weak_ptrListNode _next; // weak_ptr解决循环引用的问题他可以指向资源但是他不参与管理不增加应用计数ttang::weak_ptrListNode _prev;int _val;~ListNode(){cout ~ListNode() endl;}};// 循环引用场景// 有智能指针 n1 和 n2 指向结构体里面包含的智能指针又相互指了出现闭环导致_count无法到0无法析构// 循环引用--》内存泄漏// 主要原理成员的生命周期取决于对象的生命周期对象的生命周期结束则成员调用析构函数成员源于被另一个智能指针管理无法释放形成闭环。void test_shared_cycle(){/*ListNode* n1 new ListNode;ListNode* n2 new ListNode;n1-_next n2;n2-_prev n1; // 会出现抛异常未释放的情况所以我们现在并不推荐这么写// 那怎么写呢--》智能指针~delete n1;delete n2;*/ttang::shared_ptrListNode n1(new ListNode); // std 里面只能这样显示的调构造ttang::shared_ptrListNode n2(new ListNode);cout n1.use_count() endl;cout n2.use_count() endl;n1-_next n2; // 链接的时候智能指针n2怎么赋值给原生指针_next呢把ListNode里的指针改成智能指针就好了n2-_prev n1; cout n1.use_count() endl;cout n2.use_count() endl;}// weak_ptr// 1、他不是常规的智能指针不支持RAII// 2、支持像指针一样// 3、专门设计出来辅助解决shared_ptr的循环引用问题// weak_ptr可以指向资源但是他不参与管理不增加引用计数// 定制删除器 -- 可调用对象templateclass Tstruct DeleteArray{void operator()(T* ptr){cout void operator()(T* ptr) endl;delete[] ptr;}};//void test_shared_deletor()//{// std::shared_ptrDate spa1(new Date[10], DeleteArrayDate());// std::shared_ptrDate spa2(new Date[10], [](Date* ptr){// cout lambda delete[]ptr endl;// delete[] ptr; // });// std::shared_ptrFILE spF3(fopen(Test.cpp, r), [](FILE* ptr){// cout lambda fclose ptr endl;// fclose(ptr);// });//}void test_shared_deletor(){ttang::shared_ptrDate sp0(new Date);ttang::shared_ptrDate spa1(new Date[10], DeleteArrayDate());ttang::shared_ptrDate spa2(new Date[10], [](Date* ptr) {cout lambda delete[]: ptr endl;delete[] ptr;});ttang::shared_ptrFILE spF3(fopen(Test.cpp, r), [](FILE* ptr) {cout lambda fclose: ptr endl;fclose(ptr);});}
}// 智能指针的个间区别 unique、share、weak