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

中国建设招投标网站网站设计公司多少钱

中国建设招投标网站,网站设计公司多少钱,深圳网页设计公司,怎么对企业进行网站建设一、C中的异常 1.1C语言中关于错误的处理&#xff08;回顾&#xff09; 1.1.1处理一&#xff1a;文件中的错误码&#xff0c;错误信息 C语言中&#xff0c;文件打开成功则返回地址&#xff0c;不成功返回0 FILE* foutfopen("Test.txt","r"); cout<&…

一、C++中的异常

1.1C语言中关于错误的处理(回顾)

1.1.1处理一:文件中的错误码,错误信息

C语言中,文件打开成功则返回地址,不成功返回0

FILE* fout=fopen("Test.txt","r");
cout<<fout<<endl;//打印文件内容
cout<<errno<<endl;//打印错误码编号
perror("fopen fail:");//先打印参数中的字符串,再打印错误信息

1.1.2处理二:中止程序

当遇到内存错误,assert断言时会直接终止程序

1.2C++中异常的概念

异常是一种处理错误的方式,一个函数发现错误时可以抛出异常,再由函数本身或者调用该函数的函数进行异常处理

1.3结合例子说明抛出--匹配的规则

函数的调用栈为

此时如果输入“4 0”

首先会在add中抛异常,异常对象的类型为“const char*”

进入Func()中,会发现并没有catch类型匹配,会直接跳过Func()剩下代码到main()中,

再找到匹配的catch后进行运行,打印结果

1.4函数调用链中,异常栈展开匹配规则

①首先检查throw在不在try块内部

1>在,查catch有无类型匹配

2>不在或无类型匹配,退出当前函数,沿着函数栈去调用它的函数中查找有无try,再走一遍①的逻辑,如果到达main()结束时仍无匹配,则终止程序

int add(int a, int b)
{if (b == 0)throw 5;elsereturn a + b;
}void Func()
{int len1, len2;cin >> len1 >> len2;cout << add(len1, len2) << endl;//此处add函数的调用不在try块内部try{}catch (size_t x){cout << x << endl;}cout << "finished Func()" << endl;
}int main()
{try {Func();}catch (const char* str){cout << str << endl;}catch (...){cout << "unknown exception" << endl;}return 0;
}

 此处add函数的调用不在try块内部,不会走到size_t类型的catch上,而是会直接退回到main中,走到...

此时打印结果

②找到匹配的catch语句处理后,会继续沿当前catch子句后面执行 

③除了允许抛出派生类对象,用基类捕获之外,catch的类型严格匹配

④允许catch中异常重新抛出,捕获到什么抛出什么

1.4补:实践中的做法

实践中大多会用派生类进行抛出,用基类捕获,

会在派生类中实现一个基类虚函数的多态用来打印错误信息,基类中有一个成员变量_id来对异常进行唯一标识

如:服务器开发中的异常继承体系 

1.5异常中的安全问题

①构造函数要完成对象的构造和初始化,最好不要在其中抛异常,否则可能会导致对象不完整

②析构函数要完成资源的清理,最好不要在其中抛异常,否则可能会导致资源泄露

③因为异常的捕捉机制,可能会存在申请资源的代码执行了,但是释放资源的代码没有执行,如

1.3中的函数

void Func()
{int len1, len2;cin >> len1 >> len2;try{cout << add(len1, len2) << endl;}catch (size_t x){cout << x << endl;}cout << "finished Func()" << endl;
}

 如果它代码中的add调用之前申请资源,打印 "finished Func()"之后释放资源,我们的实际调用过程还和例子中的一致

void Func()
{int len1, len2;cin >> len1 >> len2;try{int* arr=new int(10);cout << add(len1, len2) << endl;}catch (size_t x){cout << x << endl;}cout << "finished Func()" << endl;delete arr;arr=nullptr;}

这样一来,属于arr的那一部分资源就会出现资源泄露的问题

理想的处理方式是把arr交给RAII智能指针去处理,自动进行资源释放

1.6异常的规范相关问题

①在C++11之前,通过在函数后加throw()来明确是否可能抛异常,如

void* void Func(size_t s,void* ptr) throw();

()中为空是不会抛异常;()中有内容是可能抛哪些类型的异常

②throw()的标识体系使用起来并不方便于是在C++11中新增了关键字noexcept,用来表示函数不会抛异常;而不写该关键字默认会抛异常

③加了noexcept后还去抛异常会直接报错,就算能捕获也不会捕获

④库中提供了一个exception作为异常继承的基类,其中还有虚函数what供重写来满足需求

1.7异常的优缺点总结

优点:

①相比于错误码,可以更清晰地展示出错误的信息,甚至可以包含堆栈调用地信息,帮助程序员更好的定位bug

②传统错误码返回方式中,当深层调用函数返回错误的时候只能返回到最外层函数,而异常可以在函数调用栈进行捕获

③有些函数难以通过返回值表示错误,如

T& operator[](int i)

出错时似乎返回什么都不太合理 ,使用异常的话更好处理

缺点:

①异常的跳转捕获机制会使调试的成本增大

②C++中无“垃圾自动回收机制”(java和phthon等为了避免资源泄露,都加上了资源自动销毁的“垃圾自动回收机制”),资源可能会出现内存泄露的问题,但可以通过RAII智能指针来规避

③异常的使用必须要规范,该加noexcept要加,这对程序员来说要求会高一些 

二、C++中的智能指针

2.1什么是智能指针

智能指针又称RAII,是Resource Acquisition Is Initialization的缩写

即“资源获得立即初始化

利用对象生命周期控制程序资源,在主动构造时获取资源,自动调用析构时释放资源

2.2简单的智能指针示例

template<class T>
class Smart_ptr
{
public:Smart_ptr(T* ptr):_ptr(ptr){}~Smart_ptr(){delete _ptr;}private:T* _ptr;
};//设置一个简单的自定义类型
struct A
{int _a;
};void func()
{A* a1=new A;A* a2=new A;SmartPtr<A> pt1(a1);SmartPtr<A> pt2(a2);
}

在这段示例中,自定义类型A是由new进行申请,但函数结束也没有主动去释放,而是由SmartPtr自动进行析构的调用

2.3智能指针的拷贝中出现的问题

2.3.1在2.2的例子中会出现的问题

假如此时添加这样一段代码

SmartPtr<A> pt3(pt2);

会发生什么呢?

在SmatrPtr中,拷贝构造是默认生成的,会进行浅拷贝,这样以后,

pt3与pt2会指向同一块申请出来的空间,因此会有析构两次的风险 

2.3.2库中的智能指针auto_ptr拷贝的原理

最早出现的智能指针就是auto_ptr,在C++98就已经支持了

template <class X> class auto_ptr;

它显式实现了拷贝构造,但是如果我们这样去用

auto_ptr<A> pt4(new A);
auto_ptr<A> pt5(pt4);

直接让pt4变空, auto_ptr<A> pt5(pt4);这行代码的本质是管理权转移

但实际使用过程中很少用到 

2.3.3boost库及其中的智能指针

boost是在2003-2011年之间产生的,常被称为“准C++标准库”,可以包<boost>头文件来使用

其中有三个常用的智能指针:

scoped_ptr:禁止拷贝

shared_ptr:通过引用计数的方式进行拷贝

weak_ptr:不同于上两个,它不支持直接管理资源(故不符合RAII),它的存在是为了解决shared_ptr的一个缺陷:循环引用导致资源泄露

2.3.4自boost库衍生:C++11中的常用智能指针

C++11中将boost库中几个智能指针移过来了,包括

unique_ptr:把scoped_ptr换了个名字

shared_ptr:直接移植

weak_ptr:直接移植

2.4定制删除器

2.4.1定制删除器的意义

默认的析构中调用的都是delete

但如果遇到如自定义类型,如

unique_ptr<A> up1(new A[5]);//需要delete[] _ptr

 这类的需要其他删除资源方式的自定义类型,库中的析构明显就不够了

2.4.2unique_ptr中的定制删除器

需要在模板中传入定制删除器,如

template<class T>
struct ArrayDelete
{void operator(T* ptr){delete[] ptr;}
};

unique_ptr创建过程改为

unique_ptr<A,ArrayDelete<A>> up1(new A[5]);
补:实际上因为数组类型常用,库中也提供过类模板

 unique_ptr:

template <class T, class D> class unique_ptr<T[],D>;

使用时:

unique_ptr<A[]> up1(new A[5]);

2.4.3shared_ptr中的定制删除器

不同于unique_ptr中在类模板中传入,shared_ptr是直接在函数参数列表里传可调用对象

这也意味着匿名类,lambda表达式,function等都可以直接使用

声明:

template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc);
template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc);

使用举例:

struct Deletefile
{void operator()(FILE* file){fclose(file);}
};
shared_ptr<FILE> sp2(fopen("text.txt","r"),Deletefile());

2.5快捷构建shared_ptr的make_shared函数模板

有时需要直接使用shared_ptr类型对象,如在使用weak_ptr的过程中

此时就需要用上支持可变模板参数列表的make_shared函数模板

template <class T, class... Args>shared_ptr<T> make_shared (Args&&... args);

如:

shared_ptr<Date> sp3 = make_shared<Date>(2025,3,26);

三、智能指针的模拟实现

3.1auto_ptr

基础的功能与2.2中的SmartPtr基本一致

其拷贝构造类似于

auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{ap._ptr=nullptr;}

3.2unique_ptr

即直接将拷贝构造和赋值重载置为delete

3.3shared_ptr

3.3.1基础功能的实现

拷贝时的计数逻辑是核心,其解决思路是通过成员变量额外设置值来辅助判断是否需要析构

这个值需要脱离对象本身,因为影响这个值的核心是“指向一块内存的对象数目

