六、數組類的創建


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動態申請堆空間,使得數組長度動態可變

數組對象能夠代替原生數組,並且使用上更安全

代碼優化是項目開發過程中不可或缺的環節


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM