当前位置: 首页 > news >正文

贵阳网站建设搜王道下拉如何提高网页设计

贵阳网站建设搜王道下拉,如何提高网页设计,做企业网站可以没有后台吗,西安网约车平台#x1f308;欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ )#x1f423;,我是Scort目前状态#xff1a;大三非科班啃C中#x1f30d;博客主页#xff1a;张小姐的猫~江湖背景快上车#x1f698;#xff0c;握好方向盘跟我有一起打天下嘞#xff01;送给自己的一句鸡汤欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ ),我是Scort目前状态大三非科班啃C中博客主页张小姐的猫~江湖背景快上车握好方向盘跟我有一起打天下嘞送给自己的一句鸡汤真正的大师永远怀着一颗学徒的心作者水平很有限如果发现错误可在评论区指正感谢欢迎持续关注 文章目录欢迎来到C专栏~~智能指针一. 为什么需要智能指针智能指针的原理二. C中的智能指针登场auto_ptrunique_ptrshared_ptr基本设计模拟实现线程安全问题定制删除器weak_ptrshared_ptr的循环引用问题weak_ptr解决循环引用C11和boost中智能指针的关系常见面试题写在最后一. 为什么需要智能指针 下面我们先分析一下下面这段程序有没有什么内存方面的问题提示一下注意分析MergeSort函数中的问题 int div() {int a, b;cin a b;if (b 0)throw invalid_argument(除0错误);return a / b; }void Func() {// 1、如果p1这里new 抛异常会如何// 2、如果p2这里new 抛异常会如何// 3、如果div调用这里又会抛异常会如何int* p1 new int;//p1抛异常直接跳catch没毛病int* p2 new int;//p2跑异常程序从这一步跳catchp1无法释放资源泄露cout div() endl; // div抛异常调到catchp1, p2无法释放资源泄露delete p1;delete p2; }int main() {try{Func();}catch (exception e){cout e.what() endl;}return 0; }问题分析上面的问题分析出来我们发现有什么问题 会有严重的内存泄漏的问题如果用户输入的除数为0那么div函数中就会抛出异常这时程序的执行流会直接跳转到主函数中的catch块中执行最终导致func函数中申请的内存资源没有得到释放 对此我们需要重新捕获异常捕获后先将之前申请的内存资源释放然后再将异常重新抛出 还有一种方法就是智能指针 //利用RAII思想设计delete资源的类 templateclass T class Smartptr { public:Smartptr(T* ptr):_ptr(ptr){}~Smartptr(){delete _ptr;}T operator*() //解引用{return *ptr;}T* operator-() //自定义类型{return ptr;}private:T* _ptr; };void Func() {int* p1 new int;//p1抛异常直接跳catch没毛病Smartptrint sp1(p1);//栈帧结束会调用析构函数Smartptrint sp2(new int);cout div() endl; // div抛异常调到catchp1, p2无法释放资源泄露 }代码中将申请到的内存空间交给了一个SmartPtr对象进行管理 构造的时候SmartPtr将传入的需要被管理的内存空间保存起来SmartPtr对象析构时SmartPtr的析构函数中会自动将管理的内存空间进行释放为了让SmartPtr对象能够像原生指针一样使用还需要对*和-运算符进行重载 如此一来无论是正常返回抛异常的返回只要SmartPtr对象的生命周期结束就会调用其对应的析构函数进而完成内存资源的释放。 智能指针的原理 实现智能指针时需要考虑以下三个方面的问题 在对象构造时获取资源接着控制对资源的访问使之在对象的生命周期内始终保持有效最后在对象析构的时候释放资源对*和-运算符进行重载使得该对象具有像指针一样的行为智能指针的拷贝问题 运用了RAII的思想RAIIResource Acquisition Is Initialization是一种利用对象生命周期来控制程序资源如内存、文件句柄、网络连接、互斥量等等的简单技术 我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处 不需要显式地释放资源采用这种方式对象所需的资源在其生命期内始终保持有效 为什么要解决智能指针对象的拷贝问题呢 对于当前实现的SmartPtr类如果用一个SmartPtr对象来拷贝构造另一个SmartPtr对象或是将一个SmartPtr对象赋值给另一个SmartPtr对象都会导致程序崩溃 int main() {SmartPtrint sp1(new int);SmartPtrint sp2(sp1); //拷贝构造SmartPtrint sp3(new int);SmartPtrint sp4(new int);sp3 sp4; //拷贝赋值return 0; }编译器默认生成的拷贝构造对内置类型完成值拷贝即浅拷贝因此用 sp1 拷贝构造 sp2 后相当于 sp1 和 sp2 管理了同一块内存空间当 sp1 和 sp2 析构时就会导致这块空间被释放两次 那么这里可以使用深拷贝吗不可以违背了功能的需求智能指针就是要模拟原生指针的行为当我们将一个指针赋值给另一个指针时目的就是让这两个指针指向同一块内存空间所以这里本就应该进行浅拷贝但单纯的浅拷贝又会导致空间被多次释放因此根据解决智能指针拷贝问题方式的不同从而衍生出了不同版本的智能指针 二. C中的智能指针登场 auto_ptr 管理权转移资源管理权转移不负责任的拷贝会导致被拷贝对象悬空 int main() {auto_ptrA ap1(new A);ap1-_a1;ap1-_a2;auto_ptrA ap2(ap1);return 0; }但一个对象的管理权转移后也就意味着该对象不能再用对原来管理的资源进行访问了否则程序就会崩溃因此使用auto_ptr之前必须先了解它的机制否则程序很容易出问题很多公司也都明确规定了禁止使用auto_ptr auto_ptr的模拟实现 实现步骤如下 1️⃣在构造函数中获取资源在析构函数中释放资源利用对象的生命周期来控制资源(RAII)2️⃣对*和-运算符进行重载使auto_ptr对象具有指针一样的行为3️⃣在拷贝构造函数中用传入对象管理的资源来构造当前对象并将传入对象管理资源的指针置空4️⃣在拷贝赋值函数中先将当前对象管理的资源释放然后再接管传入对象管理的资源最后将传入对象管理资源的指针ap置空 templateclass T class auto_ptr { public:auto_ptr(T* ptr nullptr):_ptr(ptr){}auto_ptr(auto_ptrT ap):_ptr(ap._ptr){ap._ptr nullptr; //管理权转移后ap置空}auto_ptrT operator(auto_ptrT ap){//检测是否为自己给自己赋值if (this ! ap){// 释放当前对象中资源if (_ptr)delete _ptr;//转移ap中资源到当前对象中_ptr ap._ptr;ap._ptr nullptr;}return *this;}~auto_ptr(){delete _ptr;}//像指针一样使用T operator*() //解引用{return *_ptr;}T* operator-() //自定义类型{return _ptr;}private:T* _ptr; };unique_ptr C11中引入的智能指针unique_ptr通过 防止拷贝 delete 的方式解决智能指针的拷贝问题 简单粗暴不让拷贝 void test_unique_ptr() {std::unique_ptrA up1(new A);//std::unique_ptrA up2(up1);//出错 }但是总会有需要拷贝的场景吧 模拟实现如下 在构造函数中获取资源在析构函数中释放资源利用对象的生命周期来控制资源对*和-运算符进行重载使unique_ptr对象具有指针一样的行为用C98的方式将拷贝构造函数和拷贝赋值函数声明为私有只声明不实现或者用C11的方式在这两个函数后面加上delete防止外部调用 templateclass T class unique_ptr { public:unique_ptr(T* ptr nullptr):_ptr(ptr){}//防止拷贝unique_ptr(unique_ptrT ap) delete;unique_ptrT operator(unique_ptrT ap) delete;~unique_ptr(){delete _ptr;}//像指针一样使用T operator*() //解引用{return *_ptr;}T* operator-() //自定义类型{return _ptr;}private:T* _ptr; };如果面试官要我们现场手撕那我们就撕一个unique_ptr shared_ptr 基本设计 是通过引用计数的方式来实现多个shared_ptr对象之间共享资源 每一个被管理的资源都有一个对应的引用计数通过这个引用计数记录着当前有多少个对象在管理着这块资源当新增一个对象管理这块资源时则将该资源对应的引用计数进行每个对象释放时--计数最后一个析构的对象释放资源 通过这种引用计数的方式就能支持多个对象一起管理某一个资源也就是支持了智能指针的拷贝并且只有当一个资源对应的引用计数减为0时才会释放资源因此保证了同一个资源不会被释放多次 void test_shared_ptr() {ljj::shared_ptrA sp1(new A);ljj::shared_ptrA sp2(sp1);sp1-_a1;sp1-_a2;std::cout sp2-_a1 : sp2-_a2 std::endl;//1 1sp2-_a1;sp2-_a2;std::cout sp1-_a1 : sp1-_a2 std::endl;//2 2 }模拟实现 shared_ptr的模拟实现步骤如下 1️⃣增加一个成员变量count表示智能指针对象管理的资源对应的引用计数2️⃣在构造的时候获取资源并将count设置为1 说明只有一个新建的对象在管理3️⃣拷贝构造的时候传入对象一起管理它管理的资源同时将该资源对应的count4️⃣在拷贝赋值函数中先将当前对象管理的资源对应的引用计数--如果减为0则需要释放然后再与传入对象一起管理它管理的资源同时需要将该资源对应的引用计数看下图5️⃣析构时候需要将count--如果减为0彻底释放资源6️⃣对*和-运算符进行重载使shared_ptr对象具有指针一样的行为 templateclass T class shared_ptr { public://RAIIshared_ptr(T* ptr nullptr):_ptr(ptr),_pCount(new int(1)) //构造的时候设为1{}//拷贝构造shared_ptr(shared_ptrT sp):_ptr(sp._ptr),_pCount(sp._pCount) //把引用指针给与共同管理{(*_pCount);}// sp1 sp5shared_ptrT operator(shared_ptrT sp){//防止同一块资源之间赋值if (_ptr ! sp._ptr){//sp的资源-- 并且判断是否要置空if ((*_pCount)-- 0){cout delete: _ptr endl;delete _ptr;delete _pt;}//一起管理新资源计数_ptr sp._ptr;_pCount sp._pCount;(*_pCount);return *this;}}~shared_ptr(){if (--(*_pCount) 0 _ptr){std::cout Delete _ptr std::endl;delete _ptr;delete _pCount;}}//像指针一样使用T operator*() //解引用{return *_ptr;}T* operator-() //自定义类型{return _ptr;}private:T* _ptr;int* _pCount; // 引用计数//int count; };为什么引用计数要设计成指针放在堆区 首先count不能设置成一个int类型的成员变量这意味着每一个对象都有属于自己的count如果多个对象要管理一个资源的时候岂不是乱套了 还有就是count也不能定义成一个静态的成员变量因为静态成员变量是所有类型对象共享的所以无论什么类型的对象都是共用一个count 所以每个资源需要管理时给构造函数构造new一个引用计数指针在堆区开辟一块空间用于存储其对应的引用计数如果有其他对象也想要管理这个资源那么除了将这个资源给它之外还需要把这个引用计数也给它 线程安全问题 后续补上 定制删除器 当智能指针对象的生命周期结束时所有的智能指针默认都是以delete的方式将资源释放这是不太合适的因为智能指针并不是只管理以new方式申请到的内存空间智能指针管理的也可能是以new[]的方式申请到的空间或管理的是一个文件指针 struct Node {int _val;ljj::weak_ptrNode _next;ljj::weak_ptrNode _prev;~Node(){cout ~Node endl;} }; void test_shared_ptr2() {std::shared_ptrNode n1(new Node);std::shared_ptrNode n1(new Node[5]);//errorstd::shared_ptrFILE sp2(fopen(test.cpp, r)); //errorstd::shared_ptrint n3(new int[5]);//内置类型没问题 }这里为什么内置类型可以通过但是自定义类型就会报错呢 涉及指针偏移 对于内置类型new实际在底层调用了malloc调用delete最终还是会调用到free自定义类型看下图 不管它底层崩不崩我们要匹配好new[]的方式申请到的内存空间必须以delete[]的方式进行释放而文件指针必须通过调用fclose函数进行释放 这时就需要用到定制删除器来控制释放资源的方式C标准库中的shared_ptr提供了如下构造函数 template class U, class D shared_ptr (U* p, D del);//构造函数参数说明 p需要让智能指针管理的资源del删除器这个删除器是一个可调用对象比如函数指针、仿函数、lambda表达式以及被包装器包装后的可调用对象 //仿函数 templateclass T struct DeleteArray {void operator()(T* ptr){cout delete[] ptr endl;delete[] ptr;} };//定值删除器 void test_shared_ptr2() {//仿函数对象std::shared_ptrNode n1(new Node);std::shared_ptrNode n2(new Node[5], DeleteArrayNode());std::shared_ptrint n3(new int[5], DeleteArrayint());//内置类型没问题//lambda对象std::shared_ptrNode l1(new Node);std::shared_ptrNode l2(new Node[5], [](Node* ptr) { delete[] ptr; });std::shared_ptrint l3(new int[5], [](int* ptr) { delete[] ptr; });std::shared_ptrint l4((int*)malloc(sizeof(12)), [](int* ptr) { free(ptr); });std::shared_ptrFILE l5(fopen(test.txt, w), [](FILE* ptr) { fclose(ptr); }); }最好用的肯定是lambda对象 在unique_ptr中却是在模板参数中给的只能在模板参数中传类型不能用lambda对象 int main() {//不能用lambdastd::unique_ptrNode up(new Node[5]);//error//模板中传的是类型不是传对象不能加(),std::unique_ptrNode, DeleteArrayNode up(new Node[5]); }模拟删除器的实现 要在当前模拟实现的shared_ptr的基础上支持定制删除器就只能给shared_ptr类再增加一个模板参数在构造shared_ptr对象时就需要指定删除器的类型。然后增加一个支持传入删除器的构造函数在构造对象时将删除器保存下来在需要释放资源的时候调用该删除器进行释放即可。最好在设置一个默认的删除器如果用户定义shared_ptr对象时不传入删除器则默认以delete的方式释放资源 namespace ljj {//默认删除器templateclass Tclass Delete{void operator()(T* ptr){delete ptr;}};templateclass T, class D DeleteTclass shared_ptr{public:void Release(){if (--(*_pCount) 0 _ptr){//cout Delete _ptr endl;//delete _ptr;//D del;//del(_ptr);D()(_ptr);//无参构造对象operator()去决定是free还是delete等等delete _pCount;}}~shared_ptr(){Release();}private:T* _ptr;int* _pCount; // 引用计数}; }weak_ptr shared_ptr的循环引用问题 shared_ptr的循环引用问题在一些特定的场景下才会产生。比如定义如下的结点类并在结点类的析构函数中打印一句提示语句便于判断结点是否正确释放 struct Node {int _val;std::shared_ptrNode _next;std::shared_ptrNode _prev;~Node(){cout ~Node endl;} };现在以new的方式在堆上构建两个结点并将这两个结点连接起来又为了防止抛异常我们把类型改成智能指针 struct Node {int _val;std::shared_ptrNode _next;std::shared_ptrNode _prev;~Node(){cout ~Node endl;} }; int main() {std::shared_ptrNode n1 (new Node);std::shared_ptrNode n2 (new Node);n1-_next n2; //智能指针和原生指针不能直接赋值n2-_prev n1;//...return 0; }此时两个结点都没有被释放但如果去掉连接结点时的两句代码中的任意一句那么这两个结点就都能够正确释放根本原因就是因为这两句连接结点的代码导致了循环引用 下面来一探究竟吧 循环引用导致资源未被释放的原因 很绕 n2先析构n1再析构所以引用计数都变成了1右边的节点释放取决于_next析构_next什么时候析构取决于左边节点的析构作为成员左边节点的析构取决于_prev的析构prev什么时候析构取决于右边节点的析构右边的节点释放又取决于_next析构 二者互相纠缠对此shared_ptr也是无能为力又要引进一员大将weak_ptr weak_ptr解决循环引用 weak_ptr就是shared_ptr的小跟班不是常规智能指针没有RAII不支持直接资源管理 weak_ptr主要用shared_ptr构造用来解决引用问题构造出来的 weak_ptr对象不参与资源释放管理可以访问和修改资源但不会增加这块资源对应的引用计数 解决方案在引用计数的场景下把节点中的_prev和_next改成weak_ptr就可以了 原理就是node1-_next node2;和node2-_prev node1;时weak_ptr的_next和 _prev不会增加node1和node2的引用计数引用计数都是1就可以按先后顺序释放了 struct Node {int _val;std::weak_ptrNode _next;std::weak_ptrNode _prev;~Node(){cout ~Node endl;} };//循环引用 void test_weak_ptr() {std::shared_ptrNode n1 (new Node);std::shared_ptrNode n2 (new Node);cout n1.use_count() endl;cout n2.use_count() endl;n1-_next n2; //智能指针和原生指针不能直接赋值;所以next变成智能指针n2-_prev n1;cout n1.use_count() endl;cout n2.use_count() endl; }通过use_count获取这两个资源对应的引用计数就会发现在结点连接前后这两个资源对应的引用计数就是1根本原因就是weak_ptr不会增加管理的资源对应的引用计数 weak_ptr的模拟实现 提供一个无参的构造函数、shared_ptr对象拷贝构造、weak_ptr对象拷贝赋值、shared_ptr对象拷贝赋值给weak_ptr 对*和-运算符进行重载使weak_ptr对象具有指针一样的行为 //辅助型智能指针配合解决shared_ptr的循环引用问题 templateclass T class weak_ptr { public:weak_ptr() //无参:_ptr(nullptr){}weak_ptr(const weak_ptrT wp) //weak类型构造:_ptr(wp._ptr){}weak_ptr(const shared_ptrT sp) //shared_ptr类型构造:_ptr(sp.get()){}weak_ptrT operator (const shared_ptrT sp){_ptr sp.get();return *this;}//像指针一样使用T operator*(){return *_ptr;}T* operator-(){return _ptr;}private:T* _ptr; };注意 shared_ptr还会提供一个get函数用于获取其管理的_ptr C11和boost中智能指针的关系 C98中产生了第一个智能指针auto_ptrCboost给出了更实用的scoped_ptr、shared_ptr和weak_ptrCTR1引入了boost中的shared_ptr等。不过注意的是TR1并不是标准版C11引入了boost中的unique_ptr、shared_ptr和weak_ptr。需要注意的是unique_ptr对应的就是boost中的scoped_ptr并且这些智能指针的实现原理是参考boost中实现的 说明一下boost库是为C语言标准库提供扩展的一些C程序库的总称boost库社区建立的初衷之一就是为C的标准化工作提供可供参考的实现比如在送审C标准库TR1中就有十个boost库成为标准库的候选方案。 常见面试题 为什么需要智能指针 —— 忘记释放/异常安全RAII是什么发展历史auto_ptr/unique_ptr/shared_ptr/weak_ptr之间区别和使用场景模拟实现简洁版的智能指针没说就实现unique不然就是shared的什么是循环引用如何解决循环引用解决的原理是什么 写在最后
http://www.hkea.cn/news/14256491/

