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

学校多语种网站建设方案网络广告的形式

学校多语种网站建设方案,网络广告的形式,网站做推广需要什么条件,memcache wordpress目录 智能指针意义 智能指针的使用及原理 RAII 智能指针的原理 std::auto_ptr std::auto_ptr的模拟实现 std::unique_ptr std::unique_ptr模拟实现 std::shared_ptr std::shared_ptr的模拟实现 循环引用问题 智能指针意义 #问:为什么需要智能指针&#…

目录

智能指针意义

智能指针的使用及原理

RAII

智能指针的原理

std::auto_ptr

std::auto_ptr的模拟实现 

std::unique_ptr

std::unique_ptr模拟实现

std::shared_ptr

std::shared_ptr的模拟实现

循环引用问题


智能指针意义

#问:为什么需要智能指针?

#include <iostream>
#include <stdexcept>int div()
{int a, b;std::cin >> a >> b;if (b == 0)throw std::invalid_argument("除0错误");return a / b;
}void Func()
{// 1、如果p1这里new 可能会抛异常。// 2、如果p2这里new 可能会抛异常。// 3、如果div调用也可能会抛异常。int *p1 = new int;int *p2 = new int;std::cout << div() << std::endl;delete p1;delete p2;
}int main()
{try{Func();}catch (std::exception &e){std::cout << e.what() << std::endl;}return 0;
}

        在一异常中,因为异常会导致程序的执行流乱跳,所以很多个会出现异常的代码,放在一起就很容易其中一个抛异常,而导致其余的未执行 / 需要释放的空间未释放。如上:p2出问题,需要释放p1,div出问题,需要释放p1、p2,智能指针就是用来解决这个问题。

智能指针的使用及原理

RAII

        RAII(Resource Acquisition Is Initialization - 获取资源即初始化)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。资源:需要手动释放的。(RAII:请求到志愿就初始化)

初始化指的是:
        调用一个其他类的构造函数,利用其他类的生命周期来进行管理。

        在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上:把管理一份资源的责任托管给了一个对象

        就是在获取到资源的时候,交给一个对象管理,于是在该对象的生命周期里这个资源始终有效。而这个对象无论如何是异常还是正常结束,都会调用这个对象的析构函数,于是利用这个对象的析构函数释放资源。

这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

智能指针的原理

最基础的智能指针:

// 使用RAII思想设计的SmartPtr类
template <class T>
class SmartPtr
{
public:SmartPtr(T *ptr = nullptr) // 将资源给智能指针: _ptr(ptr) // 智能指针将资源保存{}~SmartPtr(){if (_ptr)delete _ptr;}private:T *_ptr;
};
        上述的SmartPtr还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过"->"去访问所指空间中的内容,因此:智能指针模板类中还得需要将"*""->"重载下,才可让其像指针一样去使用
#include <iostream>
#include <stdexcept>// 1、利用RAII思想设计delete资源的类
// 2、像指针一样的行为
template<class T>
class SmartPtr
{
public:SmartPtr(T* ptr) // 将资源给智能指针:_ptr(ptr) // 智能指针将资源保存{}~SmartPtr(){cout << "delete:" << _ptr << endl;delete _ptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};int div()
{int a, b;std::cin >> a >> b;if (b == 0)throw std::invalid_argument("除0错误");return a / b;
}
void Func()
{// sp1、sp2出作用域会调用析构函数,抛异常,栈帧会正常结束SmartPtr<int> sp1(new int);SmartPtr<int> sp2(new int);*sp1 = 0;*sp2 = 2;std::cout << div() << std::endl;
}int main()
{try{Func();}catch (std::exception& e){std::cout << e.what() << std::endl;}return 0;
}

总结一下智能指针的原理:
  1. RAII特性。
  2. 重载operator*和opertaor->,具有像指针一样的行为。

Note:

        智能指针看起来很完美,但是又一个致命的问题:智能指针的拷贝问题。默认的拷贝构造只会进行浅拷贝,就会导致一个地址被析构两次。主要原因就是:智能指针管理资源的释放。

解决方案:

问:深拷贝?

        不能,违背了智能指针的功能需求,需要的就是浅拷贝,智能指针不知道该空间有多大,只是对与指针的保存。

(问题先保留看看C++库中的解决方式)

std::auto_ptr

http://www.cplusplus.com/reference/memory/auto_ptr/

        C++98版本的库中就提供了auto_ptr的智能指针。
        auto_ptr的实现原理:管理权转移的思想,下面简化模拟实现了一份bit::auto_ptr来了解它的原理。
#include <iostream>
#include <memory>class A
{
public:~A(){std::cout << "~A()" << std::endl;}int _a1 = 0;int _a2 = 0;
};int main()
{std::auto_ptr<A> ap1(new A);ap1->_a1++;ap1->_a2++;std::auto_ptr<A> ap2(ap1);//ap1->_a1++;//ap1->_a2++;return 0;
}
正是这个原因,所以:ap1就空了。

总结:

