溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

頂層父類的構建(五)

發(fā)布時間:2020-07-18 10:24:14 來源:網絡 閱讀:467 作者:上帝之子521 欄目:軟件技術

       在當代的軟件架構實踐中,我們有三條準則:1、盡量使用單重繼承的方式進行系統(tǒng)設計;2、盡量保持系統(tǒng)中只存在單一的繼承樹;3、盡量使用組合關系代替繼承關系。但是由于 C++ 語言的靈活性使得代碼中可以存在多個繼承樹,C++ 編譯器的差異使得同樣的代碼可能表現不同的行為。

        我們想下,new 操作如果失敗將會發(fā)生什么呢?那么肯定會導致異常嘛,這時我們便用到了前面構建的異常類,此時我們只需拋出一個內存不足的異常,便會得到一個提示。我們這時便有必要來創(chuàng)建一個頂層的父類了,那么創(chuàng)建它的意義在哪呢?一是遵循經典設計準則,所有的數據結構都繼承自 Object 類,二是定義動態(tài)內存申請的行為,提高代碼的移植性。下面我們來看看頂層父類的接口定義,如下所示

class Object
{
public:
    void* operator new (unsigned int size) throw();
    void operator delete (void* p);
    void* operator new[] (unigned int size) throw();
    void operator delete[] (void* p);
    virtual ~Object() = 0;
};

        下來我們還是以代碼為例來進行實驗


Object.h 源碼

#ifndef OBJECT_H
#define OBJECT_H

namespace DTLib
{

class Object
{
public:
    void* operator new (unsigned int size) throw();
    void operator delete (void* p);
    void* operator new[] (unsigned int size) throw();
    void operator delete[] (void* p);
    virtual ~Object() = 0;
};

}

#endif // OBJECT_H


Object.cpp 源碼

#include "Object.h"
#include <cstdlib>
#include <iostream>

using namespace std;

namespace DTLib
{

void* Object::operator new (unsigned int size) throw()
{
    cout << "Object::operator new : " << size << endl;
    return malloc(size);
}

void Object::operator delete (void* p)
{
    cout << "Object::operator delete : " << p << endl;
    free(p);
}

void* Object::operator new[] (unsigned int size) throw()
{
    return malloc(sizeof(size));
}

void Object::operator delete[] (void* p)
{
    free(p);
}

Object::~Object()
{

}

}


main.cpp 源碼

#include <iostream>
#include "Object.h"

using namespace std;
using namespace DTLib;

class Test : public Object
{
public:
    int i;
    int j;
};

class Child : public Test
{
public:
    int k;
};

int main()
{
    Object* obj1 = new Test();
    Object* obj2 = new Child();

    cout << "obj1 = " << obj1 << endl;
    cout << "obj2 = " << obj2 << endl;

    delete obj1;
    delete obj2;

    return 0;
}

        我們來看看編譯后的結果

頂層父類的構建(五)

        我們看到在 main 函數中我們用 Object 父類的指針來創(chuàng)建了一個 Test 子類對象和 Child 子類對象。并且在創(chuàng)建對象的時候打印了 Object::operator new ,這很明顯就是調用了我們自己指定的 malloc 方式。為什么 Test 對象打印的是 12 呢?因為它里面包含了兩個 public 成員變量(int),再加上一個指向虛函數表的指針,一共是 12 個字節(jié)。底下的 Child 子類的分析是一樣的。在析構的時候我們看到析構時也打印出了我們寫的 Object::operator delete ,由此可以看出它的析構也是調用的是我們自己定義的。

        下來我們來看看經典設計準則是怎樣的,如下,我們自己的 DTLib 中華的所有類是位于單一的繼承樹的

頂層父類的構建(五)

        我們再基于上面創(chuàng)建的頂層父類來改善下我們之前寫的異常類和智能指針(在 C++ 中有介紹過)。

        1、Exception 類繼承自 Object 類。即堆空間中創(chuàng)建異常對象失敗時返回 NULL 指針

        2、新增 InvalidOperationException 異常類。在成員函數調用時,如果狀態(tài)不正常則拋出異常類

        3、SmartPointer 類繼承自 Object 類。在堆空間中創(chuàng)建智能指針對象失敗時返回 NULL 指針

        下來我們還是以代碼為例來進行說明


