C++ 異常機制(上)
一、概念
異常:存在於運行時的反常行為,這些行為超過了函數的正常的功能範圍。
異常處理:處理程序中的錯誤,異常處理機製為程序中異常檢測和異常處理這兩部分的協作提供支持。
在C++中,異常處理包括:
- throw表達式,表示遇到了無法處理的問題
- try語句塊,處理異常;以關鍵字try開始,一個或多個catch結束
- 一套異常類,用於在throw表達式和相關的catch子句之間傳遞異常的信息。
二、異常的好處
- 整性返回值沒有語義信息,而異常包含語義信息,有時從類名便可看出。
- 異常作為一個類,有自己的成員,可以傳遞足夠的信息。
- 函數的返回值可以忽略,異常不可以忽略,可以使程序更加健壯。
三、基本語法
#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;
}
/*
結果:
對象構建!
對象構建!
對象析構!
對象析構!
異常捕獲!
*/
五、異常接口聲明
- 為了加強程序的可讀性,可以在函數聲明中列出可能拋出的所有異常類型,例如:
void func() throw (A, B, C , D); //這個函數func()能夠且只能拋出類型A B C D及其子類型的異常。 - 如果在函數聲明中沒有包含異常接口聲明,則次函數可以拋擲任何類型的異常,例如:
void func(); - 一個不拋擲任何類型異常的函數可以聲明為:
void func() throw(); - 如果一個函數拋出了它的異常接口聲明所不允許拋出的異常,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* 異常捕獲!
我剛寫異常!
*/
七、異常對象的生命周期
- catch里可以用普通類型元素,引用,指針去接
- 普通元素去接,異常對象catch處理完之後就析構
- 引用的話,不用調用拷貝構造,異常對象catch處理完之後就析構
- 指針接,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;
}
}
指針去接,結果為:
構造函數!
異常捕獲!
析構函數!
*/