当前位置:   article > 正文

c++第9章·群体类和群体数据的组织_第9章:群体类和群体数据的组织

第9章:群体类和群体数据的组织

群体数据:自定义类型的数据由多个基本类型或自定义类型的元素组成

群体类:对于群体数据,仅有系统预定义的操作是不够的,在很多情况下,还需要设计与某些具体问题相关的特殊操作,并按照面向对象的方法将数据与操作封装起来

群体

函数模板

  1. template <class 类型参数1, class类型参数2, ...>
  2. 返回值类型 模板名(形参表)
  3. {
  4. 函数体
  5. }

代码中的class也可换成typename

template<参数表>class标识符,指明可以接受一个类模板名作为参数

实例化

  1. template <class T>
  2. void Swap(T & x, T & y)
  3. {
  4. T tmp = x;
  5. x = y;
  6. y = tmp;
  7. }

T 是类型参数,代表类型。编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动地保留。同一个类型参数只能替换为同一种类型。编译器在编译到调用函数模板的语句时,会根据实参的类型判断该如何替换模板中的类型参数

  1. #include <iostream>
  2. using namespace std;
  3. template<class T>
  4. void Swap(T & x, T & y)
  5. {
  6. T tmp = x;
  7. x = y;
  8. y = tmp;
  9. }
  10. int main()
  11. {
  12. int n = 1, m = 2;
  13. Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
  14. double f = 1.2, g = 2.3;
  15. Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
  16. return 0;
  17. }

编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误

编译器对模板进行实例化时,并非只能通过模板调用语句的实参来实例化模板中的类型参数,模板调用语句可以明确指明要把类型参数实例化为哪种类型。可以用:

模板名<实际类型参数1, 实际类型参数2, ...>
  1. #include <iostream>
  2. using namespace std;
  3. template <class T>
  4. T Inc(T n)
  5. {
  6. return 1 + n;
  7. }
  8. int main()
  9. {
  10. cout << Inc<double>(4) / 2; //好好看看这里是怎么用的
  11. return 0;
  12. }

此时,Inc返回的是2.5,不因为4是int而改变(因为指明了double),函数模板是不允许隐式类型转换的,调用时类型必须严格匹配

可以有不只一个类型参数

  1. template <class Tl, class T2>
  2. T2 print(T1 argl, T2 arg2)
  3. {
  4. cout << arg1 << " " << arg2 << endl;
  5. return arg2;
  6. }

注意

类模板

使用类模板使用户可以为类定义一种模式,使得类中的某些数据成员,某些成员函数的参数,返回值或局部变量能取任意类型

类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,因此类模板是属于更高层次的抽象。由于类模板需要一种或多种类型参数,所以类模板也常常称为参数化类

代码

  1. template <typename T>
  2. class Complex{
  3. public:
  4. //构造函数
  5. Complex(T a, T b)
  6. {
  7. this->a = a;
  8. this->b = b;
  9. }
  10. //运算符重载
  11. Complex<T> operator+(Complex &c) \\Complex<T>是具化了返回类型
  12. {
  13. Complex<T> tmp(this->a+c.a, this->b+c.b);
  14. return tmp;
  15. }
  16. private:
  17. T a;
  18. T b;
  19. }
  20. int main()
  21. {
  22. //对象的定义,必须声明模板类型,因为要分配内容
  23. Complex<int> a(10,20);
  24. Complex<int> b(20,30);
  25. Complex<int> c = a + b;
  26. return 0;
  27. }

  1. template <typename T>
  2. class Blob
  3. {
  4. public:
  5. Blob();
  6. Blob(std::initializer_list<T> i);
  7. T func(T const &str);//在类内声明
  8. };
  9. //类外定义
  10. template <typename T>
  11. T Blob<T>::func(T const &str) \\这里可以好好看看
  12. {
  13. }

建立对象:

有点像vector<类型名>

类模板还有很多知识,但是书上没有了,考试应该也不会涉及

线性群体

线性群体中的元素按位置排列有序

