異常的概念
程序遇到了問題非主動退出的情況下我們稱之爲發生了異常,當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數的直接或間接的調用者處理這個錯誤。
c++中異常的處理方式:
#include<iostream>
using namespace std;
void test1()
{
FILE *fp = fopen("2222.txt", "rb");
if (fp == nullptr){
throw 1;
}
}
int main()
{
try
{
test1();
}
catch (int)
{
cout << "文件打開失敗" << endl;
}
system("pause");
return 0;
}
即通過throw拋出一種異常,由catch捕獲,try調用函數(可以理解爲有可能拋出異常,所以嘗試着調用這個函數)
異常的拋出和捕獲規則:
1.異常是通過拋出對象所觸
2.被選中的處理代碼是與拋出異常對象類型匹配且距離最近的那個
3.拋出異常會生成一個異常對象的拷貝,是一個臨時對象,所以會在catch後被銷燬
4.特殊:並不一定是類型完全匹配,有時可以拋出一個派生類對象,使用基類捕獲,這個很常用也很實用
在異常的使用和捕獲中遵循棧展開匹配原則:
首先檢查throw本身是否在try內部,如果在的話找到與之匹配的catch,如果沒有與之匹配的catch則退出當前函數棧,繼續在調用當前函數的函數棧中找與之匹配的catch,找到後就從catch後繼續執行代碼,如果一直找到main函數依舊沒有找到的話就終止程序。
異常的重現拋出:有時接收到異常的地方無法處理異常,而是需要將異常進行一些矯正處理後交給更外層的調用函數去處理異常,這裏就需要catch(...)重新拋出異常。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int Division(int a, int b)
{
if (b == 0){
throw "Division error";
}
return a / b;
}
void Fun()
{
int * arr= new int[10];
try
{
int ret = Division(10, 0);
}
catch (...)
{
delete arr;
throw;
}
}
int main()
{
try
{
Fun();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
system("pause");
return 0;
}
上面的代碼中Division拋出的異常首先由調用它的函數Fun捕獲,但是並沒有處理這個異常而是將這個異常繼續拋出到主函數中進行處理。
異常安全問題:最好不要在構造函數或析構函數中使用異常,否則可能會導致對象構造不完全或析構不徹底。C++中異常經常會導致資源泄漏的問題,比如在new和delete中拋出了異常,導致內存泄漏,在lock和unlock之間拋出了異常導致死鎖,C++經常使用RAII來解決以上問題。
異常規範:
int Division(int a, int b) throw(int,char)
{
if (b == 0){
throw "Division error";
}
return a / b;
}
通過這種方式告訴函數使用者此函數有可能拋出的異常類型,如果函數後是throw()則表示這個函數不會拋出異常。
最後總結一下異常的優缺點
優點:更清晰的展示錯誤的各種信息,以及調用堆棧的信息
缺點:使用異常會使程序跳動性比較大,執行混亂
但利大於弊,工程中鼓勵使用異常處理。