異常的概念:
程序遇到了問題非主動退出的情況下我們稱之爲發生了異常,當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數的直接或間接的調用者處理這個錯誤。
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.異常是通過拋出對象所觸發的,該對象的類型決定了該由哪個catch模塊接受。
2.被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個。並且接受異常後程序從這個位置繼續向下進行(可能導致內存泄露的原因)
3.拋出的異常是對象的一份臨時拷貝,會調用拷貝構造函數,會在catch後被銷燬。
4.catch(...)可以捕獲任意類型的異常,叫做萬能捕獲,但缺陷是不知道異常錯誤是什麼。
棧展開:
- 首先檢查throw本身是否在try塊內部,如果是再查找匹配的catch語句。如果有匹配的,則調到catch
的地方進行處理。 - 沒有匹配的catch則退出當前函數棧,繼續在調用函數的棧中進行查找匹配的catch。
- 如果到達main函數的棧,依舊沒有匹配的,則終止程序。上述這個沿着調用鏈查找匹配的catch子句的
過程稱爲棧展開。所以實際中我們最後都要加一個catch(...)捕獲任意類型的異常,否則當有異常沒捕
獲,程序就會直接終止。 - 找到匹配的catch子句並處理以後,會繼續沿着catch子句後面繼續執行。
異常的重新拋出:
有可能單個的catch不能完全處理一個異常,在進行一些校正處理以後,希望再交給更外層的調用鏈函數來處理,catch則可以通過重新拋出將異常傳遞給更上層的函數進行處理。
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;
}
異常安全:最好不要在構造函數和析構函數中使用異常,因爲有可能造成類對象未完全構造或析構。