c++異常機制(3) -- 異常類型和生命周期

目錄

拋出的異常類型大致可以分為三種。?

第一種? ? 基本類型

1. 可以直接拋出常量?

2. 也可以拋出定義好的變量?

3. 如果我們使用const,會不會影響到異常的匹配。

第二種? ? 字符串類型以及指針類型

1. 使用字符指針?

注意:?

2. 使用string類型?

第三種? 自定義類型(類類型)?

?

注意事項:

?

catch是根據我們拋出的異常信息類型來捕獲的。?

拋出的異常類型大致可以分為三種。?

第一種? ? 基本類型

?int, char, float,double等類型。

以拋出int類型的數據為例?

1. 可以直接拋出常量?
void func1() {throw - 1;printf("func1");
}int main(void) {try {func1();}catch (int error) {printf("異常處理 %d\n",error);}system("pause");return 0;
}
2. 也可以拋出定義好的變量?
void func1() {int err = -1;   // 定義變量throw err;printf("func1");
}int main(void) {try {func1();}catch (int error) {printf("異常處理 %d\n",error);}system("pause");return 0;
}
3. 如果我們使用const,會不會影響到異常的匹配。

我們對上面的代碼進行修改,?我們在func1定義err時和catch參數中兩個地方加上const,或者一個加一個不加。執行代碼,會發現依然能匹配成功。所以對于普通類型的數據,有沒有const都不會影響到異常的匹配的。

第二種? ? 字符串類型以及指針類型

首先C語言的字符串類型是字符指針,c++的字符串類型string(當然c++中包含C語言指針)。?

1. 使用字符指針?

第一種:? 直接拋出字符串常量?

對于字符串常量,我們直接使用非const指針指向它是不安全的,但是有的編譯器允許這么做。所以有的編譯器char*類型也可以與字符串常量匹配,但是有的編譯器認為字符串常量必須使用const的字符指針指向才行,所以只能與const char*匹配。

void func1() {throw "異常";printf("func1");
}int main(void) {try {func1();}catch (const char* error) {printf("異常處理 %s\n",error);}system("pause");return 0;
}
注意:?

雖然在c++中我們可以使用string定義字符串,但是string只是c++封裝的一個類型。字符串常量或者字符指針,本身表示的是一個地址,所以它是沒有辦法與string類型匹配成功的,需要使用字符指針。?

第二種:? 使用字符指針指向或者字符數組

?字符數組:??

void func1() {char arr[] = "異常";throw arr;printf("func1");
}int main(void) {try {func1();}catch (const char* error) {printf("異常處理 %s\n",error);}system("pause");return 0;
}

字符指針:? ?

void func1() {char* arr = (char*)"異常";throw arr;printf("func1");
}int main(void) {try {func1();}catch (char* error) {printf("異常處理 %s\n",error);}system("pause");return 0;
}

?無論是字符數組還是字符指針:

1. 如果我們拋出的是const 修飾的,那么只能和catch中const修飾的char*匹配。?

2. 如果我們拋出的是非const修飾的,那么catch中用不用const修飾都可以匹配成功。?

3. 其實和賦值時,const修飾的不能賦值給非const修飾的,非const修飾的可以賦值給const修飾的是一個道理的。

4. 當然如果const在*后面修飾,那么就不會影響匹配。char*const可以和char*匹配成功。

2. 使用string類型?

string類型其實和普通類型差不多,string*和char*也類似。但是string類型和char*類型是無法匹配的,雖然它們都可以表示字符串,但是是不同的類型。

第三種? 自定義類型(類類型)?

我們可以將相應的異常封裝成一個類,類中封裝一些方法,在出現這類異常之后,?可以拋出一個此類的對象。

?看下面這段代碼,將打開文件異常和寫入文件異常封裝成兩個類,然后拋出它們的對象。

