1、一些問題
順序存儲結構的線性表存在着兩個方面的問題:
- 功能方面:數組操作符的重載,線性表有可能被誤用為數組使用
- 效率方面:在一些場合中,效率上是有隱患的
解決方案:當前的庫中沒有可以代替原生數組的實現,所以有可能會被誤用,需要創建一個數組類代替原生數組。
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動態申請堆空間,使得數組長度動態可變數組對象能夠代替原生數組,並且使用上更安全
代碼優化是項目開發過程中不可或缺的環節
