• C++中的模板


    模版就是建立一个通用的模具,大大提高复用性。

    1、函数模板

    函数模板的作用:建立一个通用的函数,其函数返回值类型和形参列表可以不具体确定,用一个虚拟的类型来代表。

    目的:将类型参数化。

    语法:

    template<typename T> //函数的声明或定义

    template:声明创建模板

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

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

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

    模板并不是万能的。

    例一:交换两个数据,根据类型分别写对应函数。

    1. #include<iostream>
    2. using namespace std;
    3. //交换两个整形数据
    4. void swapInt(int& a, int& b)
    5. {
    6. int temp = a;
    7. a = b;
    8. b = temp;
    9. }
    10. //交换两个浮点型数据
    11. void swapDouble(double& a, double& b)
    12. {
    13. double temp = a;
    14. a = b;
    15. b = temp;
    16. }
    17. void test01()
    18. {
    19. int a = 10;
    20. int b = 20;
    21. swapInt(a, b);
    22. cout << a << endl;
    23. cout << b << endl;
    24. double c = 1.1;
    25. double d = 2.2;
    26. swapDouble(c,d);
    27. cout << c << endl;
    28. cout << d << endl;
    29. }
    30. int main()
    31. {
    32. test01();
    33. }

    例二:使用函数模板完成任意类型数据的相加

    1. #include<iostream>
    2. using namespace std;
    3. //交换两个T型数据
    4. template<typename T>
    5. void Myswap(T& a, T&b)
    6. {
    7. T temp = a;
    8. a = b;
    9. b = temp;
    10. }
    11. void test01()
    12. {
    13. int a = 10;
    14. int b = 20;
    15. //1、自动类型推导
    16. Myswap(a, b);
    17. //2、显示指定类型
    18. //Myswap<int>(a,b);
    19. cout << a << endl;
    20. cout << b << endl;
    21. }
    22. int main()
    23. {
    24. test01();
    25. }

    注意:模板必须确定T的数据类型,才可以使用。且数据类型必须一致。例如上例中将整形和字符型进行交换,数据类型不同将会报错。

    例:利用函数模板对不同数据类型的数组进行从大到小的选择排序。

    1. #include<iostream>
    2. using namespace std;
    3. //交换两个T型数据
    4. template<typename T>
    5. void mySwap(T&a,T&b)
    6. {
    7. T temp = a;
    8. a = b;
    9. b = temp;
    10. }
    11. template<typename T>
    12. void printArr(T arr[], int len)
    13. {
    14. for (int i = 0; i < len; i++)
    15. {
    16. cout << arr[i] << " ";
    17. }
    18. cout << endl;
    19. }
    20. template<typename T>
    21. void Mysort(T arr[], int len)
    22. {
    23. for (int i = 0; i < len; i++)
    24. {
    25. int max = i;
    26. for (int j = i + 1; j < len; j++)
    27. {
    28. if (arr[max] < arr[j])
    29. {
    30. max = j;
    31. }
    32. }
    33. if (max != i)
    34. {
    35. mySwap(arr[max], arr[i]);
    36. }
    37. }
    38. }
    39. void test01()
    40. {
    41. char charArr[] = "badcfe"; //测试char数组
    42. int num = sizeof(charArr) / sizeof(char);
    43. Mysort(charArr, num);
    44. printArr(charArr, num);
    45. }
    46. int main()
    47. {
    48. test01();
    49. }

    2、类模板

    作用:建立一个通用的类,类中的成员函数和数据类型可以不具体指定,用一个虚拟的类型代表。

    语法:

    Template<typename T>

    template<typename T> //声明创建模板

    template:声明创建模板

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

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

    类模板的传入参数也需要在确定对象时也需要初始化。

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class NameType, class AgeType>
    5. class Person
    6. {
    7. public:
    8. Person(NameType name, AgeType age)
    9. {
    10. this->m_Name = name;
    11. this->m_Age = age;
    12. }
    13. void showPerson()
    14. {
    15. cout << "Name: " << this->m_Name << endl;
    16. cout << "Age: " << this->m_Age << endl;
    17. }
    18. NameType m_Name;
    19. AgeType m_Age;
    20. };
    21. void test01()
    22. {
    23. Person<string, int> p1("wxq", 29);
    24. //<>模版的参数列表
    25. p1.showPerson();
    26. }
    27. int main()
    28. {
    29. test01();
    30. }

    2.1、类模板和函数模板的区别

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

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class NameType, class AgeType>
    5. class Person
    6. {
    7. public:
    8. Person(NameType name, AgeType age)
    9. {
    10. this->m_Name = name;
    11. this->m_Age = age;
    12. }
    13. void showPerson()
    14. {
    15. cout << "Name: " << this->m_Name << endl;
    16. cout << "Age: " << this->m_Age << endl;
    17. }
    18. NameType m_Name;
    19. AgeType m_Age;
    20. };
    21. //类模板没有自动推导类型
    22. void test01()
    23. {
    24. //Person p1("wxq", 29);
    25. //错误,无法用自动类型推导
    26. Person<string, int> p2("wxq", 29);
    27. //只能用显示指定类型
    28. p2.showPerson();
    29. }
    30. int main()
    31. {
    32. test01();
    33. }

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

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class NameType, class AgeType=int>
    5. class Person
    6. {
    7. public:
    8. Person(NameType name, AgeType age)
    9. {
    10. this->m_Name = name;
    11. this->m_Age = age;
    12. }
    13. void showPerson()
    14. {
    15. cout << "Name: " << this->m_Name << endl;
    16. cout << "Age: " << this->m_Age << endl;
    17. }
    18. NameType m_Name;
    19. AgeType m_Age;
    20. };
    21. //类模板在模板参数列表中可以有默认参数
    22. void test01()
    23. {
    24. Person<string> p2("wxq", 29);
    25. //只能用显示指定类型
    26. p2.showPerson();
    27. }
    28. int main()
    29. {
    30. test01();
    31. }

    类模板中的成员函数

    类模板中的成员函数和普通类中的成员函数的创建时机是不同的:普通类的成员函数一开始就可以创建成员函数;类模板中的成员函数在调用时创建。

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. class Person1
    5. {
    6. public:
    7. void showPerson1()
    8. {
    9. cout << "Person1 shows!" << endl;
    10. }
    11. };
    12. class Person2
    13. {
    14. public:
    15. void showPerson2()
    16. {
    17. cout << "Person2 shows!" << endl;
    18. }
    19. };
    20. template<class T>
    21. class Person
    22. {
    23. public:
    24. T obj;
    25. void func1()
    26. {
    27. obj.showPerson1();
    28. }
    29. void func2()
    30. {
    31. obj.showPerson2();
    32. }
    33. };
    34. void test01()
    35. {
    36. Person<Person1> m;
    37. m.func1();
    38. //m.func2(); //报错,因为m中无showPerson2函数
    39. }
    40. int main()
    41. {
    42. test01();
    43. }

    类模板对象做函数参数

    类模板实例出的对象作为函数的实参,一共有三种传入方式:

    1、指定类型的传入:直接显示对象的数据类型(主要使用)

    2、参数模板化:将对象中的参数变为模板进行传递(类模板配合函数模版)

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

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

    类模板与继承

    需要注意几个点:

    1、当子类继承的父类是一个类模板时,子类在声明时需要指定父类中T的类型;如果不指定,编译器则无法给子类分配内存。

    2、如果想灵活指定父类中的T的类型,子类也需要变为类模板。

    如果父类是类模板,子类在继承时需要指定父类中的数据类型。

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class T>
    5. class Base
    6. {
    7. T m;
    8. };
    9. //class Son :public Base //错误,必须知道父类的T的数据类型,才能继承给子类
    10. //class Son :public Base<int> //正确,但是子类只能为int
    11. template<class T1, class T2>
    12. class Son :public Base<T2>
    13. {
    14. T1 obj;
    15. };
    16. void test01()
    17. {
    18. Son<int,char> s1; //int传给T1,char传给T2,父类中的T也为char
    19. }
    20. int main()
    21. {
    22. test01();
    23. }

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

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class T1, class T2>
    5. class Person
    6. {
    7. public:
    8. Person(T1 name, T2 age);
    9. void show();
    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. {
    17. this->m_Name = name;
    18. this->m_Age = age;
    19. }
    20. //成员函数的类外实现
    21. template<class T1, class T2>//告诉编译器这是一个类模板的成员函数
    22. void Person<T1,T2>::show()
    23. {
    24. cout << "Name is " << this->m_Name << endl;
    25. cout << "Age is " << this->m_Age << endl;
    26. }
    27. void test01()
    28. {
    29. Person<string, int> p("Wang", 100);
    30. p.show();
    31. }
    32. int main()
    33. {
    34. test01();
    35. }

    类模板分文件编写

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

    方法:

    1、直接包含CPP源文件

    2、将声明个实现写在同一个文件中,并更改后缀名为.hpp。hpp是一个约定的概念,表示的就是类模板的分文件编写。(用的多)

    方法1:

    Person.h

    Person.cpp

    Main.cpp

    #pragma once

    #include<iostream>

    #include"string"

    using namespace std;

    template<class T1, class T2>

    class Person

    {

    public:

             Person(T1 name, T2 age);

             void show();

             T1 m_Name;

             T2 m_Age;

    };

    #include"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 << "Name is " << this->m_Name << endl;

             cout << "Age is " << this->m_Age << endl;

    }

    #include"Person.cpp"  //包含的是cpp,不是h

    //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件

    void test01()

    {

             Person<string, int> p("Wang", 100);

             p.show();

    }

    int main()

    {

             test01();

    }

    方法2

    Person.hpp

    Main.cpp

    #pragma once

    #include<iostream>

    #include"string"

    using namespace std;

    template<class T1, class T2>

    class Person

    {

    public:

             Person(T1 name, T2 age);

             void show();

             T1 m_Name;

             T2 m_Age;

    };

    //构造函数的类外实现

    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 << "Name is " << this->m_Name << endl;

             cout << "Age is " << this->m_Age << endl;

    }

    #include"Person.hpp"  //包含的是cpp,不是h

    //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件

    void test01()

    {

             Person<string, int> p("Wang", 100);

             p.show();

    }

    int main()

    {

             test01();

    }

    类模板与友元

    主要包括友元函数的类内实现和类外实现。类内直接声明友元即可,类外实现则需要提前让编译器指导全局函数的存在。

    case 1:友元函数在类内实现

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. template<class T1,class T2>
    5. class Person
    6. {
    7. public:
    8. Person(T1 name, T2 age)
    9. {
    10. this->m_Name = name;
    11. this->m_Age = age;
    12. }
    13. friend void show(Person<T1, T2> p)
    14. {
    15. cout << "Name is " <<p.m_Name << endl;
    16. cout << "Age is " << p.m_Age << endl;
    17. }
    18. private:
    19. T1 m_Name;
    20. T2 m_Age;
    21. };
    22. //1、全局函数在类内实现
    23. void test01()
    24. {
    25. Person<string, int> p("Wang", 100);
    26. show(p);
    27. }
    28. int main()
    29. {
    30. test01();
    31. }

    case 2:友元函数在类外实现

    1. #include<iostream>
    2. #include"string"
    3. using namespace std;
    4. //提前让编译器知道person类的存在
    5. template<class T1, class T2>
    6. class Person;
    7. //2、全局函数类外实现,先让编译器知道有个类外函数的存在
    8. template<class T1, class T2>
    9. void show(Person<T1, T2> p)
    10. {
    11. cout << "Name is " << p.m_Name << endl;
    12. cout << "Age is " << p.m_Age << endl;
    13. }
    14. template<class T1,class T2>
    15. class Person
    16. {
    17. public:
    18. Person(T1 name, T2 age)
    19. {
    20. this->m_Name = name;
    21. this->m_Age = age;
    22. }
    23. //加空模板的参数列表 <>
    24. //如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
    25. friend void show<>(Person<T1, T2> p);
    26. private:
    27. T1 m_Name;
    28. T2 m_Age;
    29. };
    30. void test01()
    31. {
    32. Person<string, int> p("Wang", 100);
    33. show(p);
    34. }
    35. int main()
    36. {
    37. test01();
    38. }

    类外实现比较复杂,首先需要让编译器知道有一个全局函数的存在,由于用到了person,要先让编译器知道person类存在,person类又是一个类模板,因此还得声明类模板。

    如无特殊需求,尽量类内实现。

  • 相关阅读:
    定时任务cron
    QT+Python人脸表情特征识别
    计算机网络(自顶向下方法)学习笔记(第一章)
    vue源码分析-响应式系统(二)
    F. Quests Codeforces Round #835 (Div. 4)(二分答案)
    操作系统概述
    建筑施工专业承包36项包括哪些
    蓝桥等考Python组别十三级008
    IOT云平台 simple(4)springboot netty实现简单的mqtt broker
    Java内存模型与volatile
  • 原文地址:https://blog.csdn.net/Lao_tan/article/details/125427233