C++ 泛型编程 之模板(template)

目录

1 模板

1.1 概念

1.2 特点

2 函数模板

2.1 语法与使用

2.2 示例

2.3 函数模板注意事项

2.3.1 自动推导类型,必须推导出一致的数据类型T,才可以正常使用

2.3.2 模板必须要确定T的数据类型,才可以使用

2.4 示例

3 普通函数与函数模板的区别

3.1 示例

3.2 普通函数与函数模板的调用规则

3.3 模板的局限性

3.3.1 问题

3.3.2 解决

3.3.3 总结

4 类模板

4.1 语法与使用

4.2 示例

4.3 类模板与函数模板的区别

4.4 类模板中成员函数创建时机

4.4.1 示例

4.5 类模板对象作函数参数

4.5.1 示例

4.6 类模板与继承

4.6.1 示例

4.7 类模板成员函数类外实现

4.7.1 示例

4.7.1.1 类模板构造函数类外实现

4.7.1.2 类模板成员函数类外实现

4.8 类模板分文件编写

4.8.1 示例

4.8.1.1 直接包含.cpp文件

4.8.1.2 将声明与实现写在一个hpp文件中

4.9 类模板与友元

4.9.1 示例

4.9.1.1 全局函数类内实现

4.9.1.2 全局函数类外实现

4.9.1.3 解决

4.10 类模板案例

4.10.1 有参构造函数

4.10.2 拷贝构造函数

4.10.3 operetor=等号重载

4.10.4 析构函数

4.10.5 尾插 数据

4.10.6 尾删 数据

4.10.7 以下标方式访问数组元素

4.10.8 获取大小与获取容量

4.10.9测试


1 模板

1.1 概念

C++另一种编程思想称为 泛型编程,主要利用的技术就是模板

建立通用的模具,提高复用性

拍一寸照片的模板:

img

制作PPT的模板

img

但是注意:模板并不是万能的,如人的1寸照片模板,不能用来拍其他生物的照片

而且,制作PPT时不能直接使用,需要自己加内容啥的

1.2 特点

  • 模板不能直接使用,只是一个框架,需要自己合理使用

  • 模板是通用的,但不是万能的

2 函数模板

作用:建立一个通用函数,其返回值类型与形参类型可以不指定,用一个虚拟的类型来代表

2.1 语法与使用

 template<typename T>
 函数声明或定义
  • template:声明创建模板

  • typename:表示其后面的符号是一种数据类型,可以用class

  • T:通用的数据类型,名称可以替换,通常为大写字母T

2.2 示例

接下来使用交换函数swap来举例:

首先写出int整型交换函数float浮点型交换函数

 // 整型 交换函数
 void swapInt(int& x, int& y)
 {
   int tmp = y;
   y = x;
   x = tmp;
 }
 // 浮点型 交换函数
 void swapFloat(float& x, float& y)
 {
   float tmp = y;
   y = x;
   x = tmp;
 }

测试:

 int main()
 {
   int a = 10;
   int b = 20;
   swapInt(a, b);
   cout << a <<" " << b << endl;
 ​
   float c = 10.5;
   float d = 20.5;
   swapFloat(c, d);
   cout << c << " " << d << endl;
 ​
   return 0;
 }

img

可以正常交换并输出


但是,想要交换不同的数据,就必须实现不同的函数,改变返回值类型或者形参类型,但是具体代码又类似

因此,我们使用函数模板

 template<typename T>
 void Myswap(T& x, T& y)
 {
   T tmp = y;
   y = x;
   x = tmp;
 }

调用Myswap函数

   int a = 10;
   int b = 20;
   Myswap(a, b);
   cout << a <<" " << b << endl;
 ​
   float c = 10.5;
   float d = 20.5;
   Myswap(c, d);
   cout << c << " " << d << endl;

img

而我们在调用函数时,并未告知Myswap函数我们传入的类型是什么,使编译器自动推导出类型并实现

这叫做 自动推导类型


还有个 显式指定类型

   int e = 100;
   int f = 20;
   Myswap<int>(e, f);
   cout << e << " " << f << endl;

img

就是调用函数时,在参数列表前加上<int>,这就相当于告知编译器刚才的类型T等于int

