1. 程式人生 > >第十一課、異常類的構建-------------狄泰軟件學院

第十一課、異常類的構建-------------狄泰軟件學院

end ++ pla ini images 數據 pointer oid 變量

一、自定義異常類

1、異常的類型可以是自定義的類類型

2、對於類類型的匹配依舊是之上而下的嚴格匹配

3、賦值兼容性原則在異常匹配中依然適用

所以要

(1)、匹配子類異常的catch放在上部

(2)、匹配父類異常的catch放在下部

4、異常類是數據結構所依賴的“基礎設施”(現代c++庫也必然包含充要的異常類族)

技術分享

技術分享

二、一步步打造自己的異常類

1、首先是抽象類EXception的編寫,既然是抽象類,必然含有純虛函數,通常的做法都是將析構函數作為純虛函數

頭文件:接口定義

class Exception
{
protected:
    char* m_message;
    char* m_location;
    
void init(const char* message, const char* file, int line); public: Exception(const char* message); Exception(const char* file, int line); Exception(const char* message, const char* file, int line); Exception(const Exception& e); Exception& operator = (const Exception& e);
virtual const char* message() const; virtual const char* location() const; virtual ~Exception() = 0; };

實現文件:

void Exception::init(const char* message, const char* file, int line)
{
    m_message = strdup(message);//指向的message可能在棧空間,也可能在堆空間或者全局數據去,
                                //為了安全,這裏先復制一份到堆空間
if( file != NULL) { char sl[16] = {0}; itoa(line, sl, 10);//首先將行號轉化為字符串 m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2)); m_location = strcat(m_location, file); m_location = strcat(m_location, ":"); m_location = strcat(m_location, sl); } else { m_location = NULL; } } Exception::Exception(const char* message) { init(message, NULL, 0); } Exception::Exception(const char* file, int line) { init(NULL, file, line); } Exception::Exception(const char* message, const char* file, int line) { init(message, file, line); } Exception::Exception(const Exception& e) { m_message = strdup(e.m_message); m_location = strdup(e.m_location); } Exception& Exception::operator = (const Exception& e) { if( this != &e ) { free(m_message); free(m_location); m_message = strdup(e.m_message); m_location = strdup(e.m_location); } return *this; } const char* Exception::message() const { return m_message; } const char* Exception::location() const { return m_location; } Exception::~Exception() { free(m_message); free(m_location); }

由於構造函數的幾個重載實現方式都差不多,所以定義一個init()函數來進行初始化會方便一點。那EXception類的接口函數const char* Exception::message() const和const char* Exception::location() const為什麽要定義成const屬性的呢?原因很簡單,當有人捕捉到異常後,肯定只希望看到異常是什麽樣,而不希望改變不小心改變相應的成員變量的值,那怎麽樣才能保證對象的成員變量的值不被改變?那就是將對象用const修飾使其成為只讀對象,而const對象只能調用const的成員函數,這就是為什麽將這兩個函數聲明為const的原因。後面做測試的時候我們就將看到const對象調用const函數。

可能還會有疑問的一點是析構函數不是純虛函數嗎?純虛函數不是只聲明而不定義?這是c++的語法規定,由於子類對象在析構時也必須調用父類的析構函數,故即使父類的析構函數定義為純虛函數也應該實現它。

2、各種異常類的實現:繼承EXception後調用相應的父類函數即可

如計算異常類的構建(其他的異常類只需將相應的類名換掉即可)

//計算類異常
class ArithmeticException:public Exception
{
public:
    ArithmeticException():Exception(0) {}
    ArithmeticException(const char* message):Exception(message) {}
    ArithmeticException(const char* file, int line):Exception(file, line) {}
    ArithmeticException(const char* message, const char* file, int line):Exception(message, file, line) {}
    ArithmeticException(const ArithmeticException& e): Exception(e) {}
    ArithmeticException& operator = (const ArithmeticException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

3、至此,我們的異常類的構建已全部完成,接下來是測試階段

為了使用方便,我們可以首先定義一個宏,以後使用時使用該宏只需要填上相應的類名和拋出的信息即可

#define THROW_EXCEPTION(e,m)  (throw e(m, __FILE__, __LINE__))

測試代碼:

int main()
{
    try
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "test");
    }
    catch(const IndexOutOfBoundsException& e)
    {
        cout << "ArithmeticException" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const Exception& e)
    {
        cout << "Exception" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    return 0;
}

一定要註意匹配子類異常的catch放在上部,匹配父類異常的catch放在下部。

三、完整代碼

技術分享
#ifndef EXCEPTION_H
#define EXCEPTION_H

namespace DTLib
{

#define THROW_EXCEPTION(e,m)  (throw e(m, __FILE__, __LINE__))

class Exception
{
protected:
    char* m_message;
    char* m_location;
    void init(const char* message, const char* file, int line);

public:
    Exception(const char* message);
    Exception(const char* file, int line);
    Exception(const char* message, const char* file, int line);

    Exception(const Exception& e);
    Exception& operator = (const Exception& e);

    virtual const char* message() const;
    virtual const char* location() const;

    virtual ~Exception() = 0;
};

//計算類異常
class ArithmeticException:public Exception
{
public:
    ArithmeticException():Exception(0) {}
    ArithmeticException(const char* message):Exception(message) {}
    ArithmeticException(const char* file, int line):Exception(file, line) {}
    ArithmeticException(const char* message, const char* file, int line):Exception(message, file, line) {}
    ArithmeticException(const ArithmeticException& e): Exception(e) {}
    ArithmeticException& operator = (const ArithmeticException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

//空指針異常
class NullPointerException:public Exception
{
public:
    NullPointerException():Exception(0) {}
    NullPointerException(const char* message):Exception(message) {}
    NullPointerException(const char* file, int line):Exception(file, line) {}
    NullPointerException(const char* message, const char* file, int line):Exception(message, file, line) {}
    NullPointerException(const NullPointerException& e): Exception(e) {}
    NullPointerException& operator = (const NullPointerException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

//越界異常
class IndexOutOfBoundsException:public Exception
{
public:
    IndexOutOfBoundsException():Exception(0) {}
    IndexOutOfBoundsException(const char* message):Exception(message) {}
    IndexOutOfBoundsException(const char* file, int line):Exception(file, line) {}
    IndexOutOfBoundsException(const char* message, const char* file, int line):Exception(message, file, line) {}
    IndexOutOfBoundsException(const IndexOutOfBoundsException& e): Exception(e) {}
    IndexOutOfBoundsException& operator = (const IndexOutOfBoundsException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

//內存不足異常
class NoEnoughMemoryException:public Exception
{
public:
    NoEnoughMemoryException():Exception(0) {}
    NoEnoughMemoryException(const char* message):Exception(message) {}
    NoEnoughMemoryException(const char* file, int line):Exception(file, line) {}
    NoEnoughMemoryException(const char* message, const char* file, int line):Exception(message, file, line) {}
    NoEnoughMemoryException(const NoEnoughMemoryException& e): Exception(e) {}
    NoEnoughMemoryException& operator = (const NoEnoughMemoryException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

//參數錯誤異常
class InvalidParameterException:public Exception
{
public:
    InvalidParameterException():Exception(0) {}
    InvalidParameterException(const char* message):Exception(message) {}
    InvalidParameterException(const char* file, int line):Exception(file, line) {}
    InvalidParameterException(const char* message, const char* file, int line):Exception(message, file, line) {}
    InvalidParameterException(const InvalidParameterException& e): Exception(e) {}
    InvalidParameterException& operator = (const InvalidParameterException& e)
    {
        Exception::operator =(e);
        return *this;
    }
};

}

#endif // EXCEPTION_H
EXception.h 技術分享
#include "Exception.h"
#include <cstring>
#include <cstdlib>

using namespace std;

namespace DTLib
{

void Exception::init(const char* message, const char* file, int line)
{
    m_message = strdup(message);//指向的message可能在棧空間,也可能在堆空間或者全局數據去,
                                //為了安全,這裏先復制一份到堆空間

    if( file != NULL)
    {
        char sl[16] = {0};
        itoa(line, sl, 10);//首先將行號轉化為字符串

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));
        m_location = strcat(m_location, file);
        m_location = strcat(m_location, ":");
        m_location = strcat(m_location, sl);
    }
    else
    {
        m_location = NULL;
    }
}

Exception::Exception(const char* message)
{
    init(message, NULL, 0);
}

Exception::Exception(const char* file, int line)
{
    init(NULL, file, line);
}

Exception::Exception(const char* message, const char* file, int line)
{
    init(message, file, line);
}

Exception::Exception(const Exception& e)
{
    m_message = strdup(e.m_message);
    m_location = strdup(e.m_location);
}

Exception& Exception::operator = (const Exception& e)
{
    if( this != &e )
    {
        free(m_message);
        free(m_location);
        m_message = strdup(e.m_message);
        m_location = strdup(e.m_location);
    }

    return *this;
}

const char* Exception::message() const
{
    return m_message;
}

const char* Exception::location() const
{
    return m_location;
}

Exception::~Exception()
{
        free(m_message);
        free(m_location);
}

}
EXception.cpp 技術分享
#include <iostream>
#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    try
    {
        THROW_EXCEPTION(IndexOutOfBoundsException, "test");
    }
    catch(const IndexOutOfBoundsException& e)
    {
        cout << "ArithmeticException" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    catch(const Exception& e)
    {
        cout << "Exception" << endl;
        cout << e.message() << endl;
        cout << e.location() << endl;
    }
    return 0;
}
main.cpp

四、小結

(1)、現代c++庫必然包含充要的異常類族

(2)、所有庫中的數據結構都依賴於異常機制

(3)、異常機制能夠分離庫中代碼的正常邏輯和異常邏輯

第十一課、異常類的構建-------------狄泰軟件學院