您好,登錄后才能下訂單哦!
在當代的軟件架構實踐中,我們有三條準則:1、盡量使用單重繼承的方式進行系統設計;2、盡量保持系統中只存在單一的繼承樹;3、盡量使用組合關系代替繼承關系。但是由于 C++ 語言的靈活性使得代碼中可以存在多個繼承樹,C++ 編譯器的差異使得同樣的代碼可能表現不同的行為。
我們想下,new 操作如果失敗將會發生什么呢?那么肯定會導致異常嘛,這時我們便用到了前面構建的異常類,此時我們只需拋出一個內存不足的異常,便會得到一個提示。我們這時便有必要來創建一個頂層的父類了,那么創建它的意義在哪呢?一是遵循經典設計準則,所有的數據結構都繼承自 Object 類,二是定義動態內存申請的行為,提高代碼的移植性。下面我們來看看頂層父類的接口定義,如下所示
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 父類的指針來創建了一個 Test 子類對象和 Child 子類對象。并且在創建對象的時候打印了 Object::operator new ,這很明顯就是調用了我們自己指定的 malloc 方式。為什么 Test 對象打印的是 12 呢?因為它里面包含了兩個 public 成員變量(int),再加上一個指向虛函數表的指針,一共是 12 個字節。底下的 Child 子類的分析是一樣的。在析構的時候我們看到析構時也打印出了我們寫的 Object::operator delete ,由此可以看出它的析構也是調用的是我們自己定義的。
下來我們來看看經典設計準則是怎樣的,如下,我們自己的 DTLib 中華的所有類是位于單一的繼承樹的
我們再基于上面創建的頂層父類來改善下我們之前寫的異常類和智能指針(在 C++ 中有介紹過)。
1、Exception 類繼承自 Object 類。即堆空間中創建異常對象失敗時返回 NULL 指針
2、新增 InvalidOperationException 異常類。在成員函數調用時,如果狀態不正常則拋出異常類
3、SmartPointer 類繼承自 Object 類。在堆空間中創建智能指針對象失敗時返回 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 函數中的 SmartPointer 和 INvalidOPerationException 的 new 和 delete 操作中打上斷點,看看程序的執行流,如下
第一幅圖是執行 SmartPointer 指針的 new 和 delete 操作時輸出的信息,第二幅圖是執行 INvalidOPerationException 指針的 new 和 delete 操作時輸出的信息。我們可以看到調用的 new 和 delete 操作都是 Object 中的函數。也就是說,我們現在的所有操作都是基于 Object 頂層父類的,由它統一 new 和 delete 的行為操作。我哦們在進行 DTLib 庫的開發時需要注意:1、迭代 開發:也就是每次完成一個小目標,持續開發,最終打造可復用類庫;2、單一繼承樹:所有樹都繼承自 Object,規范堆對象創建時的行為;3、只拋異常,不處理異常:使用 THROW_EXCEPTION 拋出異常,提高可移植性;4、弱耦合性:盡量不適應標準庫中的類和函數,提高可移植性。通過今天的學習,總結如下:1、Object 類是 DTLib 庫中數據結構類的頂層父類;2、Object 類用于統一動態內存申請的行為;3、在堆中創建 Object 子類的對象,失敗時返回 NULL 值;4、Object 類為純虛父類,所有子類都能進行動態類型識別。至此我們的庫的基礎設施構建基本已經完成:頂層父類、智能指針、異常類。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。