24.异常
int main()try{// 这里如果系统内存不够也会抛异常// 这里越界会抛异常catch (const exception& e) // 这里捕获父类对象就可以return 0;
·

一.C语言传统的处理错误的方式

在设计C语言的时候,我们要独立设计一些错误码进行返回
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<thread>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;
int main()
{
FILE* fout = fopen("Test.txt", "r");
cout << fout << endl;
cout << errno << endl;
perror("fopen fail");
return 0;
}

系统识别的错误码对应得错误信息
二. C++异常概念


三.异常的使用
1.异常的抛出和捕获



double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
throw "Division by zero condition!";
else
return ((double)a / (double)b);
}
void Func()
{
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
cout << len << " " << time << endl;
}
int main()
{
try {
Func();
}
catch (const char* errmsg) {
cout << errmsg << endl;
}
catch(...){
cout<<"unkown exception"<<endl;
}
return 0;
}

try catch没有抛出异常是不会进去的,只有我们抛出异常,才会进去,没有抛出异常,直接就进行跳过
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
string s("Division by zero condition!");
throw s;
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int len, time;
cin >> len >> time;
try
{
cout << Division(len, time) << endl;
}
catch (size_t x)
{
cout << x << endl;
}
cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}
int main()
{
while (1)
{
try
{
Func();
}
catch (const string& errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}
}
return 0;
}

double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
string s("Division by zero condition!");
throw s;
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int len, time;
cin >> len >> time;
try
{
cout << Division(len, time) << endl;
}
catch (size_t x)
{
cout << x << endl;
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}
int main()
{
while (1)
{
try
{
Func();
}
catch (const string& errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}
}
return 0;
}

如果一个捕获的都没有的话,程序就会出现异常

double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
string s("Division by zero condition!");
throw s;
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int len, time;
cin >> len >> time;
try
{
cout << Division(len, time) << endl;
}
catch (size_t x)
{
cout << x << endl;
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
cout << "xxxxxxxxxxxxxxxxxxxxx" << endl;
}
int main()
{
while (1)
{
try
{
Func();
}
catch (const string& errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}
}
return 0;
}


2.捕获任意的异常
try
{
Func();
}
catch (const string& errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}

我们一般是不会抛一些字符串,整性啥的(和C语言本质上没啥改变),而是抛出一个自定义类型(包含信息错误描述 + 编号等)
我们还可以通过日志等来进行操作
3.异常不受隐式类型转换的影响
![]()
如果我们抛出上面这种异常,我们来进行接收(不能走隐式类型转换)




类型匹配一定要严格匹配,不能进行隐式类型转换

4.捕获异常不一定要类型完全一样

下面有一个案例( 服务器开发中通常使用的异常继承体系 ):
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<thread>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;
// 服务器开发中通常使用的异常继承体系
class Exception
{
public:
Exception(const string& errmsg, int id)
:_errmsg(errmsg)
, _id(id)
{}
virtual string what() const
{
return _errmsg;
}
int getid() const
{
return _id;
}
protected:
string _errmsg;
int _id;
};
class SqlException : public Exception
{
public:
SqlException(const string& errmsg, int id, const string& sql)
:Exception(errmsg, id)
, _sql(sql)
{}
virtual string what() const
{
string str = "SqlException:";
str += _errmsg;
str += "->";
str += _sql;
return str;
}
private:
const string _sql;
};
class CacheException : public Exception
{
public:
CacheException(const string& errmsg, int id)
:Exception(errmsg, id)
{}
virtual string what() const
{
string str = "CacheException:";
str += _errmsg;
return str;
}
};
class HttpServerException : public Exception
{
public:
HttpServerException(const string& errmsg, int id, const string& type)
:Exception(errmsg, id)
, _type(type)
{}
virtual string what() const
{
string str = "HttpServerException:";
str += _type;
str += ":";
str += _errmsg;
return str;
}
private:
const string _type;
};
void SQLMgr()
{
if (rand() % 7 == 0)
{
throw SqlException("权限不足", 100, "select * from name = '张三'");
}
cout << "调用成功" << endl;
}
void CacheMgr()
{
if (rand() % 5 == 0)
{
throw CacheException("权限不足", 100);
}
else if (rand() % 6 == 0)
{
throw CacheException("数据不存在", 101);
}
SQLMgr();
}
void seedmsg(const string& s)
{
//cout << "void seedmsg(const string& s)" << endl;
//throw HttpServerException("网络不稳定,发送失败", 102, "put");
if (rand() % 2 == 0)
{
throw HttpServerException("网络不稳定,发送失败", 102, "put");
}
else if (rand() % 3 == 0)
{
throw HttpServerException("你已经不是对象的好友,发送失败", 103, "put");
}
else
{
cout << "发送成功" << endl;
}
}
void HttpServer()
{
/*if (rand() % 3 == 0)
{
throw HttpServerException("请求资源不存在", 100, "get");
}
else if (rand() % 4 == 0)
{
throw HttpServerException("权限不足", 101, "post");
}*/
// 失败以后,再重试3次
for (size_t i = 0; i < 4; i++)
{
try
{
seedmsg("今天一起看电影吧");
break;
}
catch (const Exception& e)
{
if (e.getid() == 102)
{
if (i == 3)
throw e;
cout << "开始第" << i+1 << "重试" << endl;
}
else
{
throw e;
}
}
}
CacheMgr();
}
int main()
{
srand(time(0));
while (1)
{
this_thread::sleep_for(chrono::seconds(1));
try
{
HttpServer();
}
catch (const Exception& e) // 这里捕获父类对象就可以
{
using std::chrono::system_clock;
// 多态
system_clock::time_point today = system_clock::now();
std::time_t tt = system_clock::to_time_t(today);
cout << ctime(&tt) << e.what() << endl << endl;
}
catch (...)
{
cout << "Unkown Exception" << endl;
}
}
return 0;
}



