筆記四:線性表——陣列描述
線性表
定義:有序表,元素按照一定順序形成的有序集合。
陣列描述的線性表:
1、程式碼:
#pragma warning(disable:4996)
#include<iostream>
#include<iterator>
#include<algorithm>
using namespace std;
typedef struct{
int a;
int b;
} Mytype;
template<typename T>
class linearList
{
public:
virtual ~linearList() {}; //解構函式
virtual bool empty() const = 0; //返回true,當且僅當線性表為空
virtual int size() const = 0; //返回線性表的元素個數
virtual T& get(int theIndex) const = 0; //返回索引為theIndex的元素
virtual int indexOf(const T& theElement) const = 0; //返回元素theElement第一次出現時的索引
virtual void erase(int theIndex) = 0 ; //刪除索引為theIndex的元素
virtual void insert(int theIndex, const T& theElement) = 0; //把theElement插入線性表中索引為theIndex的位置
virtual void output(ostream& out) const = 0; //把線性表插入的輸出流out
};
template<typename T>
class arrayList : public linearList<T>
{
public :
//建構函式、複製建構函式、解構函式
arrayList(int initialCapacity = 10);
arrayList(const arrayList<T>&);
~arrayList() { delete[] element; }
//ADT方法
bool empty() const;
int size() const;
T& get(int theIndex) const;
int indexOf(const T& theElement) const;
void erase(int theIndex);
void insert(int theIndex, const T& theElement);
void output(ostream& out) const;
public:
void changeLength1D(T*& a, int oldLength, int newLength);
friend ostream& operator<<(ostream& out, const arrayList<T>& x);
void checkIndex(int theIndex) const;
private:
int listSize; //線性表的元素個數
int arrayLength; //一維陣列的容量
T* element; //儲存線性表元素的一維陣列
};
template<typename T> arrayList<T>::arrayList(int initialCapacity)
{//建構函式
arrayLength = initialCapacity;
element = new T[arrayLength];
listSize = 0;
}
template<typename T> arrayList<T>::arrayList(const arrayList<T>& theList)
{//複製建構函式
arrayLength = theList.arrayLength;
listSize = theList.listSize;
element = new T[arrayLength];
copy(theList.element, theList.element + listSize, element);
}
template<typename T> bool arrayList<T>::empty() const
{
return (listSize == 0 ? true : false);
}
template<typename T> int arrayList<T>::size() const
{
return listSize;
}
template<typename T> T& arrayList<T>::get(int theIndex) const
{
checkIndex(theIndex);
return element[theIndex];
}
template<typename T> int arrayList<T>::indexOf(const T& theElement) const
{
//指向theElement的指標與陣列首指標的差值
int theIndex = (int)(find(element, element + listSize, theElement) - element);
return (theIndex == listSize ? -1 : theIndex);
}
template<typename T> void arrayList<T>::erase(int theIndex)
{
checkIndex(theIndex);
//將theIndex後面的元素依次前移一位,通過佔據theIndex元素的位置將其覆蓋
copy(element + theIndex + 1, element + listSize, element + theIndex);
element[--listSize].~T(); //刪除最後一個元素,避免重複
}
template<typename T> void arrayList<T>::insert(int theIndex, const T& theElement)
{
//theIndex不合法
if (theIndex <0 || theIndex > listSize)
{
cout << "元素插入的theIndex下標不合理! ";
exit(-1);
}
//有效索引,判斷陣列是否已滿
if (listSize == arrayLength)
{
//增加陣列容量
changeLength1D(element, arrayLength, arrayLength * 2);
arrayLength = arrayLength * 2; //修改陣列容量值
}
copy_backward(element + theIndex, element + listSize, element + listSize + 1);
element[theIndex] = theElement;
++listSize;
}
template<typename T> void arrayList<T>::changeLength1D(T*& a, int oldLength, int newLength)
{
//判斷新的容量是否合法
if (newLength < 0)
{
cout << "錯誤:newLength 小於 0!";
exit(-1);
}
T *tmp = new T[newLength];
int number = min(oldLength, newLength); //複製的元素的數量判斷
copy(a, a + number, tmp); //元素複製
a = tmp;
}
template<typename T> ostream& operator<<(ostream& out, const arrayList<T>& x)
{
x.output(out);
return out;
}
template<typename T> void arrayList<T>::output(ostream& out) const
{
//把線性表插入輸出流
copy(element, element + listSize, ostream_iterator<T>(cout, " "));
}
template<typename T> void arrayList<T>::checkIndex(int theIndex) const
{
if (theIndex < 0 || theIndex >= listSize)
{
cout << "錯誤:theIndex 不合法!";
exit(-1);
}
}
int main(int argc, char * argv[])
{
arrayList<int> aL(10);
//測試insert函式
aL.insert(0, 5);
aL.insert(1, 4);
aL.insert(2, 3);
aL.insert(3, 2);
aL.insert(4, 1);
//測試output函式
cout << "初始線性表為:";
aL.output(cout);
cout << endl;
cout << "初始線性表的大小為:" << aL.size()<<endl;
//測試erase函式
cout << "刪除theIndex = 2的元素後線性表為:";
aL.erase(2);
aL.output(cout);
cout << " 大小size: " << aL.size() << endl;
//測試insert函式
aL.insert(0, 0);
cout << "在theIndex = 0的位置插入元素0後,線性表為:";
aL.output(cout);
cout << " 大小size: " << aL.size() << endl;
//測試indexOf函式
cout << "元素值為5的下標是:" << aL.indexOf(5) << endl;
//測試get函式
cout << "下標為1的元素是:" << aL.get(1) << endl;
return 0;
}
2、執行:
擴充套件:
純虛擬函式:為後代型別提供了可以覆蓋的介面、但是該類中的版本絕不會呼叫,且不允許建立物件。在函式形參表後面寫上 = 0 用以指定純虛擬函式。
virtual fun() = 0 ;
抽象基類:含有(或繼承)一個或多個純虛擬函式的類。不能建立例項物件。
過載操作符:
1、過載操作符必須具有一個類型別或列舉型別的運算元。
2、一般將算數和關係操作符定義為非成員函式,而將賦值操作符定義為成員函式.
3、作為類成員的過載函式,其形參比運算元目少1,因為第一個運算元為隱含的this形參Sale_item& operator+=(const Sale_item&);
4、操作符定義為非成員函式時,通常設定為友元friend ostream& operator<<(ostream& out, const arrayList<T>& x);
建構函式與複製建構函式:
1、建構函式:保證每個物件的資料成員具有合適的初始值A::A(const int& x) : a(0), b(x) {};
2、複製建構函式:只有單個形參,且該形參是對本類型別的引用(常用const修飾)。解構函式與虛解構函式:
1、解構函式:完成所需的資源回收,作為類建構函式的補充。解構函式無法過載。
2、許多類不需要顯示解構函式,尤其是具有建構函式的類不一定要定義自己的建構函式。如果類需要解構函式,則它也需要賦值操作符和複製建構函式——三法則。
3、動態分配的記憶體只有在指向該物件的指標被刪除時才撤銷,如果沒有刪除指向動態物件的指標,則不會執行該物件的解構函式,物件就一直存在,從而導致記憶體洩露。
4、賦值操作符:1)過載的賦值操作符Sale_item& operator=(const Sale_item&);
,2)合成賦值操作符——逐個成員賦值。
5、賦值操作符與複製建構函式的呼叫區別:如果物件在宣告的同時,將一個已存在的物件賦值給它,則呼叫複製建構函式;如果物件已經存在,再將另一個已存在的物件賦給它,則呼叫賦值操作符。
3、虛解構函式:如果刪除基類指標,則需執行基類解構函式,若物件是派生型別,則不需要定義該行為。為了保證執行適當的解構函式,基類中的解構函式必須為虛解構函式。(即使解構函式沒有工作要做,繼承層次的根類也應該定義一個虛解構函式。)copy()與copy_backward(): copy()函式實現元素左移功能,copy_backward()實現元素右移功能。