C++ 異常機制(上)

東南亞季風發表於2021-01-16

一、概念

異常:存在於執行時的反常行為,這些行為超過了函式的正常的功能範圍。

異常處理:處理程式中的錯誤,異常處理機制為程式中異常檢測和異常處理這兩部分的協作提供支援。

在C++中,異常處理包括:

  • throw表示式,表示遇到了無法處理的問題
  • try語句塊,處理異常;以關鍵字try開始,一個或多個catch結束
  • 一套異常類,用於在throw表示式和相關的catch子句之間傳遞異常的資訊。

二、異常的好處

  1. 整性返回值沒有語義資訊,而異常包含語義資訊,有時從類名便可看出。
  2. 異常作為一個類,有自己的成員,可以傳遞足夠的資訊。
  3. 函式的返回值可以忽略,異常不可以忽略,可以使程式更加健壯。

三、基本語法

#include<iostream>
using namespace std;

//異常基本語法

int divide(int x ,int y){
	if (y == 0){
		throw y;  //拋異常
	}
	return x / y;
}
void test01(){

	//試著去捕獲異常
	try{
		divide(10, 0);
	}
	catch (int e){ //異常時根據型別進行匹配
		cout << "除數為" << e << "!" << endl;
	}	
}


void CallDivide(int x,int y){	
	divide(x, y);
}
//a() -> b() - >c()  -> d(),d()中的異常一層層向上拋到terminate的標準庫函式,直到處理為止

void test02(){	
	try{
		CallDivide(10,0);
	}
	catch (int e){
		cout << "除數為" << e << endl;
	}
}

//C++異常機制跨函式
//異常必須處理,如果異常拋到頂層還沒有處理,程式便會掛掉。
int main(){
	
	//test01();
	test02();
}

四、棧解旋

異常被丟擲後,從進入try塊起,到異常被拋前,這期間在棧上構造的所有物件,都會被自動析構,析構的順序與構造的順序相反,這一過程即為棧解旋

建構函式沒有返回型別,無法通過返回值來報告執行狀態,所以通過異常機制來解決建構函式的出錯問題。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class Person{
public:
	Person(){
		cout << "物件構建!" << endl;
	}
	~Person(){
		cout << "物件析構!" << endl;
	}
};

int divide(int x,int y){
	Person p1, p2;
	if (y == 0){
		throw y;
	}
	return  x / y;
}

void test01(){

	try{
		divide(10,0);//棧解旋
	}
	catch (int e){
		cout << "異常捕獲!" << endl;
	}
}

int main(void)
{
	test01();
	return 0;
}
/*
結果:
    物件構建!
	物件構建!
	物件析構!
	物件析構!
	異常捕獲!
*/

五、異常介面宣告

  1. 為了加強程式的可讀性,可以在函式宣告中列出可能丟擲的所有異常型別,例如:
    void func() throw (A, B, C , D); //這個函式func()能夠且只能丟擲型別A B C D及其子型別的異常。
  2. 如果在函式宣告中沒有包含異常介面宣告,則次函式可以拋擲任何型別的異常,例如:
    void func();
  3. 一個不拋擲任何型別異常的函式可以宣告為:
    void func() throw();
  4. 如果一個函式丟擲了它的異常介面宣告所不允許丟擲的異常,unexpected函式會被呼叫,該函式預設行為呼叫terminate函式中止程式
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

//這個函式只能丟擲int float char三種型別異常,丟擲其他的就報錯
void func() throw(int,float,char){
	throw "abc";
}

//不能丟擲任何異常
void func02() throw(){
	throw -1;
}

//可以丟擲任何型別異常
void func03(){
}

int main(void)
{
	try{
		func();
	}
	catch (char* str){
		cout << str << endl;
	}
	catch (int e){
		cout << "異常!" << endl;
	}
	catch (...){ //捕獲所有異常
		cout << "未知型別異常!" << endl;
	}
	return 0;
}

//結果: 未知型別異常!

六、異常物件的記憶體模型

throw的異常是有型別的,可以是數字、字串、類物件,catch需嚴格匹配異常型別。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

void func01(){
	throw 1; //丟擲int型別異常
}

void func02(){
	throw "exception";
}

class MyException{
public:
	MyException(const char* str){
		error = new char[strlen(str)+1];
		strcpy(error, str);
	}
	
	MyException(const MyException& ex){
		this->error = new char[strlen(ex.error) + 1];
		strcpy(this->error,ex.error);
	}
	MyException& operator=(const MyException& ex){
		if (this->error != NULL){
			delete[] this->error;
			this->error = NULL;
		}
		this->error = new char[strlen(ex.error) + 1];
		strcpy(this->error, ex.error);
	}
	
	void what(){
		cout << error << endl;
	}
	~MyException(){
		if (error != NULL){
			delete[] error;
		}
	}
public:
	char* error;
};

void fun03(){
	throw MyException("我剛寫異常!");
}

void test01(){	
	try{
		func01();
	}
	catch (int e){
		cout << "int 異常捕獲!" << endl;
	}
//----------------------------------
	try{
		func02();
	}
	catch (const char* e){
		cout << "const char* 異常捕獲!" << endl;
	}
//----------------------------------
	try{
		fun03();
	}
	catch (MyException e){
		e.what();
	}
}
int main(void){	
	test01();
	return 0;
}
/*
int 異常捕獲!
const char* 異常捕獲!
我剛寫異常!
*/

七、異常物件的生命週期

  1. catch裡可以用普通型別元素,引用,指標去接
  2. 普通元素去接,異常物件catch處理完之後就析構
  3. 引用的話,不用呼叫拷貝構造,異常物件catch處理完之後就析構
  4. 指標接,throw的時候必須用new才能接的到,catch裡必須要delete
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class MyException {
public:
	MyException() {
		cout << "建構函式!" << endl;
	}
	MyException(const MyException& ex) {
		cout << "拷貝構造!" << endl;
	}
	~MyException() {
		cout << "解構函式!" << endl;
	}
};
void func() {
	//throw &(MyException()); //建立匿名物件,呼叫構造
    //throw new MyException();//用指標接
	throw MyException();
}
void test01();
int main(void) {
	test01();
	return 0;
}

/*
void test01();{
	try {
		func();
	}
	catch (MyException e) {
		cout << "異常捕獲!" << endl;
	}
}
普通型別去接,結果為:
	建構函式!
	拷貝構造!
	異常捕獲!
	解構函式!
	解構函式!
*/

/*
void test01();{
	try {
		func();
	}
	catch (MyException& e) {
		cout << "異常捕獲!" << endl;
	}
}
引用去接,結果為:
	建構函式!
	異常捕獲!
	解構函式!
*/
/*
void test01();{
	try {
		func();
	}
	catch (MyException* e) {
		cout << "異常捕獲!" << endl;
		detele e;
	}
}
指標去接,結果為:
	建構函式!
	異常捕獲!
	解構函式!
*/

相關文章