C++利用动态数组实现顺序表(不限数据类型)

  通过类模板实现顺序表时,若进行比较和遍历操作,模板元素可以通过STL中的equal_to仿函数实现,或者通过回调函数实现。若进行复制操作,可以采用STL的算法函数,也可以通过操作地址实现。关于回调函数和地址操作可以查看:C语言利用动态数组实现顺序表(不限数据类型)

  主要功能:初始化,按照索引插入,删除,遍历,按索引返回元素,返回顺序表的容量,元素个数,及扩容操作。

  1 #include <iostream>
  2 #include <vector>
  3 #include <string>
  4 
  5 
  6 using namespace std;
  7 
  8 //异常类
  9 class illegalParameterValue{
 10 public:
 11     illegalParameterValue();
 12     illegalParameterValue(string myMessage);
 13     void outPutMessage();
 14 private:
 15     string message;
 16 };
 17 
 18 illegalParameterValue::illegalParameterValue(){
 19     this->message = "illegal Parameter Value";
 20 }
 21 
 22 illegalParameterValue::illegalParameterValue(string myMessage){
 23     this->message = myMessage;
 24 }
 25 
 26 void illegalParameterValue::outPutMessage(){
 27     cout << this->message << endl;
 28 }
 29 
 30 //自定义顺序表类模板
 31 template<class T>
 32 class arrayList{
 33 public:
 34     //构造函数
 35     arrayList();
 36     arrayList(int iniCapacity);
 37     //复制构造函数
 38     arrayList(const arrayList<T>& theList);
 39     
 40     ~arrayList(){ delete[] element; }
 41 
 42     //ADT方法
 43     int mySize() const { return this->arrayListSize; }
 44     int myCapacity() const { return this->arrayListCapacity; }
 45     bool myEmpty() const { return arrayListSize == 0; }
 46 
 47     void myForeach() const;
 48     T& myGet(int index) const;
 49     void myErasePos(int index);
 50     void myEraseValue(T& theElement);
 51     void myInsert(int index, T& theElement);
 52     void output(ostream& out)const;
 53 private:
 54     int arrayListCapacity;
 55     int arrayListSize;
 56     T* element;
 57 };
 58 
 59 //构造函数,抛出异常时需要注意释放已创建的动态数据
 60 //如果抛出异常后,没有处理,会继续向上抛出,直到main函数处理
 61 //这里只是抛出,并没有捕获,因此不会显示message
 62 template <class T>
 63 arrayList<T>::arrayList(){}
 64 
 65 template <class T>
 66 arrayList<T>::arrayList(int iniCapacity){
 67     if (iniCapacity < 1)
 68     {
 69         string message = "the iniCapacity must be > 0";
 70         throw illegalParameterValue(message);
 71     }
 72     //throw 1; 
 73     arrayListCapacity = iniCapacity;
 74     element = new T[arrayListCapacity];
 75     arrayListSize = 0;
 76 }
 77 
 78 template <class T>
 79 arrayList<T>::arrayList(const arrayList<T>& theList){
 80     arrayListCapacity = theList.arrayListCapacity;
 81     arrayListSize = theList.arrayListSize;
 82     element = new T[arrayListCapacity];
 83     copy(theList.element, theList.element + arrayListSize, element);
 84 }
 85 
 86 //ADT方法
 87 //根据pos获取元素
 88 template<class T>
 89 T &arrayList<T>::myGet(int index)const{
 90     if (index < 0 || index >= arrayListSize){
 91         throw illegalParameterValue("the index is wrong.");
 92     }
 93     return element[index];
 94 }
 95 
 96 //按位置删除元素
 97 template <class T>
 98 void arrayList<T>::myErasePos(int index){
 99     if (index < 0 || index >= arrayListSize){
100         throw illegalParameterValue("the index is wrong.");
101     }
102     //整体移动
103     copy(element + index + 1, element + arrayListSize, element+ index );
104     arrayListSize--;
105 }
106 
107 //按值删除,仅删除第一次出现的位置,通过equal_to实现
108 template <class T>
109 void arrayList<T>::myEraseValue(T &theElement){
110     
111     for (int i = 0; i != arrayListSize; i++){
112         if (equal_to<T>()(element[i], theElement)){
113 
114             cout << "mid" << endl;
115             myErasePos(i);
116             break;
117         }
118     }
119 }
120 
121 //按位置插入元素,并扩容
122 //这里有个非常隐蔽的错误,tmpElement通过new开辟内存空间
123 //按道理需要释放tmpElement,但tmpElement的地址赋给element,两个变量指向同一个内存地址
124 //element本身运行或下次进入条件就会析构掉,释放掉该块内存
125 //如果提前释放掉tmpElement,则提前释放了该块内存,则会二次释放造成内存泄漏
126 template <class T>
127 void arrayList<T>::myInsert(int index, T &theElement){
128     if (index < 0 || index > arrayListSize){
129         throw illegalParameterValue("the index is wrong.");
130     }
131 
132     if (arrayListSize >= arrayListCapacity){
133         int tmpCapacity = arrayListCapacity * 2;
134         T *tmpElement = new T[tmpCapacity];
135         copy(this->element, this->element+this->arrayListSize, tmpElement);
136         this->~arrayList(); 
137         this->element = tmpElement;
138         arrayListCapacity = arrayListCapacity * 2;
139         //delete [] tmpElement;
140     }
141     copy_backward(this->element + index, this->element + arrayListSize, this->element + arrayListSize + 1);
142     this->element[index] = theElement;
143     arrayListSize++;
144 }
145 
146 //输出函数
147 template <class T>
148 void arrayList<T>::output(ostream& out)const{
149     copy(element, element + arrayListSize, ostream_iterator<T>(out, " "));
150 }
151 //重载<<
152 template <class T>
153 ostream& operator<<(ostream& out, const arrayList<T>& x){
154     x.output(out);
155     return out;
156 }
157 
158 int main(){
159     arrayList<int>mylist(2);
160     int a1 = 1, a2 = 2, a3 = 3;
161     
162     mylist.myInsert(0, a1);
163     mylist.myInsert(1, a2);
164     mylist.myInsert(2, a3);
165     
166     mylist.myErasePos(0);
167     int b = 2;
168     mylist.myEraseValue(b);
169     mylist.output(cout);
170     cout << endl;
171     
172     cout << mylist.myGet(1) << endl;
173     mylist.output(cout);
174     
175     system("pause");
176     return 0;
177 }
完整代码