2.3 函数模板注意事项

2.3.1 自动推导类型,必须推导出一致的数据类型T,才可以正常使用

以我们上面的交换函数Myswap举例:

 template<typename T>
 void Myswap1(T& x)
 {
   T tmp = y;
   y = x;
   x = tmp;
 }

我们写2个不同的数据类型,尝试调用

   int a = 10;
   float b = 22.4;
   Myswap1(a, b);

直接报错

img

因此不能使用

不过,如果在函数模板里写2个typename,则可以正常调用

 template<typename T,typename Y>
 void Myswap1(T& x, Y& y)
 {
   Y tmp = y;
   y = x;
   x = tmp;
 }

img

2个数据类型

   int a = 10;
   float b = 22.4;
   Myswap1(a, b);

此时可以正常输出,但是输出结果都是int整型

img

2.3.2 模板必须要确定T的数据类型,才可以使用

 template<typename T>
 void test01()
 {
   cout << "ko" << endl;
 }

此时函数内未声明T的数据类型

尝试调用

img

直接报错

因为未确定T的函数类型


解决:

在函数调用参数列表前加上<int>,即随便指定个数据类型,因为函数中没有使用具体的类型

img

2.4 示例

使用函数模板实现选择排序对不同数据类型的数组进行降序排序

首先使用模板实现选择排序

 template<typename T>
 void Select_sort(T arr[],int len)
 {
   int index = 0;
   for (int i = 0; i < len - 1; i++)
   {
     index = i;
     for (int j = i + 1; j < len; j++)
     {    // 我们认定的最大值比数组中某个值小
       if (arr[index] < arr[j])
       {   // 交换两者下标
         index = j;
       }
     }
     if (index != i) // 如果交换了下标(不等于原来的i)
     {
       MYswap(arr[index], arr[i]);//进行数组中数据的交换
     }
   }
 }

最后一步有数据的交换,我们接着使用模板实现交换函数

 template<typename T>
 void MYswap(T& x, T& y)
 {
   T tmp = x;
   x = y;
   y = tmp;
 }

这里就完成了,不过为了使数组中内容可以呈现在屏幕上,我再额外实现一个打印函数

 template<typename T>
 void print(T arr, int len)
 {
   for (int i = 0; i < len; i++)
   {
     cout << arr[i] << " ";
   }
   cout << endl;
 }
  • int类型数组测试

 void int_test()
 {
   // 整型数组测试
   int arr[] = { 2,4,6,1,3,26,7,1,2,4,9,0,2,3 };
   int len = sizeof(arr) / sizeof(arr[0]);
   print(arr, len);
   Select_sort(arr, len);
   cout << endl;
   print(arr, len);
 }
  • char类型数组测试

 void char_test()
 {
   // 字符数组测试
   char arr2[] = "zcnasijda";
   int len2 = sizeof(arr2) / sizeof(char);
   print(arr2, len2);
   Select_sort(arr2, len2);
   cout << endl;
   print(arr2, len2);
 }

img

成功进行降序排序

3 普通函数与函数模板的区别

  • 普通函数调用时,可以发生自动类型转换,(隐式类型转换)

  • 函数模板调用时,如果使用自动类型推导,则不会发生隐式类型转换

  • 函数模板调用时,如果使用显式指定类型,则可以发生隐式类型转换

3.1 示例

首先创建普通函数

 // 普通函数
 void add01(int x, int y)
 {
   cout << x + y << endl;
 }

调用:

   add01(10, 20);

img

创建字符变量c并调用

   char c = 'c';
   add01(10, c);

img

因为cASCII码值是9999+10=109,函数在内部进行隐式类型转换

下面用函数模板实现

 template<typename T>
 void add02(T x, T y)
 {
   cout << x + y << endl;
 }

使用自动类型推导调用10与字符c

img

直接报错,因为不能进行隐式类型转换

而我们使用显式指定类型

img

可以正常输出,因为进行了隐式类型转换,无论你传入什么数据,都给你转成int,转不成就报错

3.2 普通函数与函数模板的调用规则

  • 如果普通函数与函数模板都可以实现,优先调用普通函数