#define BUFFER_SIZE 1024class OpenFileError {
public:OpenFileError(int err) :errorData(err) {};void print() {switch (errorData) {case -1:printf("源文件打開失敗 %d\n", errorData);break;case -2:printf("目的文件打開失敗 %d\n", errorData);break;}}
private:int errorData;
};class WriteFileError {
public:void print() {printf("文件寫入失敗");}
};// 將一個文件中的內容拷貝到另外一個文件中去
int makeFile(const char* dest, const char* src) {// 定義文件指針FILE* fp1 = NULL, * fp2 = NULL;// 打開文件, 以只讀二進制形式打開文件,打開失敗返回NULLfp1 = fopen(src, "rb"); // 判斷文件是否成功打開if (!fp1) {throw OpenFileError(-1);}// 打開文件,以只寫二進制形式打開文件,打開失敗返回NULLfp2 = fopen(dest, "wb");// 判斷文件是否成功打開if (!fp2) {throw OpenFileError(-2);    //  返回錯誤標記,表示目標文件打開失敗}// 進行文件的拷貝char buffer[BUFFER_SIZE];    // 1024字節的緩存int readLen, writeLen;       // 每次讀取的長度和寫入的長度// 讀取的長度大于0,說明有內容可以寫入,執行循環體的寫入內容while ((readLen = fread(buffer, 1, BUFFER_SIZE, fp1)) > 0) {writeLen = fwrite(buffer, 1, readLen, fp2);// 如果一次寫入的長度和讀取的長度不等,那么說明寫入失敗if (readLen != writeLen) {throw WriteFileError(); }}// 關閉文件fclose(fp1);fclose(fp2);return 0;  // 一切正常返回0
}int makeFile2(const char* dest, const char* src) {int ret;ret = makeFile(dest, src);printf("makeFile2 函數被調用");return ret;
}int main(void) {int ret = 0;try {ret = makeFile2("dest.txt", "src.txt");}catch (OpenFileError& error) {error.print();}catch (WriteFileError& error) {error.print();}system("pause");return 0;
}

其實拋出類對象的寫法不止一種,但是我們為什么選擇使用上面的方式呢????

我們使用下面的代碼進行說明。

class Error {
public:Error(int err) :errorData(err) {cout << "構造函數" << errorData << endl;};~Error() {cout << "析構函數" << errorData << endl;};Error(const Error& error) {errorData = error.errorData;cout << "拷貝構造函數"<< errorData << endl;};void print() {printf("異常:%d", errorData);}
public:int errorData;
};void func1() {Error err(-1);throw err;printf("func1");
}int main(void) {try {func1();}catch (Error error) {error.errorData = 10;printf("異常處理 %d\n",error);}system("pause");return 0;
}

運行結果:?

?

分析:?

上面代碼,我們拋出異常時是拋出的定義的對象,在catch接收的時候也是直接使用的普通參數形式(Error error)。 我們使用類對象的構造,析構,拷貝來觀察拋出的過程。

運行結果:??

第一個構造函數是用來構造我們的func1函數中的error對象的。?

第一個拷貝構造函數是在拋出的時候,編譯器會根據我們拋出的error對象,創建一個匿名對象進行拋出,所以會調用一次拷貝構造函數。?

第二個拷貝構造函數是我們拋出的匿名對象,在與catch中的參數Error error配對之后,直接將匿名對象在error初始化時賦值給它。?

第一個析構函數是我們拋出創建的匿名對象后,func1函數就執行結束了,error是其內部的局部變量,就會被銷毀,所以這個析構函數是用來銷毀func1中的error對象的。?

異常處理 10是我們配對成功,之后執行的異常處理代碼。?

析構函數 10是我們在和catch匹配的時候,根據其參數創建了對象error,其作用域就是這個catch開始到結束,catch的代碼執行完,結束的時候,這個對象的生命周期也就結束了,所以調用析構函數?

析構函數 -1是用來銷毀系統拋出的匿名對象,而調用的構造函數。

?

說明:?

最后兩個析構函數我們怎么能確定是來銷毀哪個對象的呢?在代碼中我們在對應的構造函數中打印了數據errorData的值,我們在func1中創建對象時將其初始化為-1,然后編譯器會將其拷貝給匿名對象,匿名對象內的屬性值我們無法處理,但是在catch接收匿名對象的時候,定義了另外一個對象error,我們將這個對象的值顯示修改為10。所以,析構函數 10就是用來析構它的。

對比:?

我們將第二段代碼進行修改 --? 在拋出時,使用匿名對象,接收是使用引用

class Error {
public:Error(int err) :errorData(err) {cout << "構造函數" << errorData << endl;};~Error() {cout << "析構函數" << errorData << endl;};Error(const Error& error) {errorData = error.errorData;cout << "拷貝構造函數"<< errorData << endl;};void print() {printf("異常:%d", errorData);}
public:int errorData;
};void func1() {throw Error(-1);printf("func1");
}int main(void) {try {func1();}catch (Error& error) {error.errorData = 10;printf("異常處理 %d\n",error);}system("pause");return 0;
}

運行結果:?

?

分析:? ?

首先第一眼看,這個運行的效率就比前面的效率高很多。?就是在拋出類對象的時候,直接拋出匿名對象,在catch的參數中寫使用類的引用接收。

?

運行結果:?

構造函數 -1:? ?創建匿名對象并拋出。?

異常處理 10:? ?匹配成功,執行異常處理代碼。?

析構函數 10:?? 調用析構函數,銷毀匿名對象。

?

說明:??

為什么構造函數和析構函數打印出來的errorData不一樣?因為我們在構造匿名函數時將errorData初始化為-1,但是在catch捕獲異常的時候,我們將其修改為了10。因為我們catch中使用的是引用,所以error還是表示哪個匿名對象,所以在析構時errorData變為了10。?

