前几节我们开发的智能指针类和异常类族并没有继承自Object,现在我们需要将它们进行整合,作为DTLib这个类库的基础设施。整合的时候需要遵循现代软件的架构模式。
遵循经典设计准则
DTLib中所有类位于单一继承树,可以根据下图中的方式进行整合:
在异常类族中有了新的成员InvalidOperationException异常类。为什么要增加这个异常类呢?因为我们创建的数据结构类是有状态的,在不同状态下,成员函数调用的行为是不同的。特别的在某些类对象的初识状态下,某些成员函数是不能调用的,如果硬是要调用这些函数,就会抛出这个非法操作的异常。
改进的关键点:
Exception类继承自Object类
堆空间中创建异常对象失败时,返回NULL指针
新增InvalidOperationException异常类
成员函数调用时,如果状态不正确则抛出异常
SmartPointer类继承自Object类
堆空间中创建智能指针对象失败时,返回NULL指针
DTLib中类的使用原则:
如果在堆空间中创建DTLib库中的对象,创建完之后要判断一下是否返回了一个合法的指针。因为创建失败时是不会返回异常的。
改造后的程序库如下:
Exception.h
1 #ifndef EXCEPTION_H 2 #define EXCEPTION_H 3 4 #include "Object.h" 5 6 namespace DTLib 7 { 8 9 #define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__)) 10 11 class Exception : public Object 12 { 13 protected: 14 char *m_message; 15 char *m_location; 16 17 void init(const char* message, const char* file, int line); 18 public: 19 Exception(const char* message); 20 Exception(const char* file, int line); 21 Exception(const char* message, const char* file, int line); 22 23 Exception(const Exception& e); 24 Exception& operator= (const Exception& e); 25 26 virtual const char* message() const; 27 virtual const char* location() const; 28 29 virtual ~Exception() = 0; 30 }; 31 32 class ArithmeticException : public Exception 33 { 34 public: 35 ArithmeticException() : Exception(0, 0, 0) {} 36 ArithmeticException(const char* message) : Exception(message) {} 37 ArithmeticException(const char* file, int line) : Exception(file, line) {} 38 ArithmeticException(const char* message, const char* file, int line) : Exception(message, file, line){} 39 40 ArithmeticException(const ArithmeticException& e) : Exception(e) {} 41 42 ArithmeticException& operator=(const ArithmeticException& e) 43 { 44 Exception::operator =(e); 45 46 return *this; 47 } 48 }; 49 50 class NullPointerException : public Exception 51 { 52 public: 53 NullPointerException() : Exception(0, 0, 0) {} 54 NullPointerException(const char* message) : Exception(message) {} 55 NullPointerException(const char* file, int line) : Exception(file, line) {} 56 NullPointerException(const char* message, const char* file, int line) : Exception(message, file, line){} 57 58 NullPointerException(const NullPointerException& e) : Exception(e) {} 59 60 NullPointerException& operator=(const NullPointerException& e) 61 { 62 Exception::operator =(e); 63 64 return *this; 65 } 66 }; 67 68 class IndexOutOfBoundsException : public Exception 69 { 70 public: 71 IndexOutOfBoundsException() : Exception(0, 0, 0) {} 72 IndexOutOfBoundsException(const char* message) : Exception(message) {} 73 IndexOutOfBoundsException(const char* file, int line) : Exception(file, line) {} 74 IndexOutOfBoundsException(const char* message, const char* file, int line) : Exception(message, file, line){} 75 76 IndexOutOfBoundsException(const IndexOutOfBoundsException& e) : Exception(e) {} 77 78 IndexOutOfBoundsException& operator=(const IndexOutOfBoundsException& e) 79 { 80 Exception::operator =(e); 81 82 return *this; 83 } 84 }; 85 86 class NoEnoughMemoryException : public Exception 87 { 88 public: 89 NoEnoughMemoryException() : Exception(0, 0, 0) {} 90 NoEnoughMemoryException(const char* message) : Exception(message) {} 91 NoEnoughMemoryException(const char* file, int line) : Exception(file, line) {} 92 NoEnoughMemoryException(const char* message, const char* file, int line) : Exception(message, file, line){} 93 94 NoEnoughMemoryException(const NoEnoughMemoryException& e) : Exception(e) {} 95 96 NoEnoughMemoryException& operator=(const NoEnoughMemoryException& e) 97 { 98 Exception::operator =(e); 99 100 return *this; 101 } 102 }; 103 104 class InvalidParameterException : public Exception 105 { 106 public: 107 InvalidParameterException() : Exception(0, 0, 0) {} 108 InvalidParameterException(const char* message) : Exception(message) {} 109 InvalidParameterException(const char* file, int line) : Exception(file, line) {} 110 InvalidParameterException(const char* message, const char* file, int line) : Exception(message, file, line){} 111 112 InvalidParameterException(const InvalidParameterException& e) : Exception(e) {} 113 114 InvalidParameterException& operator=(const InvalidParameterException& e) 115 { 116 Exception::operator =(e); 117 118 return *this; 119 } 120 }; 121 122 class InvalidOperationException : public Exception 123 { 124 public: 125 InvalidOperationException() : Exception(0, 0, 0) {} 126 InvalidOperationException(const char* message) : Exception(message) {} 127 InvalidOperationException(const char* file, int line) : Exception(file, line) {} 128 InvalidOperationException(const char* message, const char* file, int line) : Exception(message, file, line){} 129 130 InvalidOperationException(const InvalidOperationException& e) : Exception(e) {} 131 132 InvalidOperationException& operator=(const InvalidOperationException& e) 133 { 134 Exception::operator =(e); 135 136 return *this; 137 } 138 }; 139 140 } 141 142 #endif // EXCEPTION_H
Exception.cpp也进行改造,如下:
1 #include "Exception.h" 2 #include <cstring> 3 #include <cstdlib> 4 5 using namespace std; 6 7 namespace DTLib 8 { 9 10 void Exception::init(const char *message, const char *file, int line) 11 { 12 m_message = strdup(message); 13 14 if(file != NULL) 15 { 16 char sl[16] = {0}; 17 18 itoa(line, sl, 10); 19 20 m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2)); 21 22 if(m_location != NULL) 23 { 24 m_location = strcpy(m_location, file); 25 m_location = strcat(m_location, ":"); 26 m_location = strcat(m_location, sl); 27 } 28 } 29 else 30 { 31 m_location = NULL; 32 } 33 } 34 35 Exception::Exception(const char *message) 36 { 37 init(message, NULL, 0); 38 } 39 40 Exception::Exception(const char *file, int line) 41 { 42 init(NULL, file, line); 43 } 44 45 Exception::Exception(const char *message, const char *file, int line) 46 { 47 init(message, file, line); 48 } 49 50 Exception::Exception(const Exception &e) 51 { 52 m_message = strdup(e.m_message); 53 m_location = strdup(e.m_location); 54 } 55 56 Exception& Exception::operator =(const Exception& e) 57 { 58 if(this != &e) 59 { 60 free(m_message); 61 free(m_location); 62 63 m_message = strdup(e.m_message); 64 m_message = strdup(e.m_location); 65 } 66 67 return *this; 68 } 69 70 const char* Exception::message() const 71 { 72 return m_message; 73 } 74 75 const char* Exception::location() const 76 { 77 return m_location; 78 } 79 80 Exception::~Exception() 81 { 82 free(m_message); 83 free(m_location); 84 } 85 86 }
第22行的init函数中我们加入了空指针的判断,当malloc申请不成功时,也即m_location为空时,我们什么也不做,就让m_location为空。如果m_location为空,我们抛出异常,会造成死循环,因为在init函数中,我们的Exception对象还没有构造好呢。
SmartPointer.h
1 #ifndef SMARTPOINTER_H 2 #define SMARTPOINTER_H 3 4 #include "Object.h" 5 6 namespace DTLib 7 { 8 9 template <typename T> 10 class SmartPointer : public Object 11 { 12 protected: 13 T *m_pointer; 14 15 public: 16 SmartPointer(T *p = NULL) 17 { 18 m_pointer = p; 19 } 20 21 SmartPointer(const SmartPointer<T>& obj) 22 { 23 m_pointer = obj.m_pointer; 24 25 const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; 26 } 27 28 SmartPointer<T>& operator= (const SmartPointer<T>& obj) 29 { 30 if(this != &obj) 31 { 32 delete m_pointer; 33 34 m_pointer = obj.m_pointer; 35 36 const_cast<SmartPointer<T>&>(obj).m_pointer = NULL; 37 } 38 39 return *this; 40 } 41 42 T* operator-> () 43 { 44 return m_pointer; 45 } 46 47 T& operator* () 48 { 49 return *m_pointer; 50 } 51 52 bool isNull() 53 { 54 return (m_pointer == NULL); 55 } 56 57 T* get() 58 { 59 return m_pointer; 60 } 61 62 ~SmartPointer() 63 { 64 delete m_pointer; 65 } 66 }; 67 68 } 69 70 #endif // SMARTPOINTER_H
main函数测试程序如下:
1 #include <iostream> 2 #include "Exception.h" 3 #include "Object.h" 4 #include "SmartPointer.h" 5 using namespace std; 6 using namespace DTLib; 7 8 9 10 int main() 11 { 12 SmartPointer<int> *sp = new SmartPointer<int>(); 13 14 delete sp; 15 16 return 0; 17 }
我们在Exception.cpp中new和delete重载的地方打上断点,执行程序结果如下:
可以看到,程序最终执行到了我们自己定义的new和delete处。
DTLib的开发方式和注意事项:
迭代开发:
每次完成一个小的目标,持续开发,最终打造一个可复用类库
单一继承树:
所有类都继承自Object,规范堆对象创建时的行为
只抛异常,不处理异常:
使用THROW_EXCEPTION抛出异常,提高可移植性,有些老的编译器不支持异常,不支持try...catch...,或者有的公司规定不能使用异常机制,这时,如果还想使用
这个库,我们只需将这个宏定义为空
弱耦合性:
尽量不使用标准库中的类和函数,提高可移植性
第一阶段的总结:
数据结构与算法之间的关系
算法效率的度量方法
DTLib的基础设施构建
顶层父类
智能指针
异常类