相关文章:

  • 网站开发PRD做模板网站价格
  • 做网站推广的需要了解哪些知识南宁在那里推广网站
  • 怎么做一个门户网站温州网站建设seo
  • 网站制作维护费 归属泉州网络公司都
  • 男装网站模板演示如何提取网页中的视频
  • 织梦教育网站开发电子商务的建站流程
  • 网站开发的目的意义特色创新开发工具有哪些
  • 建设一个网站需要贵阳经济技术开发区网站
  • 大网站开发费用网站优化要怎么做
  • 机械类网站建设网站建设的相关技术方案
  • 合肥最好的网站建设公司排名物联网的网络架构
  • 网站建设基本知识代码做网站攻击
  • 四川省铁路建设有限公司网站广州互联网营销师
  • 安徽网站建设获客企业wordpress调用产品图片
  • 高效网站建设与维护岗位职责快手网页版
  • 广西建设职业技术学院网站wordpress 设计
  • 深圳网站公司招聘信息新开传奇网站排行
  • jsp酒店预订网站开发手机能开wordpress吗
  • 汕头企业网站模板建站东莞市建设信息网
  • 恩施做网站公司响应式网页模板制作
  • 现在的网站开发框架怎么自学室内设计与装修
  • 培训网站建设公司排名wordpress虚拟3d网站
  • 企业营销网站建设步骤天美影视传媒的广告片拍摄技巧
  • 在哪个网站上找超市做生鲜怎么做网站的ico
  • 永嘉网站制作哪家好优秀的软文广告案例
  • 工艺品网站模版今天有什么新闻
  • 衡水网站建设选哪家wordpress仿淘宝页面
  • 如何建设网站 企业网络推广渠道有哪些及策划思路
  • 八度 网站建设微网站建设使用程序
  • 网站建设费可以进广告宣传费吗使用wordpress的企业