数组类(九)

        我们上节博客说到了自己完成一个数组类,以此来区分数组和线性表。那么我们来看看数组类的具体实现,它也同样有两个子类 StaticArrayDynamicArray。如下

图片.png

        下来我们先来看看 StaticArray 的实现,那么我们创建的数组类是用来代替原生数组的使用。这个数组类应包含长度信息,数组类也能主动发现越界访问。那么 Array 的设计要点有哪些呢?1、抽象类模板,存储空间的位置和大小由子类完成;2、重载数组操作度,判断访问下标是否合法;3、提供数组长度的抽象访问函数;4、提供数组对象间的赋值操作。

        Array 类的声明如下

template < typename T >
class Array : public Object
{
protected:
    T* m_array;
public:
    virtual bool set(int i, const T& e);
    virtual bool get(int i, T& e) const;
    virtual int length() const = 0;
    
    // 数组访问操作符
    T& operator[] (int i);
    T operator[] (int i) const;
}

        下来我们来看看 Array 类的具体实现


Array.h 源码

#ifndef ARRAY_H
#define ARRAY_H

#include "Object.h"
#include "Exception.h"

namespace DTLib
{

template < typename T >
class Array : public Object
{
protected:
    T* m_array;
public:
    virtual bool set(int i, const T& e)
    {
        int ret = ((0 <= i) && (i < length()));

        if( ret )
        {
            m_array[i] = e;
        }

        return ret;
    }

    virtual bool get(int i, T& e) const
    {
        int ret = ((0 <= i) && (i < length()));

        if( ret )
        {
            e = m_array[i];
        }

        return ret;
    }

    T& operator[] (int i)
    {
        int ret = ((0 <= i) && (i < length()));

        if( ret )
        {
            return m_array[i];
        }
        else
        {
            THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
        }
    }

    T operator[] (int i) const
    {
        return (const_cast<Array<T>&>(*this))[i];
    }

    virtual int length() const = 0;
};

}

#endif // ARRAY_H

        A、下来我们来实现 StaticArray 类,先来看看 StaticArray 类的设计要点(以类模板的形式):1、封装原生数组;2、使用模板参数决定数组大小;3、实现函数返回数组长度;4、拷贝构造和赋值操作。


StaticArray.h 源码

#ifndef STATICARRAY_H
#define STATICARRAY_H

#include "Array.h"
#include "Exception.h"

namespace DTLib
{

template < typename T, int N >
class StaticArray : public Array<T>
{
protected:
    T m_space[N];
public:
    StaticArray()
    {
        this->m_array = m_space;
    }

    StaticArray(const StaticArray<T, N>& obj)
    {
        this->m_array = obj.m_array;

        for(int i=0; i<N; i++)
        {
            m_space[i] = obj.m_space[i];
        }
    }

    StaticArray<T, N>& operator= (const StaticArray<T, N>&  obj)
    {
        if( this != &obj )
        {
            for(int i=0; i<N; i++)
            {
                m_space[i] = obj.m_space[i];
            }
        }

        return *this;
    }

    int length() const
    {
        return N;
    }
};

}

#endif // STATICARRAY_H

        下来我们来测试下这个 StaticArray 类,看看是否完整实现,main.cpp

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

using namespace std;
using namespace DTLib;

int main()
{
    StaticArray<int, 5> s1;

    for(int i=0; i<s1.length(); i++)
    {
        s1[i] = i * i;
    }

    for(int i=0; i<s1.length(); i++)
    {
        cout << s1[i] << endl;
    }

    StaticArray<int, 5> s2;

    s2 = s1;

    for(int i=0; i<s2.length(); i++)
    {
        cout << s2[i] << endl;
    }

    int s3[5];

    s3[6] = 100;

    return 0;
}

        我们定义了数组 s1,它一共有5个元素。我们将其赋值 i*i,再定义数组 s2,将 s1 直接赋值 s2。那么 s2 的结果应该和 s1 是一样的。再次定义了 s3[5] ,访问 s3[6],明显看出越界了。我们看看结果

图片.png

        我们看到编译运行正常结束了,和我们分析的是一样的。s3 的越界问题也没有暴露出来,那么这个 bug 究竟会何时出问题呢?谁也不知道。我们再次在程序中访问 s2[6] ,来看看编译是否会通过

图片.png

        我们看到运行直接出错了,越界异常。

        B、我们下来看看 DynamicArray 类的实现。那么我们先来看看 DynamicArray 类的设计要点(也是以类模板的形式):1、动态确定内部数组空间的大小;2、实现函数返回数组长度;3、拷贝构造和赋值操作。


DynamicArray.h 源码

#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H

#include "Array.h"
#include "Exception.h"

namespace DTLib
{

template < typename T >
class DynamicArray : public Array<T>
{
protected:
    int m_length;

