顶层父类的构建(五)

       在当代的软件架构实践中,我们有三条准则: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 函数中的 SmartPointerINvalidOPerationException 的  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 类为纯虚父类,所有子类都能进行动态类型识别。至此我们的库的基础设施构建基本已经完成:顶层父类、智能指针、异常类