• 数据结构实战开发教程(三)线性表的本质和操作、顺序存储结构的抽象实现、顺序存储线性表的分析、数组类的创建


    十四、线性表的本质和操作

    1、生活中的智慧

    幼儿园老师总会让小朋友以同样的排队秩序出行。

    2、线性表(List)的表现形式

    • 零个多个数据元素组成的集合
    • 数据元素在位置上是有序排列的
    • 数据元素的个数是有限的
    • 数据元素的类型必须相同

    3、线性表(List)的抽象定义

            

    4、线性表(List)的性质

            

    5、思考题

    • 下面的关系中可以用线性表描述的是
      • 班级中同学的友谊关系 (X一对多)
      • 公司中的上下级关系 (X一对多)
      • 冬天图书馆用物品排队占座 (X数据类型不同)

            

    6、问题

            线性表只是一个单纯的概念吗?如何在程序中描述和使用一个线性表?

    7、线性表的一些常用操作

    • 将元素插入线性表
    • 将元素从线性表中删除
    • 获取目标位置处元素的值
    • 设置目标位置处元素的值
    • 获取线性表的长度
    • 清空线性表

    8、线性表在程序中表现为一种特殊的数据类型

            

    9、编程实验:线性表抽象类的创建

    List.h

    1. #ifndef LIST_H_
    2. #define LIST_H_
    3. /*******************************************************************************
    4. * Include Files *
    5. *******************************************************************************/
    6. #include "Object.h"
    7. /*******************************************************************************
    8. * Type Definition *
    9. *******************************************************************************/
    10. namespace DTLib
    11. {
    12. template < typename T >
    13. class List : public Object
    14. {
    15. protected:
    16. List(const List<T>&); // 拷贝构造函数 对于容器类型的类,可以考虑禁用拷贝构造和赋值操作。
    17. List<T>& operator= (const List<T>&); // 赋值操作符重载函数 对于容器类型的类,可以考虑禁用拷贝构造和赋值操作。
    18. public:
    19. List() { } // 构造函数
    20. virtual bool insert(const T& e) = 0; // 在线性表尾部插入e
    21. virtual bool insert(int i, const T& e) = 0; // 在i处插入e
    22. virtual bool remove(int i) = 0; // 删除第i个元素
    23. virtual bool set(int i, const T& e) = 0; // 设置第i个元素为e
    24. virtual bool get(int i, T& e) const = 0; // 获取第i个元素为e
    25. virtual int find(const T& e) const = 0; // 查找指定的元素
    26. virtual int length() const = 0; // 返回当前有效值的数量
    27. virtual void clear() = 0; // 清空链表
    28. List<T>& self()
    29. {
    30. return *this;
    31. }
    32. };
    33. }
    34. #endif // LIST_H_

    10、小结

    • 线性表是数据元素的有序并且有限的集合
    • 线性表中的数据元素必须是类型相同的
    • 线性表可用于描述排队关系的问题
    • 线性表在程序中表现为一种特殊的数据类型
    • 线性表在C++中表现为一个抽象类

    十五、线性表的顺序存储结构

    1、顺序存储的定义

    线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表中的数据元素。

            

    2、设计思路

    • 可以用一维数组来实现顺序存储结构
      • 存储空间:T*m_array;
      • 当前长度:int m_length;

            

    3、顺序存储结构的元素获取操作

    • 判断目标位置是否合法
    • 将目标位置作为数组下标获取元素

            

    4、编程实验:图解元素获取

            通过数组下标获取元素

     

    5、顺序存储结构的元素插入操作

            1. 判断目标位置是否合法

            2. 将目标位置之后的所有元素后移一个位置

            3. 将新元素插入目标位置

            4. 线性表长度加1

    6、顺序存储结构的元素插入示例

            

    7、编程实验:图解元素插入

            线性表长度+1 ===> 会不会越界;

            插入的位置是否合理(不能插入的位置前后都没数据)

    8、顺序存储结构的元素删除操作

    1. 判断目标位置是否合法

    2. 将目标位置后的所有元素前移一个位置

    3. 线生表长度减1

    9、顺序存储结构的元素删除示例

            

    10、编程实验:图解元素删除

            删除的位置是否合理(不能删除的位置没数据)

     

    11、实战预告

    To be continued …

    十六、顺序存储结构的抽象实现

    1、课程目标

    完成顺序存储结构线性表的抽象实现

    2、SeqList设计要点

    • 抽象类模板,存储空间的位置和大小由子类完成
    • 实现顺序存储结构线性表的关键操作(增,删,查,等)
    • 提供数组操作符,方便快速获取元素

            

    3、编程实验:顺序存储线性表

    SeqList.h

    1. #ifndef SEQLIST_H_
    2. #define SEQLIST_H_
    3. /*******************************************************************************
    4. * Include Files *
    5. *******************************************************************************/
    6. #include "List.h"
    7. #include "Exception.h"
    8. /*******************************************************************************
    9. * Type Definition *
    10. *******************************************************************************/
    11. namespace DTLib
    12. {
    13. template < typename T >
    14. class SeqList : public List<T>
    15. {
    16. protected:
    17. T* m_array; // 数组地址
    18. int m_length; // 当前线性表长度
    19. public:
    20. /**********************************************************************
    21. * Function: insert()
    22. * Description: 在i位置,插入e
    23. * Input: i 待插入的位置
    24. * e 待插入的数据
    25. * Output:
    26. * Return: bool 是否插入成功
    27. * Others: O(n+5) => O(n)
    28. * Modify Date Version Author Modification
    29. * ------------------------------------------------------------
    30. * 2022/03/10 1.0 Create
    31. **********************************************************************/
    32. bool insert(int i, const T& e)
    33. {
    34. bool ret = ((0 <= i) && (i <= m_length)); // i的合法性 (不能插入的位置前后都没数据)
    35. ret = ret && (m_length < capacity()); // 线性表长度未越界
    36. if( ret )
    37. {
    38. for(int p=m_length-1; p>=i; p--) // 将目标位置之后的所有元素后移一个位置
    39. {
    40. m_array[p + 1] = m_array[p];
    41. }
    42. m_array[i] = e; // 将新数据填入目标位置
    43. m_length++; // 线性表长度+1
    44. }
    45. return ret;
    46. }
    47. /**********************************************************************
    48. * Function: insert()
    49. * Description: 在线性表尾部,插入e
    50. * Input: e 待插入的数据
    51. * Output:
    52. * Return: bool 是否插入成功
    53. * Others: O(n)
    54. * Modify Date Version Author Modification
    55. * ------------------------------------------------------------
    56. * 2022/03/10 1.0 Create
    57. **********************************************************************/
    58. bool insert(const T& e)
    59. {
    60. return insert(m_length, e);
    61. }
    62. /**********************************************************************
    63. * Function: remove()
    64. * Description: 删除i位置的元素
    65. * Input: i 待删除的位置
    66. * Output:
    67. * Return: bool 是否删除成功
    68. * Others: O(n)
    69. * Modify Date Version Author Modification
    70. * ------------------------------------------------------------
    71. * 2022/03/10 1.0 Create
    72. **********************************************************************/
    73. bool remove(int i)
    74. {
    75. bool ret = ((0 <= i) && (i < m_length)); // i的合法性
    76. if( ret )
    77. {
    78. for(int p=i; p<m_length-1; p++) // 将目标位置后的所有元素前移一个位置
    79. {
    80. m_array[p] = m_array[p+1];
    81. }
    82. m_length--; // 线性表长度-1
    83. }
    84. return ret;
    85. }
    86. /**********************************************************************
    87. * Function: set()
    88. * Description: 更新i位置的元素
    89. * Input: i 待更新的位置
    90. * e 待更新的数据
    91. * Output:
    92. * Return: bool 是否更新成功
    93. * Others:
    94. * Modify Date Version Author Modification
    95. * ------------------------------------------------------------
    96. * 2022/03/10 1.0 Create
    97. **********************************************************************/
    98. bool set(int i, const T& e)
    99. {
    100. bool ret = ((0 <= i) && (i < m_length)); // i的合法性
    101. if( ret )
    102. {
    103. m_array[i] = e; // 更新数据
    104. }
    105. return ret;
    106. }
    107. /**********************************************************************
    108. * Function: get()
    109. * Description: 获取i位置的元素
    110. * Input: i 待获取的位置
    111. * Output: e 获取的元素
    112. * Return: bool 是否获取成功
    113. * Others: O(n)
    114. * Modify Date Version Author Modification
    115. * ------------------------------------------------------------
    116. * 2022/03/10 1.0 Create
    117. **********************************************************************/
    118. bool get(int i, T& e) const
    119. {
    120. bool ret = ((0 <= i) && (i < m_length)); // i的合法性
    121. if( ret )
    122. {
    123. e = m_array[i]; // 获取数据
    124. }
    125. return ret;
    126. }
    127. /**********************************************************************
    128. * Function: find()
    129. * Description: 查找指定的元素
    130. * Input: i 待寻找的元素
    131. * Output:
    132. * Return: int 元素的位置
    133. * Others: O(n)
    134. * Modify Date Version Author Modification
    135. * ------------------------------------------------------------
    136. * 2022/03/10 1.0 Create
    137. **********************************************************************/
    138. int find(const T& e) const
    139. {
    140. int ret = -1;
    141. for(int i=0; i<m_length; i++) // 遍历寻找
    142. {
    143. if( m_array[i] == e )
    144. {
    145. ret = i;
    146. break;
    147. }
    148. }
    149. return ret;
    150. }
    151. /**********************************************************************
    152. * Function: length()
    153. * Description: 返回当前线性表的所用数量
    154. * Input:
    155. * Output:
    156. * Return: int 当前线性表的所用数量
    157. * Others:
    158. * Modify Date Version Author Modification
    159. * ------------------------------------------------------------
    160. * 2022/03/10 1.0 Create
    161. **********************************************************************/
    162. int length() const
    163. {
    164. return m_length;
    165. }
    166. /**********************************************************************
    167. * Function: clear()
    168. * Description: 清空链表
    169. * Input:
    170. * Output:
    171. * Return:
    172. * Others:
    173. * Modify Date Version Author Modification
    174. * ------------------------------------------------------------
    175. * 2022/03/10 1.0 Create
    176. **********************************************************************/
    177. void clear()
    178. {
    179. m_length = 0;
    180. }
    181. /**********************************************************************
    182. * Function: operator[]()
    183. * Description: []操作符重构函数
    184. * Input: i 待返回的元素位置
    185. * Output:
    186. * Return: T 元素的值
    187. * Others: 数组访问方式
    188. * Modify Date Version Author Modification
    189. * ------------------------------------------------------------
    190. * 2022/03/10 1.0 Create
    191. **********************************************************************/
    192. T& operator[] (int i)
    193. {
    194. if( (0 <= i) && (i < m_length) ) // i的合法性
    195. {
    196. return m_array[i];
    197. }
    198. else
    199. {
    200. THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    201. }
    202. }
    203. /**********************************************************************
    204. * Function: operator[]()
    205. * Description: []操作符重构函数(const类型的对象使用)
    206. * Input: i 待返回的元素位置
    207. * Output:
    208. * Return: T 元素的值
    209. * Others: 该函数调用了上一个[]操作符重构函数
    210. * Modify Date Version Author Modification
    211. * ------------------------------------------------------------
    212. * 2022/03/10 1.0 Create
    213. **********************************************************************/
    214. T operator[] (int i) const
    215. {
    216. return (const_cast<SeqList<T>&>(*this))[i];
    217. }
    218. /**********************************************************************
    219. * Function: capacity()
    220. * Description: 顺序存储空间的容量
    221. * Input:
    222. * Output:
    223. * Return: int 顺序存储空间的容量
    224. * Others: 纯虚函数
    225. * Modify Date Version Author Modification
    226. * ------------------------------------------------------------
    227. * 2022/03/10 1.0 Create
    228. **********************************************************************/
    229. virtual int capacity() const = 0;
    230. };
    231. }
    232. #endif // SEQLIST_H_

    4、To be continued ...

    十七、StaticList 和 DynamicList

    1、课程目标

    • 完成StaticList类的具体实现
    • 完成DynamicList类的具体实现

    2、StaticList 设计要点

    • 类模板
      • 使用原生数组作为顺序存储空间
      • 使用模板参数决定数组大小

    ·

    3、编程实验:StaticList的实现

    StaticList.h

    1. #ifndef STATICLIST_H_
    2. #define STATICLIST_H_
    3. /*******************************************************************************
    4. * Include _Files *
    5. *******************************************************************************/
    6. #include "SeqList.h"
    7. /*******************************************************************************
    8. * Type Definition *
    9. *******************************************************************************/
    10. namespace DTLib
    11. {
    12. template < typename T, int N >
    13. class StaticList : public SeqList<T>
    14. {
    15. protected:
    16. T m_space[N]; // 元素总数量
    17. public:
    18. StaticList() // 构造函数 - 指定父类成员的具体值
    19. {
    20. this->m_array = m_space; // 初始化元素总数量
    21. this->m_length = 0; // 初始化当前线性表长度
    22. }
    23. int capacity() const // 获取元素总数量
    24. {
    25. return N;
    26. }
    27. };
    28. }
    29. #endif // STATICLIST_H_

    4、DynamicList设计要点

    • 类模
      • 申请连续堆空间作为顺序存储空间
      • 动态设置顺序存储空间的大小
      • 保证重置顺序存储空间时的异常安全性
    • 函数异常安全的概念
      • 不泄漏任何资源
      • 不允许破坏数据
    • 函数异常安全的基本保证
      • 如果异常被抛出
        • 对象内的任何成员仍然能保持有效状态
        • 没有数据的破坏及资源泄漏

            

    5、编程实验:DynamicList的实现

    DynamicList.h

    1. #ifndef DYNAMICLIST_H_
    2. #define DYNAMICLIST_H_
    3. /*******************************************************************************
    4. * Include _Files *
    5. *******************************************************************************/
    6. #include "SeqList.h"
    7. #include "Exception.h"
    8. /*******************************************************************************
    9. * Type Definition *
    10. *******************************************************************************/
    11. namespace DTLib
    12. {
    13. template < typename T >
    14. class DynamicList : public SeqList<T>
    15. {
    16. protected:
    17. int m_capacity; // 元素总数量
    18. public:
    19. /**********************************************************************
    20. * Function: DynamicList()
    21. * Description: 构造函数
    22. * Table Accessed:
    23. * Table Updated:
    24. * Input: capacity 元素总数量
    25. * Output:
    26. * Return:
    27. * Others:
    28. * Modify Date Version Author Modification
    29. * ------------------------------------------------------------
    30. * 2022/03/10 1.0 Create
    31. **********************************************************************/
    32. DynamicList(int capacity) // 构造函数 - 申请空间
    33. {
    34. this->m_array = new T[capacity]; // 申请堆空间
    35. if( this->m_array != NULL )
    36. {
    37. this->m_length = 0; // 初始化当前线性表长度
    38. this->m_capacity = capacity; // 初始化元素总数量
    39. }
    40. else
    41. {
    42. THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
    43. }
    44. }
    45. /**********************************************************************
    46. * Function: capacity()
    47. * Description: 返回元素总数量
    48. * Table Accessed:
    49. * Table Updated:
    50. * Input:
    51. * Output:
    52. * Return: int 返回 元素总数量
    53. * Others:
    54. * Modify Date Version Author Modification
    55. * ------------------------------------------------------------
    56. * 2022/03/10 1.0 Create
    57. **********************************************************************/
    58. int capacity() const
    59. {
    60. return m_capacity;
    61. }
    62. /**********************************************************************
    63. * Function: resize()
    64. * Description: 重新设置元素数量
    65. * Table Accessed:
    66. * Table Updated:
    67. * Input: capacity 新元素总数量
    68. * Output:
    69. * Return:
    70. * Others:
    71. * Modify Date Version Author Modification
    72. * ------------------------------------------------------------
    73. * 2022/03/10 1.0 Create
    74. **********************************************************************/
    75. void resize(int capacity)
    76. {
    77. if( capacity != m_capacity )
    78. {
    79. T* array = new T[capacity]; // 重新申请一块符合大小的堆空间
    80. if( array != NULL ) // 成功申请
    81. {
    82. int length = (this->m_length < capacity ? this->m_length : capacity); // 获取当前元素的数量
    83. for(int i=0; i<length; i++) // 将length个元素复制到新申请的堆空间中
    84. {
    85. array[i] = this->m_array[i]; /* 此可能发生异常,动态链表依然可用,但是会发生内存泄漏。且无法避免。*/
    86. } /* T是一个泛型类型,赋值操作符可能被重载,并且重载的实现出现的异常,上层修改即可*/
    87. T* temp = this->m_array; /* 此处不直接释放堆空间,防止此处抛出异常,保障动态链表异常安全性 */
    88. this->m_array = array; // 更新 顺序存储空间
    89. this->m_length = length; // 更新 当前线性表长度
    90. this->m_capacity = capacity; // 更新 返回元素总数量
    91. delete[] temp; // 删除原来的堆空间
    92. }
    93. else
    94. {
    95. THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
    96. }
    97. }
    98. }
    99. /**********************************************************************
    100. * Function: ~DynamicList()
    101. * Description: 析构函数
    102. * Table Accessed:
    103. * Table Updated:
    104. * Input:
    105. * Output:
    106. * Return:
    107. * Others: 释放堆内存空间
    108. * Modify Date Version Author Modification
    109. * ------------------------------------------------------------
    110. * 2022/03/10 1.0 Create
    111. **********************************************************************/
    112. ~DynamicList()
    113. {
    114. delete[] this->m_array;
    115. }
    116. };
    117. }
    118. #endif // DYNAMICLIST_H_

    6、问题

    是否可以将DynamicList 作为StaticList的子类实现?

    7、小结

    • StaticList 通过模板参数定义顺序存储空间
    • DynamicList 通过动态内存申请定义顺序存储空间
    • DynamicList 支持动态重置顺序存储空间的大小
    • DynamicList 中的resize()函数实现需要保证异常安全

    十八、顺序存储线性表的分析

    1、效率分析

            

    2、问题

            长度相同的两个SeqList插入删除操作的平均耗时是否相同?

                    要具体问题具体分析。

    3、编程实验:SeqList效率分析

    • T为int和String时,字符串的拷贝更耗时。
    • 由于数据类型的不同,赋值操作的效率和耗时不同。取决于线性表里存储的数据类型。
    • 不能单纯只看时间复杂度O,只是参考指标,不是绝对指标,要根据实际情况分析。
    • 基于顺序存储结构的线性表不适合使用类类型的对象作为数据元素存储。

    4、下面的代码正确吗?为什么?

            

            

    拷贝使得两个对象指向同一份堆空间,导致同一个堆空间被两次。

    5、分析

    对于容器类型的类,可以考虑禁用拷贝构造和赋值操作。

            

    6、编程实验:代码优化

            禁用拷贝构造和赋值操作。

    7、下面的代码正确吗?为什么?不正确

    线性表必须先插入元素,才能使用操作符[]访问元素。

    8、问题分析

            顺序存储结构线性表提供了数组操作符重载,通过重载能够快捷方便的获取目标位置处的数据元素,在具体的使用形式上类似数组,但是由于本质不同,不能代替数组使用

    9、实战预告:数组类开发

    10、小结

    • 顺序存储线性表的插入和删除操作存在重大效率隐患(不适合使用类类型的对象作为数据元素存储)
    • 线性表作为容器类,应该避免拷贝构造和拷贝赋值
    • 顺序存储线性表可能被当成数组误用
    • 工程开发中可以考虑使用数组类代替原生数组使用

    十九、数组类的创建(上)

    1、课程目标

    • 完成Array类的具体实现
    • 完成StaticArray类的具体实现

    2、需求分析

    • 创建数组类代替原生数组的使用
      • 数组类包含长度信息
      • 数组类能够主动发现越界访问

    3、Array设计要点

    • 抽象类模板,存储空间的位置和大小由子类完成
    • 重载数组操作符,判断访问下标是否合法
    • 提供数组长度的抽象访问函数
    • 提供数组对象间的复制操作

    4、Array类的声明

            

    5、编程实验:数组抽象类实现

    Array.h

    1. #ifndef ARRAY_H_
    2. #define ARRAY_H_
    3. /*******************************************************************************
    4. * Include Files *
    5. *******************************************************************************/
    6. #include "Object.h"
    7. #include "Exception.h"
    8. /*******************************************************************************
    9. * Type Definition *
    10. *******************************************************************************/
    11. namespace DTLib
    12. {
    13. template < typename T >
    14. class Array : public Object
    15. {
    16. protected:
    17. T* m_array;
    18. public:
    19. /**********************************************************************
    20. * Function: set()
    21. * Description: 设置数组下标为i的元素值为e
    22. * Input: i 下标为i
    23. * e 元素值为e
    24. * Output:
    25. * Return: bool 返回是否成功
    26. * Others: 与原生数组差异:预防下标越界
    27. * Modify Date Version Author Modification
    28. * ------------------------------------------------------------
    29. * 2022/03/10 1.0 Create
    30. **********************************************************************/
    31. virtual bool set(int i, const T& e)
    32. {
    33. bool ret = ((0 <= i) && (i < length()));
    34. if( ret )
    35. {
    36. m_array[i] = e;
    37. }
    38. return ret;
    39. }
    40. /**********************************************************************
    41. * Function: get()
    42. * Description: 获取数组下标为i的元素值
    43. * Input: i 下标为i
    44. * e 元素值为e
    45. * Return: bool 返回是否成功
    46. * Others: 与原生数组差异:预防下标越界
    47. * Modify Date Version Author Modification
    48. * ------------------------------------------------------------
    49. * 2022/03/10 1.0 Create
    50. **********************************************************************/
    51. virtual bool get(int i, T& e) const
    52. {
    53. bool ret = ((0 <= i) && (i < length()));
    54. if( ret )
    55. {
    56. e = m_array[i];
    57. }
    58. return ret;
    59. }
    60. /**********************************************************************
    61. * Function: operator[]()
    62. * Description: []操作符重载函数
    63. * Input: i 数组下标i的元素
    64. * Output:
    65. * Return: T 返回元素的值
    66. * Others: 下标越界,抛出异常
    67. * Modify Date Version Author Modification
    68. * ------------------------------------------------------------
    69. * 2022/03/10 1.0 Create
    70. **********************************************************************/
    71. virtual T& operator[] (int i)
    72. {
    73. if((0 <= i) && (i < length()))
    74. {
    75. return m_array[i];
    76. }
    77. else
    78. {
    79. THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    80. }
    81. }
    82. /**********************************************************************
    83. * Function: operator[]()
    84. * Description: []操作符重载函数(传入const)
    85. * Input: i 数组下标i的元素
    86. * Output:
    87. * Return: T 返回元素的值
    88. * Others: 下标越界,抛出异常
    89. * Modify Date Version Author Modification
    90. * ------------------------------------------------------------
    91. * 2022/03/10 1.0 Create
    92. **********************************************************************/
    93. virtual T operator[] (int i) const
    94. {
    95. return (const_cast<Array<T>&>(*this))[i];
    96. }
    97. /**********************************************************************
    98. * Function: array()
    99. * Description: 返回数组的地址
    100. * Input:
    101. * Output:
    102. * Return: T 返回数组的地址
    103. * Others:
    104. * Modify Date Version Author Modification
    105. * ------------------------------------------------------------
    106. * 2022/03/10 1.0 Create
    107. **********************************************************************/
    108. T* array() const
    109. {
    110. return m_array;
    111. }
    112. Array<T>& self()
    113. {
    114. return *this;
    115. }
    116. virtual int length() const = 0;
    117. };
    118. }
    119. #endif // ARRAY_H_

    6、StaticArray设计要点

    • 类模板
      • 封装原生数组
      • 使用模板参数决定数组大小
      • 实现函数返回数组长度
      • 拷贝构造赋值操作

    7、StaticArray类的声明

            

    8、编程实验:静态数组类的实现

    StaticArray.h

    1. #ifndef STATICARRAY_H_
    2. #define STATICARRAY_H_
    3. /*******************************************************************************
    4. * Include Files *
    5. *******************************************************************************/
    6. #include "Array.h"
    7. /*******************************************************************************
    8. * Type Definition *
    9. *******************************************************************************/
    10. namespace DTLib
    11. {
    12. template < typename T, int N >
    13. class StaticArray : public Array<T>
    14. {
    15. protected:
    16. T m_space[N];
    17. public:
    18. StaticArray() // 构造函数
    19. {
    20. this->m_array = m_space; // 初始化父类成员
    21. }
    22. StaticArray(const StaticArray<T, N>& obj) // 拷贝构造函数
    23. {
    24. this->m_array = m_space; // 初始化父类成员
    25. for(int i=0; i<N; i++) // 循环赋值
    26. {
    27. m_space[i] = obj.m_space[i];
    28. }
    29. }
    30. StaticArray<T, N>& operator= (const StaticArray<T, N>& obj) // 赋值构造函数
    31. {
    32. if( this != &obj ) // 不允许自赋值
    33. {
    34. for(int i=0; i<N; i++) // 循环赋值
    35. {
    36. m_space[i] = obj.m_space[i];
    37. }
    38. }
    39. return *this; // 返回当前类的指针,代表允许连续赋值
    40. }
    41. int length() const // 返回数组的成员数量
    42. {
    43. return N;
    44. }
    45. };
    46. }
    47. #endif // STATICARRAY_H_

    9、实战预告

    To be continued …

    二十、数组类的创建(下)

    1、课程目标

    完成DynamicArray 类的具体实现

    2、DynamicArray设计要点

    • 类模板
      • 动态确定内部数组空间的大小
      • 实现函数返回数组长度
      • 拷贝构造赋值操作

    3、DynamicArray类的声明

            

    4、编程实验:动态数组的实现

    5、问题

    DynamicArray类中的函数实现存在重复的逻辑,如何进行代码优化?

    6、重复代码逻辑的抽象

    • init
      • 对象构造时的初始化操作
    • copy
      • 在堆空间中申请新的内存,并执行拷贝操作
    • update
      • 将指定的堆空间作为内部存储数组使用

    7、编程实验:动态数组的优化

    DynamicArray.h

    1. #ifndef DYNAMICARRAY_H_
    2. #define DYNAMICARRAY_H_
    3. /*******************************************************************************
    4. * Include _Files *
    5. *******************************************************************************/
    6. #include "Array.h"
    7. #include "Exception.h"
    8. /*******************************************************************************
    9. * Type Definition *
    10. *******************************************************************************/
    11. namespace DTLib
    12. {
    13. template < typename T >
    14. class DynamicArray : public Array<T>
    15. {
    16. protected:
    17. int m_length;
    18. /**********************************************************************
    19. * Function: copy()
    20. * Description: 动态调整数组大小
    21. * Input: array 数组类地址
    22. * len 原数组成员数量
    23. * newLen 新数组成员数量
    24. * Output:
    25. * Return: bool 返回数组类地址
    26. * Others: O(n)
    27. * Modify Date Version Author Modification
    28. * ------------------------------------------------------------
    29. * 2022/03/10 1.0 Create
    30. **********************************************************************/
    31. T* copy(T* array, int len, int newLen)
    32. {
    33. T* ret = new T[newLen];
    34. if( ret != NULL )
    35. {
    36. int size = (len < newLen) ? len : newLen; // 取数组成员数量小的,进行数组拷贝
    37. for(int i=0; i<size; i++)
    38. {
    39. ret[i] = array[i];
    40. }
    41. }
    42. return ret;
    43. }
    44. /**********************************************************************
    45. * Function: update()
    46. * Description: 动态调整后更新
    47. * Input: array 数组类地址
    48. * length 数组元素数量
    49. * Output:
    50. * Return:
    51. * Others:
    52. * Modify Date Version Author Modification
    53. * ------------------------------------------------------------
    54. * 2022/03/10 1.0 Create
    55. **********************************************************************/
    56. void update(T* array, int length)
    57. {
    58. if( array != NULL )
    59. {
    60. T* temp = this->m_array; /* 此处不直接释放堆空间,防止此处抛出异常,保障动态数组异常安全性 */
    61. this->m_array = array; // 更新数组地址
    62. this->m_length = length; // 更新数组元素数量
    63. delete[] temp;
    64. }
    65. else
    66. {
    67. THROW_EXCEPTION(NoEnoughMemoryException, "No memory to update DynamicArray object ...");
    68. }
    69. }
    70. /**********************************************************************
    71. * Function: init()
    72. * Description: 初始化数组大小
    73. * Input: array 数组类地址
    74. * length 数组元素数量
    75. * Output:
    76. * Return:
    77. * Others:
    78. * Modify Date Version Author Modification
    79. * ------------------------------------------------------------
    80. * 2022/03/10 1.0 Create
    81. **********************************************************************/
    82. void init(T* array, int length)
    83. {
    84. if( array != NULL )
    85. {
    86. this->m_array = array; // 初始化数组地址
    87. this->m_length = length; // 初始化数组元素数量
    88. }
    89. else
    90. {
    91. THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicArray object ...");
    92. }
    93. }
    94. public:
    95. /**********************************************************************
    96. * Function: DynamicArray()
    97. * Description: 构造函数 - 创建数组
    98. * Input: length 数组元素数量
    99. * Output:
    100. * Return:
    101. * Others:
    102. * Modify Date Version Author Modification
    103. * ------------------------------------------------------------
    104. * 2022/03/10 1.0 Create
    105. **********************************************************************/
    106. DynamicArray(int length = 0)
    107. {
    108. init(new T[length], length);
    109. }
    110. /**********************************************************************
    111. * Function: DynamicArray()
    112. * Description: 拷贝构造函数
    113. * Input: obj 待拷贝的数组类
    114. * Output:
    115. * Return:
    116. * Others: 先创建拷贝数组类(copy),再初始化数组地址和数组元素数量(init)
    117. * Modify Date Version Author Modification
    118. * ------------------------------------------------------------
    119. * 2022/03/10 1.0 Create
    120. **********************************************************************/
    121. DynamicArray(const DynamicArray<T>& obj)
    122. {
    123. init(copy(obj.m_array, obj.m_length, obj.m_length), obj.m_length);
    124. }
    125. /**********************************************************************
    126. * Function: operator=()
    127. * Description: 赋值构造函数
    128. * Input: obj 待拷贝的数组类
    129. * Output:
    130. * Return:
    131. * Others: 先创建拷贝数组类(copy),再更新数组地址和数组元素数量(update)
    132. * Modify Date Version Author Modification
    133. * ------------------------------------------------------------
    134. * 2022/03/10 1.0 Create
    135. **********************************************************************/
    136. DynamicArray<T>& operator= (const DynamicArray<T>& obj)
    137. {
    138. if( this != &obj ) // 禁止自赋值
    139. {
    140. update(copy(obj.m_array, obj.m_length, obj.m_length), obj.m_length);
    141. }
    142. return *this;
    143. }
    144. /**********************************************************************
    145. * Function: length()
    146. * Description: 返回数组元素数量
    147. * Input:
    148. * Output:
    149. * Return: 返回数组元素数量
    150. * Others:
    151. * Modify Date Version Author Modification
    152. * ------------------------------------------------------------
    153. * 2022/03/10 1.0 Create
    154. **********************************************************************/
    155. int length() const
    156. {
    157. return m_length;
    158. }
    159. /**********************************************************************
    160. * Function: resize()
    161. * Description: 重新分配数组的元素数量
    162. * Input: length 重新分配的数组元素数量
    163. * Output:
    164. * Return:
    165. * Others: 先创建拷贝数组类(copy),再更新数组地址和数组元素数量(update)
    166. * Modify Date Version Author Modification
    167. * ------------------------------------------------------------
    168. * 2022/03/10 1.0 Create
    169. **********************************************************************/
    170. virtual void resize(int length)
    171. {
    172. if( length != m_length )
    173. {
    174. update(copy(this->m_array, m_length, length), length);
    175. }
    176. }
    177. /**********************************************************************
    178. * Function: ~DynamicArray()
    179. * Description: 析构函数
    180. * Input:
    181. * Output:
    182. * Return:
    183. * Others: 释放数组的堆空间
    184. * Modify Date Version Author Modification
    185. * ------------------------------------------------------------
    186. * 2022/03/10 1.0 Create
    187. **********************************************************************/
    188. ~DynamicArray()
    189. {
    190. delete[] this->m_array;
    191. }
    192. };
    193. }
    194. #endif // DYNAMICARRAY_H_

    8、小结

    • StaticArray通过封装原生数组的方式实现数组类
    • DynamicArray动态申请堆空间,使得数组长度动态可变
    • 数组对象能够代替原生数组,并且使用上更安全
    • 代码优化是项目开发过程中不可或缺的环节

  • 相关阅读:
    [附源码]SSM计算机毕业设计8号体育用品销售及转卖系统JAVA
    pandas数据类型之Series
    Vue 源码解读(6)—— 实例方法
    爱上开源之DockerUI-如何实现Web端的Xshell终端模拟器
    Python使用turtle绘制简单图形
    使用Next.js和web3auth来创建DApps
    java计算机毕业设计高校网上报销系统源码+mysql数据库+系统+lw文档+部署
    Netty(三)- NIO三大组件之Channel
    一本通1057;简单计算器(2)
    线程是如何实现的?
  • 原文地址:https://blog.csdn.net/qq_38426928/article/details/125464524