六、数组类的创建

1、一些问题

顺序存储结构的线性表存在着两个方面的问题:

  1. 功能方面:数组操作符的重载,线性表有可能被误用为数组使用
  2. 效率方面:在一些场合中,效率上是有隐患的

解决方案:当前的库中没有可以代替原生数组的实现,所以有可能会被误用,需要创建一个数组类代替原生数组。

2、数组类抽象类模板的创建

需求分析:创建数组类代替原生数组的使用

  • 如何通过类的对象来模拟数组的行为?
  • 原生数组使用过程中存在的问题:
    • 数组类长度信息丢失:定义一个数组,长度信息必须指定,但是指定之后,长度信息不能在数组本身中找到,需要用另一个变量来保存
    • 数组越界问题:数组是一片连续的内存空间,但是原生数组发生越界时,不能立即发现,这是工程中bug来源之一,需求:数组类能主动发现越界访问

Array设计要点:

  • 抽象类模板,存储空间的位置和大小由子类完成
  • 重载数组操作符,判断访问下标是否合法
  • 提供数组长度的抽象访问函数
  • 提供数组对象间的复制操作(C++原生数组之间是不能直接通过赋值操作符进行相互复制)

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;
}
template <typename T>
class Array : public Object
{
protected:
    T* m_array;
public:
    virtual bool set(int i, const T& e)
    {
        bool ret = ((i >= 0) && (i < length()));
        if (ret)
        {
            m_array[i] = e;
        }
        return ret;
    }

    virtual bool get(int i, T& e)
    {
        bool ret = ((i >= 0) && (i < length()));
        if (ret)
        {
            e = m_array[i];
        }
        return ret;
    }
    virtual int length() const = 0;

    // 数组访问操作符
    T& operator [] (int i)
    {
        if ((i >=0) && (i < length()))
        {
            return m_array[i];
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "Parameter i is invalid...");
        }
    }

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

3、StaticArray类模板创建

StaticArray设计要点:类模板

  • 封装原生组:父类只定义了操作,没有具体定义保存数据的地方,StaticArray中将数据保存在原生数组中,原生数组的表现形式就是StaticArray中的一个成员
  • 使用模板参数决定原生数组大小
  • 实现函数返回数组长度
  • 拷贝构造和赋值操作
template < typename T, int N >
class StaticArray : public Array<T>
{
protected:
    T m_space[N];
public:
    StaticArray();
    // 拷贝构造和赋值操作
    StaticArray(const StaticArray<T, N>& obj);
    StaticArray<T, N>& operator = (const StaticArray<T, N>& obj);
    int length() const;
};
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 = m_space;
        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;
    }
};

4、DynamicArray类模板创建

StaticArray的对象,数组的大小是明确指定的,创建动态数组类模板,数组大小可以动态指定

DynamicArray设计要点:类模板

  • 动态确定内部数组空间的大小
  • 实现函数返回数组长度
  • 拷贝构造和赋值操作

DynamicArray类的声明:

template < typename T >
class DynamicArray : public Array<T>
{
public:
    int m_length;
public:
    DynamicArray(int m_length);
    DynamicArray(const DynamicArray<T>& obj);
    DynamicArray<T>& operator = (const DynamicArray<T>& obj);
    
    int length() const;
    void resize(int length);    // 动态重置数组的长度
    
    ~DynamicArray();
};
template < typename T >
class DynamicArray : public Array<T>
{
public:
    int m_length;

public:
    DynamicArray(int length)
    {
        // 在堆空间中申请内存
        this->m_array = new T[length];
        if (this->m_array != NULL)
        {
            this->m_length =length;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to creat DynamicArray object...");
        }
    }

    DynamicArray(const DynamicArray<T>& obj)
    {
        // 数组长度以参数对象的长度为准
        this->m_array = new T[obj.m_length];
        if (this->m_array != NULL)
        {
            cout << "DynamicArray(const DynamicArray<T>& obj)" << endl;
            // 长度设置
            this->m_length = obj.m_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 creat 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 < size; 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;
    }
};

5、代码优化

分析构造函数、拷贝构造函数、赋值操作符重载函数和resize函数,程序逻辑为:

构造函数:

  • 堆空间中申请一片内存
  • 对数组类对象的成员变量进行赋值操作

拷贝构造函数:

  • 堆空间中申请一片内存
  • 进行数据元素的复制操作
  • 对成员变量进行赋值

赋值操作符重载函数:

  • 堆空间中申请一片内存
  • 进行数据元素的复制操作
  • 将指定的堆空间作为内部存储数组使用,更新成员变量的值

resize函数:

  • 堆空间中申请一片内存
  • 进行数据元素的复制操作,根据长度信息进行复制
  • 将指定的堆空间作为内部存储数组使用,更新类成员变量的值

总结:赋值操作符重载和resize代码中有很多重复的逻辑, 构造函数和拷贝构造函数也有很多重复代码,如何进行代码优化

重复代码逻辑的抽象

  • init:对象构造时的初始化操作,对成员变量进行初始化赋值
  • copy:在堆空间中申请新的内存,并执行拷贝复制操作
  • update:将指定的堆空间作为内部存储数组使用,更新成员变量的值
void init(T* array, int length)
{
    if (array != NULL)
    {
        this->m_array = array;
        this->m_length = length;
    }
    else
    {
        THROW_EXCEPTION(NoEnoughMemoryException, "No memory to init DynamicArray object...");
    }
}
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 DynamicArray 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 DynamicArray object...");
     }
}

这三个函数均放在protected成员函数内

于是代码简化为:

public:
    DynamicArray(int length)
    {
        init(new T[length], length);
    }

    DynamicArray(const DynamicArray<T>& obj)
    {
        init(copy(obj.m_array, obj.m_length, obj.m_length), obj.m_length);
    }

    DynamicArray<T>& operator = (const DynamicArray<T>& obj)
    {
        if ( this != &obj)
        {
            update(copy(obj.m_array, obj.m_length, obj.m_length), obj.m_length);
        }

        return *this;
    }

    int length() const
    {
        return m_length;
    }

    void resize(int length)    // 动态重置数组的长度
    {
        update(copy(this->m_array, this->m_length, length), length);
    }

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

6、总结

StaticArray通过封装原生数组的方式实现数组类

DynamicArray动态申请堆空间,使得数组长度动态可变

数组对象能够代替原生数组,并且使用上更安全

代码优化是项目开发过程中不可或缺的环节

posted @ 2018-08-16 22:27  小胖鼠  阅读(3052)  评论(0编辑  收藏  举报