zoukankan      html  css  js  c++  java
  • C++函数模板&类模板

    函数模板

    模板概念及语法

      主要目的,简化代码,减少重复代码。基本语法格式:  template<class T> 或者 template<typename T> //末尾不加分号  

     1 template <class T> //等价于 template <typename T>
     2 
     3 void mySwap(T &a, T &b){
     4     T tmp = b;
     5     b = a;
     6     a = tmp;
     7 }
     8 
     9 void test02(){
    10     int a = 10, b = 20;
    11     //自动推导类型,里面必须有参数,而且参数类型必须相同,因为两者是同一个类型T,这也是类型参数化的意义
    12     mySwap(a, b);
    13     //显式调用,显式指定类型
    14     mySwap<int>(a, b);
    15 
    16     cout << "a = " << a << "b = " << b << endl;
    17 }

    函数模板与普通函数 

    1.与类模板区别

      template声明下面是函数定义,则为函数模板,否则为类模板。注意:每个函数模板前必须有且仅有一个template声明,不允许多个template声明后只有一个函数模板,也不允许一个template声明后有多个函数模板。

    2. 与普通函数的区别

      (1)函数模板不可以进行隐式类型转换,普通函数可以进行隐式类型转换;

      (2)两者的调用规则:

        ①若函数模板和普通函数出现了重载(两者仅仅参数类型不同,一个为T,一个为具体类型),优先使用普通函数调用,若普通函数没有实现,则会出现“无法解析命令”的错误,不会因此而调用函数模板;

        ②如果想强制调用模板,可以使用空函数列表

        ③函数模板可以发生重载(参数类型和个数等)

        ④如果函数模板可以更好的匹配,优先调用函数模板,如果函数模板和普通函数都能匹配,优先调用普通函数(这一点和①类似)。

    3. 模板机制剖析

      (1)模板不是万能的,不能处理所有的数据;

      (2)函数模板不可以直接调用,需要编译器处理成模板函数后才能调用;

      (3)编译器对函数模板进行两次编译,一次是在声明的地方对模板代码(函数模板)进行编译,一次对调用时对参数替换后的代码(模板函数)进行编译。

     1 template <class T>
     2 bool myComp(T &a, T &b){
     3 
     4     cout << "模板函数调用" << endl;
     5     if (a == b)
     6         return true;
     7     else
     8         return false;
     9 }
    10 
    11 //函数模板重载
    12 template <class T>
    13 bool myComp(T &a, T &b, T &c){
    14 
    15     cout << "模板函数调用" << endl;
    16     if (a == b == c)
    17         return true;
    18     else
    19         return false;
    20 }
    21 
    22 ////如果int类型的参数调用上述函数模板,会替换参数类型处理为模板函数如下
    23 //bool myComp(int &a, int &b){
    24 //    if (a == b)
    25 //        return true;
    26 //    else
    27 //        return false;
    28 //}
    29 
    30 bool myComp(int a, int b){
    31 
    32     cout << "普通函数调用" << endl;
    33 
    34     if (a == b)
    35         return true;
    36     else
    37         return false;
    38 }
    39 
    40 void test02(){
    41     int a = 10, b = 20, c = 40;
    42     float d = 40;
    43 
    44     //普通函数和模板函数都可以匹配,优先调用普通函数
    45     myComp(a, b);
    46     //普通函数中形参可以隐式转换(隐式强转),函数模板不可以
    47     myComp(a, d);
    48 }

    4. 函数模板的局限性

      (1)模板不能解决所有数据类型;

      (2)如果出现不能解决的类型,可以通过数据类型具体化来满足要求;

      (3)数据类型具体化语法: template <> 返回值 函数名 <具体类型> (参数) ,将数据类型具体化,仅仅是数据类型,不能修改返回值类型和函数名。

      (4)当具体化和参数化同时匹配时,优先使用具体化

     1 class Person{
     2 public:
     3     Person(string name, int age){
     4         m_Name = name;
     5         m_Age = age;
     6     }
     7 
     8     string m_Name;
     9     int m_Age;
    10 
    11 };
    12 
    13 template <class T>
    14 bool myComp(T &a, T &b){
    15     if (a == b)
    16         return true;
    17     else
    18         return false;
    19 }
    20 
    21 //自定义数据无法适应模板,会报如下错误
    22 //error C2678: 二进制“==”: 没有找到接受“Person”类型的左操作数的运算符(或没有可接受的转换)
    23 //另外,将数据类型具体化,仅仅是数据类型,不能修改返回值类型和函数名
    24 template<> bool myComp<Person>(Person &a, Person &b){
    25     if (a.m_Age == b.m_Age){
    26         return true;
    27     }
    28     else
    29         return false;
    30 }
    31 
    32 void test02(){
    33     int a = 10, b = 20;
    34     int ret = myComp(a, b);
    35     cout << "ret = " << ret << endl;
    36 
    37     
    38     Person p1("tom", 1);
    39     Person p2("jerry", 1);
    40     int ret1 = myComp(p1, p2); 
    41 
    42     cout << "ret = " << ret1 << endl;
    43 }

    类模板

    1. 基本使用

      与函数模板相同,紧跟在template声明后,同样一个template对应一个类模板。

      与函数模板的区别:(1)类模板不支持自动类型推导;(2)数据类型可以有默认参数.

     1 //类模板可以有默认参数
     2 //template<class NameType, class AgeType = int>
     3 template<class NameType, class AgeType>
     4 class Person{
     5 public:
     6     Person(NameType name, AgeType age){
     7         m_Name = name;
     8         m_Age = age;
     9     }
    10 
    11     NameType m_Name;
    12     AgeType m_Age;
    13 };
    14 
    15 void test02(){
    16     //类模板不支持自动类型推导
    17     //缺少 类模板 "Person" 的参数列表
    18     //Person p("啊呀呀", 10);
    19 
    20     Person<string, int> p("啊呀呀", 10);
    21 }

    2. 成员函数创建时机

      成员函数只有在编译或运行时才会创建,仅仅写出来不会报错,如示例所示

     1 class Person1{
     2 public:
     3     void showPerson1(){
     4         cout << "Person1调用" << endl;
     5     }
     6 };
     7 
     8 class Person2{
     9 public:
    10     void showPerson2(){
    11         cout << "Person2调用" << endl;
    12     }
    13 };
    14 
    15 template<class T>
    16 class myclass{
    17 public:
    18     T obj;
    19 
    20     void func1(){
    21         obj.showPerson1();
    22     }
    23 
    24     void func2(){
    25         obj.showPerson2();
    26     }
    27 };
    28 
    29 void test02(){
    30     myclass<Person1>tmp;
    31 
    32     tmp.func1();
    33 
    34     //成员函数只有在程序编译或运行时才会创建,只是写出来不会报错,在编译或运行时会报错
    35     //tmp.func2();
    36 }

    3. 类模板作函数的参数

      有三种方式:(1)显示指定类型;(2)参数模板化;(3)整体模板化

     1 template<class NameType, class AgeType>
     2 class Person{
     3 public:
     4     Person(NameType name, AgeType age){
     5         m_Name = name;
     6         m_Age = age;
     7     }
     8 
     9     void showPerson(){
    10         cout << "name: " << this->m_Name << " age: " << this->m_Age << endl;
    11     }
    12 
    13     NameType m_Name;
    14     AgeType m_Age;
    15 };
    16 
    17 //1.显式指定类型
    18 void doWork(Person<string, int> &p){
    19     p.showPerson();
    20 }
    21 
    22 //2.参数模板化
    23 template<class NameType, class AgeType>
    24 void doWork2(Person<NameType, AgeType> &p){
    25     p.showPerson();
    26 }
    27 
    28 //3.整体模板化
    29 template<class PersonType>
    30 void doWork3(PersonType &p){
    31     p.showPerson();
    32 }
    33 
    34 void test02(){
    35     Person<string, int>p("tom", 10);;
    36     doWork(p);
    37     doWork2(p);
    38     doWork3(p);
    39 }

    4. 继承中的类模板

       如果基类是模板类,则子类需要告诉编译器基类中的T是什么类型,如果不告诉编译器无法通过,不能分配内存。

     1 template<class T>
     2 class Person{
     3 public:
     4     T m_Age;
     5 };
     6 //如果子类不显式标明父类中模板的数据类型,会报错:缺少类模板的参数列表
     7 class Son :public Person<int>{
     8 
     9 };
    10 
    11 //子类中标明的数据类型也可以是模板
    12 template<class T1, class T2>
    13 class Son1 :public Person<T2>{
    14     T1 m_name;
    15 };
    16 
    17 void test02(){
    18     Son1<string, int>son; //标明Person类中的T为int型,T1为string
    19 }

    5. 类模板下的类外成员函数实现

       普通的类外成员函数实现时,需要将作用域写上即可,但在类模板下,需要将作用域写为类模板形式。另外在类外实现时,需要在类内声明。

     1 template<class T1, class T2>
     2 class Person{
     3 public:
     4     //类内实现
     5     Person(T1 name, T2 age;
     6     //{
     7     //    this->m_Name = name;
     8     //    this->m_Age = age;
     9     //}
    10 
    11     void showPerson();
    12     //{
    13     //    cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
    14     //}
    15 
    16     T1 m_Name;
    17     T2 m_Age;
    18 };
    19 
    20 //类外实现
    21 template<class T1, class T2>
    22 Person<T1, T2>::Person(T1 name, T2 age){
    23     this->m_Name = name;
    24     this->m_Age = age;
    25 }
    26 
    27 template<class T1, class T2>
    28 void Person<T1, T2>::showPerson(){
    29     cout << "name: " << this->m_Name << "age: " << this->m_Age << endl;
    30 }
    31 
    32 void test02(){
    33     Person<string, int>p("tom", 10);
    34     p.showPerson();
    35 }

    6. 类模板分文件编写问题

      一般进行分文件编写时,.h,.cpp分别写声明和实现,但是由于类模板的成员函数运行阶段才去创建,导致包含.h头文件,不会创建函数的实现,无法解析外部命令,此时

    建议类模板不要分文件编写,写到一个类中即可,类内进行声明和实现,最后文件后缀改为hpp.

    类模板下的友元函数

    1. 类内友元函数实现

      类内友元函数实现, friend void printPerson(Person<T1, T2> &p) 

     1 template<class T1, class T2>
     2 class Person{
     3     //友元函数遇到类模板,在类内实现
     4     friend void printPerson(Person<T1, T2> &p){
     5         cout << "name : " << p.m_Name << " age :" <<p.m_Age<< endl;
     6     }
     7 
     8 public:
     9     Person(T1 name, T2 age){
    10         this->m_Name = name;
    11         this->m_Age = age;
    12     }
    13 
    14     T1 m_Name;
    15     T2 m_Age;
    16 };
    17 
    18 void test02(){
    19     Person<string, int> p("tom", 10);
    20     printPerson(p);
    21 }

    2. 类外友元函数实现

      类外实现时,需要在类内通过空参数列表,告诉编译器,这是模板函数的声明,不加<>表示普通函数声明。另外,需要让编译器提前看到函数及类的声明。

     1 template<class T1, class T2>
     2 class Person;
     3 
     4 //让编译器提前看到printPerson声明,但里面有Person声明,需要提前声明Person类
     5 template<class T1, class T2> 
     6 void printPerson(Person<T1, T2> &p);
     7 
     8 template<class T1, class T2>
     9 class Person{
    10     //友元函数类外实现时,需要在类内通过空参数列表,告诉编译器,这是模板函数的声明
    11     friend void printPerson<>(Person<T1, T2> &p);
    12     //{
    13         //cout << "name : " << p.m_Name << " age :" <<p.m_Age<< endl;
    14     //}
    15 
    16 public:
    17     Person(T1 name, T2 age){
    18         this->m_Name = name;
    19         this->m_Age = age;
    20     }
    21 
    22     T1 m_Name;
    23     T2 m_Age;
    24 };
    25 
    26 template<class T1, class T2>
    27 void printPerson(Person<T1, T2> &p){
    28     cout << "name : " << p.m_Name << " age :" << p.m_Age << endl;
    29 }
    30 
    31 void test02(){
    32     Person<string, int> p("tom", 10);
    33     printPerson(p);
    34 }
  • 相关阅读:
    Mac上的USB存储设备使用痕迹在新版操作系统有所变化
    Beware of the encrypted VM
    A barrier for Mobile Forensics
    Second Space could let suspect play two different roles easily
    Take advantage of Checkra1n to Jailbreak iDevice for App analysis
    Find out "Who" and "Where"
    Where is the clone one and how to extract it?
    Downgrade extraction on phones running Android 7/8/9
    高版本安卓手机的取证未来
    How to extract WeChat chat messages from a smartphone running Android 7.x or above
  • 原文地址:https://www.cnblogs.com/qinguoyi/p/10297692.html
Copyright © 2011-2022 走看看