日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【C++基础】自定义异常类与多重捕获

發(fā)布時間:2023/12/1 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++基础】自定义异常类与多重捕获 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 自定義異常類
    • 構(gòu)建過程
    • 例:Vec3D類的數(shù)組下標(biāo)越界的異常類
  • 捕獲多種無關(guān)異常
    • 不同的異常的捕獲
    • 捕獲派生異常
    • 異常處理的次序
    • 例子:多重捕獲異常類
    • catch塊的參數(shù)類型可以不用引用類型嗎?

自定義異常類

自定義異常類通常由exception或其后代類派生。這樣我們就可以去override e.what() 函數(shù)。

構(gòu)建過程

這里我們以構(gòu)建一個Vec3D類的數(shù)組下標(biāo)越界的異常類為例子。

自定義異常類過程:
1、找一個高級一點的類,繼承一下
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ù)的初始化列表上對父類的構(gòu)造函數(shù)也進行初始化。
注意exception基類構(gòu)造函數(shù)是不接受參數(shù)的,其派生類可以接受一個字符串參數(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:實現(xiàn)Vec3D::operator[](const int index)
當(dāng)index越界時,拋出RangeException的對象
task4:在主函數(shù)創(chuàng)建Vec3D對象并調(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:
這里需要注意一個點:數(shù)組下標(biāo)運算符不加&時,返回的是一個右值,不能通過數(shù)組下標(biāo)運算符修改數(shù)組的值。加上&后,返回的就是一個左值了。

#pragma once #include <array> #include "RangeException.h" class Vec3D { private:std::array <double, 3> v{1.0,1.0,1.0}; 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 <= 2) {return v[index];}else {throw RangeException(3,index);}}};

當(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; }

可以看見,拋出了我們自定義的異常。

接下來我們捕獲這個異常:
注意,此時我們是捕獲exception基類類型的異常,由于RangeException是從exception繼承下來的,所以我們能夠捕獲,但是我們沒有對exception的what進行覆寫,所以不會打印異常信息。

#include <iostream> #include <exception> #include "Vec3D.h" using namespace std;int main() {Vec3D v1 {1.2,1.3,1.4};try {cout << v1[4];}catch (exception & e) {cout << "Exception :" << e.what() << endl;}return 0; }


接下來我們將exception轉(zhuǎn)換為RangeException,再調(diào)用RangeException的成員函數(shù)進行異常信息查詢。

int main() {Vec3D v1 {1.2,1.3,1.4};try {cout << v1[4];}catch (exception & e) {cout << "Exception :" << e.what() << endl;if (typeid(e) == typeid(RangeException)) {auto r = dynamic_cast<RangeException &>(e);cout << "Vector Dimension :" << r.getDimension() << endl;cout << "Index: " << r.getIndex() << endl;}}return 0; }

捕獲多種無關(guān)異常

不同的異常的捕獲

try塊中的代碼可能會拋出不同類型的異常:
注意,throw出去的是對象,這里我們創(chuàng)建的匿名對象。

class EA: public exception { }; class EB: public exception { }; class C { public:void foo(int x) {if (x == 1)throw EA();else if (x == 2)throw EB();} };

而一個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ù)類型為基類異常類型,則可以匹配:能捕獲基類對象、也能捕獲派生類對象

try {throw MyException(); // 拋出派生異常對象 } catch (logic_error& e) { // catch參數(shù)為基類異常,但可以捕獲所有派生類異常對象MyException* p = dynamic_cast<MyException*>(&e); // 轉(zhuǎn)指針失敗不會再拋異常if (p != nullptr)cout << p->what() << endl;elsecout << e.what() << endl; }

之前也提到過:dynamic_cast(obj)

  • 若轉(zhuǎn)型失敗且NewType是指針類型,則返回nullptr。
  • 若轉(zhuǎn)型失敗且NewType是引用類型,則拋出std::bad_cast類型的異常
  • 異常處理的次序

    捕獲異常的正確次序:

    派生類的catch塊在前、基類的catch塊在后

    這種寫法是錯誤的:

    // (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)向量除以一個數(shù)為0時拋該異常
    該異常應(yīng)該繼承runtime_error
    task3:重載operator / () ,為Vec3D類添加標(biāo)量除法(向量除以一個數(shù))
    當(dāng)除數(shù)為0.0時拋異常。
    根據(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)前對象的拷貝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 () 括號中的異常對象參數(shù)可否不用引用類型?
    2、catch () 括號中的異常對象參數(shù)可否使用指針類型,比如: catch (Exception* e)
    3、在多重異常捕獲的代碼中,若幾個catch()括號中的參數(shù)是某個類繼承鏈中不同層次的類的對象,此時括號中的參數(shù)可否不用引用類型?為什么?

    1、catch()括號中的異常對象參數(shù) 可以使用:

    1對象指針 catch(Exception * o)2對象引用catch(Exception & o)3一個對象catch(Exception o) < >

    2、 在多重異常捕獲的代碼中,若幾個catch()括號中的參數(shù)是某個繼承鏈中不同層次的類的對象,此時括號中的參數(shù)可以不用引用或指針類型,編譯可以通過。

    catch()參數(shù)如果是對象會發(fā)生什么?

    1、會發(fā)生數(shù)據(jù)成員丟失

    繼承鏈上多態(tài)polymophic(虛函數(shù)的動態(tài)綁定)只有使用對象引用、指針類型才能發(fā)生。
    當(dāng)一個子類對象賦值給一個父類對象,這時放生的是對象之間數(shù)據(jù)成員間的拷貝,如果子類中比父類多出了一些數(shù)據(jù)成員,多出的數(shù)據(jù)成員會被截斷丟棄,并且通過被賦值后的父類對象調(diào)用的虛方法只能是父類本身的虛方法,無法調(diào)用子類的虛方法,這是靜態(tài)綁定。

    2、會發(fā)生淺拷貝

    形參與實參兩個異常類對象的對象指針數(shù)據(jù)成員,指向堆中同一個new出來對象。
    發(fā)生拷貝構(gòu)造之后,原來作為catch()函數(shù)實參的異常對象要被銷毀(先析構(gòu)再收回空間),異常對象中對象指針指成員指向堆中new出來的對象地址空間被收回。但是作為catch()函數(shù)形參的異常對象還在,當(dāng)形參離開catch塊作用域范圍前也需要被析構(gòu),并其離開catch塊時將形參異常對象POP從棧中彈出釋放空間。而在形參異常對象被析構(gòu)時,需要先delete已經(jīng)在堆中已經(jīng)回收的對象地址,所以刪除一個不存在的地址程序會出錯。

    3、·作為throw出的異常應(yīng)該是對象,有可能被catch形參及塊內(nèi)數(shù)據(jù)覆蓋:

    在函數(shù)作用域范圍內(nèi),被拋出的異常對象作為本地變量,本地變量存儲在棧中,棧存儲規(guī)律是先入后出,并且地址的大小排列順序由高向低,而棧幀尋址方式重棧頂(低地址)到棧底(高地址)。

    Throw拋異常是逐漸出棧過程,當(dāng)遇到匹配的catch函數(shù)時停止出棧,轉(zhuǎn)入catch作用域中。

    也就是說throw出的異常對象高懸在棧頂,但是異常對象的空間已經(jīng)被收回(對象已經(jīng)POP出棧)。

    但是異常對象存儲的數(shù)據(jù)成員還存在與棧中,由與棧的寫入數(shù)據(jù)序是有高向低地址,被throw拋出的異常對象地址在棧頂最小地址,所以很難被其他數(shù)據(jù)覆蓋。

    在catch函數(shù)作用域中只要抓取到被throw拋出的異常對象地址,就可以使用這個異常對象中的數(shù)據(jù)成員。

    如果catch函數(shù)的形式參數(shù)如果是對象,形參對象也要入棧分配空間,這有可能會導(dǎo)致被throw拋出的異常對象數(shù)據(jù)被覆蓋,所以使用對象指針或引用作為catch()函數(shù)的形參比較安全,指針或引用占用棧的空間比較小

    總結(jié)

    以上是生活随笔為你收集整理的【C++基础】自定义异常类与多重捕获的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。