①首先考虑使用静态成员变量int _pcount,但这会出现一个问题:虽然可以统计指向一块内存的资源个数,可如果有两块内存就无法进行合理划分了

②为了解决①中的问题,可以考虑给int*类型的_pcount,如果多块内存,就让指针指向不一样的空间 

拷贝构造时:每次复制以后++对应_pcount

赋值重载:可能会出现接受赋值对象原来就指向一块空间的情况,因此需要走

析构逻辑+拷贝构造

3.3.2定制删除器的实现

可以用包装器来包装可调用对象,需要有缺省值,因为析构函数中我们不能再只用delete了,而要去调用_del

function<void(T* ptr)> _del = [](T* ptr) {delete ptr; };

为了正常使用包装器,我们还需要补充一个模板构造函数

template<class D>shared_ptr(T* ptr,D del):_ptr(ptr), _pcount(new int(1)),_del(del){}

3.3.3示例代码:

template<class T>
class shared_ptr
{
public:shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)){}template<class D>shared_ptr(T* ptr,D del):_ptr(ptr), _pcount(new int(1)),_del(del){}shared_ptr(const shared_ptr& sp):_ptr(sp._ptr), _pcount(sp._pcount){++(*_pcount);}void delete_sp(){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;_ptr = nullptr;_pcount = nullptr;}}shared_ptr& operator=(const shared_ptr& sp){if (_ptr != sp._ptr){delete_sp();_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}int use_count(){return *_pcount;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}~shared_ptr(){delete_sp();}private:T* _ptr;int* _pcount;//定制删除器,服务于T的特定析构方式function<void(T* ptr)> _del = [](T* ptr) {delete ptr; };};

3.3.4循环引用:会导致内存泄漏

场景:

struct ListNode
{int _data;shared_ptr<ListNode> _next;//直接将资源交给shared_ptr管理shared_ptr<ListNode> _prev;
};int main()
{shared_ptr<ListNode> n1(new ListNode);shared_ptr<ListNode> n2(new ListNode);n1->_next=n2;n2->_prev=n1;return 0;
}

此时n1与n2的关系是:

 

一旦析构:

首先,n1与n2析构后,计数减到1

然后问题就来了:

①右边节点什么时候释放?

_next析构,则右边节点释放

②_next什么时候析构?

_next为左边节点成员,左边节点析构,_next析构

③左边节点什么时候释放?

_prev析构,则左边节点释放

④_prev什么时候析构?

_prev为右边节点成员,右边节点析构,_prev析构

这里①-④形成了一个逻辑闭环,最后造成谁也没析构/释放,出现资源泄露 

3.3.5循环引用的解决:weak_ptr 

它的存在就是为了解决循环引用的问题,特点是使用shared_ptr传参时,构造/赋值时引用计数不增加

注意:weak_ptr不支持直接管理资源,即不支持RAII

它的声明中有传参

template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;

可以利用make_shared传入

此外,weak_ptr是有计数的,只是计数不增加;他也有use_count()接口

四、内存泄漏

4.1什么是内存泄漏

指因疏忽或错误造成程序未能释放已不再使用的内存的情况

4.2内存泄露的危害

对于长期运行的程序,会导致响应越来越慢,最终卡死

http://www.dtcms.com/wzjs/478406.html

相关文章:

  • 郑州网站建设公司排行自媒体培训学校
  • 嘉兴seo排名扣费seo官网
  • 公司网站开发费用济南兴田德润o评价网站设计框架
  • 做网站用的幻灯片大小沈阳网站优化
  • 企业自助建站程序阿里指数app下载
  • 佛山做网站优化培训心得体会范文
  • 做导购网站赚钱吗朝阳seo
  • 南昌有没有做企业网站和公司品牌如何推广
  • 网站 成功因素百度指数明星人气榜
  • wordpress discuz整合windows优化大师免费版
  • 鞍山市信息网站网络推广app是违法的吗
  • 长春做网站多少钱交换友情链接前后必须要注意的几点
  • 做设计兼职的网站有哪些谷歌seo零基础教程
  • 匿名网站建设成都seo服务
  • 263网站建设怎么样网络广告案例
  • 服装网站建设配色seo工具不包括
  • 邢台哪有学做网站的十大网络营销经典案例
  • wordpress插 件李勇seo的博客
  • 做网站界面多少钱网店代运营的套路
  • 南宁手机平台网网站建设今日网站收录查询
  • 厦门手机网站建设sem账户托管
  • 网站浮动qq如何营销推广
  • 成品网站 源码1688抖音关键词优化排名靠前
  • 东营网站备案代理公司百度获客平台
  • 小说网站开发中遇到的问题霸屏推广
  • 公司网站做的很烂网站推广渠道
  • 做推广用那个网站自己怎么制作网站
  • 全省政府网站建设工作会议移动建站优化
  • 房产信息门户网站建设方案营销推广活动方案
  • 政协机关网站建设网站推广优化流程