【c++】模板

【c++】模板

一.模板的概念

        1. 什么是模板

        2. 模板的作用

        3. 模板的语法

二.函数模板

三.类模板

四.模板的编译类型

参考:《c++从入门到精通》 人民邮电出本社


    在制作ppt时,我们可以使用模板,其中已为我们提供了基本元素,只需要自己填入相应数据即可。在C++中,也有这样的模板,只需在使用前填入自己需要的数据类型即可。

    模板是c++语音中一个相对较新的重要特性,是实现代码重用机制的一种工具。模板可以分为两类:函数模板和类模板。本节介绍模板的概念、定义以及使用方法,以便后续能正确使用C++系统日渐庞大的标准模板类库,以及定义自己的模板类和模板函数,进行更大规模的软件开发。

一.  模板的概念

1. 什么是模板

    模板是实现代码重用机制的一种工具,可以实现类型参数化,即把类型定义为参数,从而实现代码的可重用性。

    利用模板,我们可以把功能相似、仅数据类型不同的函数或类设计为通用的函数模板或类模板,提供给用户。

    模板是“泛型编程”的基础,所谓泛型编程就是独立于任何特定类型的方式编写代码。所以,类是对象的抽象,而模板又是类的抽象,用模板能定义出具体的类(类是类模板的抽象)。

2. 模板的作用

    之前我们学过函数的重载,以便于实现同名函数对不同类型参数的区分。例如:max()函数的重载,

                    

    每个要比较的类型都需要单独定义,非常麻烦。而,每个max()的功能都相同,函数体也相同,仅仅是参数类型不同,那能不能只写一套代码,对于任一类型T的两个对象a,b,max(a,b)总能使得编译系统理解其意义并实现呢?

    可以!于是c++引入了模板机制。

模板的作用:

(1)实现代码的可重用性

(2)模板能减少源代码量,并提高代码的机动性,而不会降低类型安全。

3. 模板的语法

(一)函数模板的一般形式:

template <class T1 ,class T2 ,typenameT3 ……>  返回类型 函数名(函数形参表)

{

// 定义函数体

}

注意:

(1)模板形参表不能为空。模板形参可分为:模板类型参数(代表一种类型)和模板非类型参数(代表一个常量表达式)。

(2)模板非类型参数的类型必须是下面的一种:整型或枚举、指针类型、引用类型

        其他类型不允许作为模板的非参数类型使用。

关键字typenameclass有何区别呢?

    在模板定义中,二者意义相同,可以互换使用,甚至可以在同一模板形参表中同时存在。

(二)类模板的一般形式:

template <class T1>

class  类名

{

// 类定义

}

    注意:

        1.Template是声明各模板的关键字,表示声明是一个模板。

        2.  模板参数应是抽象化的结果(class,typename),而不应是具体的(int\float等)。

        3.  类模板中,成员函数不能声明为虚函数。 

二.函数模板

函数模板的一般形式:

template <class T1 ,class T2 ,typenameT3 ……>  返回类型 函数名(函数形参表)

{

// 定义函数体

}
 

【函数模板实例】实现max()函数

//函数模板.cpp
#include<iostream> 
using namespace std;

template <class T> 
 T _max(T a,T b)
{
	return (a>b)? a:b ;
}


int main(int argc,char *argv[])
{
	int a1=3,a2=5;
	double b1=3.5,b2=1.5;
	cout<<"函数模板"<<endl;
	cout<<"max(3, 5)="<<_max(a1,a2)<<endl; 
    cout<<"max(3.5, 1.5)="<<_max(b1,b2)<<endl; 
	return 0;
}

运行结果:

【实例分析】

    (1) 程序中定义的template <class T>  T _max(T a,T b) ,这并不是一个实实在在的函数(类似于虚函数),编译器不会为其产生任何可执行代码。该定义只是对函数的描述,表示它每次都能单独处理在类型形参表中说明的数据类型。

    (2) 调用max(a1,a2)时,编译器发现调用max(a1,a2),则根据实参类型int,先生成一个重载函数:

int max(int a,int b)  {return a>b? a:b ;}

    该重载函数的定义体跟函数模板的相同,而形参以实参类型为依据。(所以,函数模板的内核是函数重载)。该重载函数称为模板函数,由函数模板生成模板函数的过程称为函数模板的实例化。

    (3) 可用下图表示函数模板的实例化过程:

            

    注意:max(a,b)中的a.b为同一类型,若一个为int,一个为double,则调用会出错。

三.类模板

类模板的一般形式:

template <class T1>

class  类名

{

// 类定义

}

【类模板实例】不同类型参数的构造函数