?

那么為什么可以使用引用來接收函數拋出的匿名對象呢?

因為,我們在第二段代碼中知道,在異常機制中,函數拋出的匿名對象析構要在catch中創建的對象析構之后。所以,我們使用引用來接收函數返回的匿名對象之后,在catch語句結束時,是引用的量先被釋放,匿名對象后被釋放。這樣就不會存在引用指向局部變量的問題了。

?

總結:? ??

綜上所述,我們在使用自定義類拋出異常的時候,應該直接拋出其匿名對象,并且使用引用來接收。這樣就會少調用幾次構造函數和析構函數,那么就大大提高了效率。

?

注意事項:

1.? 上面說到,catch的參數中對象的引用對象的定義都可以與拋出的匿名對象進行匹配,那么如果同時存在兩個catch,參數分別為這兩種,那么發生什么??

會出錯,因為兩個都能匹配,編譯器區分不了,自然就報錯了。?

2.? ?1.中的情況,不僅是類類型,對于普通類型和字符串類型也是一樣的。?

3.? ?上面說到,普通類型,catch中參數加const和不加const都能匹配,所以兩者都存在的話,編譯器也無法區分,會報錯。?但是對于指針就不會報錯。

4.? ?其它情況也還類似。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/711310.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/711310.shtml
英文地址,請注明出處:http://en.pswp.cn/news/711310.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算機專業大學生的簡歷,為何會出現在垃圾桶

為什么校招過后垃圾桶里全是簡歷&#xff0c;計算機專業的學生找工作有多難&#xff1f; 空哥這么跟你說吧&#xff0c;趁現在還來得及&#xff0c;這些事情你一定要聽好了。 第一&#xff0c;計算機專業在學校學的東西是非常有限的&#xff0c;985211的還好&#xff0c;如果…

GPS歷史軌跡優化算法的研究與實現

GPS歷史軌跡優化算法的研究與實現 摘要 本研究提出了一種綜合利用數據清洗、密度聚類、卡爾曼濾波和地圖匹配的新算法,命名為“DSKF-Match”。該算法旨在處理GPS軌跡數據,通過清洗、聚類、平滑和匹配等步驟,提高數據的質量和準確性。首先,算法利用時間窗口法進行數據清洗…

D365:LookUp

文章目錄 前言一、復制onLookUp事件方法二、LookUp方法 前言 在Form的字段的onLookUp方法中&#xff0c;添加下拉框。 一、復制onLookUp事件方法 二、LookUp方法 [FormControlEventHandler(formControlStr(EcoResProductDetailsExtended, VyaKeyItemType_VyaMaterialSubCode…

Vue2:路由守衛實現權限管理之獨享路由守衛

一、情景說明 單獨給某個路由組件配置守衛 二、案例 給news路由配置獨享路由守衛 在進入該路由組件前&#xff0c;會觸發相關函數 函數內編寫鑒權功能的相關代碼即可 關鍵配置&#xff1a;beforeEnter {name:xinwen,path:news,component:News,meta:{isAuth:true,title:新聞}…

【PyTorch知識點匯總】

PyTorch是一個廣泛使用的深度學習框架&#xff0c;它提供了許多功能強大的工具和函數&#xff0c;用于構建和訓練神經網絡。以下是一些PyTorch的常用知識點和示例說明&#xff1a; 張量&#xff08;Tensors&#xff09; 創建張量&#xff1a;使用torch.tensor()?、torch.Tenso…

面試經典150題——用最少數量的箭引爆氣球

"The only person you are destined to become is the person you decide to be." - Ralph Waldo Emerson 1. 題目描述 2. 題目分析與解析 這個題目開始讀題的時候是有點不好理解題意的&#xff0c;因此我先做個圖讓大家對于題意有更好更直觀的理解再來分析題目。 …

如何使用Portainer創建Nginx容器并搭建web網站發布至公網可訪問【內網穿透】

文章目錄 前言1. 安裝Portainer1.1 訪問Portainer Web界面 2. 使用Portainer創建Nginx容器3. 將Web靜態站點實現公網訪問4. 配置Web站點公網訪問地址4.1公網訪問Web站點 5. 固定Web靜態站點公網地址6. 固定公網地址訪問Web靜態站點 前言 Portainer是一個開源的Docker輕量級可視…

SQL 常見命令及規范