直接访问群体——数组类

  1. #ifndef ARRAY_H
  2. #define ARRAY_H
  3. #include <cassert>
  4. template<class T>
  5. class Array {
  6. private:
  7. T* list; //T类型指针,用于存放动态分配的数组内存首地址
  8. int size; //数组大小(元素个数)
  9. public:
  10. Array(int sz = 50); //构造函数
  11. Array(const Array<T>& a); //复制构造函数
  12. ~Array(); //析构函数
  13. Array<T>& operator=(const Array<T>& rhs); //重载"="使数组对象可以整体赋值
  14. T& operator[](int i); //重载"[]",使Array对象可以起到C++普通数组的作用
  15. const T& operator [] (int i) const; //"[]"运算符针对const的重载
  16. operator T* (); //重载到T*类型的转换,使Array对象可以起到C++普通数组的作用
  17. operator const T* () const; //到T*类型转换操作符针对const的重载
  18. int getSize() const; //取数组的大小
  19. void resize(int sz); //修改数组的大小
  20. };
  21. //构造函数
  22. template<class T>
  23. Array<T>::Array(int sz) {
  24. assert(sz >= 0);
  25. size = sz;
  26. list = new T[size];
  27. }
  28. //析构函数
  29. template<class T>
  30. Array<T>::~Array() {
  31. delete[] list;
  32. }
  33. //复制构造函数
  34. template<class T>
  35. Array<T>::Array(const Array<T>& a) {
  36. size = a.size;
  37. list = new T[size];
  38. for (int i = 0; i < size; i++)
  39. list[i] = a.list[i];
  40. }
  41. //重载"="运算符,将对象rhs赋值给本对象,实现对象之间的整体赋值
  42. template<class T>
  43. Array<T>& Array<T>::operator=(const Array<T>& rhs) {
  44. if (&rhs != this) {
  45. if (size != rhs.size) {
  46. delete[] list;
  47. size = rhs.size;
  48. list = new T[size];
  49. }
  50. for (int i = 0; i < size; i++)
  51. list[i] = rhs.list[i];
  52. }
  53. return *this; //因为返回的是引用类型,所以这里加了*,返回的引用会自动绑定
  54. }
  55. //重载下标运算符,实现与普通数组一样通过下标访问元素,并且具有越界检查功能
  56. template<class T>
  57. T& Array<T>::operator[] (int n) {
  58. assert(n >= 0 && n < size);
  59. return list[n];
  60. }
  61. template<class T>
  62. const T& Array<T>::operator[] (int n) const {
  63. assert(n >= 0 && n < size);
  64. return list[n];
  65. }
  66. //重载指针转换运算符,将Array类的对象名转换为T类型的指针
  67. template<class T>
  68. Array<T>::operator T* () { //因为返回的是地址,所以没有返回类型
  69. return list;
  70. }
  71. template<class T>
  72. Array<T>::operator const T* () const {
  73. return list;
  74. }
  75. //取当前数组的大小
  76. template<class T>
  77. int Array<T>::getSize() const {
  78. return size;
  79. }
  80. //将数组大小修改为sz
  81. template<class T>
  82. void Array<T>::resize(int sz) {
  83. assert(sz >= 0);
  84. if (sz == size)
  85. return;
  86. T* newList = new T[sz];
  87. int n = (sz < size) ? sz : size;
  88. for (int i = 0; i < n; i++)
  89. newList[i] = list[i];
  90. delete[] list;
  91. list = newList;
  92. size = sz; //这里不用delete[] newList,因为这一段内存空间已经给予了list了
  93. }
  94. #endif

注意看一下 =,T* 的运算符重载,返回类型是引用(原因后面有说)

浅复制与深复制

如果类模板中涉及到地址(或是如果类中成员函数有指针)则需要设计复制构造函数实现深复制(浅复制在之前已经说过——共用一段内存空间,在执行析构函数时会将两个对象都删掉)

特殊的运算符重载

返回值类型为对象的引用

简而言之——当这个运算符在我们一般写法是放在左边的,那么他的重载函数返回类型就得是引用(指针符例外)

指针转换运算符的作用

在上面的那段长代码中表现的就是operator T*

顺序访问群体——链表类

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/748552
推荐阅读
相关标签
  

闽ICP备14008679号