实现一个大致相同普通函数与函数模板

 void print(int x, int y)
 {
   cout << "普通函数" << endl;
 }
 template<typename T>
 void print(T x, T y)
 {
   cout << "函数模板" << endl;
 }

调用生成

img

  • 在上个情况下,通过空模板参数列表,可以强制调用函数模板

img

即在函数调用的参数列表前加<>

  • 函数模板也可以发送重载

 template<typename T>
 void print(T x, T y)
 {
   cout << "函数模板" << endl;
 }
 template<typename T>
 void print(T x, T y,T z) // 重载
 {
   cout << "函数模板" << endl;
 }

img

  • 如果函数模板可以发生更好的匹配,优先调用函数模板

img

2个char类型的变量,传入普通函数需要进行隐式类型转换,而传入函数模板只需要进行自动类型推导,因此优先调用函数模板

3.3 模板的局限性

局限性:模板的通用并不万能

3.3.1 问题

举例:写一个判断变量是否相等的模板,以及相等输出相等,否则输出不等的函数

 template<typename T>
 bool My_compare(T a, T b)
 {
   if (a == b)
   {
     return true;
   }
   else
   {
     return false;
   }
 }
 void judge(bool a)
 {
   if (a)
   {
     cout << "相等" << endl;
   }
   else
   {
     cout << "不等" << endl;
   }
 }

测试:

 void test01()
 {
   int a = 10;
   int b = 10;
   bool ret = My_compare(a, b);
   judge(ret);
 }

很明显,a==b

img

但是,如果a和b的类型是一个class

 class person
 {
 public:
   person(string name,int age)
   {
     m_age = age;
     m_name = name;
   }
   int m_age;
   string m_name;
 };
 ​
 void test02()
 {
   person p1("Tim", 22);
   person p2("Tim", 22);
   bool ret = My_compare(p1, p2);
   judge(ret);
 }

很明显,p1==p2,但是编译器无法正常运行

img

因为person自定义数据类型,编译器不知道咋办

3.3.2 解决

因此,我们使用具体化person的版本实现代码,具体化优先调用

在下面重写一个模板,前面加template<>,后面数据类型写person

 template<>bool My_compare(person& p1, person& p2)
 {
   if (p1.m_age == p2.m_age && p1.m_name == p2.m_name)
   {
     return true;
   }
   else
   {
     return false;
   }
 }

再次尝试运行

 void test02()
 {
   person p1("Tim", 22);
   person p2("Tim", 22);
   bool ret = My_compare(p1, p2);
   judge(ret);
 }

img

成功,而传入数组判断相等也是同样的问题

3.3.3 总结

  • 利用具体化的模板,可以解决自定义类型的通用化

  • 学习模板不是为了写模板,而是为了在STL能够运用系统提供的模板

4 类模板

作用:建立一个通用类,类中的成员属性的数据类型可以不具体指定,用一个虚拟的类型来表示

4.1 语法与使用

 template<class T>
 类

与函数模板基本相同,只要把typename改成class就行,而且二者可以互换,效果相同

4.2 示例

写一个类模板,其中有属性nameage,不指定类型,调用输出

 template<class Name_type,class Age_type >
 class person
 {
 public:
   person(Name_type name, Age_type age)
   {
     m_name = name;
     m_age = age;
   }
   void show()
   {
     cout << this->m_name << this->m_age << endl;
   }
   Name_type m_name;
   Age_type m_age;
 };

测试:

 void test01()
 {
   person<string, int> p1("Joyce", 22);
   p1.show();
 }

img

4.3 类模板与函数模板的区别

  • 类模板没有自动类型推导使用方式

对于刚才的类模板

 template<class Name_type,class Age_type >
 class person
 {
 public:
   person(Name_type name, Age_type age)
   {
     m_name = name;
     m_age = age;
   }
   void show()
   {
     cout << this->m_name << this->m_age << endl;
   }
   Name_type m_name;
   Age_type m_age;
 };

如果我们使用自动类型推导:

img

直接报错

img

而只能用显式指定类型

  • 类模板在模板参数列表中可以有默认参数

我们在模板的参数列表中加入默认参数

img