常見命令 1. 查看當前所有數據庫 show databases; 2. 打開指定的庫 use 庫名 ; 3. 查看當前庫的所有表 show tables; 4. 查看其他庫的所有表 show tables from 庫名 ; 5. 創建表 cerate table 表名 ( 列名 列類型&#xff0c; 列名 列類型&#xff0c; ..... …

基于YOLO家族最新模型YOLOv9開發構建自己的個性化目標檢測系統從零構建模型完整訓練、推理計算超詳細教程【以自建數據酸棗病蟲害檢測為例】

在我前面的系列博文中,對于目標檢測系列的任務寫了很多超詳細的教程,目的是能夠讀完文章即可實現自己完整地去開發構建自己的目標檢測系統,感興趣的話可以自行移步閱讀: 《基于官方YOLOv4-u5【yolov5風格實現】開發構建目標檢測模型超詳細實戰教程【以自建缺陷檢測數據集為…

C# OpenVINO Crack Seg 裂縫分割 裂縫檢測

目錄 效果 模型信息 項目 代碼 數據集 下載 C# OpenVINO Crack Seg 裂縫分割 裂縫檢測 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-02-29T16:35:48.364242 author&#xff1a;Ultralytics task&#xff1a;segment version&…

去掉WordPress網頁圖片默認鏈接功能

既然是wordpress自動添加的&#xff0c;那么我們在上傳圖片到wordpress后臺多媒體的時候&#xff0c;就可以手動改變鏈接指向或者刪除掉&#xff0c;問題是每次都要這么做很麻煩&#xff0c;更別說有忘記的時候。一次性解決這個問題有兩種方法&#xff0c;一種是No Image Link插…

【生成式AI】ChatGPT原理解析(1/3)- 對ChatGPT的常見誤解

Hung-yi Lee 課件整理 文章目錄 誤解1誤解2ChatGPT真正在做的事情-文字接龍 ChatGPT是在2022年12月7日上線的。 當時試用的感覺十分震撼。 誤解1 我們想讓chatGPT講個笑話&#xff0c;可能會以為它是在一個笑話的集合里面隨機地找一個笑話出來。 我們做一個測試就知道不是這樣…

C# Post數據或文件到指定的服務器進行接收

目錄 應用場景 實現原理 實現代碼 PostAnyWhere類 ashx文件部署 小結 應用場景 不同的接口服務器處理不同的應用&#xff0c;我們會在實際應用中將A服務器的數據提交給B服務器進行數據接收并處理業務。 比如我們想要處理一個OFFICE文件&#xff0c;由用戶上傳到A服務器…

中國汽車電子行業發展現狀分析及投資前景預測報告

全版價格&#xff1a;壹捌零零 報告版本&#xff1a;下單后會更新至最新版本 交貨時間&#xff1a;1-2天 第一章 汽車電子相關概述 1.1 汽車的相關介紹 1.1.1 汽車的概念 我國國家最新標準《汽車和掛車類型的術語和定義》&#xff08;GB/T3730&#xff0e;1—2001&…

基于springboot+vue的貿易行業crm系統

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…

Flink分區相關

0、要點 Flink的分區列不會存數據&#xff0c;也就是兩個列有一個分區列&#xff0c;則文件只會存另一個列的數據 1、CreateTable 根據SQL的執行流程&#xff0c;進入TableEnvironmentImpl.executeInternal&#xff0c;createTable分支 } else if (operation instanceof Crea…

Java-nio

一、NIO三大組件 NIO的三大組件分別是Channel&#xff0c;Buffer與Selector Java NIO系統的核心在于&#xff1a;通道(Channel)和緩沖區(Buffer)。通道表示打開到 IO 設備(例如&#xff1a;文件、套接字)的連接。若需要使用 NIO 系統&#xff0c;需要獲取用于連接 IO 設備的通…

Spring的簡單使用及內部實現原理

在現代的Java應用程序開發中&#xff0c;Spring Framework已經成為了不可或缺的工具之一。它提供了一種輕量級的、基于Java的解決方案&#xff0c;用于構建企業級應用程序和服務。本文將介紹Spring的簡單使用方法&#xff0c;并深入探討其內部實現原理。 首先&#xff0c;讓我們…

mysql8.0使用MGR實現高可用

一、三節點MGR集群的安裝部署 1. 安裝準備 準備好下面三臺服務器&#xff1a; IP端口角色192.168.150.213306mgr1192.168.150.223306mgr2192.168.150.233306mgr3 配置hosts解析 # cat >> /etc/hosts << EOF 192.168.150.21 mgr1 192.168.150.22 mgr2 192.168…

Windows環境下的調試器探究——硬件斷點

與軟件斷點與內存斷點不同&#xff0c;硬件斷點不依賴被調試程序&#xff0c;而是依賴于CPU中的調試寄存器。 調試寄存器有7個&#xff0c;分別為Dr0~Dr7。 用戶最多能夠設置4個硬件斷點&#xff0c;這是由于只有Dr0~Dr3用于存儲線性地址。 其中&#xff0c;Dr4和Dr5是保留的…