        std::auto_ptr是采用的资源管理权转移。但是,是不负责任的拷贝,会导致被拷贝对象悬空。所以多年以来被挂在耻辱柱上,很多公司明确要求不能使用它。

std::auto_ptr的模拟实现 

        注意:不是交换 —— 是管理权的转移,所以如果是:

std::auto_ptr<A> ap1(new A);
std::auto_ptr<A> ap2(new A);
ap2 = ap1;

        ap2是获取ap1的资源(资源管理权的转移) ,ap2之前的资源自动调用析构释放,ap1置空(nullptr)

namespace cr
{template<class T>class auto_ptr {public:auto_ptr(T* ptr = nullptr): _ptr(ptr){}// 不是交换 —— 是管理权的转移// 不是交换auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}// 不是交换 —— 是管理权的转移// ap1 = ap2;auto_ptr<T>& operator=(auto_ptr<T>& ap){if (this != &ap){if (_ptr){cout << "Delete:" << _ptr << endl;delete _ptr;}_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}~auto_ptr(){if (_ptr){cout << "Delete:" << _ptr << endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}

std::unique_ptr

        C++11中开始提供更靠谱的unique_ptr

https://cplusplus.com/reference/memory/unique_ptr/

        unique_ptr的实现原理不让你拷贝,简单粗暴的防拷贝,下面简化模拟实现了一份UniquePtr来了解它的原理。只适用于不需要拷贝的场景。

std::unique_ptr模拟实现

namespace cr
{template<class T>class unique_ptr{private: // 防止有人跑到类外实现写一个浅拷贝// 防拷贝 C++98 - 当时还是boost库中// 只声明不实现 // unique_ptr(unique_ptr<T>& ap);// unique_ptr<T>& operator=(unique_ptr<T>& ap);public:unique_ptr(T* ptr = nullptr): _ptr(ptr){}// 防拷贝 C++11unique_ptr(unique_ptr<T>& ap) = delete;unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;~unique_ptr(){if (_ptr){cout << "Delete:" << _ptr << endl;delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
}

std::shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr 。

www.cplusplus.com/reference/memory/shared_ptr/
#include <memory>
#include <iostream>class A
{
public:~A(){std::cout << "~A()" << std::endl;}int _a1 = 0;int _a2 = 0;
};void test_shared_ptr()
{std::shared_ptr<A> sp1(new A);std::shared_ptr<A> sp2(sp1);sp1->_a1++;sp1->_a2++;std::cout << sp2->_a1 << ":" << sp2->_a2 << std::endl;sp2->_a1++;sp2->_a2++;std::cout << sp1->_a1 << ":" << sp1->_a2 << std::endl;
}int main()
{test_shared_ptr();return 0;
}

        shared_ptr的原理是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。例如: 学校老师晚上在下班之前都会通知,让最后走的学生记得把门锁下,并且打扫一下教室。

  1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享
  2. 对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

std::shared_ptr的模拟实现

        不能用static修饰计数器,因为这样会让所有的模拟实现的shared_ptr的不同类型的实例化,同用一个计数器。需要的是一个资源,配一个计数器,多个智能指针对象共管静态计数对象,所以资源都只有一个计数,因为静态成员属于整个类,属于类的所有对象。

        所以使用的是一个指针,这个时候这个指针指向的空间就是new出来的,使用构造函数new,这就保证了同一个资源,用一个计数器。并且这样这个指针就会指向需释放的资源,也指向了计数。

        即:每个资源需要管理的时候,会给构造函数,构造new一个计数。

namespace cr
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr): _ptr(ptr), _pCount(new int(1)){}// 判断是否释放void Release(){// 减减被赋值对象的计数,如果是最后一个对象,要释放资源if (--(*_pCount) == 0){cout << "Delete:" << _ptr << endl;delete _ptr;delete _pCount;}}~shared_ptr(){Release();}// sp1(sp2)shared_ptr(const shared_ptr<T>& sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount)++;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){// 防止自己给自己赋值,导致计数--,出现内存泄漏if (_ptr == sp._ptr){return *this;}// 判断是否释放 -- 防止_ptr原来的数据未计数--,导致内存泄漏Release();// 共管新资源,++计数_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;// 引用计数int* _pCount;};
}

shared_ptr的问题:

shared_ptr看起来很完美,但是也有问题:

  • 在多线程使用shared_ptr的时候,shared_ptr不是线程安全的。
  • shared_ptr存在一个循环引用的问题。

循环引用问题

(是一个非常特殊的情况下)

#include <iostream>
#include <memory>class Node
{
public:~Node(){std::cout << "~Node" << std::endl;}int val = 0;std::shared_ptr<Node> _next;std::shared_ptr<Node> _prev;
};void test_shared_ptr()
{std::shared_ptr<Node> s1(new Node);std::shared_ptr<Node> s2(new Node);s1->_next = s2;s2->_prev = s1;
}int main()
{test_shared_ptr();return 0;
}

        我们可以发现上面代码并没有像我们想象的一样,自动调用析构函数:

分析:

        当下图的时候,是没有问题的,也就是对于shared_ptr智能指针的使用,shared_ptr的计数器分别都++。

        但是当继续向下执行的时候,就是问题的关键所在。

        此处:shared_ptr类型的s1,经过operator ->的重载,于是变为(Node*)->_next = s2,此处Note中的_next对象是shared_ptr类型的,所以会调用s2中的智能指针的赋值。

        同理:会调用s1中的智能指针的赋值。

        于是s1与s2的计数器分别都++到了2。

 #问:通过以上为什么不会调用析构?

        因为,根据函数的创建顺序,s2后构造所以先析构,s1先构造所以后析构。

        于是s2与s1就分别调用它们的析构函数了,于是计数器分别都--到了1。但是由于:

  • _next:管着右边的节点内存块。
  • _prev:管着左边的节点内存块。

        于是,便出现:理想状态下,_next释放那右边就释放,_prev释放那左边就释放。

        但是会出现一个问题,_next与_prev分别数据不同的成员节点,对于对象中的成员是需要该对象调用析构函数才会释放其中的成员,于是对于左节点是需要计数器--到0,才能释放左节点,_next才会析构。同样的,右节点是需要计数器--到0,才会释放右节点,_prev才会析构

        这就是一个死循环,所以没有调用Node的析构函数,也就不可能看到Node类型对象的析构释放显示。

解决方式:

         这个地方想使用shared_ptr进行Node类型的对象中类似的操作是把必定错的,没有办法,所以C++也为我们提供了一个新的方式:weak_ptr(弱指针)

        weak_ptr与其他的智能指针都不一样,其并不是常规智能指针,没有RAII,不支持直接管理资源。weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题

https://legacy.cplusplus.com/reference/memory/weak_ptr/?kw=weak_ptr

        主要用share_ptr来构造自己,并不支持用一个资源构造自己,所以其并不支持RAII。

  • 特点:不++share_ptr的计数器。
#include <iostream>
#include <memory>class Node
{
public:~Node(){std::cout << "~Node" << std::endl;}int val = 0;std::weak_ptr<Node> _next;std::weak_ptr<Node> _prev;
};void test_shared_ptr()
{std::shared_ptr<Node> s1(new Node);std::shared_ptr<Node> s2(new Node);s1->_next = s2;s2->_prev = s1;
}int main()
{test_shared_ptr();return 0;
}

        当 _next 与 _prev 是 weak_ptr 的时候,它们不参加资源的释放管理,但是可以访问和修改数据,且并不增加计数,所以不会存在循环引用的问题了。(这个方法可行,但是因为不计数,多以我们要识别到这个问题)

Note:

        想看到计数的对应变化,可以使用shared_ptr的成员函数 use_count

weak_ptr的模拟实现