Age_type直接等于int

 void test02()
 {
   person<string> p2("tatina", 22); // 显式指定类型
   p2.show();
 }

在调用时,我们不用写其类型也可以正常运行

img

4.4 类模板中成员函数创建时机

  • 普通类中:成员函数一开始就可创建

  • 类模板中:成员函数在调用时才创建

4.4.1 示例

首先创建2个类,区分为类1与类2,内部分别创建函数输出数字1与2

 class person1
 {
 public:
   void show1()
   {
     cout << "1" << endl;
   }
 };
 class person2
 {
 public:
   void show2()
   {
     cout << "2" << endl;
   }
 };

下面实现类模板

 template<class T>
 class Myclass
 {
 public:
   T obj;
   void m_show1()
   {
     obj.show1();
   }
   void m_show2()
   {
     obj.show2();
   }
 };

测试:参数列表传person1,调用m_show1函数

 void test02()
 {
   Myclass<person1> m;
   m.m_show1();
 }

img

输出1


尝试调用2:

img

报错

此时就已经说明类模板中成员函数一开始没有创建,只有在调用了,才能确定对象的类型,才能创建成员函数

而我们类型传入person2,运行结果相反

img

4.5 类模板对象作函数参数

三种方式:

  • 指定传入的类型: 直接显示对象的数据类型(日常主要用的类型)

  • 参数模板化: 将对象中的参数变为模板进行传递

  • 整个类 模板化: 将这个对象类型 模板化进行传递

4.5.1 示例

创建一个类模板参数类型为T1与T2

 template<class T1,class T2>
 class person
 {
 public:
   person(T1 name, T2 age)
   {
     m_name = name;
     m_age = age;
   }
   void showinfo()
   {
     cout << this->m_name << this->m_age << endl;
   }
   T1 m_name;
   T2 m_age;
 };
  • 指定传入的类型

 void print1(person<string, int>& p)// 直接把下面的类型拿来使用
 {
   p.showinfo();
 }
 void test01()
 {
   person<string, int> p1("joyce", 21);
   print1(p1);
 }

可见,print1函数参数直接拿p1的数据类型使用,这就是指定传入的类型

img


  • 参数模板化

 void test02()
 {
   person<string, int> p2("tatina", 20);
   print2(p2);
 }

print2函数

 template<class T1, class T2> 
 void print2(person<T1, T2>& p2)// 参树模板化为T1与T2
 {
   p2.showinfo();
   cout << typeid(T1).name() << endl;// 查看T1的类型
   cout << typeid(T2).name() << endl;// 查看T2的类型
 }

就是将参数也模板化为T1T2

同时,如果想查看数据的类型,可以使用typeid().name()函数

img

  • 整个类模板化

同样的测试函数

 void test03()
 {
   person<string, int> p3("yomi", 1);
   print3(p3);
 }

然后是print3,将整个类模板化

 template<class T>
 void print3(T& p)// 直接用T,让编译器推导出类型
 {
   p.showinfo();
 }

看一下T的类型

img

img

4.6 类模板与继承

注意:

  • 当子类继承的父类是一个类模板时,子类在声明时,要指定出父类中T的类型

  • 如果不指定,编译器无法给子类分配内存,因为不知道父类中成员的大小

  • 如果想灵活指出父类中T的类型,子类也需要变成类模板

4.6.1 示例

首先,创建父类base类模板

 template<class T>
 class base
 {
 public:
 ​
   T m_a;
 };
  • 尝试创建子类

 class son :public base
 {
   ;
 };

直接报错:

img

需要指定父类中T的类型

img

这样就可以

不过此时,父类中的T只能是指定的T类型,为了解决这个问题:

  • 将子类也类模板

 template<class T1,class T2>
 class son2 :public base<T2>
 {
   T1 m_a;
 };
 void test02()
 {
   son2<string,int> s2; // 显式指定类型
 }

string就是T1int就是T2

T2就是继承父类base,并指定父类中T的类型为int


接着我们输出T1T2的类型

img

img

4.7 类模板成员函数类外实现

掌握类模板中的成员函数类外实现

4.7.1 示例

首先,我们写一个常规的person类模板,其中有属性m_namem_age,类型分别为T1T2

