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

    在 C++ 中,模板分为函数模板和类模板两种。函数模板是用于生成函数的,类模板则是用于生成类的。

    函数模板&模板函数     类模板&模板类  必须区分概念

    函数模板是模板,模板函数时具体的函数
    类模板是模板,模板类时具体的类
    函数模板实例化而得到的函数称为模板函数
    类模板实例化得到的类叫模板类

    一.函数模板

    函数模板的基本写法为:
    template <typename 类型参数1, typename 类型参数2, ...> 
    返回值类型  函数模板名(形参表)
    {
        函数体
    }

    其中的 typename 关键字也可以用 class 关键字替换,例如:
    template <class 类型参数1, class 类型参数2, ...>

    举例,求两个值的最大值Max:

    #include<iostream>
    using namespace std;
    
    template <typename T>                   
    T const& Max(T const& a, T const& b)
    {
    	return a < b ? b : a;
    }
    
    int main()
    {
    	int i = 20;
    	int j = 30;
    	int result1 = Max(i, j); //编译到这一句时,编译器自动生成int Max(int, int)函数
    
    	double f1 = 12.5;
    	double f2 = 20.88;
    	double result2 = Max(f1, f2); //编译到这句时,编译器自动生成double Max(double, double)函数
    
    	string s1 = "Hello";
    	string s2 = "world";
    	string result3 = Max(s1, s2); //编译到这句时,编译器自动生成string Max(string, string)函数
    	return 0;
    }

    模板在定义时,编译器不会对它编译,因为没有一个实体可用。只有在编译到某句代码时,如int result1 = Max(i, j);时,根据模板再将Max(i, j)编译成int Max(int, int)这样具体的函数。
    编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在大部分编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误

    编译器对模板进行实例化时,并非只能通过模板调用语句的实参来实例化模板中的类型参数,模板调用语句可以明确指明要把类型参数实例化为哪种类型。可以用:

    函数名<实际类型参数1, 实际类型参数2, ...>

    的方式告诉编译器应该如何实例化模板函数。例如下面的程序:

    #include<iostream>
    using namespace std;
    
    template <typename T>
    T add(T n)
    {
    	return n+1;
    }
    
    int main()
    {
    	cout << add(4) / 2 << endl; //正常写法,输出2
    	cout << add<double>(4) / 2 << endl; //指明要用double来实例化add函数,输出2.5
    	return 0;
    }

    第一句正常调用输出2,第二句为编译器指定类型,则输出2.5

     

    二.类模板

    人们需要编写多个形式和功能都相似的函数,因此有了函数模板来减少重复劳动;人们也需要编写多个形式和功能都相似的类,于是C++引入了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。

    类模板的写法如下:
    template <class 类形参数1, class 类型参数2, ...>
    class 类模板名称
    {
        成员函数和成员变量
    };

    具体如下:

    template<class T1, class T2> 
    class A
    {
    public:
    	T1 value1;
    	T2 value2;
    	T1 add(T1 a, T2 b);
    };

    类模板中的成员函数放在类外定义时的语法如下:
    template <class 类形参数1, class 类型参数2, ...>
    返回值类型  类模板名<class 类塑参数1, class 类型参数2, ...>::成员函数名(形参表)
    {
        。。。
    }

    具体如下:

    template<class T1,class T2>
    T1 A<T1, T2>::add(T1 a, T2 b)
    {
    	return a + b;
    }

    用类模板定义对象的写法如下:
    类模板名<真实类型参数表> 对象名(构造函数实际参数表);
    若构造函数无参的话则为:
    类模板名 <真实类型参数表> 对象名;

    下面实现一个简单的key,value的键值对----类模板mDictionary

    实践中常常会碰到,某项数据记录由两部分组成,一部分是关键字,另一部分是值。关键字用来对记录进行排序和检索,根据关键字能查到值。例如,学生记录由两部分组成,一部分是姓名,另一部分是年纪。要能根据姓名对学生进行查询检索,key就是姓名,value就是年龄。

    template<class T1, class T2> 
    class mDictionary
    {
    public:
    	T1 key;      //关键字
    	T2 value;    //值
    	mDictionary(T1 k, T2 v) :key(k), value(v)
    	{
    	};
    	T2 getvalue(T1 k);
    };
    
    template<class T1,class T2>
    T2 mDictionary<T1, T2>::getvalue(T1 k)
    {
    	if (k == this->key)    //如果姓名传进来相同,则返回年龄
    	{
    		return this->value;
    	}
    	else
    	{
    		return 0;
    	}
    }
    
    int main()
    {
    	mDictionary<string, int> dic("Bob",22);
    	std::cout << dic.getvalue("Bob")<<std::endl;    //返回22
    	std::cout << dic.getvalue("Tom") << std::endl;  //返回0
    	return 0;
    }

    实例化一个类模板时,如第main函数第一行-------mDictionary<string, int>  dic("Bob",22);
    真实类型参数表中的参数是具体的类型名,如 string、int 等,它们用来一一对应地替换类模板定义中“类型参数表”中的类型参数。类模板名 <真实类型参数表>就成为一个具体的类的名字,就是mDictionary<string, int>就可以看成一个类型
    如有一个类A, 初始化类A的时候,一般都是A  a; 
    mDictionary<string, int>就等价于A,可以看成一个A类型

    编译器编译到这一行时,就会用 string 替换 mDictionary模板中的 T1,用 int 替换 T2,其余部分原样保留,这样就自动生成了一个新的类。这个类的名字编译器是如何处理的不需要知道,可以认为它的名字就是mDictionary<string, int>。也可以说,dic 对象的类型就是 Pair<string, int>。
    mDictionary<string, int> 类的成员函数自然也是通过替换 mDictionary模板的成员函数中的 T1、T2 得到的。
    编译器由类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类叫模板类

    函数模板作为类模板成员
    类模板中的成员函数还可以是一个函数模板。成员函数模板只有在被调用时才会被实例化。例如下面的程序:

    #include <iostream>
    using namespace std;
    template <class T>
    class A
    {
    public:
        template <class T2>             //定义新类型T2
        void Func(T2 t) { cout << t; }  //成员函数模板
    };
    int main()
    {
        A<int> a;
        a.Func('K');  //成员函数模板Func被实例化
        a.Func("hello");
        return 0;
    }

    程序的输出结果是:Khello

    这个例子与上面例子不同的是,mDictionary中的getvalue是一个类模板中的成员函数,没有定义新的模板,而Func函数是一个新的函数模板,在Func函数上面一句,定义了新类型T2,姑且先这样理解。其实只要了解有这种写法即可,既类模板中还可以定义函数模板

  • 相关阅读:
    课时8:环绕通知
    课时7:后置通知、异常通知
    课时6::AOP、execution表达式、前置通知
    课时:5 使用注解实现声明式事务
    课时22::PageHelper分页插件
    课时21 :使用MyBatis实现批量操作
    课时4:特殊值的注入问题和各种类型的自动装配
    课时3:三种方式的依赖注入、给各种集合类型的属性注入
    课时2:解耦合发展史、控制反转、依赖注入
    课时1:Spring环境搭建、STS工具、第一个Spring程序
  • 原文地址:https://www.cnblogs.com/kevinWu7/p/10163448.html
Copyright © 2011-2022 走看看