1.C++11的处理方式
主要分为异常的抛出与捕获两步.
throw(是一个关键字),用于抛出一个对象,与try catch一起使用.
cpp
void func()
{
int t; cin >> t;
if (t == 0)
{
string s = "throw\n";
throw s;
}
}
int main()
{
while (1)
{
try
{
func();
cout << "No throw" << endl;
}
catch (string& a)
{
cout << a << endl;
}
}
return 0;
}
当throw发生时,其会沿着函数链直至找到离其最近的类型匹配的catch并运行内部代码(catch形参的类型要与throw对象类型匹配(这里与函数传参类似,不匹配的函数就直接销毁并走到下一个函数))(除了到catch匹配的函数才会往下运行代码,其余直接离开的函数就直接销毁且不会运行内部的代码)
总结:throw是写在try catch的try内部的(包含的函数内部的throw也是一部分),当触发throw时,其会储存一个对象(表面是一个函数中的局部变量,但实际是拷贝了一份,用这份拷贝往其他函数中走的),带着这个对象从当前函数的catch匹配,匹配就走这个catch,不匹配就通过函数链往后面的函数中的catch找直至匹配为止。
2.栈展开
就是找匹配的catch的过程,当到了mian函数也没有匹配的catch时就调用标准库的terminate函数直接中止程序(就是报error弹窗).
cpp
try
{//..}
catch(//../)
//...就叫收纳所有的类型,用于兜底防止程序崩毁(此处也说明了多个catch可以对应同一个try)
catch(...)
{//...}
一般要求throw出来的对象类型与catch类型完美匹配,但也有一些例外:
(1)数组与指向数组元素的指针
(2)函数与函数指针
(3)子类与指向父类的类型转换(即多态)
3.应用
(1)捕获基类(异常throw出来的是一个基类,该基类与其的派生类都是用于报异常的(因此也叫异常类))
cpp
struct gal
{
virtual void func()
{
cout << "gal error" << endl;
}
};
struct rewrite :gal
{
virtual void func()
{
cout << "rewrite error" << endl;
}
};
struct kauakaua :gal
{
virtual void func()
{
cout << "kauakaua error" << endl;
}
};
void print()
{
if (rand() % 7 == 0)
throw kauakaua();
else if (rand() % 5 == 0)
throw rewrite();
else if (rand() % 4 == 0)
throw gal();
else cout << "cute" << endl;
}
int main()
{
srand(time(NULL));
while (1)
{
Sleep(100);
try
{
//封装下的不同throw代表不同的异常
print();
}
//基类来获取不同派生类
catch(gal& s)
{
s.func();
}
}
return 0;
}
总结有:基类为原始报异常并创建虚函数(目的是形成多态),该多态就可以调用不同的虚函数从而产生不同的报异常。
(2)异常的重新抛出
应用中会对每一个异常类进行编号,目的是对不同的异常来进行更精细的处理划分。
cpp
throw;
只有一个throw时需要已经接收到异常,因此该代码就是用于catch中写的,此处的throw可以让前面被throw的变量throw出当前的catch并进入后面函数链的catch的匹配中。
cpp
struct gal
{
gal(int a = 6969)
:id(a)
{ }
virtual void func()
{
cout << "gal error" << endl;
}
virtual int getid() { return id; }
int id;
};
struct rewrite :gal
{
rewrite(int a = 0720)
:gal(a)
{ }
virtual void func()
{
cout << "rewrite error" << endl;
}
virtual int getid() { return gal::id; }
//int id = 0720;
};
struct kauakaua :gal
{
kauakaua(int a = 0721)
:gal(a)
{ }
virtual void func()
{
cout << "kauakaua error" << endl;
}
virtual int getid() { return gal::id; }
//int id = 0721;
};
void print()
{
if (rand() % 7 == 0)
cout << "cute" << endl;
else if (rand() % 5 == 0)
throw rewrite(0720);
else if (rand() % 4 == 0)
throw gal(6969);
else throw kauakaua(0721);
}
void _print()
{
//当kauakaku为异常时,想让kauakaua的异常走多3次直到正常
for (int a = 1; a <= 4; a++)
{
try
{
print();
//如果正常运行就走到这里离开函数
break;
}
catch (gal& g)
{
if (g.getid() == 0721)
{
//还不正常就throw离开进行异常报错
if (a == 4)throw;
cout << "再次生成" << a <<"次" << endl;
}
else
{
//其他类型的异常直接throw,不进行操作
throw;
}
}
}
}
int main()
{
srand(time(NULL));
while (1)
{
Sleep(500);
try
{
//封装的这一层是为了对kauakaua的异常进行特殊处理
_print();
}
catch(gal& s)
{
s.func();
}
}
return 0;
}
这里的本质就是提前捕获异常对其进行操作罢了。
4.异常规范
异常的安全问题在于资源可能没有完全释放。
noexcept:
(1)作为关键字时来说明这个函数内部没有异常,如果声明了异常的函数抛出了异常,程序会调用terminate来终止程序运行。
cpp
void func() noexcept
{}
(2)作为函数时检测该函数(严格来说是所有的表达式)内部是否有异常,可能有异常就返回0(她并不运行该表达式,只是扫一下代码看看是否有try catch throw这些关键字而已),没有返回1.
(3)标准库中有自己的异常类exception。