带有构造函数与void show函数

 template<class T1,class T2>
 class person
 {
 public:
   person(T1 name,T2 age)
   {
     this->m_name = name;
     this->m_age = age;
   }
   void show()
   {
     cout << this->m_name << " " << this->m_age << endl;
   }
   T1 m_name;
   T2 m_age;
 };
4.7.1.1 类模板构造函数类外实现

接下来,我们先将类模板中的构造函数的实现部分屏蔽只留下声明,在类模板外实现构造函数

 template<class T1,class T2>
 person<T1, T2>::person(T1 name, T2 age)
 {
   this->m_name = name;
   this->m_age = age;
 }

即在构造函数person(T1 name, T2 age)前加上作用域person::以及参数列表<T1,T2>

4.7.1.2 类模板成员函数类外实现
 template<class T1,class T2>
 void person<T1,T2>::show() // 虽未用到T1T2,但由于其是类模板中成员函数,因此仍要写入T1/T2
 {
   cout << this->m_name << " " << this->m_age << endl;
 }

一样的加作用域与参数


测试:

 void test01()
 {
   person<string, int> p("joyce",21);
   p.show();
 }

img

4.8 类模板分文件编写

首先,分文件编写类模板时(类模板.h头文件,其中的成员函数在.cpp源文件实现),成员函数创建时机是在调用阶段,导致编写时链接不到

4.8.1 示例

先创建person.h头文件,其中写入person类模板

 template<class T1, class T2>
 class person
 {
 public:
   person(T1 name, T2 age);
 ​
   void show();
 ​
   T1 m_name;
   T2 m_age;
 };

然后创建person.cpp源文件,其中实现person的函数,并包含.h头文件

 // 构造函数
 template<class T1, class T2>
 person<T1, T2>::person(T1 name, T2 age)
 {
   this->m_name = name;
   this->m_age = age;
 }
 // 成员函数
 template<class T1, class T2>
 void person<T1, T2>::show()
 {
   cout << this->m_name << " " << this->m_age << endl;
 }

到目前为止,代码运行没有任何问题

img

但是,我们在测试函数中调用构造函数与成员函数

 #include"person.h"
 ​
 void test01()
 {
   person<string, int> p("joyce",21);
   p.show();
 }

img

直接报错 ,因为编译器无法链接类模板成员函数的实现cpp文件

4.8.1.1 直接包含.cpp文件

img

我们将源文件包含的person.h文件改为person.cpp文件,这样编译器直接链接到了cpp文件与h文件

img

成功实现

4.8.1.2 将声明与实现写在一个hpp文件中

约定俗成名为hpp,并非必须

img

上面是声明,下面是类外实现

img

在源文件中包含

img

成功运行

4.9 类模板与友元

2种实现:

  • 全局函数类内实现:直接在类内声明友元即可

  • 全局函数类外实现:先提前让编译器知道全局函数的存在

4.9.1 示例

首先,写一个person类模板,其中有属性m_agem_name,以及构造函数person

 template<class T1, class T2>
 class person
 {
 public:
   person(T1 name, T2 age)
   {
     this->m_name = name;
     this->m_age = age;
   }
 ​
 private:
   T1 m_name;
   T2 m_age;
 };
4.9.1.1 全局函数类内实现

接下来,在其中加上友元的全局函数

   // 全局函数 类内实现
   friend void print1(person <T1, T2>p)
   {
     cout << p.m_name << " " << p.m_age << endl;
   }

测试:

 void test01()
 {
   person<string,int> p("joyce", 21);
   print1(p);
 }

img

4.9.1.2 全局函数类外实现

首先在类内写上声明

img

然后在类外实现

 template<class T1,class T2>
 void print2(person <T1, T2>p) // 因为有T1/T2,因此需要加上类模板
 {
   cout << p.m_name << " " << p.m_age << endl;
 }

测试:

 void test02()
 {
   person<string, int> p("joyce", 21);
   print2(p);
 }

img

依然是链接错误,因为类模板内我们写的是普通函数的声明,而下面实现写的是函数模板的实现,二者毫不相干


4.9.1.3 解决
  • 我们在声明的函数名后加上<>空参数列表