上面那个样例,有两个点要进行理解:
1.我们在不同的模块,可以抛出对应得错误,这样我们能根据抛出错误得信息进行分析
2.我们在接收到异常的时候,我们不想处理,我们可以再次进行抛出
四.异常的安全
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
// 重新抛出去。
//
// 更好解决方案是智能指针
int* array1 = new int[10];
int* array2 = new int[10];
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
// ...
cout << "delete []" << array1 << endl;
delete[] array1;
cout << "delete []" << array2 << endl;
delete[] array2;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}


double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
// 重新抛出去。
int* array = new int[10];
try
{
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (...)
{
cout << "delete []" << array << endl;
delete[] array;
throw; // 异常重新抛出,捕获到什么抛出什么
}
// ...
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}


这个地方不是为了将我们捕获到的异常进行处理,而是为了解决我们的内存泄漏问题
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
// 重新抛出去。
//
// 更好解决方案是智能指针
int* array1 = new int[10];
int* array2 = new int[10]; // 抛异常呢
try
{
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (...)
{
cout << "delete []" << array1 << endl;
cout << "delete []" << array2 << endl;
delete[] array1;
delete[] array2;
throw; // 异常重新抛出,捕获到什么抛出什么
}
// ...
cout << "delete []" << array1 << endl;
delete[] array1;
cout << "delete []" << array2 << endl;
delete[] array2;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}
上面这段程序还是会存在我们的异常:

所以,我们的new也是会进行抛异常的,但是这样我们代码写起来就不优雅了(太冗余了)
后续我们可以使用智能指针来解决这种问题
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
struct A
{
~A() noexcept
{
cout << "~A()" << endl;
}
int _a1 = 0;
int _a2 = 0;
};
template<class T>
class SmartPtr
{
public:
// RAII
SmartPtr(T* ptr)
:_ptr(ptr)
{}
~SmartPtr()
{
delete _ptr;
}
// 重载运算符,模拟指针的行为
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
void Func()
{
// 更好解决方案是智能指针
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2(new A);
sp1->_a1++;
sp1->_a2++;
shared_ptr<A> sp3(sp1);
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "未知异常"<< endl;
}
return 0;
}
五.异常规范


noexcept 关键字:
确定不抛异常的,就不用加 noexcept 关键字

六.自定义异常体系

七.C++标准库的异常体系




int main()
{
try{
vector<int> v(10, 5);
// 这里如果系统内存不够也会抛异常
v.reserve(1000000000);
// 这里越界会抛异常
v.at(10) = 100;
}
catch (const exception& e) // 这里捕获父类对象就可以
{
cout << e.what() << endl;
}
catch (...)
{
cout << "Unkown Exception" << endl;
}
return 0;
}




如 out_of_range() 的使用:


八.异常的优缺点
1.优点





2.缺点

总结:

更多推荐




所有评论(0)