自定义顺序表类

  通过类模板自定义顺序表类,并在类内实现了返回顺序表的容量和元素个数等操作。

 1 //自定义顺序表类模板
 2 template<class T>
 3 class arrayList{
 4 public:
 5     //构造函数
 6     arrayList();
 7     arrayList(int iniCapacity);
 8     //复制构造函数
 9     arrayList(const arrayList<T>& theList);
10     
11     ~arrayList(){ delete[] element; }
12 
13     //ADT方法
14     int mySize() const { return this->arrayListSize; }
15     int myCapacity() const { return this->arrayListCapacity; }
16     bool myEmpty() const { return arrayListSize == 0; }
17 
18     void myForeach() const;
19     T& myGet(int index) const;
20     void myErasePos(int index);
21     void myEraseValue(T& theElement);
22     void myInsert(int index, T& theElement);
23     void output(ostream& out)const;
24 private:
25     int arrayListCapacity;
26     int arrayListSize;
27     T* element;
28 };

 

初始化

  利用C++类模板实现顺序表,最重要的是构造函数及复制构造函数。顺序表中的元素移动,通过STL的内置copy算法实现。另外,类外实现成员函数时,函数需要加作用域,成员属性可以直接使用,如element等。

 1 //构造函数,抛出异常时需要注意释放已创建的动态数据
 2 //如果抛出异常后,没有处理,会继续向上抛出,直到main函数处理
 3 //这里只是抛出,并没有捕获,因此不会显示message
 4 template <class T>
 5 arrayList<T>::arrayList(){}
 6 
 7 template <class T>
 8 arrayList<T>::arrayList(int iniCapacity){
 9     if (iniCapacity < 1)
10     {
11         string message = "the iniCapacity must be > 0";
12         throw illegalParameterValue(message);
13     }
14     //throw 1; 
15     arrayListCapacity = iniCapacity;
16     element = new T[arrayListCapacity];
17     arrayListSize = 0;
18 }
19 
20 template <class T>
21 arrayList<T>::arrayList(const arrayList<T>& theList){
22     arrayListCapacity = theList.arrayListCapacity;
23     arrayListSize = theList.arrayListSize;
24     element = new T[arrayListCapacity];
25     copy(theList.element, theList.element + arrayListSize, element);
26 }

 