//类模板.cpp
#include<iostream> 
using namespace std;

template <class T1,class T2> 
class myClass
{
	private:
		T1 a;
		T2 b;
	public:
		myClass(T1 x,T2 y)
		{
			a=x;
			b=y;
		}
		void show()
		{
			cout<<"a="<<a<<" b="<<b<<endl;
		}
};


int main(int argc,char *argv[])
{
	cout<<"类模板"<<endl;
	myClass<int,int> c1(1,2);
	c1.show(); 
	myClass<int,double> c2(5, 2.3);
	c2.show(); 
	myClass<char,int> c3('z',3);
	c3.show(); 
	
	return 0;
}

运行结果:

        

【实例分析】

        

类模板与模板类的关系?

        类模板是模板的定义,它属于一种模板,是抽象的,相当于一种数据类型。而模板类实实在在的类,它是由类模板定义的实例类。所以类模板是模板类的抽象,而模板类是类模板的实例。

类模板与函数模板的区别?

        函数模板的实例化是由编译程序在处理函数调用时自动完成的,与函数模板不同的是,类模板的实例化必须由程序员在程序中显式地指定。

例:函数模板的实例化:inta1=3,a2=5;  max(a1,a2);

        类模板的实例化:myClass<int,int>c1(1,2); 

注意:类模板的实例化时,已经标明了实参类型<int ,int >

四.模板的编译类型

    标准c++为编译模板代码定义了两种模型

        (1) 包含编译模型

        (2) 分离编译模型

    在这两种模型中,构造程序的方式基本相同,类定义和函数声明都放在头文件(.h文件)中,而函数定义和成员定义则放在源文件(.cpp文件)中。

    两种模型的不同之处,主要在于编译器怎样使用来自源文件的定义。

(1)包含编译模型

模板的编译有什么特殊之处?

    一般来说,调用函数时,编译器只需要看到函数的声明或类的定义。但模板则不同,当编译器看到模板定义的时候,并不会立即产生代码,只有在调用了函数模板或类模板的对象时,才产生特定类型的模板实例。因此要进行实例化,编译器必须能够访问定义模板的源代码。当调用函数模板或类模板的成员函数时,编译器需要那些通常放在源代码中的代码。

例如,将前面的max()函数模板进行改写。

在头文件max.h中进行函数模板的声明:(注意添加  #include"max.cpp"

#ifndef MAX_H
#define MAX_H
template<class T> T max(T a,T b);  //函数模板声明
#include"max.cpp"
#endif // MAX_H

在max.cpp中进行函数模板定义

template <class T> T max_(T a,T b)
{
   return a>b? a:b;
}

在main()函数中:

#include <iostream>
#include"max.h"
using namespace std;
int main()
{
   cout<<"包含编译模型"<<endl;
   cout<<"max_(1,2)="<<max_(1,2)<<endl;
   return 0;

}

运行结果:

 

注意:

    “包含编译模型”并不是类模板的声明和定义放在一个头文件里;

    “分离编译模型”也不是将声明和定义分开。

(2) 分离编译模型

    在分离编译模型中,编译器会为我们跟踪相关的模板定义。但是,必须让编译器知道要记住给定的模板定义,怎么办?可以使用export关键字来实现。

Export关键字有什么用?

    例如,一般在函数模板定义中,可通过在关键字template之前包含export关键字,以指明函数模板为导出的。

    注意:在程序中,一个函数模板只能被定义为export一次。//仅在定义时export,声明时不用export。

    分离编译模式使得我们能够更好的把类模板的接口同其实现分离开,它使得我们能够这样来组织程序:把类模板的接口放在头文件中,而把具体实现放在源文件中。

【编程实例】

Max.h: //定义类模板 myClass

#ifndef MAX_H
#define MAX_H
#include <iostream>

template <class T> //T max_(T a,T b)
 class myClass
{
private:
    T a;
    T b;
public:
    myClass(T x,T y)
    {
        a=x;
        b=y;

    }

    void show()
    {
        std::cout<<"a="<<a<<" b="<<b<<std::endl;
    }
};
#endif // MAX_H

Max.cpp中://在cpp中export类模板

export template<class T> class myClass;
#include"max.h"
//接下来是类成员的定义

Main.cpp中:

#include <iostream>
#include"max.h"
using namespace std;

int main()
{
    cout<<"分离编译模型"<<endl;
    myClass<int> aa(1,2);
    aa.show();
    return 0;
}

运行结果:

            

 -------------------------------------------         END      -------------------------------------

猜你喜欢

转载自blog.csdn.net/u012679707/article/details/80294458