zoukankan      html  css  js  c++  java
  • 六、数组类的创建

    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动态申请堆空间,使得数组长度动态可变

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

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

  • 相关阅读:
    查看tomcat启动文件都干点啥---server对象
    Spring的AOP浅尝
    Spark1.3.0安装
    HMM的概率计算问题和预测问题的java实现
    C语言实现求字符串子集问题
    AdaBoost的java实现
    ID3决策树预测的java实现
    决策树ID3算法的java实现
    Naive Bayes在mapreduce上的实现
    Kmeans在MapReduce中的实现
  • 原文地址:https://www.cnblogs.com/chenke1731/p/9490549.html
Copyright © 2011-2022 走看看