此时,还未完成,仍然无法使用,紧接着:

  • 先将类外实现部分移动到类模板的最上方

img

  • 然后在类外实现部分的上面加上类模板的声明

img

这样下来,才能正确运行

img

4.10 类模板案例

实现一个通用的数组类,要求:

  • 可以对内置数据类型以及自定义数据类型进行存储

  • 将数组中的数据存储到堆区

  • 构造函数中要传入数组的容量

  • 提供拷贝构造函数以及operator=,以防止浅拷贝问题

  • 提供尾插法与尾删法对数组中数据进行增加与删除

  • 通过下标的方式访问数组中的元素

  • 获取当前数组中元素的个数以及数组的容量


首先,创建my_array类模板,包含属性p_array存储开辟的数组,m_capacity数组容量,m_size数组当前大小

 template<class T>
 class my_array
 {
 public:
 ​
   // 防止浅拷贝问题的拷贝构造函数与operator=
   my_array(int capacity);// 有参构造函数
 ​
   my_array(const my_array &p);// 拷贝构造函数
 ​
   my_array& operator=(const my_array& p); // 复制运算符的重载,返回引用才是返回自身
 ​
   void push_back();// 尾插
 ​
   void pop_back();  // 尾删
 ​
   int get_capacity();// 获取容量
 ​
   int get_size();    // 获取大小
 ​
   ~my_array();// 析构函数
 ​
 private:
   T* p_array;    // 存储开辟的数组
   int m_capacity;// 数组的容量
   int m_size;    // 数组当前大小
 };

然后开始实现各个函数

4.10.1 有参构造函数

   my_array(int capacity)// 有参构造函数
   {
     this->m_capacity = capacity;
     this->m_size = 0;
     this->p_array = new T[this->m_capacity]; // 开辟堆区空间以存储p_array
     cout << "有参构造函数" << endl;
   }

4.10.2 拷贝构造函数

   my_array(const my_array &p)// 拷贝构造函数
   {
     this->m_capacity = p.m_capacity; 
     this->m_size = p.m_size;
     //this->p_array = p.p_array;// 带来浅拷贝问题
     this->p_array = new T(p.m_capacity);// 根据p的大小重新开辟空间以赋值
     // 将p中的数据也拷贝过来
     for (int i = 0; i < p.m_size; i++)
     {
       this->p_array[i] = p.p_array[i];
     }
     cout << "拷贝构造函数" << endl;
   }

4.10.3 operetor=等号重载

   my_array& operator=(const my_array& p) // 赋值运算符的重载,返回引用才是返回自身
   {
     if (this->p_array != NULL) // 判断是否有属性在堆区,有则释放
     {
       delete[] this->p_array;
       this->p_array = NULL;
       this->m_capacity = 0;
       this->m_size = 0;
     }
     // 深拷贝
     this->m_capacity = p.m_capacity;
     this->m_size = p.m_size;
     this->p_array = new T[p.m_capacity]; // 重新开辟空间
     for (int i = 0; i < p.m_size; i++) // 数据拷贝
     {
       this->p_array[i] = p.p_array[i];
     }
     cout << "operator=" << endl;
     return *this;
   }

4.10.4 析构函数

   ~my_array()// 析构函数
   {
     if (this->p_array != NULL)
     {
       delete[] this->p_array;
       this->p_array = NULL;
     }
     cout << "析构函数" << endl;
   }

此时,我们先运行一下各个函数

 #include"my_array.hpp"
 int main()
 {
   my_array<int> arr1(5); // 有参构造
   my_array<int> arr2(arr1);// 拷贝构造
   my_array<int> arr3(100); 
   arr1 = arr3; // 自定义类型=赋值
 return0;
 }

img

4.10.5 尾插 数据

首先,判断数组当前元素个数size是否等于容量capacity,如果相等则数组满了,直接return

否则,直接将传入的数据赋给数组第size个元素,arr[size],因为当前个数为size个,数组下标为0-size-1的元素都有了,下一个为空的就是第size个,因此赋给第size

   void push_back(const T& val)// 尾插
   {
     // 先判断数组是否满
     if (this->m_size == this->m_capacity)
     {
       return;
   /*    int new_capacity = m_capacity + 1;
       this->p_array = new T(new_capacity);*/
     }
     this->p_array[this->m_size ] = val; // 尾插数据
     this->m_size++; // 当前大小+1
   }