        对于前面所模拟实现的shared_ptr同样的也具有循环引用的问题。

#include <iostream>namespace cr
{template<class T>class shared_ptr{public:shared_ptr(T* ptr = nullptr): _ptr(ptr), _pCount(new int(1)){}// 判断是否释放void Release(){// 减减被赋值对象的计数,如果是最后一个对象,要释放资源if (--(*_pCount) == 0){delete _ptr;delete _pCount;}}~shared_ptr(){Release();}// sp1(sp2)shared_ptr(const shared_ptr<T>& sp): _ptr(sp._ptr), _pCount(sp._pCount){(*_pCount)++;}shared_ptr<T>& operator=(const shared_ptr<T>& sp){// 防止自己给自己赋值,导致计数--,出现内存泄漏if (_ptr == sp._ptr)return *this;// 判断是否释放 -- 防止_ptr原来的数据未计数--,导致内存泄漏Release();// 共管新资源,++计数_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;// 引用计数int* _pCount;};
}class Node
{
public:~Node(){std::cout << "~Node" << std::endl;}int val = 0;cr::shared_ptr<Node> _next;cr::shared_ptr<Node> _prev;
};void test_shared_ptr()
{cr::shared_ptr<Node> s1(new Node);cr::shared_ptr<Node> s2(new Node);s1->_next = s2;s2->_prev = s1;
}int main()
{test_shared_ptr();return 0;
}

        于是可以通过模拟实现weak_ptr进行避免我们所模拟实现的shared_ptr,出现循环引用的问题。


#include <iostream>namespace cr
{template <class T>class shared_ptr{public:shared_ptr(T *ptr = nullptr) : _ptr(ptr), _pCount(new int(1)) {}// 判断是否释放void Release(){// 减减被赋值对象的计数,如果是最后一个对象,要释放资源if (--(*_pCount) == 0){delete _ptr;delete _pCount;}}~shared_ptr() { Release(); }// sp1(sp2)shared_ptr(const shared_ptr<T> &sp) : _ptr(sp._ptr), _pCount(sp._pCount) { (*_pCount)++; }shared_ptr<T> &operator=(const shared_ptr<T> &sp){// 防止自己给自己赋值,导致计数--,出现内存泄漏if (_ptr == sp._ptr)return *this;// 判断是否释放 -- 防止_ptr原来的数据未计数--,导致内存泄漏Release();// 共管新资源,++计数_ptr = sp._ptr;_pCount = sp._pCount;(*_pCount)++;return *this;}T &operator*() { return *_ptr; }T *operator->() { return _ptr; }// 便于外部获取ptr,如weak_ptrT *get()const{return _ptr;}private:T *_ptr;// 引用计数int *_pCount;};// 辅助型智能指针,使命配合解决shared_ptr循环引用问题template <class T>class weak_ptr{public:weak_ptr(): _ptr(nullptr){}weak_ptr(const shared_ptr<T> &sp): _ptr(sp.get()){}weak_ptr(const weak_ptr<T> &wp): _ptr(wp._ptr){}weak_ptr<T>& operator=(const shared_ptr<T>& sp){_ptr = sp.get();return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T *_ptr;};
}class Node
{
public:~Node() { std::cout << "~Node" << std::endl; }int val = 0;cr::weak_ptr<Node> _next;cr::weak_ptr<Node> _prev;
};void test_shared_ptr()
{cr::shared_ptr<Node> s1(new Node);cr::shared_ptr<Node> s2(new Node);s1->_next = s2;s2->_prev = s1;
}int main()
{test_shared_ptr();return 0;
}

需要掌握:

  • 为什么需要智能指针?

        主要的原因还是因为内存泄漏:忘记释放的问题,更重要还有异常安全的问题。

  • RAII?

        资源获得,资源请求机立即初始化。指的就是把资源交给一个对象,利用构造将其资源交给一个对象去管理。

  • 发展历史
  • auto_ptr / unique_ptr / shared_ptr / weak_ptr之间的区别与使用场景。
  • 模拟实现简洁版智能指针。
  • 什么是循环引用?如何解决循环引用?解决的原理是什么?
http://www.rdtb.cn/news/1948.html

相关文章:

  • 青岛做网站的网络公司营销推广运营
  • 新浪博客怎么做网站亚洲长尾关键词挖掘
  • 温州企业做网站株洲网络推广
  • 做网站策划需要什么技能永久免费客服系统有哪些软件
  • 北京网站空间信息推广服务
  • 网站图片如何优化厦门seo公司到1火星
  • 佛山效果好的网页设计培训在哪里杭州做seo的公司
  • 武汉seo公司出 名郑州seo外包公司哪家好
  • 北京社保网站减员怎么做ciliba最佳磁力搜索引擎
  • 网站做投票seo的目的是什么
  • 网站建设的硬件平台做百度推广需要什么条件
  • 网上骗人彩票网站是怎么做的做教育培训应该注册什么公司
  • 专科毕业设计代做网站yahoo搜索
  • 企业网站访问量的第一来源是( )百度信息流开户多少钱
  • 砀山做网站的公司优化公司哪家好
  • vue发布停运公告温州seo优化公司
  • 上海服饰网站建设搜索推广平台
  • 网站建设多少钱个人宣传推广渠道有哪些
  • 广东一站式网站建设推荐一键优化免费下载
  • wordpress 后台界面抖音seo关键词优化
  • 免费做二建题的网站百度一下知道官网
  • 网站改名工信部需要怎么做seo搜索优化公司排名
  • 河南久久建筑宁波seo搜索引擎优化
  • 新开家政如何做网站重庆网络推广专员
  • 专业做视频的网站有哪些内容业务推广公司
  • 网站建设平台合同模板下载广州seo网络推广员
  • 李沧网站建设电话seo优化一般多少钱
  • 学校网站怎么做优化谷歌排名规则
  • 建网站系统国内十大软件培训机构
  • 购物网站开发要解决的问题整站优化网站