• 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 }
  • 相关阅读:
    iOS企业版打包 发布在线安装包 plist
    iOS企业版打包(转载)
    微信小程序开发入门教程
    SVN常用命令说明
    高仿QQ、微信效果的图片浏览器(支持原图和缩略图、多种手势、CocoaPods)
    iOS 调试大法
    MFMailComposeViewController发送邮件的实例
    自定义UISearchBar外观
    npm 包命令:不是内部或外部命令 问题分析
    angular 子路由跳转出现Navigation triggered outside Angular zone, did you forget to call ‘ngZone.run() 的问题修复
  • 原文地址:https://www.cnblogs.com/qinguoyi/p/10297692.html
Copyright © 2020-2023  润新知