01 泛型编程
所谓的泛型编程,指的就是编写程序的时候,是独立于任何特定的类型来编写代码
那么什么叫做独立于任何特定的类型呢?
如:设计一个数组类型(class Array),这个数组有可能只能存放 int 类型的元素,但是你在代码中使用这个数组的时候却不一定总是 int 类型的元素,可能需要数组能够存放其他类型的元素
也就是说,数组是一个通用的概念,不应该局限于某一种特定的类型
所以,数组元素的类型,应该可以独立表示,类似于函数的参数,可以在实例化数组的时候,可以传递一个类型参数指定数组中的元素类型
如果设计出来的数组,能够存放任意类型的元素,那么这就是泛型编程(程序逻辑和类型没有关系,可以应用于任何类型)
泛型编程如何实现呢?
使用模板实现
02 模板
在C++中使用模板来实现泛型编程,或者说使用模板来表示一个通用的概念
模板允许我们在定义函数或者类的时候,将类型作为参数(代码里面不包含具体的类型)
编译器可以根据你提供的类型自动生成特定的函数或者类
如:
指定数组元素类型为double,编译器就可以根据模板自动生成一个存放double类型元素的数组类
指定数组元素类型为int,编译器就可以根据模板自动生成一个存放int类型元素的数组类
指定数组元素类型为string,编译器就可以根据模板自动生成一个存放string类型元素的数组类
...
简单的说,模板就是用来创建一个类或者一个函数的公式(蓝图)如:
以前我们创建一个类,可以用来实例化对象
现在我们可以定义一个模板,可以用来实例化一个具体的类型,再使用创建出来的类实例化对象根据一个模板创建一个类/函数的过程叫做实例化
模板可以分为两种:
a. 函数模板,可以用来创建具体类型的函数
b. 类模板,可以用来创建具体类型的类
03 函数模板
语法规则:
template<typename T1, typename T2 ......> // 模板头
返回值类型 函数名(参数列表) {
// 函数体;
}
T:类型参数的形参名,在后面的函数体中,可以直接使用这个类型参数
如果该函数模板涉及到多个类型参数,只需要在尖括号中以逗号隔开即可!编译器会自动的推导参数类型去匹配函数模板
类型参数化!!!对于函数模板,编译器可以自动推导参数的类型
例子:
定义一个函数模板,用于计算两个对象的和
04 类模板
C++中除了支持函数模板还支持类模板(class template)
函数模板中定义的类型参数(T)可以在函数的声明和定义中使用
类模板中定义的类型参数可以在类的声明和类的实现中使用,类模板的目的是将数据的类型参数化
声明类模板的语法:
template<typename T1, typename T2>
class 类名 {
public:...
private:
...
protected:
...
};类模板和函数模板都是以template开头,后面跟上类型参数,类型参数不能为空,多个类型参数使用逗号隔开
一旦声明了类模板,就可以将类型参数用于类的成员变量和成员函数
类模板的使用方式:
调用函数模板只需要提供函数值,编译器会自动的根据用户提供的值去推导值的类型,
再自动生成相应的函数版本
但是我们在使用类模板的时候,必须显示的指定目标参数
因为:
Array a; 没有任何数据可以表示类型,必须明确的指定参数
========>
格式:
模板类名<实际类型1, 实际类型2> 对象名;SeqStack<int> s; // 实例化一个栈对象,栈中的元素都是int
SeqStack<string> s1; // 实例化一个栈对象,栈中的元素都是string
SeqStack<Test> s2; // 实例化一个栈对象,栈中的元素都是Test
...
vector<int> v;
...
注意:1. 如果模板类的成员函数在类外实现(声明和定义分开),则该成员函数必须有模板声明,并且作用域也必须指定模板参数
//构造函数 template<typename DataType> Stack<DataType>::Stack(int capacity) : m_top{-1}, m_capacity{capacity}, m_data{new DataType[capacity]} { cout << "constructors" << endl; // 为栈开辟存储元素的空间 // m_data = new DataType[capacity]; } // 析构函数 template<typename DataType> Stack<DataType>::~Stack() { cout << "de-structors" << endl; delete [] m_data; } // 拷贝构造函数 template<typename DataType> Stack<DataType>::Stack(const Stack &oth) { cout << "copy-constructors" << endl; m_top = oth.m_top; m_capacity = oth.m_capacity; m_data = new DataType[m_capacity]; // memcpy:内存拷贝 src:源地址,dest:目标地址,count:拷贝字节数 memcpy(m_data, oth.m_data, sizeof(DataType) * m_capacity); }
2. 如果模板函数和模板类的声明和定义分开,只能写在同一个文件中的不同位置,不能把声明和定义写在不同的文件中,也就是说,模板的声明和定义必须在同一个文件中,意味着模板实现的功能必须开源
3. 在模板声明中,使用关键字typename来说明类型参数的名字
但是在早期的C++中,曾经使用的关键字叫做class在新标准中,建议使用typename
如: template<class T1, class T2> class 类名 { private: ...... protected: ...... public: ...... };
05 模板的特化
解决开发过程中由于要对特殊类型做特殊的处理,不能直接使用函数模板(针对特定类型应该使用特定版本)
C++中的模板分为类模板和函数模板,虽然它引进到C++标准中的时间不是很长,但是却得到了广泛的应用,这一点在STL中有着充分的体现目前,STL在C++社区中得到了广泛的关注,应用和研究
理解和掌握模板是学习、应用和研究以及扩充STL的基础(STL就是使用模版实现的模板函数和模板类),而STL模板实例中又充斥着大量的模板特化和偏特化
06 类模板特化
有时为了需要,针对特定的类型,需要对模板进行特化,也就是特殊处理
例如:stack类模板针对bool类型,因为实际上bool类型只需要一个二进制位就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的
// Stack类模板的实现template<class T>
class Stack {
// 使用T类型
};
================>
针对数据类型为bool的栈进行特殊化处理的类模板
template<>
class Stack<bool> {
// 针对数据为bool的特殊处理
};
上述定义中template<>告诉编译器这是一个特化(特殊化)的模板
当用户使用 Stack<bool> bs; 的时候会使用下面的特化模板
模板的特化和偏特化_模板的特化与偏特化,就测测模板的基础编程-优快云博客
07 函数模板的特化
08 模板特化时的匹配规则