zhouqijie

第一节 函数模板和类模板



参数多态性

⭐参数多态性就是将程序所处理的对象的类型 参数化,使得一段程序可以用于处理多种/不同类型对象。(泛型思想)



函数模板

⭐定义格式:

template<模板参数表> 类型名 函数名 (参数表)  
{  
    //函数体 
}

⭐尖括号的模板参数表由用逗号隔开的模板参数构成。模板参数用”typename”或者”class”修饰。



类模板

⭐定义格式:

template<模板参数表> class 类名
{
	//类成员声明
}

⭐使用类模板使得类中某些数据成员、成员函数的参数/返回值/局部变量能取任意类型。



模板参数(类型参数和非类型参数)

类型参数

示例:template< class T >

使用关键字class或typename指出T为类型参数。

非类型参数

示例:template< int n >

非类型参数又称为表达式参数。

常用于数组等容器。

  1. 表达式参数只可以是整型、枚举、引用、指针。目前的C++11标准还不支持浮点数作为表达式参数。
  2. 不能修改参数的值,也不能使用参数的地址。
  3. 每个值都将生成自己的模板。而使用构造函数传递参数值只会生成一个类声明,并且更灵活通用。



※ 其他多功能性 ※

子类继承的父类是一个类模板,那么子类在声明时需要指定父类中T的类型。否则编译器无法为子类分配内存。

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

可以递归使用模板。例如Container<Container<int, 5>, 10> container;

如上文所述。

类模板可以作为类模板的类型参数。格式template<template<typename T> class TC>











第一节补充-模板相关



类模板相比函数模板的区别

template<class T> class Object
{
public:
    T value;
    Object(T value);
}  

Object obj("aaa");    //错误的
Object<string> obj("aaa");  //正确的
template<class T = string> class Object
{} 

函数模板的类型参数不能带有默认值,但是非类型参数可以有默认值。



普通类和类模板区别

普通类的成员函数一开始就创建。
类模板的成员函数调用时才创建。

template<class T>
class TestClass
{
public :
	T obj;
	void Test()
	{
		obj.Foo();//可以编译通过
	}
};



模板的实例化和具体化

隐式实例化

模板默认使用隐式实例化。

实例化对象时,指出所需类型,然后编译器生成具体的类定义。编译器在需要对象之前,不会生成类的隐式实例化。

CRE:也就是说类定义是发生在编译期间的。而C#泛型类型的替换在运行时执行,并且可以获取类型信息。

显式实例化

使用template关键字并指出所需类型来声明类时,编译器将生成类声明的显示实例化。声明必须位于模板定义所在的名称空间中。

在这种情况下,虽然没有创建或提及类对象,编译器也将生成类定义。

示例:template class Container<string, 100>;

显式具体化和部分具体化

有时可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同以满足需求。这种情况下可以创建显式具体化(explicit specialization)

示例:

//假设Comparable模板类的定义对于数字比较管用,但是无法用于字符串类型。就可以显示具体化。      
template<typename T> class Comparable
{
	//...
}
//显式具体化
template<> class Comparable<string>
{
	//...
}

C++还允许部分具体化(partial specialization)

示例:

template<class T1, class T2> class Pair{...}
template<class T1> class Pair<T1, int>{...}    



成员模板

类模板和函数模板可用作结构体、类、类模板的成员。并且模板类的模板参数和其成员的模板参数可以不一样。

很老的编译器不支持模板成员。有一些编译器支持模板成员但是不支持类外定义。

模板类外定义成员模板函数的时候,如果模板是嵌套的,template语句也需要嵌套。

template <typename T>
class Test
{
public:
	template<typename U> 
	U Foo(U u);
}

//成员定义
template <typename T>
	template <typename U>
		U Test<T>::Foo(U u)
		{
			//....
		}



类模板成员的类外实现和文件外实现

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

解决方法1:直接包含.cpp文件。
解决方法2:把.h和.cpp中的内容写到一个文件,默认后缀名.hpp。



模板与友元

模板类声明也可以由友元。模板的友元分为3类:

  1. 非模板友元。
  2. 约束(bound)模板友元。
  3. 非约束(unbound)模板友元。

非模板友元

friend void counts();

上述声明使该函数成为模板所有实例化的友元。

模板类的约束模板友元函数

template <typename T> void counts();
template <typename T> void report(T &);
template <typename TC>
class Test
{
	friend void counts<TC>();
	friend void counts<Test<TC>>(Test<TC> &); 
	//可以省略为friend void count<>(Test<TC> &); //编译器自动推断
}
template <typename TC>
class Test
{
	//....友元声明略	
}

//模板友元函数定义
template <typename T>
void counts(){ ... }

template <typenmae T>
void report(T & arg) { ... }

模板类的非约束模板友元函数

通过在类内部声明模板,可以创建非约束友元函数。即每个函数具体化都是每个类具体化的友元。

template<typename T>
class Test
{
	template <typename C, typename D> friend void foo(C &, D &);
}
template <typename C, typename D> void foo(C & c, D & d){ ... }

补充:操作符重载声明为友元函数

示例:

friend ostream & operator << <>(ostream & os, T & obj);    



模板别名(C++11)

typedef std::array<double, 12> arrd;
typedef std::array<int, 12> arri;













第二节 泛型程序设计

泛型程序设计

⭐泛型程序设计,就是编写不依赖具体数据类型的程序。
⭐在C++中,模板是泛型程序设计的主要工具。
⭐泛型编程常应用于编写不同类型通用的数据结构。

术语

概念:用于描述泛型编程中作为参数的数据类型所需要具备的功能。(内涵是功能,外延是具备这些功能的所有数据类型)
模型:具备一个概念所需要功能的数据类型称为这一概念的一个模型。





第三节 C++标准模板库(STL)

STL简介

⭐标准模板库(Standard Template Library,STL)最初是由HP公司开发的一个用于支持C++泛型编程的模板库。后被纳入C++标准,成为C++的一部分。STL有不同的实现和版本,但是它们为用户提供的接口都遵循共同标准。
⭐STL提供了一些常用的数据结构和算法。例如向量容器vector、链表容器等。STL还提供排序、查找等现有的函数模板。
⭐STL更大的意义在于,它定义了一套概念体系,为泛型程序设计提供了逻辑基础。STL中的各个类模板、函数模板的参数都是用这个体系中的概念来规定的。使用STL的一个模板时所提供的类型参数既可以是C++标准库已有类型,也可以是自定义的类型,只要这些类型是所要求概念的模型。因此,STL是一个开放的体系。