Exception.h 源碼

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "Object.h"

namespace DTLib
{

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

class Exception : public Object
{
private:
    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();
};

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

class INvalidOPerationException : public Exception
{
public:
    INvalidOPerationException() : Exception(0) {}
    INvalidOPerationException(const char* message) : Exception(message) {}
    INvalidOPerationException(const char* file, int line) : Exception(file, line) {}
    INvalidOPerationException(const char* message, const char* file, int line) : Exception(message, file, line) {}

    INvalidOPerationException(const INvalidOPerationException& e) : Exception(e) {}
    INvalidOPerationException& operator= (const InvalidParameterException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

}

#endif // EXCEPTION_H



Exception.cpp 源碼

#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 = (message ? strdup(message) : NULL);

    if( file != NULL )
    {
        char s1[16] = {0};

        itoa(line, s1, 10);

        m_location = static_cast<char*>(malloc(strlen(file) + strlen(s1) + 2));
        m_location = strcpy(m_location, file);
        m_location = strcat(m_location, ":");
        m_location = strcat(m_location, s1);
    }
    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 = e.m_message;
    m_location = e.m_location;
}

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

        m_message = e.m_message;
        m_location = 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);
}

}


SmartPointer.h 源碼

#ifndef SMARTPOINTER_
H#define SMARTPOINTER_H

#include "Object.h"

namespace DTLib
{

template < typename T >
class SmartPointer : public Object
{
private:
    T* m_pointer;
public:
    SmartPointer(T* p = NULL)
    {
        m_pointer = p;
    }

    SmartPointer(const SmartPointer<T>& obj)
    {
        m_pointer = obj.m_pointer;

        const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
    }

    SmartPointer<T>& operator= (const SmartPointer<T>& obj)
    {
        if( this != &obj )
        {
            delete m_pointer;

            m_pointer = obj.m_pointer;

            const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
        }

        return *this;
    }

    T* operator-> ()
    {
        return m_pointer;
    }

    T& operator* ()
    {
        return *m_pointer;
    }

    bool isNull()
    {
        return (m_pointer == NULL);
    }

    T* get()
    {
        return m_pointer;
    }

    ~SmartPointer()
    {
        delete m_pointer;
    }
};

}

#endif // SMARTPOINTER_H


main.cpp 源碼

#include <iostream>
#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    SmartPointer<int>* p = new SmartPointer<int>();

    delete p;

    INvalidOPerationException* e = new INvalidOPerationException();

    delete e;

    return 0;
}

        我們在 Object 頂層父類中的 new 和 delete 函數中搭上斷點,同時也在 main 函數中的 SmartPointerINvalidOPerationException 的  new 和 delete 操作中打上斷點,看看程序的執(zhí)行流,如下

頂層父類的構建(五)

頂層父類的構建(五)

        第一幅圖是執(zhí)行 SmartPointer 指針的 new 和 delete 操作時輸出的信息,第二幅圖是執(zhí)行 INvalidOPerationException 指針的 new 和 delete 操作時輸出的信息。我們可以看到調用的 new 和 delete 操作都是 Object 中的函數。也就是說,我們現在的所有操作都是基于 Object 頂層父類的,由它統(tǒng)一 new 和 delete 的行為操作。我哦們在進行 DTLib 庫的開發(fā)時需要注意:1、迭代 開發(fā):也就是每次完成一個小目標,持續(xù)開發(fā),最終打造可復用類庫;2、單一繼承樹:所有樹都繼承自 Object,規(guī)范堆對象創(chuàng)建時的行為;3、只拋異常,不處理異常:使用 THROW_EXCEPTION 拋出異常,提高可移植性;4、弱耦合性:盡量不適應標準庫中的類和函數,提高可移植性。通過今天的學習,總結如下:1、Object 類是 DTLib 庫中數據結構類的頂層父類;2、Object 類用于統(tǒng)一動態(tài)內存申請的行為;3、在堆中創(chuàng)建 Object 子類的對象,失敗時返回 NULL 值;4、頂層父類的構建(五)Object 類為純虛父類,所有子類都能進行動態(tài)類型識別。至此我們的庫的基礎設施構建基本已經完成:頂層父類、智能指針、異常類


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI