【C++基础】自定义异常类与多重捕获
目錄
- 自定義異常類
- 構(gòu)建過程
- 例:Vec3D類的數(shù)組下標(biāo)越界的異常類
- 捕獲多種無關(guān)異常
- 不同的異常的捕獲
- 捕獲派生異常
- 異常處理的次序
- 例子:多重捕獲異常類
- catch塊的參數(shù)類型可以不用引用類型嗎?
自定義異常類
自定義異常類通常由exception或其后代類派生。這樣我們就可以去override e.what() 函數(shù)。
構(gòu)建過程
這里我們以構(gòu)建一個(gè)Vec3D類的數(shù)組下標(biāo)越界的異常類為例子。
自定義異常類過程:
1、找一個(gè)高級(jí)一點(diǎn)的類,繼承一下
class RangeException : public out_of_range
2、定義一些能夠記錄異常問題的變量
size_t dimension;
int index;
3、由異常類來記錄問題,所以要將變量放到異常類的構(gòu)造函數(shù)中,
RangeException(size_t dimension, int index);
4、給父類加入提示信息,在異常類的構(gòu)造函數(shù)的初始化列表上對(duì)父類的構(gòu)造函數(shù)也進(jìn)行初始化。
注意exception基類構(gòu)造函數(shù)是不接受參數(shù)的,其派生類可以接受一個(gè)字符串參數(shù),用來描述該異常
RangeException () : out_of_range(“Vec index error”) {};
例:Vec3D類的數(shù)組下標(biāo)越界的異常類
task1:創(chuàng)建Vec3D類,用array保存向量成員
task2:創(chuàng)建RangeException類,定義構(gòu)造函數(shù)
RangeException(std::size_t dimension,const int index)
task3:實(shí)現(xiàn)Vec3D::operator[](const int index)
當(dāng)index越界時(shí),拋出RangeException的對(duì)象
task4:在主函數(shù)創(chuàng)建Vec3D對(duì)象并調(diào)用[]制造越界問題,捕獲異常并輸出異常中的信息。
RangeException.h:
#pragma once#include <iostream> #include <exception> class RangeException : public std::exception { private:std::size_t dimension{ 3 };int index{ 0 }; public:RangeException (std::size_t dimension,const int index) {this->dimension = dimension;this->index = index;}std::size_t getDimension() {return dimension;}int getIndex() {return index;} };Vec3D.h:
這里需要注意一個(gè)點(diǎn):數(shù)組下標(biāo)運(yùn)算符不加&時(shí),返回的是一個(gè)右值,不能通過數(shù)組下標(biāo)運(yùn)算符修改數(shù)組的值。加上&后,返回的就是一個(gè)左值了。
當(dāng)主函數(shù)為:
#include <iostream> #include "Vec3D.h" using namespace std;int main() {Vec3D v1 {1.2,1.3,1.4};cout << v1[4];return 0; }可以看見,拋出了我們自定義的異常。
接下來我們捕獲這個(gè)異常:
注意,此時(shí)我們是捕獲exception基類類型的異常,由于RangeException是從exception繼承下來的,所以我們能夠捕獲,但是我們沒有對(duì)exception的what進(jìn)行覆寫,所以不會(huì)打印異常信息。
接下來我們將exception轉(zhuǎn)換為RangeException,再調(diào)用RangeException的成員函數(shù)進(jìn)行異常信息查詢。
捕獲多種無關(guān)異常
不同的異常的捕獲
try塊中的代碼可能會(huì)拋出不同類型的異常:
注意,throw出去的是對(duì)象,這里我們創(chuàng)建的匿名對(duì)象。
而一個(gè)catch塊只能捕獲一種異常:
int main() {C c { };try {c.foo(1);c.foo(2);} catch (EA& a) {cout << a.what() << endl;} catch (EB& b) {cout << b.what() << endl;}捕獲派生異常
派生異常類:
class MyException: public logic_error { };catch參數(shù)類型為基類異常類型,則可以匹配:能捕獲基類對(duì)象、也能捕獲派生類對(duì)象
try {throw MyException(); // 拋出派生異常對(duì)象 } catch (logic_error& e) { // catch參數(shù)為基類異常,但可以捕獲所有派生類異常對(duì)象MyException* p = dynamic_cast<MyException*>(&e); // 轉(zhuǎn)指針失敗不會(huì)再拋異常if (p != nullptr)cout << p->what() << endl;elsecout << e.what() << endl; }之前也提到過:dynamic_cast(obj)
異常處理的次序
捕獲異常的正確次序:
派生類的catch塊在前、基類的catch塊在后
這種寫法是錯(cuò)誤的:
// (a) try {... } catch (logic_error& e) {... } catch (MyException& e) {... }例子:多重捕獲異常類
task1:基于Vec3D類、RangeException異常類修改
1.1:將Vec3D的維數(shù)抽取出來
1.2:將RangeException改為繼承 out_of_range
task2:添加ZeroException,當(dāng)向量除以一個(gè)數(shù)為0時(shí)拋該異常
該異常應(yīng)該繼承runtime_error
task3:重載operator / () ,為Vec3D類添加標(biāo)量除法(向量除以一個(gè)數(shù))
當(dāng)除數(shù)為0.0時(shí)拋異常。
根據(jù)IEEE 754 rules:
x > 0.0 : x/0.0 = INF
x < 0.0 : x/0.0 = -INF
0.0 / 0.0 = NaN
Vec3D.h:
#pragma once#include <array> #include <string> #include <cmath> #include <limits> #include "RangeException.h" #include "ZeroException.h" class Vec3D { public:constexpr static std::size_t DIMENSION = 3;private:std::array <double, DIMENSION> v{1.0,1.0,1.0};bool AreSame(double a, double b) {return std::fabs(a - b) < std::numeric_limits<double>::epsilon();} public:Vec3D() = default;Vec3D(double x, double y, double z){v[0] = x;v[1] = y;v[2] = z;}double &operator [] (const int index) {if(index >=0 && index < DIMENSION) {return v[index];}else {throw RangeException(DIMENSION,index);}}Vec3D operator /(const double divisor) {//構(gòu)造當(dāng)前對(duì)象的拷貝Vec3D t(*this);if (AreSame(divisor,0.0))throw ZeroException();for (auto &i : t.v) {i /= divisor;}return t;}};RangeException.h:
#pragma once#include <iostream> #include <exception> class RangeException : public std::out_of_range { private:std::size_t dimension{ 0 };int index{ 0 }; public:RangeException (std::size_t dimension,const int index) : out_of_range("index exceeds Vector dimension"){this->dimension = dimension;this->index = index;}std::size_t getDimension() {return dimension;}int getIndex() {return index;} };ZeroException.h:
#pragma once#include <stdexcept> #include <exception>class ZeroException : public std::runtime_error { public:ZeroException() : runtime_error("Divided by 0.0") {};ZeroException(const char* msg) : runtime_error("Divided by 0.0") {}; };main.cpp:
#include <iostream> #include <exception> #include "RangeException.h" #include "Vec3D.h" using namespace std;int main() {Vec3D v1 {1.2,1.3,1.4};try {cout << (v1 / 0.0)[0] << endl;}catch (RangeException & e) {cout << "Exception :" << e.what() << endl;cout << "Vector Dimension :" << e.getDimension() << endl;cout << "Index: " << e.getIndex() << endl;}catch (ZeroException & e) {cout << "Exception :" << e.what() << endl;}return 0; }效果:
catch塊的參數(shù)類型可以不用引用類型嗎?
1、catch () 括號(hào)中的異常對(duì)象參數(shù)可否不用引用類型?
2、catch () 括號(hào)中的異常對(duì)象參數(shù)可否使用指針類型,比如: catch (Exception* e)
3、在多重異常捕獲的代碼中,若幾個(gè)catch()括號(hào)中的參數(shù)是某個(gè)類繼承鏈中不同層次的類的對(duì)象,此時(shí)括號(hào)中的參數(shù)可否不用引用類型?為什么?
1、catch()括號(hào)中的異常對(duì)象參數(shù) 可以使用:
1對(duì)象指針 catch(Exception * o)2對(duì)象引用catch(Exception & o)3一個(gè)對(duì)象catch(Exception o) < >2、 在多重異常捕獲的代碼中,若幾個(gè)catch()括號(hào)中的參數(shù)是某個(gè)繼承鏈中不同層次的類的對(duì)象,此時(shí)括號(hào)中的參數(shù)可以不用引用或指針類型,編譯可以通過。
catch()參數(shù)如果是對(duì)象會(huì)發(fā)生什么?:
1、會(huì)發(fā)生數(shù)據(jù)成員丟失
繼承鏈上多態(tài)polymophic(虛函數(shù)的動(dòng)態(tài)綁定)只有使用對(duì)象引用、指針類型才能發(fā)生。
當(dāng)一個(gè)子類對(duì)象賦值給一個(gè)父類對(duì)象,這時(shí)放生的是對(duì)象之間數(shù)據(jù)成員間的拷貝,如果子類中比父類多出了一些數(shù)據(jù)成員,多出的數(shù)據(jù)成員會(huì)被截?cái)鄟G棄,并且通過被賦值后的父類對(duì)象調(diào)用的虛方法只能是父類本身的虛方法,無法調(diào)用子類的虛方法,這是靜態(tài)綁定。
2、會(huì)發(fā)生淺拷貝
形參與實(shí)參兩個(gè)異常類對(duì)象的對(duì)象指針數(shù)據(jù)成員,指向堆中同一個(gè)new出來對(duì)象。
發(fā)生拷貝構(gòu)造之后,原來作為catch()函數(shù)實(shí)參的異常對(duì)象要被銷毀(先析構(gòu)再收回空間),異常對(duì)象中對(duì)象指針指成員指向堆中new出來的對(duì)象地址空間被收回。但是作為catch()函數(shù)形參的異常對(duì)象還在,當(dāng)形參離開catch塊作用域范圍前也需要被析構(gòu),并其離開catch塊時(shí)將形參異常對(duì)象POP從棧中彈出釋放空間。而在形參異常對(duì)象被析構(gòu)時(shí),需要先delete已經(jīng)在堆中已經(jīng)回收的對(duì)象地址,所以刪除一個(gè)不存在的地址程序會(huì)出錯(cuò)。
3、·作為throw出的異常應(yīng)該是對(duì)象,有可能被catch形參及塊內(nèi)數(shù)據(jù)覆蓋:
在函數(shù)作用域范圍內(nèi),被拋出的異常對(duì)象作為本地變量,本地變量存儲(chǔ)在棧中,棧存儲(chǔ)規(guī)律是先入后出,并且地址的大小排列順序由高向低,而棧幀尋址方式重棧頂(低地址)到棧底(高地址)。
Throw拋異常是逐漸出棧過程,當(dāng)遇到匹配的catch函數(shù)時(shí)停止出棧,轉(zhuǎn)入catch作用域中。
也就是說throw出的異常對(duì)象高懸在棧頂,但是異常對(duì)象的空間已經(jīng)被收回(對(duì)象已經(jīng)POP出棧)。
但是異常對(duì)象存儲(chǔ)的數(shù)據(jù)成員還存在與棧中,由與棧的寫入數(shù)據(jù)序是有高向低地址,被throw拋出的異常對(duì)象地址在棧頂最小地址,所以很難被其他數(shù)據(jù)覆蓋。
在catch函數(shù)作用域中只要抓取到被throw拋出的異常對(duì)象地址,就可以使用這個(gè)異常對(duì)象中的數(shù)據(jù)成員。
如果catch函數(shù)的形式參數(shù)如果是對(duì)象,形參對(duì)象也要入棧分配空間,這有可能會(huì)導(dǎo)致被throw拋出的異常對(duì)象數(shù)據(jù)被覆蓋,所以使用對(duì)象指針或引用作為catch()函數(shù)的形參比較安全,指針或引用占用棧的空間比較小
總結(jié)
以上是生活随笔為你收集整理的【C++基础】自定义异常类与多重捕获的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 偶是菜鸟,弱弱问下补丁如何安装?
- 下一篇: 摩尔庄园洒水机怎么升级