    T* copy(T* array, int len, int newlen)
    {
        T* ret = new T[newlen];

        if( ret != NULL )
        {
            int size = (len < newlen) ? len : newlen;

            for(int i=0; i<size; i++)
            {
                ret[i] = array[i];
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to copy object ...");
        }

        return ret;
    }

    void update(T* array, int length)
    {
        if( array != NULL )
        {
            T* temp = this->m_array;

            this->m_array = array;
            this->m_length = length;

            delete[] temp;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to update object ...");
        }
    }

    void init(T* array, int length)
    {
        if( array != NULL )
        {
            this->m_array = array;
            this->m_length = length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }
public:
    DynamicArray(int length)
    {
        this->m_array = new T[length];
        
        if( this->array != NULL )
        {
            this->m_length = length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }

    DynamicArray(const DynamicArray<T>& obj)
    {
        this->m_array = new T[obj.m_length];
        
        if( this->array != NULL )
        {
            this->m_length = length;
            
            for(int i=0; i<obj.m_length; i++)
            {
                this->m_array[i] = obj.m_array[i];
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }

    DynamicArray<T>& operator= (const DynamicArray<T>& obj)
    {
        if( this != &obj )
        {
            T* array = new T[obj.m_length];
            
            if( array != NULL )
            {
                for(int i=0; i<obj.m_length; i++)
                {
                    array[i] = obj.m_array[i];
                }
                
                T* temp = this->m_array;

                this->m_array = array;
                this->m_length = obj.m_length;
    
                delete[] temp;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to copy DynamicArray object ...");
            }
        }

        return *this;
    }

    int length() const
    {
        return m_length;
    }

    void resize(int length)
    {
        if( length != m_length )
        {
            T* array = new T[length];
            
            if( array != NULL )
            {
                int size = (length < m_length) ? length : m_length;
                
                for(int i=0; i<obj.m_length; i++)
                {
                    array[i] = this->m_array[i];
                }
                
                T* temp = this->m_array;

                this->m_array = array;
                this->m_length = length;
    
                delete[] temp;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicArray object ...");
            }
        }
    }

    ~DynamicArray()
    {
        delete[] this->m_array;
    }
};

}

#endif // DYNAMICARRAY_H

        我们还是写个 main.cpp 测试下我们实现的 DynamicArray 类

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

using namespace std;
using namespace DTLib;

int main()
{
    DynamicArray<int> s1(5);

    for(int i=0; i<s1.length(); i++)
    {
        s1[i] = i * i;
    }

    for(int i=0; i<s1.length(); i++)
    {
        cout << s1[i] << endl;
    }

    s1.resize(8);

    s1.set(6, 60);

    for(int i=0; i<s1.length(); i++)
    {
        cout << s1[i] << endl;
    }

    return 0;
}

        我们猜想的结果应该是先打印出 0, 1, 4,9, 16。接着再次打印它们,s1[6] = 60,s1[5] 和 s1[7] 应该是随机数。我们来看看结果

图片.png

        我们看到已经正确实现了效果。我们看到在 DynamicArray 类中的函数实现存在大量的重复逻辑,那么我们该如何进行优化呢?我们可以将重复的代码逻辑进行抽象。可以抽象出三个函数:a> init,用于对象构造时的初始化操作;b> copy,用以在堆空间中申请新的内存,并执行拷贝操作;c> update,用于将指定的堆空间作为内部存储数组使用。我们来看看优化后的 DynamicArray 类。


DynamicArray.h 源码

#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H

#include "Array.h"
#include "Exception.h"

namespace DTLib
{

template < typename T >
class DynamicArray : public Array<T>
{
protected:
    int m_length;

    T* copy(T* array, int len, int newlen)
    {
        T* ret = new T[newlen];

        if( ret != NULL )
        {
            int size = (len < newlen) ? len : newlen;

            for(int i=0; i<size; i++)
            {
                ret[i] = array[i];
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to copy object ...");
        }

        return ret;
    }

    void update(T* array, int length)
    {
        if( array != NULL )
        {
            T* temp = this->m_array;

            this->m_array = array;
            this->m_length = length;

            delete[] temp;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to update object ...");
        }
    }

    void init(T* array, int length)
    {
        if( array != NULL )
        {
            this->m_array = array;
            this->m_length = length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }
public:
    DynamicArray(int length)
    {
        this->m_array = new T[length];

        if( this->array != NULL )
        {
            this->m_length = length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }

    DynamicArray(const DynamicArray<T>& obj)
    {
        this->m_array = new T[obj.m_length];

        if( this->array != NULL )
        {
            this->m_length = length;

            for(int i=0; i<obj.m_length; i++)
            {
                this->m_array[i] = obj.m_array[i];
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
        }
    }

    DynamicArray<T>& operator= (const DynamicArray<T>& obj)
    {
        if( this != &obj )
        {
            T* array = new T[obj.m_length];

            if( array != NULL )
            {
                for(int i=0; i<obj.m_length; i++)
                {
                    array[i] = obj.m_array[i];
                }

                T* temp = this->m_array;

                this->m_array = array;
                this->m_length = obj.m_length;

                delete[] temp;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to copy DynamicArray object ...");
            }
        }

        return *this;
    }

    int length() const
    {
        return m_length;
    }

    void resize(int length)
    {
        if( length != m_length )
        {
            T* array = new T[length];

            if( array != NULL )
            {
                int size = (length < m_length) ? length : m_length;

                for(int i=0; i<obj.m_length; i++)
                {
                    array[i] = this->m_array[i];
                }

                T* temp = this->m_array;

                this->m_array = array;
                this->m_length = length;

                delete[] temp;
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicArray object ...");
            }
        }
    }

    ~DynamicArray()
    {
        delete[] this->m_array;
    }
};

}

#endif // DYNAMICARRAY_H

        我们再来编译看看结果

图片.png

        结果还是对的,那么此时我们的 DynamicArray 类已经很一目了然了。通过今天对 StaticArrayDynamicArray 的学习,总结如下:1、StaticArray 通过封装原生数组的方式实现数组类;2、DynamicArray 动态申请堆空间,使得数组长度动态可变;3、数组对象能够代替原生数组,并且使用上更加安全;4、代码优化是项目开发过程中不可或缺的环节,这样我们的代码质量会更高。

猜你喜欢

转载自blog.51cto.com/12810168/2160938