• [黑马程序员C++笔记]P174-P184模板-类模板


    视频地址:黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili

    目录

    P174模板-类模板基本语法

    P175模板-类模板和函数模板区别

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

    P177模板-类模板对象做函数参数

    P178模板-类模板与继承

    P179模板-类模板成员函数类外实现

    P180模板-类模板分文件编写

    P181模板-类模板与友元

    P182-P184模板-类模板案例


    P174模板-类模板基本语法

     类模板的作用:

    建立一个通用类,类中的成员数据类型可以不具体确定,用一个虚拟的类型来代表

    语法:

    1. template<typename T>

    解释:

    template --- 声明创建模板

    typename --- 表面其后面的符号是一种数据类型,可以用class代替

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

    示例:

    1. using namespace std;
    2. #include<iostream>
    3. #include<string>
    4. //类模板
    5. template <class NameType,class AgeType>
    6. class Person {
    7. public:
    8. Person(NameType name, AgeType age) {
    9. this->m_name = name;
    10. this->m_age = age;
    11. }
    12. void showPerson()
    13. {
    14. cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    15. }
    16. public:
    17. NameType m_name;
    18. AgeType m_age;
    19. };
    20. void test01() {
    21. Person <string, int > P1("孙悟空", 10000);
    22. P1.showPerson();
    23. }
    24. int main()
    25. {
    26. test01();
    27. system("pause");
    28. return 0;
    29. }

    结果:

    总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板

    P175模板-类模板和函数模板区别

    类模板与函数模板区别主要有两点:

    1、类模板没有自动类型推导的使用方式

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

    示例:

    1. #include <string>
    2. //类模板
    3. template<class NameType, class AgeType = int>
    4. class Person
    5. {
    6. public:
    7. Person(NameType name, AgeType age)
    8. {
    9. this->mName = name;
    10. this->mAge = age;
    11. }
    12. void showPerson()
    13. {
    14. cout << "name: " << this->mName << " age: " << this->mAge << endl;
    15. }
    16. public:
    17. NameType mName;
    18. AgeType mAge;
    19. };
    20. //1、类模板没有自动类型推导的使用方式
    21. void test01()
    22. {
    23. // Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
    24. Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
    25. p.showPerson();
    26. }
    27. //2、类模板在模板参数列表中可以有默认参数
    28. void test02()
    29. {
    30. Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数
    31. p.showPerson();
    32. }
    33. int main() {
    34. test01();
    35. test02();
    36. system("pause");
    37. return 0;
    38. }

    总结:

    • 类模板使用只能用显示指定类型方式

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

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

    总结:

    • 类模板使用只能用显示指定类型方式

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

    示例:

    1. class Person1
    2. {
    3. public:
    4. void showPerson1()
    5. {
    6. cout << "Person1 show" << endl;
    7. }
    8. };
    9. class Person2
    10. {
    11. public:
    12. void showPerson2()
    13. {
    14. cout << "Person2 show" << endl;
    15. }
    16. };
    17. template<class T>
    18. class MyClass
    19. {
    20. public:
    21. T obj;
    22. //类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
    23. void fun1() { obj.showPerson1(); }
    24. void fun2() { obj.showPerson2(); }
    25. };
    26. void test01()
    27. {
    28. MyClass<Person1> m;
    29. m.fun1();
    30. //m.fun2();//编译会出错,说明函数调用才会去创建成员函数
    31. }
    32. int main() {
    33. test01();
    34. system("pause");
    35. return 0;
    36. }

    总结:类模板中的成员函数并不是一开始就创建的,在调用时才去创建

    P177模板-类模板对象做函数参数

    学习目标:类模板实例化出的对象,向函数传参的方式

    一共有三种传入方式:

    1、指定传入的类型 --直接显示对象的数据类型

    2、参数模板化        --将对象中的参数变为模板进行传递

    3、整个类模板化    --将这个对象类型模板化进行传递

    示例:

    1. using namespace std;
    2. #include<iostream>
    3. #include<string>
    4. //类模板对象做函数参数
    5. //1
    6. template <class T1,class T2>
    7. class Person {
    8. public:
    9. Person(T1 name, T2 age) {
    10. this->m_Age = age;
    11. this->m_Name = name;
    12. }
    13. void showPerson() {
    14. cout << "name:" << this->m_Name << "age:" << this->m_Age << endl;
    15. }
    16. public:
    17. T1 m_Name;
    18. T2 m_Age;
    19. };
    20. //1、指定传入类型
    21. void printPerson1(Person<string, int>& p) {
    22. p.showPerson();
    23. }
    24. void test01() {
    25. Person<string, int>p("孙悟空", 100);
    26. printPerson1(p);
    27. }
    28. //2、参数模板化
    29. template <class T1,class T2>
    30. void printPerson2(Person<T1, T2>& p) {
    31. p.showPerson();
    32. cout << "T1的类型为: " << typeid(T1).name() << endl;
    33. cout << "T2的类型为: " << typeid(T2).name() << endl;
    34. }
    35. void test02() {
    36. Person<string, int>p("猪八戒", 90);
    37. printPerson2(p);
    38. }
    39. //3、整个类模板化
    40. template<class T>
    41. void printPerson3(T& p) {
    42. cout << "T的类型为: " << typeid(T).name() << endl;
    43. p.showPerson();
    44. }
    45. void test03()
    46. {
    47. Person <string, int >p("唐僧", 30);
    48. printPerson3(p);
    49. }
    50. int main()
    51. {
    52. test01();
    53. test02();
    54. test03();
    55. system("pause");
    56. return 0;
    57. }

    结果:

     总结:

    • 通过类模板创建的对象,可以有三种方式向函数中进行传参

    • 使用比较广泛是第一种:指定传入的类型

    P178模板-类模板与继承

    当类模板碰到继承时,需要注意一下几点:

    • 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
    • 如果不指定,编译器无法给子类分配内存
    • 如果想灵活指定出父类中T的类型,子类也需变为类模板

    示例:

    1. template<class T>
    2. class Base
    3. {
    4. T m;
    5. };
    6. //class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
    7. class Son :public Base<int> //必须指定一个类型
    8. {
    9. };
    10. void test01()
    11. {
    12. Son c;
    13. }
    14. //类模板继承类模板 ,可以用T2指定父类中的T类型
    15. template<class T1, class T2>
    16. class Son2 :public Base<T2>
    17. {
    18. public:
    19. Son2()
    20. {
    21. cout << typeid(T1).name() << endl;
    22. cout << typeid(T2).name() << endl;
    23. }
    24. };
    25. void test02()
    26. {
    27. Son2<int, char> child1;
    28. }
    29. int main() {
    30. test01();
    31. test02();
    32. system("pause");
    33. return 0;
    34. }

    总结:如果父类是类模板,子类需要指定出父类中T的数据类型

    P179模板-类模板成员函数类外实现

    示例:

    1. #include <string>
    2. //类模板中成员函数类外实现
    3. template<class T1, class T2>
    4. class Person {
    5. public:
    6. //成员函数类内声明
    7. Person(T1 name, T2 age);
    8. void showPerson();
    9. public:
    10. T1 m_Name;
    11. T2 m_Age;
    12. };
    13. //构造函数 类外实现
    14. template<class T1, class T2>
    15. Person<T1, T2>::Person(T1 name, T2 age) {
    16. this->m_Name = name;
    17. this->m_Age = age;
    18. }
    19. //成员函数 类外实现
    20. template<class T1, class T2>
    21. void Person<T1, T2>::showPerson() {
    22. cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
    23. }
    24. void test01()
    25. {
    26. Person<string, int> p("Tom", 20);
    27. p.showPerson();
    28. }
    29. int main() {
    30. test01();
    31. system("pause");
    32. return 0;
    33. }

    总结:类模板中成员函数类外实现时,需要加上模板参数列表

    P180模板-类模板分文件编写

    问题:

    • 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

    解决:

    • 解决方式1:直接包含.cpp源文件

    • 解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

    示例:

    person.hpp中代码:

    1. #pragma once
    2. #include <iostream>
    3. using namespace std;
    4. #include <string>
    5. template<class T1, class T2>
    6. class Person {
    7. public:
    8. Person(T1 name, T2 age);
    9. void showPerson();
    10. public:
    11. T1 m_Name;
    12. T2 m_Age;
    13. };
    14. //构造函数 类外实现
    15. template<class T1, class T2>
    16. Person<T1, T2>::Person(T1 name, T2 age) {
    17. this->m_Name = name;
    18. this->m_Age = age;
    19. }
    20. //成员函数 类外实现
    21. template<class T1, class T2>
    22. void Person<T1, T2>::showPerson() {
    23. cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
    24. }

    类模板分文件编写.cpp中代码:
     

    1. #include<iostream>
    2. using namespace std;
    3. //#include "person.h"
    4. #include "person.cpp" //解决方式1,包含cpp源文件
    5. //解决方式2,将声明和实现写到一起,文件后缀名改为.hpp
    6. #include "person.hpp"
    7. void test01()
    8. {
    9. Person<string, int> p("Tom", 10);
    10. p.showPerson();
    11. }
    12. int main() {
    13. test01();
    14. system("pause");
    15. return 0;
    16. }

    总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp

    P181模板-类模板与友元

    学习目标:

    • 掌握类模板配合友元函数的类内和类外实现

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

    全局函数类外实现 - 需要提前让编译器知道全局函数的存在

    1. #include <string>
    2. //2、全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
    3. template<class T1, class T2> class Person;
    4. //如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
    5. //template<class T1, class T2> void printPerson2(Person<T1, T2> & p);
    6. template<class T1, class T2>
    7. void printPerson2(Person<T1, T2> & p)
    8. {
    9. cout << "类外实现 ---- 姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
    10. }
    11. template<class T1, class T2>
    12. class Person
    13. {
    14. //1、全局函数配合友元 类内实现
    15. friend void printPerson(Person<T1, T2> & p)
    16. {
    17. cout << "姓名: " << p.m_Name << " 年龄:" << p.m_Age << endl;
    18. }
    19. //全局函数配合友元 类外实现
    20. friend void printPerson2<>(Person<T1, T2> & p);
    21. public:
    22. Person(T1 name, T2 age)
    23. {
    24. this->m_Name = name;
    25. this->m_Age = age;
    26. }
    27. private:
    28. T1 m_Name;
    29. T2 m_Age;
    30. };
    31. //1、全局函数在类内实现
    32. void test01()
    33. {
    34. Person <string, int >p("Tom", 20);
    35. printPerson(p);
    36. }
    37. //2、全局函数在类外实现
    38. void test02()
    39. {
    40. Person <string, int >p("Jerry", 30);
    41. printPerson2(p);
    42. }
    43. int main() {
    44. //test01();
    45. test02();
    46. system("pause");
    47. return 0;
    48. }

    P182-P184模板-类模板案例

    案例描述: 实现一个通用的数组类,要求如下:

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

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

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

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

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

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

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

    示例:

    myArray.hpp中代码

    1. #pragma once
    2. #include <iostream>
    3. using namespace std;
    4. template<class T>
    5. class MyArray
    6. {
    7. public:
    8. //构造函数
    9. MyArray(int capacity)
    10. {
    11. this->m_Capacity = capacity;
    12. this->m_Size = 0;
    13. pAddress = new T[this->m_Capacity];
    14. }
    15. //拷贝构造
    16. MyArray(const MyArray & arr)
    17. {
    18. this->m_Capacity = arr.m_Capacity;
    19. this->m_Size = arr.m_Size;
    20. this->pAddress = new T[this->m_Capacity];
    21. for (int i = 0; i < this->m_Size; i++)
    22. {
    23. //如果T为对象,而且还包含指针,必须需要重载 = 操作符,因为这个等号不是 构造 而是赋值,
    24. // 普通类型可以直接= 但是指针类型需要深拷贝
    25. this->pAddress[i] = arr.pAddress[i];
    26. }
    27. }
    28. //重载= 操作符 防止浅拷贝问题
    29. MyArray& operator=(const MyArray& myarray) {
    30. if (this->pAddress != NULL) {
    31. delete[] this->pAddress;
    32. this->m_Capacity = 0;
    33. this->m_Size = 0;
    34. }
    35. this->m_Capacity = myarray.m_Capacity;
    36. this->m_Size = myarray.m_Size;
    37. this->pAddress = new T[this->m_Capacity];
    38. for (int i = 0; i < this->m_Size; i++) {
    39. this->pAddress[i] = myarray[i];
    40. }
    41. return *this;
    42. }
    43. //重载[] 操作符 arr[0]
    44. T& operator [](int index)
    45. {
    46. return this->pAddress[index]; //不考虑越界,用户自己去处理
    47. }
    48. //尾插法
    49. void Push_back(const T & val)
    50. {
    51. if (this->m_Capacity == this->m_Size)
    52. {
    53. return;
    54. }
    55. this->pAddress[this->m_Size] = val;
    56. this->m_Size++;
    57. }
    58. //尾删法
    59. void Pop_back()
    60. {
    61. if (this->m_Size == 0)
    62. {
    63. return;
    64. }
    65. this->m_Size--;
    66. }
    67. //获取数组容量
    68. int getCapacity()
    69. {
    70. return this->m_Capacity;
    71. }
    72. //获取数组大小
    73. int getSize()
    74. {
    75. return this->m_Size;
    76. }
    77. //析构
    78. ~MyArray()
    79. {
    80. if (this->pAddress != NULL)
    81. {
    82. delete[] this->pAddress;
    83. this->pAddress = NULL;
    84. this->m_Capacity = 0;
    85. this->m_Size = 0;
    86. }
    87. }
    88. private:
    89. T * pAddress; //指向一个堆空间,这个空间存储真正的数据
    90. int m_Capacity; //容量
    91. int m_Size; // 大小
    92. };

    类模板案例—数组类封装.cpp中

    1. #include "myArray.hpp"
    2. #include <string>
    3. void printIntArray(MyArray<int>& arr) {
    4. for (int i = 0; i < arr.getSize(); i++) {
    5. cout << arr[i] << " ";
    6. }
    7. cout << endl;
    8. }
    9. //测试内置数据类型
    10. void test01()
    11. {
    12. MyArray<int> array1(10);
    13. for (int i = 0; i < 10; i++)
    14. {
    15. array1.Push_back(i);
    16. }
    17. cout << "array1打印输出:" << endl;
    18. printIntArray(array1);
    19. cout << "array1的大小:" << array1.getSize() << endl;
    20. cout << "array1的容量:" << array1.getCapacity() << endl;
    21. cout << "--------------------------" << endl;
    22. MyArray<int> array2(array1);
    23. array2.Pop_back();
    24. cout << "array2打印输出:" << endl;
    25. printIntArray(array2);
    26. cout << "array2的大小:" << array2.getSize() << endl;
    27. cout << "array2的容量:" << array2.getCapacity() << endl;
    28. }
    29. //测试自定义数据类型
    30. class Person {
    31. public:
    32. Person() {}
    33. Person(string name, int age) {
    34. this->m_Name = name;
    35. this->m_Age = age;
    36. }
    37. public:
    38. string m_Name;
    39. int m_Age;
    40. };
    41. void printPersonArray(MyArray<Person>& personArr)
    42. {
    43. for (int i = 0; i < personArr.getSize(); i++) {
    44. cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl;
    45. }
    46. }
    47. void test02()
    48. {
    49. //创建数组
    50. MyArray<Person> pArray(10);
    51. Person p1("孙悟空", 30);
    52. Person p2("韩信", 20);
    53. Person p3("妲己", 18);
    54. Person p4("王昭君", 15);
    55. Person p5("赵云", 24);
    56. //插入数据
    57. pArray.Push_back(p1);
    58. pArray.Push_back(p2);
    59. pArray.Push_back(p3);
    60. pArray.Push_back(p4);
    61. pArray.Push_back(p5);
    62. printPersonArray(pArray);
    63. cout << "pArray的大小:" << pArray.getSize() << endl;
    64. cout << "pArray的容量:" << pArray.getCapacity() << endl;
    65. }
    66. int main() {
    67. //test01();
    68. test02();
    69. system("pause");
    70. return 0;
    71. }

    总结:

    能够利用所学知识点实现通用的数组

  • 相关阅读:
    FTP主动模式和被动模式(2)- 防火墙对FTP的影响 ASPF
    STM32存储左右互搏 I2C总线读写FRAM MB85RC16
    R语言使用epiDisplay包的aggregate函数将数值变量基于因子变量拆分为不同的子集,计算每个子集的汇总统计信息、计算单个连续变量的分组汇总统计信息
    Python 绘制反高斯光束光强分布
    FGH40N60SMD安森美车规IGBT,ASEMI原厂代理FGH40N60SMD
    三、创建各个展示模块组件
    egui常用组件
    Flink 实时数仓(一)【实时数仓&离线数仓对比】
    [附源码]计算机毕业设计JAVA龙虎时代健身房管理系统
    web前端面试-- js深拷贝的一些bug,特殊对象属性(RegExp,Date,Error,Symbol,Function)处理,循环引用weekmap处理
  • 原文地址:https://blog.csdn.net/weixin_45922730/article/details/128177230