4.10.6 尾删 数据

使用户访问不到最后一个元素即可,即 使size-1,这样数组元素就少了一个

   void pop_back()  // 尾删
   {
     if (this->m_size == 0)
     {
       cout << "数组为空!" << endl;
       return;
     }
     this->m_size--;
   }

4.10.7 以下标方式访问数组元素

由于my_Array是我们自己创建的数组类型,其中的元素数据类型都是T,因此我们不能直接用[]访问到元素,需要我们重载[]运算符


我们使用[]下标访问元素是为了获取一个数据,所以我们重载就直接返回一个元素的数据就行

   T& operator[](int index)
   {
     return this->p_array[index];
   }

返回值类型就是我们数组my_array中的数据类型T,同时保证返回前后是同一个元素我们用&

4.10.8 获取大小与获取容量

直接返回sizecapacity即可

   int get_capacity() // 获取容量
   {
     return this->m_capacity;
   }
 ​
   int get_size()     // 获取大小
   {
     return this->m_size;
   }

4.10.9测试

  • 我们创建数组,并打印输出

 template<class T>
 void print(T&arr) // 打印函数
 {
   for (int i = 0; i < 5; i++)
   {
     cout << arr[i] << " ";
   }
 }
 int main()
 {
   my_array<int> arr1(5); // 有参构造
   for (int i = 0; i < 5; i++)
   {
     arr1.push_back(i);
   }
   print(arr1);
 ​
   return 0;
 }

img

成功输出范围内的数据

  • 我们再看一下我们创建数组的容量和大小

   cout << "capacity" << arr1.get_capacity() << endl;
   cout << "size" << arr1.get_size() << endl;

img

  • 我们拷贝arr1构造arr2,并尾删数据,再打印输出

 void test01()
 {
   my_array<int> arr1(5); // 有参构造
 ​
   for (int i = 0; i < 5; i++)
   {
     arr1.push_back(i);
   }
   print(arr1);
   cout << "capacity" << arr1.get_capacity() << endl;
   cout << "size" << arr1.get_size() << endl;
 ​
   my_array<int> arr2(arr1);// 拷贝构造
   print(arr2);
   arr2.pop_back();
   print(arr2);
   cout << "capacity" << arr2.get_capacity() << endl;
   cout << "size" << arr2.get_size() << endl;
 }

img

  • 我们创建自定义数据类型,然后使用尾插尾删等的函数

先创建自定义数据类型person

 class person
 {
 public:
   person() {};
   person( string name,int age )
   {
     this->m_age = age;
     this->m_name = name;
   }
   int m_age;
   string m_name;
 };

创建打印函数print2(以我们自定义数据类型为准来创建)

 void print2(my_array<person>& arr)
 {
   for (int i = 0; i < arr.get_size(); i++)
   {
     cout << arr[i].m_name <<arr[i].m_age<<endl;
   }
   cout << endl;
 }

创建数组arr并初始化,先输出每个元素的信息与大小容量

然后尾删2个元素后,再次输出每个元素的信息与大小容量

 void test02()
 {
   // 创建数组并初始化
   my_array<person>arr(10);
   person a1("joyce", 21);
   person a2("tatina", 20);
   person a3("knaz", 40);
   person a4("nana", 20);
   person a5("yomi", 1);
   // 尾插到数组中
   arr.push_back(a1);
   arr.push_back(a2);
   arr.push_back(a3);
   arr.push_back(a4);
   arr.push_back(a5);
   // 打印数组中的每个元素的数据与大小容量
   print2(arr);
   cout << "capacity" << arr.get_capacity() << endl;
   cout << "size" << arr.get_size() << endl;
   // -----------------------------------------------------
   // 尾删2个元素
   arr.pop_back();
   arr.pop_back();
   // 打印数组中的每个元素的数据与大小容量
   print2(arr);
   cout << "capacity" << arr.get_capacity() << endl;
   cout << "size" << arr.get_size() << endl;
 }

img

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值