1. 当代软件架构实践中的经验
(1)尽量使用单重继承的方式进行系统设计
(2)尽量保持系统中只存在单一的继承树
(3)尽量使用组合关系代替继承关系
2. 不幸的事实
(1)C++语言的灵活性使得代码中可以存在多个继承树
(2)C++编译器的差异使得同样的代码可能表现不同的行为(如new操作结果失败,有的编译器会返回NULL,有的会抛std::bad_alloc异常)
3. 创建DTLib::Object类的意义
(1)遵循经典设计准则,所有数据结构都继承自Object类
(2)定义动态内存申请的行为,提高代码的移植性(即重载operator new,让new的失败时在不同的编译器下都能返回NULL,而不是抛出异常或返回NULL等多种结果)
【编程实验】顶层父类的创建
//Object.h
#ifndef _OBJECT_H_ #define _OBJECT_H_ namespace DTLib { class Object { public: //以下四个重载函数用于统一不同编译器new失败时的结果不同的问题。 //throw()表示不抛出异常,即如果申请内请失败时,统一返回NULL而不抛异常 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() { //当用new Test[5]时,只须传入数组元素的个数,编译器会向operator new[](...)函数的参数 //传入5*sizeof(Test) + sizeof(unsigned int),其中的sizeof(unsigned int)为额外 //空间,用于保存元素的个数。 cout <<"Object::operator new[]: " << size << endl; return malloc(size); } void Object::operator delete[](void *p) { cout <<"Object::operator delete[]: " << p << endl; 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; } /*输出结果 Object::operator new: 12 Object::operator new: 16 obj1 = 0x3d12a8 obj2 = 0x3d12c0 Object::operator delete: 0x3d12a8 Object::operator delete: 0x3d12c0 */
4. 小结
(1)Object类是DTLib中数据结构类的顶层父类
(2)Object类用于统一动态内存申请的行为
(3)在堆中创建Object子类的对象,失败时返回NULL值。
(4)Object类为纯虚父类,所有子类都能进行动态类型识别。