索引元素

1 //根据pos获取元素
2 template<class T>
3 T &arrayList<T>::myGet(int index)const{
4     if (index < 0 || index >= arrayListSize){
5         throw illegalParameterValue("the index is wrong.");
6     }
7     return element[index];
8 }

 

索引插入

  索引插入时,需要对边界条件进行判断,由STL内置copy_backward算法实现顺序表元素的移动,因为需要将最右侧的元素先移动;copy算法是将最左侧的元素先移动。

  如果顺序表容量过小时,则需要对顺序表进行扩容,扩容需要注意两点:(1)需要将element指向新的空间;(2)利用临时变量动态扩容时,需要注意释放时机和释放对象。

 1 //按位置插入元素,并扩容
 2 //这里有个非常隐蔽的错误,tmpElement通过new开辟内存空间
 3 //按道理需要释放tmpElement,但tmpElement的地址赋给element,两个变量指向同一个内存地址
 4 //element本身运行或下次进入条件就会析构掉,释放掉该块内存
 5 //如果提前释放掉tmpElement,则提前释放了该块内存,则会二次释放造成内存泄漏
 6 template <class T>
 7 void arrayList<T>::myInsert(int index, T &theElement){
 8     if (index < 0 || index > arrayListSize){
 9         throw illegalParameterValue("the index is wrong.");
10     }
11 
12     if (arrayListSize >= arrayListCapacity){
13         int tmpCapacity = arrayListCapacity * 2;
14         T *tmpElement = new T[tmpCapacity];
15         copy(this->element, this->element+this->arrayListSize, tmpElement);
16         this->~arrayList(); 
17         this->element = tmpElement;
18         arrayListCapacity = arrayListCapacity * 2;
19         //delete [] tmpElement;
20     }
21     copy_backward(this->element + index, this->element + arrayListSize, this->element + arrayListSize + 1);
22     this->element[index] = theElement;
23     arrayListSize++;
24 }

 

索引删除

   判断边界条件时需要注意,动态数组的索引与位置相差1,因此当index == arrayListsize时,也会报错。另外,需要注意删除后,元素个数减一。

 1 //按位置删除元素
 2 template <class T>
 3 void arrayList<T>::myErasePos(int index){
 4     if (index < 0 || index >= arrayListSize){
 5         throw illegalParameterValue("the index is wrong.");
 6     }
 7     //整体移动
 8     copy(element + index + 1, element + arrayListSize, element+ index );
 9     arrayListSize--;
10 }

 

按值删除

   按值删除,这里仅实现了删除元素第一次出现的位置,顺序表中元素间的比较通过STL关系运算符仿函数实现。另外,该函数不具有普适性,对int, float等大部分内置类型可以适用,对于结构体等数据类型,需要对函数进行重载,改变函数参数。如,age和name的class,仿函数不能直接对对象进行比较,只能是对某一个成员属性进行比较。C语言中是用回调函数,让用户自定义操作。

 1 //按值删除,仅删除第一次出现的位置,通过equal_to实现
 2 template <class T>
 3 void arrayList<T>::myEraseValue(T &theElement){
 4     
 5     for (int i = 0; i != arrayListSize; i++){
 6         if (equal_to<T>()(element[i], theElement)){
 7 
 8             cout << "mid" << endl;
 9             myErasePos(i);
10             break;
11         }
12     }
13 }

 

遍历

  对于类模板实现遍历操作, 需要用到STL中的输出流迭代器,另外需要对<<进行重载。C语言中是用回调函数进行操作的,需要用户根据数据类型自定义输出函数。

 1 //输出函数
 2 template <class T>
 3 void arrayList<T>::output(ostream& out)const{
 4     copy(element, element + arrayListSize, ostream_iterator<T>(out, " "));
 5 }
 6 //重载<<
 7 template <class T>
 8 ostream& operator<<(ostream& out, const arrayList<T>& x){
 9     x.output(out);
10     return out;
11 }

 

posted @ 2019-02-21 15:49  两猿社  阅读(454)  评论(0编辑  收藏  举报