C++泛编程-类模板

类模板-模板类的基本概念

类模板是通用类的描述,使用任意类型(泛型)来描述类的定义。
使用类模板的时候,指定具体的数据类型,让编译器生成该类型的类定义。
语法:

template<class T>
class 模板类名
{
	类定义
}

函数模板建议用typename描述通用数据类型,类模板建议用class
类模板的简单使用:

#include<iostream>
#include<algorithm>
using namespace std;

template <class T1,class T2>
class AA {
public:
	T1 m_a;//通用类型用于成员变量
	T2 m_b;//通用类型用于成员变量

	AA() {}//默认构造函数
	//通用类型用于成员函数的参数
	AA(T1 a,T2 b):m_a(a),m_b(b){}
	
	//通用类型用于成员函数的返回值。
	T1 geta() {
		T1 a = 2;//通用类型用于成员函数的代码
		return m_a + a;
	}
	T2 getb() {
		T2 b = 1;//通用类型用于成员函数的代码
		return m_b + b;
	}
};
int main() {
	//用int和double来取代T1,T2
	AA<int, double>a;//用类模板AA创建对象

	a.m_a = 20;
	a.m_b = 30;
	cout << "a.geta()=" << a.geta() << endl;
	cout << "b.getb()=" << a.getb() << endl;
}

注意:

  • 在创建对象的时候,必须指明具体的数据类型。
  • 使用类模板时,数据类型必须适应类模板中的代码。(比如上面代码T2定义为string ,但是让string+1不行的。)
  • 类模板可以为通用参数指定缺省的数据类型(C++11标准的函数模板也可以)。
    例如:
#include<iostream>
#include<algorithm>
using namespace std;

template <class T1,class T2=string>
class AA {
public:
	T1 m_a;//通用类型用于成员变量
	T2 m_b;//通用类型用于成员变量

	AA() {}//默认构造函数
	//通用类型用于成员函数的参数
	AA(T1 a,T2 b):m_a(a),m_b(b){}
	
	//通用类型用于成员函数的返回值。
	T1 geta() {
		T1 a = 2;//通用类型用于成员函数的代码
		return m_a + a;
	}
	T2 getb() {
		//通用类型用于成员函数的代码
		return m_b;
	}
};
int main() {
	//用int和double来取代T1,T2
	AA<int>a;//用类模板AA创建对象

	a.m_a = 20;
	a.m_b = "西施";
	cout << "a.geta()=" << a.geta() << endl;
	cout << "b.getb()=" << a.getb() << endl;
}
  • 类的成员函数可以在类外实现。
#include<iostream>
#include<algorithm>
using namespace std;

template <class T1,class T2>
class AA {
public:
	T1 m_a;//通用类型用于成员变量
	T2 m_b;//通用类型用于成员变量

	AA() {}//默认构造函数
	//通用类型用于成员函数的参数
	AA(T1 a,T2 b):m_a(a),m_b(b){}
	
	//通用类型用于成员函数的返回值。
	T1 geta() {
		T1 a = 2;//通用类型用于成员函数的代码
		return m_a + a;
	}
	T2 getb();
};
template <class T1, class T2>
T2 AA<T1,T2>::getb() {
	//通用类型用于成员函数的代码
	return m_b;
}
int main() {
	//用int和double来取代T1,T2
	AA<int,string>a;//用类模板AA创建对象

	a.m_a = 20;
	a.m_b = "西施";
	cout << "a.geta()=" << a.geta() << endl;
	cout << "b.getb()=" << a.getb() << endl;
}
  • 可以用new创建模板对象。
#include<iostream>
#include<algorithm>
using namespace std;

template <class T1,class T2>
class AA {
public:
	T1 m_a;//通用类型用于成员变量
	T2 m_b;//通用类型用于成员变量

	AA() {}//默认构造函数
	//通用类型用于成员函数的参数
	AA(T1 a,T2 b):m_a(a),m_b(b){}
	
	//通用类型用于成员函数的返回值。
	T1 geta() {
		T1 a = 2;//通用类型用于成员函数的代码
		return m_a + a;
	}
	T2 getb();
};
template <class T1, class T2>
T2 AA<T1,T2>::getb() {
	//通用类型用于成员函数的代码
	return m_b;
}
int main() {
	//用int和double来取代T1,T2
	AA<int,string> *a=new AA<int,string>(3,"西施");//用类模板AA创建对象

	/*a->m_a = 20;
	a->m_b = "西施";*/
	cout << "a.geta()=" << a->geta() << endl;
	cout << "b.getb()=" << a->getb() << endl;
}
  • 在程序中,模板类的成员函数使用了才会创建。

模板类的示例-栈

  • 模板类最常用的就是作为容器类。
  • C++标准库:栈、数组、链表、二叉树和哈希表。
    听懂很容易但是实际动手写很难。
#include<iostream>
#include<algorithm>
using namespace std;
class Stack//栈类
{
private:
	int* items;//栈数组
	int stacksize;//栈实际大小
	int top;//栈顶指针
public:
	//构造函数1:分配栈数组内存2:把栈顶指针初始化为0
	Stack(int size) :stacksize(size), top(0) {
		items = new int[stacksize];
	}
	~Stack() {
		delete [] items;
		items = nullptr;
	}
	bool isempty()const {//判断栈是否为空
		if (top == 0) return true;
		return false;
	}
	bool isfull()const {//判断栈是否为满
		return top == stacksize;
	}
	bool push(const int& item) {//元素入栈
		if (top < stacksize) {
			items[top++] = item;
			return true;
		}
		return false;
	}
	bool pop(int& item) {//出栈
		if (top > 0) {
			item = items[--top];
			return true;
		}
		return false;
	}
};
int main() {
	Stack ss(5);//创建栈对象,大小为5
	//元素入栈
	ss.push(1);ss.push(2);ss.push(3);ss.push(4);ss.push(5);
	//元素出栈
	int item;
	while (!ss.isempty()) {
		ss.pop(item);
		cout << "item=" << item << endl;
	}
}

这个是可以的,但是这个栈只支持整数,是不行的。如果不使用类模板的话,这种方法是比较好的。
在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
typedef string DataType;
class Stack//栈类
{
private:
	DataType* items;//栈数组
	int stacksize;//栈实际大小
	int top;//栈顶指针
public:
	//构造函数1:分配栈数组内存2:把栈顶指针初始化为0
	Stack(int size) :stacksize(size), top(0) {
		items = new DataType[stacksize];
	}
	~Stack() {
		delete [] items;
		items = nullptr;
	}
	bool isempty()const {//判断栈是否为空
		if (top == 0) return true;
		return false;
	}
	bool isfull()const {//判断栈是否为满
		return top == stacksize;
	}
	bool push(const DataType& item) {//元素入栈
		if (top < stacksize) {
			items[top++] = item;
			return true;
		}
		return false;
	}
	bool pop(DataType& item) {//出栈
		if (top > 0) {
			item = items[--top];
			return true;
		}
		return false;
	}
};
int main() {
	Stack ss(5);//创建栈对象,大小为5
	//元素入栈
	//ss.push(1);ss.push(2);ss.push(3);ss.push(4);ss.push(5);
	ss.push("西施");ss.push("冰冰"); ss.push("幂幂"); ss.push("金莲");
	//元素出栈
	DataType item;
	while (!ss.isempty()) {
		ss.pop(item);
		cout << "item=" << item << endl;
	}
}

使用类模板:
在这里插入图片描述在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
template<class DataType>
class Stack//栈类
{
private:
	DataType* items;//栈数组
	int stacksize;//栈实际大小
	int top;//栈顶指针
public:
	//构造函数1:分配栈数组内存2:把栈顶指针初始化为0
	Stack(int size) :stacksize(size), top(0) {
		items = new DataType[stacksize];
	}
	~Stack() {
		delete [] items;
		items = nullptr;
	}
	bool isempty()const {//判断栈是否为空
		if (top == 0) return true;
		return false;
	}
	bool isfull()const {//判断栈是否为满
		return top == stacksize;
	}
	bool push(const DataType& item) {//元素入栈
		if (top < stacksize) {
			items[top++] = item;
			return true;
		}
		return false;
	}
	bool pop(DataType& item) {//出栈
		if (top > 0) {
			item = items[--top];
			return true;
		}
		return false;
	}
};
int main() {
	Stack<string> ss(5);//创建栈对象,大小为5
	//元素入栈
	//ss.push(1);ss.push(2);ss.push(3);ss.push(4);ss.push(5);
	ss.push("西施");ss.push("冰冰"); ss.push("幂幂"); ss.push("金莲");
	//元素出栈
	string item;
	while (!ss.isempty()) {
		ss.pop(item);
		cout << "item=" << item << endl;
	}
}

然后我们总结一下创建模板类的方法:

先写一个普通类,用具体的数据类型。
调试普通类。
把普通类改为模板类。

模板类的示例-数组

  • 定长数组: array容器(C++11标准)。
  • 可变数组:vector容器。
  • 类模板的非通用类型参数。

定长数组

类模板可以有非通用类型参数:1)通常是整型(C++20标准可以用其它); 2)实例化模板时必须用常量表达式;3)模板中不能修改参数的值。

  • 优点:在栈上分配内存,易维护,执行速度快,合适小型数组。
  • 缺点:在程序中,不同的非通用类型参数将导致编译器生成不同的类。

构造函数的方法更通用,因为数据的大小是类的成员(而不是硬编码),可以创建数组大小可变的类。

#include<iostream>
#include<algorithm>
using namespace std;
//类型和数组大小
template<class T,int len>
class Array {
private:
	T items[len];//数组元素
public:
	Array() { }//默认构造函数
	~Array(){}//析构函数
	T& operator[](int ii)//重载操作符[],可以修改数组中的元素
	{
		return items[ii];
	}
	const T& operator[](int ii)const//重载操作符[],不能修改数组中的元素
	{
		return items[ii];
	}
};


int main() {
	Array<string,10> aa;//创建模板类Array的对象
	//aa[0] = 5;aa[1] = 6;aa[2] = 7;aa[3] = 9;aa[4] = 0;
	aa[0] = "西施"; aa[1] = "冰冰"; aa[2] = "幂幂"; aa[3] = "金莲"; aa[4] = "小乔";
	for (int i = 0;i < 5;i++) {
		cout << "aa[" << i << "]=" << aa[i] << endl;
	}
}

变长数组
Vector最主要的就是扩展数组大小,一开始有一个len,如果访问的超出这个len,那么就扩展数组,一次可以扩展多个。

#include<iostream>
#include<algorithm>
using namespace std;
//类型和数组大小
template<class T,int len>
class Array {
private:
	T items[len];//数组元素
public:
	Array() { }//默认构造函数
	~Array(){}//析构函数
	T& operator[](int ii)//重载操作符[],可以修改数组中的元素
	{
		return items[ii];
	}
	const T& operator[](int ii)const//重载操作符[],不能修改数组中的元素
	{
		return items[ii];
	}
};

template<class T>
class Vector {
private:
	int len;//数组元素个数
	T* items;//数组元素
public:
	//默认构造函数,分配内存
	Vector(int size=10):len(size) {
		items = new T[len];
	}
	~Vector() {//析构函数
		delete[] items;
		items = nullptr;
	}
	//扩展数组内存大小
	void resize(int size) {
		if (size <= len) return;//只能往大了扩展
		T* tmp = new T[size];//分配更大的空间
		for (int i = 0;i < len;i++) tmp[i] = items[i];//把原来数组中的元素复制到新的数组
		delete[] items;//释放原来的数组
		items = tmp;//让数组指针指向新的数组
		len = size;
	}
	int size()const {//获取原来数组长度
		return len;
	}
	T& operator[](int ii)//重载操作符[],可以修改数组中的元素
	{
		if (ii >= len) resize(ii + 1);//如果访问超出,就扩展数组
		return items[ii];
	}
	const T& operator[](int ii)const//重载操作符[],不能修改数组中的元素
	{
		return items[ii];
	}
};


int main() {
	//Array<string,10> aa;//创建模板类Array的对象
	Vector<string> aa(1);
	//aa[0] = 5;aa[1] = 6;aa[2] = 7;aa[3] = 9;aa[4] = 0;
	aa[0] = "西施"; aa[1] = "冰冰"; aa[2] = "幂幂"; aa[3] = "金莲"; aa[4] = "小乔";
	for (int i = 0;i < 5;i++) {
		cout << "aa[" << i << "]=" << aa[i] << endl;
	}
}

嵌套和递归使用模板类

  • 容器中有容器。
  • 数组的元素可以是栈。
  • 栈中的元素可以是数组。

但是有一点需要注意:就是在vector扩张内存的时候,将原来数组中的元素复制到新数组,如果复制的是C++内置的数据类型,没有什么问题,但是如果复制的是类,并且类中使用了堆区内存,就存在浅拷贝问题,所以我们需要对stack进行深拷贝,对于Stack这个类,我们一定要重写拷贝构造函数,和赋值函数。

创建vector容器,容器中的元素用stack

#include<iostream>
#include<algorithm>
using namespace std;
template<class DataType>
class Stack//栈类
{
private:
	DataType* items;//栈数组
	int stacksize;//栈实际大小
	int top;//栈顶指针
public:
	//构造函数1:分配栈数组内存2:把栈顶指针初始化为0
	Stack(int size=3) :stacksize(size), top(0) {
		items = new DataType[stacksize];
	}
	~Stack() {
		delete[] items;
		items = nullptr;
	}
	Stack& operator=(const Stack& v)//重载复制运算符,实现深拷贝
	{
		delete[] items;//释放原内存
		stacksize = v.stacksize;//栈实际大小
		items = new DataType[stacksize];//重新分配数组
		for (int i = 0;i < stacksize;i++) items[i] = v.items[i];//复制数组中的元素
		top = v.top;//栈顶指针
		return *this;

	}

	bool isempty()const {//判断栈是否为空
		if (top == 0) return true;
		return false;
	}
	bool isfull()const {//判断栈是否为满
		return top == stacksize;
	}
	bool push(const DataType& item) {//元素入栈
		if (top < stacksize) {
			items[top++] = item;
			return true;
		}
		return false;
	}
	bool pop(DataType& item) {//出栈
		if (top > 0) {
			item = items[--top];
			return true;
		}
		return false;
	}
};

//类型和数组大小
template<class T>
class Vector {//变长数组
private:
	int len;//数组元素个数
	T* items;//数组元素
public:
	//默认构造函数,分配内存
	Vector(int size=2):len(size) {
		items = new T[len];
	}
	~Vector() {//析构函数
		delete[] items;
		items = nullptr;
	}
	//扩展数组内存大小
	void resize(int size) {
		if (size <= len) return;//只能往大了扩展
		T* tmp = new T[size];//分配更大的空间
		for (int i = 0;i < len;i++) tmp[i] = items[i];//把原来数组中的元素复制到新的数组
		delete[] items;//释放原来的数组
		items = tmp;//让数组指针指向新的数组
		len = size;
	}
	int size()const {//获取原来数组长度
		return len;
	}
	T& operator[](int ii)//重载操作符[],可以修改数组中的元素
	{
		if (ii >= len) resize(ii + 1);//如果访问超出,就扩展数组
		return items[ii];
	}
	const T& operator[](int ii)const//重载操作符[],不能修改数组中的元素
	{
		return items[ii];
	}
};


int main() {
	//vector容器的大小缺省值是2,Stack容器的大小缺省值是3
	//创建vector容器,容器中的元素用stack
	Vector<Stack<string>>vs;//C++11之前,>>之间要加空格
	Stack<string>vs1[2];
	string vs2[2][3];
	//手工的往容器中插入数据
	vs[0].push("西施1");vs[0].push("西施2");vs[0].push("西施3");//vs容器中第0个栈
	vs[1].push("西瓜1");vs[1].push("西瓜2");vs[1].push("西瓜3");//vs容器中第1个栈
	vs[2].push("冰冰");vs[2].push("幂幂");//vs容器中第2个栈
	//用嵌套的循环,把容器中的数据显示出来
	for (int i = 0;i < vs.size();i++) {//遍历Vector
		while (vs[i].isempty() == false) {
			//遍历Stack
			string item;
			vs[i].pop(item);
			cout << "item=" << item << endl;
		}
	}



}

然后我们创建一个Stack容器,容器里面的元素用vector<string>。还有vector中嵌套vector

#include<iostream>
#include<algorithm>
using namespace std;
template<class DataType>
class Stack//栈类
{
private:
	DataType* items;//栈数组
	int stacksize;//栈实际大小
	int top;//栈顶指针
public:
	//构造函数1:分配栈数组内存2:把栈顶指针初始化为0
	Stack(int size=3) :stacksize(size), top(0) {
		items = new DataType[stacksize];
	}
	~Stack() {
		delete[] items;
		items = nullptr;
	}
	Stack& operator=(const Stack& v)//重载复制运算符,实现深拷贝
	{
		delete[] items;//释放原内存
		stacksize = v.stacksize;//栈实际大小
		items = new DataType[stacksize];//重新分配数组
		for (int i = 0;i < stacksize;i++) items[i] = v.items[i];//复制数组中的元素
		top = v.top;//栈顶指针
		return *this;

	}

	bool isempty()const {//判断栈是否为空
		if (top == 0) return true;
		return false;
	}
	bool isfull()const {//判断栈是否为满
		return top == stacksize;
	}
	bool push(const DataType& item) {//元素入栈
		if (top < stacksize) {
			items[top++] = item;
			return true;
		}
		return false;
	}
	bool pop(DataType& item) {//出栈
		if (top > 0) {
			item = items[--top];
			return true;
		}
		return false;
	}
};

//类型和数组大小
template<class T>
class Vector {//变长数组
private:
	int len;//数组元素个数
	T* items;//数组元素
public:
	//默认构造函数,分配内存
	Vector(int size=2):len(size) {
		items = new T[len];
	}
	~Vector() {//析构函数
		delete[] items;
		items = nullptr;
	}
	Vector& operator=(const Vector& v)//重载复制运算符,实现深拷贝
	{
		delete[] items;//释放原内存
		len = v.len;//数组实际大小
		items = new T[len];//重新分配数组
		for (int ii = 0; ii < len; ii++) items[ii] = v.items[ii];//复制数组中的元素
		return *this;
	}
	//扩展数组内存大小
	void resize(int size) {
		if (size <= len) return;//只能往大了扩展
		T* tmp = new T[size];//分配更大的空间
		for (int i = 0;i < len;i++) tmp[i] = items[i];//把原来数组中的元素复制到新的数组
		delete[] items;//释放原来的数组
		items = tmp;//让数组指针指向新的数组
		len = size;
	}
	int size()const {//获取原来数组长度
		return len;
	}
	T& operator[](int ii)//重载操作符[],可以修改数组中的元素
	{
		if (ii >= len) resize(ii + 1);//如果访问超出,就扩展数组
		return items[ii];
	}
	const T& operator[](int ii)const//重载操作符[],不能修改数组中的元素
	{
		return items[ii];
	}
};


int main() {
	//vector容器的大小缺省值是2,Stack容器的大小缺省值是3
	//创建vector容器,容器中的元素用stack
	Vector<Stack<string>>vs;//C++11之前,>>之间要加空格
	//手工的往容器中插入数据
	vs[0].push("西施1");vs[0].push("西施2");vs[0].push("西施3");//vs容器中第0个栈
	vs[1].push("西瓜1");vs[1].push("西瓜2");vs[1].push("西瓜3");//vs容器中第1个栈
	vs[2].push("冰冰");vs[2].push("幂幂");//vs容器中第2个栈
	//用嵌套的循环,把容器中的数据显示出来
	for (int i = 0;i < vs.size();i++) {//遍历Vector
		while (vs[i].isempty() == false) {
			//遍历Stack
			string item;
			vs[i].pop(item);
			cout << "item=" << item << endl;
		}
	}


	//创建Stack容器,容器中的元素用vector<string>
	Stack<Vector<string>> sv;
	Vector<string>tmp;//栈的元素,临时vector<string>容器
	//第一个入栈的元素
	tmp[0] = "西施1";tmp[1] = "西施2";sv.push(tmp);
	//第二个入栈的元素
	tmp[0] = "西瓜1";tmp[1] = "西瓜2";sv.push(tmp);
	//第三个入栈的元素
	tmp[0] = "冰冰1";tmp[1] = "冰冰2";tmp[2] = "冰冰3";tmp[3] = "冰冰4";sv.push(tmp);
	//用一个嵌套的,把sv容器的数据显示出来
	while (sv.isempty() == false) {
		sv.pop(tmp);
		for (int i = 0;i < tmp.size();i++) {
			cout << "vs[" << i << "]=" << tmp[i] << endl;
		}
	}
	//创建Vector容器,容器中的元素用Vector<string>。
	Vector<Vector<string>> vv;//递归使用模板类。
	vv[0][0] = "西施1";vv[0][1]="西施2"; vv[0][2] = "西施3";
	vv[1][0] = "西瓜1"; vv[1][1]="西瓜2";
	vv[2][0] = "冰冰1";vv[2][1] = "冰冰2";vv[2][2]="冰冰3";vv[2][3]="冰冰4";
	//用嵌套的循环,把vv容器中的数据显示出来。
	for (int ii = 0; ii < vv.size(); ii++) {
		for (int jj = 0; jj < vv[ii].size(); jj++) {
			cout << vv[ii][jj] << " ";
		}
		cout << endl;
	}


}

模板类具体化

模板类具体化(特化、特例化)有两种:完全具体化和部分具体化。
语法请见示例程序。
具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。
示例:

#include<iostream>
#include<algorithm>
using namespace std;
//没有具体化版本,通用版本
template<class T1,class T2>
class AA {
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "类模板:构造函数.\n"; }
	void show() const;
};

template<class T1, class T2>
void AA<T1, T2>::show() const {//成员函数类外实现
	cout << "类模板:x=" << m_x << ",y=" << m_y << endl;

}

//类模板完全具体化
template<>
class AA<int, string> {
public:
	int m_x;
	string m_y;
	AA(const int x, const string y) :m_x(x), m_y(y) { cout << "完全具体化:构造函数。\n"; }
	void show()const;
};

void AA<int, string>::show() const {//成员函数类外实现
	cout << "完全具体话:x=" << m_x << ",y=" << m_y << endl;
}
/


//类模板部分显示具体化
template<class T1>
class AA<T1, string> {
public:
	int m_x;
	string m_y;
	AA(const int x, const string y) :m_x(x), m_y(y) { cout << "部分具体化:构造函数。\n"; }
	void show()const;
};

void AA<int, string>::show() const {//成员函数类外实现
	cout << "部分具体话:x=" << m_x << ",y=" << m_y << endl;
}

int main() {
	//具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。
	AA<int, string> aa(8, "我是一只小小鸟。");
	aa.show();
}

类模板-模板类与继承

注意继承的时候构造函数需要修改

  1. 模板类继承普通类(常见)。
    首先我们先看一个代码:定义了一个类AA和一个类BB。
#include<iostream>
#include<algorithm>
using namespace std;
class AA {
public:
	int m_a;
	AA(int a) :m_a(a) { cout << "调用了AA的构造函数\n"; }
	void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl; }
};
template<class T1,class T2>
class BB {//模板类BB
public:
	T1 m_x;
	T2 m_y;
	BB(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
	void func2()const { cout << "调用了func2()函数:x=" << m_x << ",y=" << m_y << endl; }

};

int main() {
	BB<int, string >bb(8, "我是一只小小鸟。");
	bb.func2();
}

然后我们让BB继承AA。只需要解决BB的构造函数就行

#include<iostream>
#include<algorithm>
using namespace std;
class AA {
public:
	int m_a;
	AA(int a) :m_a(a) { cout << "调用了AA的构造函数\n"; }
	void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl; }
};
template<class T1,class T2>
class BB:public AA {//模板类BB
public:
	T1 m_x;
	T2 m_y;
	BB(const T1 x, const T2 y,int a) :AA(a),m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
	void func2()const { cout << "调用了func2()函数:x=" << m_x << ",y=" << m_y << endl; }

};

int main() {
	BB<int, string>bb(8, "我是一只小小鸟。",4);
	bb.func2();
	bb.func1();
}

  1. 普通类继承模板类的实例版本。
    这时候BB是模板类,可以实例出来无数个类出来,让AA继承,然后也需要修改AA的构造函数
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class BB {//模板类BB
public:
	T1 m_x;
	T2 m_y;
	BB(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
	void func2()const { cout << "调用了func2()函数:x=" << m_x << ",y=" << m_y << endl; }

};

class AA:public BB<int,string> {
public:
	int m_a;
	AA(int a,int x,string y) :BB(x,y),m_a(a) { cout << "调用了AA的构造函数\n"; }
	void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl; }
};


int main() {
	AA aa(3,8, "我是一只小小鸟。");
	aa.func2();
	aa.func1();
	return 0;
}

  1. 普通类继承模板类。(常见)
    这个是上面那个的进化版,这个时候AA也成了模板类。
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class BB {//模板类BB
public:
	T1 m_x;
	T2 m_y;
	BB(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
	void func2()const { cout << "调用了func2()函数:x=" << m_x << ",y=" << m_y << endl; }

};
template<class T1, class T2>
class AA:public BB<T1,T2> {
public:
	int m_a;
	AA(int a,T1 x,T2 y) :BB<T1,T2>(x,y),m_a(a) { cout << "调用了AA的构造函数\n"; }
	void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl; }
};


int main() {
	AA<int,string> aa(3,8, "我是一只小小鸟。");
	aa.func2();
	aa.func1();
	return 0;
}

  1. 模板类继承模板类。
    这个时候需要这个template<class T,class T1,class T2>,BB两个T1和T2,CC一个T。然后还是得需要修改CC的构造函数.
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class BB {//模板类BB
public:
	T1 m_x;
	T2 m_y;
	BB(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
	void func2()const { cout << "调用了func2()函数:x=" << m_x << ",y=" << m_y << endl; }

};
template<class T1, class T2>
class AA:public BB<T1,T2> {
public:
	int m_a;
	AA(int a,T1 x,T2 y) :BB<T1,T2>(x,y),m_a(a) { cout << "调用了AA的构造函数\n"; }
	void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl; }
};


template<class T,class T1,class T2>
class CC :public BB<T1,T2>{
public:
	T m_c;
	CC(const T c,const T1 x,const T2 y) :BB<T1,T2>(x,y), m_c(c) { cout << "调用了CC的构造函数。\n"; }
	void func3() { cout << "调用了func3()函数:m_c=" << m_c << endl; }
};

int main() {
	CC<int,int,string> cc(3,8, "我是一只小小鸟。");
	cc.func3();
	cc.func2();
	return 0;
}

  1. 模板类继承模板参数给出的基类(不能是模板类)。
#include<iostream>
#include<algorithm>
using namespace std;
class AA {
public:
	AA() { cout << "调用了AA的构造函数AA()。\n"; }
	AA(int a){ cout << "调用了AA的构造函数AA(int a)。\n"; }
};

class BB {
public:
	BB() { cout << "调用了BB的构造函数BB()。\n"; }
	BB(int a) { cout << "调用了BB的构造函数BB(int a)。\n"; }
};

class CC {
public:
	CC() { cout << "调用了CC的构造函数CC()。\n"; }
	CC(int a) { cout << "调用了CC的构造函数CC(int a)。\n"; }
};
template<class T>
class DD {
public:
	DD() { cout << "调用了CC的构造函数DD()。\n"; }
	DD(int a) { cout << "调用了DD的构造函数DD(int a)。\n"; }
};

template<class T>
class EE:public T {//模板类继承参数给出的基类
public:
	EE():T() { cout << "调用了EE的构造函数EE()。\n"; }
	EE(int a):T(a) { cout << "调用了CC的构造函数EE(int a)。\n"; }
};

int main() {
	EE<AA> ea1;//AA作为基类。
	EE<BB> eb1;//BB作为基类。
	EE<CC> ec1;// CC作为基类。
	EE<DD<int> > ed1; //EE<int>作为基类。

	return 0;
}

类模板-模板类与函数

应用的经验
模板类可以用于函数的参数和返回值,有三种形式:

  1. 普通函数,参数和返回值是模板类的实例化版本。
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {//有两个通用类型参数
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show()const { cout << "show() x=" << m_x << ",y=" << m_y << endl; }
};
//普通函数,参数和返回值是模板类的实例化版本。
AA<int,string> func(AA<int,string>& aa) {
	aa.show();
	cout << "调用了func(AA<int,string>&aa)函数.\n";
	return aa;
}
int main() {
	AA<int, string> aa(3, "我是一只小小鸟。");
	func(aa);
}
  1. 函数模板,参数和返回值是某种的模板类。
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {//有两个通用类型参数
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show()const { cout << "show() x=" << m_x << ",y=" << m_y << endl; }
};
//普通函数,参数和返回值是模板类的实例化版本。
//AA<int,string> func(AA<int,string>& aa) {
//	aa.show();
//	cout << "调用了func(AA<int,string>&aa)函数.\n";
//	return aa;
//}
//函数模板,参数和返回值都是模板类AA
template<typename T1,typename T2>
AA<T1, T2> func(AA<T1, T2>& aa) {
	aa.show();
	cout << "调用了func(AA<int,string>&aa)函数.\n";
	return aa;
}


int main() {
	AA<int, string> aa(3, "我是一只小小鸟。");
	func(aa);
}
  1. 函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。

这种比较通用

#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {//有两个通用类型参数
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show()const { cout << "show() x=" << m_x << ",y=" << m_y << endl; }
};
//普通函数,参数和返回值是模板类的实例化版本。
//AA<int,string> func(AA<int,string>& aa) {
//	aa.show();
//	cout << "调用了func(AA<int,string>&aa)函数.\n";
//	return aa;
//}
//函数模板,参数和返回值都是模板类AA
//template<typename T1,typename T2>
//AA<T1, T2> func(AA<T1, T2>& aa) {
//	aa.show();
//	cout << "调用了func(AA<int,string>&aa)函数.\n";
//	return aa;
//}

//函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。
template<typename T>
T func(T & aa) {
	aa.show();
	cout << "调用了func(T & aa)函数.\n";
	return aa;
}


int main() {
	AA<int, string> aa(3, "我是一只小小鸟。");
	func(aa);
}
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {//有两个通用类型参数
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show()const { cout << "show() x=" << m_x << ",y=" << m_y << endl; }
};
class BB {//有两个通用类型参数
public:
	void show() { cout << "调用了BB的show()方法" << endl; }
};
//普通函数,参数和返回值是模板类的实例化版本。
//AA<int,string> func(AA<int,string>& aa) {
//	aa.show();
//	cout << "调用了func(AA<int,string>&aa)函数.\n";
//	return aa;
//}
//函数模板,参数和返回值都是模板类AA
//template<typename T1,typename T2>
//AA<T1, T2> func(AA<T1, T2>& aa) {
//	aa.show();
//	cout << "调用了func(AA<int,string>&aa)函数.\n";
//	return aa;
//}

//函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。
template<typename T>
T func(T & aa) {
	aa.show();
	cout << "调用了func(T & aa)函数.\n";
	return aa;
}


int main() {
	AA<int, string> aa(3, "我是一只小小鸟。");
	BB b;
	func(aa);
	func(b);
}


然后还有个例子:

#include<iostream>
#include<algorithm>
using namespace std;
void show() {
	cout << "调用了show()函数" << endl;
}
class BB {//普通类
public:
	void operator()() {//重载了()运算符(仿函数)
		cout << "调用了BB类的仿函数\n";
	}
};
//函数模板,参数和返回值是任意类型(支持普通类和模板韩素华,还可以是其他的)
template<typename T>
void func(T tt) {
	tt();
}
int main() {
	BB bb;
	func(bb);
	func(show);
}

模板类与友元

我们看这个代码:这里我们将类aa设置成了全局变量.但是这样缺乏通用性.

#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {
private:
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x,const T2 y):m_x(x),m_y(y){}
	friend void show();
};
AA<int, string> a(88, "我是一只小小鸟");
void show() {
	cout << "x=" << a.m_x << ",y=" << a.m_y << endl;
}
int main() {
	show();
}

模板类的友元函数有三类:

  1. 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数。
#include<iostream>
#include<algorithm>
using namespace std;
template<class T1,class T2>
class AA {
private:
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x,const T2 y):m_x(x),m_y(y){}
	//非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。
	friend void show(const AA<T1,T2>& a) {
		cout << "x=" << a.m_x << ",y=" << a.m_y << endl;
	}
};

int main() {
	AA<int, string> a(88, "我是一只小小鸟");
	show(a);
}
  1. 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
#include<iostream>
#include<algorithm>
using namespace std;
//约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数
template<typename T>
void show(T& a);//第一步:在模板类的定义前面,声明友元函数模板


template<class T1, class T2>
class AA {//模板类AA.
	friend void show<>(AA<T1, T2>& a);//第二步:在模板类中,再次声明友元函数模板
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}

};
template<typename T>//第三步:友元函数模板的定义
void show(T& a) {
	cout << "通用:x=" << a.m_x << ",y=" << a.m_y << endl;
}

template<>  //第三步:具体化版本
void show(AA<int, string>& a) {
	cout << "具体化<int,string>:x=" << a.m_x << ",y=" << a.m_y << endl;
}
int main() {
	AA<int, string> a(88, "我是一只小小鸟。");
	show(a);

	AA<char, string> b(77, "我是一只傻傻鸟。");
	show(b);
}
  1. 非约束模板友元︰模板类实例化时,如果实例化了n个类,也会实例化n个友元函数,每个实例化的类都拥有n个友元函数。(这个不怎么用不科学)
#include<iostream>
#include<algorithm>
using namespace std;
//非类模板约束的友元函数,实例化后,每个函数都是每个类的友元
template<class T1, class T2>
class AA {//模板类AA.
	template<typename T>friend void show(T& a);
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}

};
template<typename T>void show(T& a) {//通用的函数模板
	cout << "通用:x=" << a.m_x << ",y=" << a.m_y << endl;
}


template<>void show(AA<int, string>& a) {//具体化版本
	cout << "具体化<int,string>:x=" << a.m_x << ",y=" << a.m_y << endl;
}
int main() {
	AA<int, string> a(88, "我是一只小小鸟。");
	show(a);

	AA<char, string> b(77, "我是一只傻傻鸟。");
	show(b);
}

类模板-模板类的成员模板

类模板中的成员模板,就是一个类模板中还含有一个类模板

#include<iostream>
#include<algorithm>
using namespace std;
template<class T1, class T2>
class AA {//模板类AA.
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show() { cout << "m_x=" << m_x << ",m_y=" << m_y << endl; }
	template<class T>
	class BB {
	public:
		T m_a;
		T1 m_b;
		BB(){}
		void show() {
			cout << "m_a=" << m_a << ",m_b" << m_b << endl;
		}
	};
	BB<string>m_bb;
	template<typename T>
	void show(T tt) {
		cout << "tt=" << tt << endl;
		cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
		m_bb.show();
	}
};
int main() {
	AA<int, string> a(88, "我是一只小小鸟。");
	a.show();
	a.m_bb.m_a = "我是一只小小鸟";
	a.m_bb.show();
	a.show("我是一只小小鸟?");
}

有一个语法需要说明一下:
就是如果将里面嵌套类的BB的show()函数写在类的外面应该怎么写:
在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
template<class T1, class T2>
class AA {//模板类AA.
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show() { cout << "m_x=" << m_x << ",m_y=" << m_y << endl; }
	template<class T>
	class BB {
	public:
		T m_a;
		T1 m_b;
		BB(){}
		void show(); 
	};
	BB<string>m_bb;
	template<typename T>
	void show(T tt) {
		cout << "tt=" << tt << endl;
		cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
		m_bb.show();
	}
};
template<class T1, class T2>
template<class T>
void AA<T1,T2>::BB<T>::show() {
	cout << "m_a=" << m_a << ",m_b" << m_b << endl;
}
int main() {
	AA<int, string> a(88, "我是一只小小鸟。");
	a.show();
	a.m_bb.m_a = "我是一只小小鸟";
	a.m_bb.show();
	a.show("我是一只小小鸟?");
}

这个时我们将AA和BB中的show()都写在外面

#include<iostream>
#include<algorithm>
using namespace std;
template<class T1, class T2>
class AA {//模板类AA.
public:
	T1 m_x;
	T2 m_y;
	AA(const T1 x, const T2 y) :m_x(x), m_y(y) {}
	void show() { cout << "m_x=" << m_x << ",m_y=" << m_y << endl; }
	template<class T>
	class BB {
	public:
		T m_a;
		T1 m_b;
		BB(){}
		void show(); 
	};
	BB<string>m_bb;
	template<typename T>
	void show(T tt);
};
template<class T1, class T2>
template<class T>
void AA<T1,T2>::BB<T>::show() {
	cout << "m_a=" << m_a << ",m_b" << m_b << endl;
}

template<class T1, class T2>
template<class T>
void AA<T1,T2>::show(T tt) {
	cout << "tt=" << tt << endl;
	cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
	m_bb.show();
}
int main() {
	AA<int, string> a(88, "我是一只小小鸟。");
	a.show();
	a.m_bb.m_a = "我是一只小小鸟";
	a.m_bb.show();
	a.show("我是一只小小鸟?");
}

类模板-将模板类用作参数

首先我们看这个链表和数组

template<class T1,int len>
class LinkList {//链表类模板
public:
	T1* m_head;//链表头节点
	int m_len = len;//表长
	void insert() { cout << "向链表中插入一条记录。" << endl; }
	void ddelete() { cout << "向链表中删除一条记录。" << endl; }
	void update() { cout << "向链表中更新一条记录。" << endl; }
};
template<class T1,int len>
class Array {//数组类模板
public:
	T1* m_data;//数组指针
	int m_len = len;//表长
	void insert() { cout << "向数组中插入一条记录。" << endl; }
	void ddelete() { cout << "向数组中删除一条记录。" << endl; }
	void update() { cout << "向数组中更新一条记录。" << endl; }

};

里面有很多代码时重复的,我们看看能不能将这两个弄一个模板类

#include<iostream>
#include<algorithm>
using namespace std;
template<class T,int len>
class LinkList {//链表类模板
public:
	T* m_head;//链表头节点
	int m_len = len;//表长
	void insert() { cout << "向链表中插入一条记录。" << endl; }
	void ddelete() { cout << "向链表中删除一条记录。" << endl; }
	void update() { cout << "向链表中更新一条记录。" << endl; }
};
template<class T,int len>
class Array {//数组类模板
public:
	T* m_data;//数组指针
	int m_len = len;//表长
	void insert() { cout << "向数组中插入一条记录。" << endl; }
	void ddelete() { cout << "向数组中删除一条记录。" << endl; }
	void update() { cout << "向数组中更新一条记录。" << endl; }

};
//线性表模板类:tabletype-线性表类型,data-type-线性表数据类型
template<template<class,int>class tabletype,class datatype,int len>
class LinearList {
public:
	tabletype<datatype, len>m_table;//创建线性表对象
	void insert() { m_table.insert(); }//线性表插入
	void ddelete() { m_table.ddelete(); }//线性表删除
	void update() { m_table.update(); };//线性表更新

	void oper() {//按业务要求操作线性表
		cout << "len=" << m_table.m_len << endl;
		m_table.insert();
		m_table.update();

	}
};
int main() {
	//创建线性表对象,容器类型为链表,链表的数据类型为int,表长为20。
	LinearList<LinkList, int, 20>a;
	a.insert();
	a.ddelete();
	a.update();
	
	
	//创建线性表对象,容器类型为数组,数组的数据类型为string,表长为20。
	LinearList<Array, string, 20>b;
	b.insert();
	b.ddelete();
	b.update();
}

对于这个我们是不是一下子看蒙了

template<template<class,int>class tabletype,class datatype,int len>

我们改造一下这个函数:

template<template<class, int>class T1, class T2, int len>
class LinearList {
public:
	T1<T2, len>m_table;//创建线性表对象
	void insert() { m_table.insert(); }//线性表插入
	void ddelete() { m_table.ddelete(); }//线性表删除
	void update() { m_table.update(); };//线性表更新

	void oper() {//按业务要求操作线性表
		cout << "len=" << m_table.m_len << endl;
		m_table.insert();
		m_table.update();

	}
};

然后对于这个template<template<class, int>class T1, class T2, int len>其实里面就是有三个参数,一个是template<class, int>class T1,一个是class T2,另一个是int len,也就是第一个参数是一个模板.

#include<iostream>
#include<algorithm>
using namespace std;
template<class T,int len>
class LinkList {//链表类模板
public:
	T* m_head;//链表头节点
	int m_len = len;//表长
	void insert() { cout << "向链表中插入一条记录。" << endl; }
	void ddelete() { cout << "向链表中删除一条记录。" << endl; }
	void update() { cout << "向链表中更新一条记录。" << endl; }
};
template<class T,int len>
class Array {//数组类模板
public:
	T* m_data;//数组指针
	int m_len = len;//表长
	void insert() { cout << "向数组中插入一条记录。" << endl; }
	void ddelete() { cout << "向数组中删除一条记录。" << endl; }
	void update() { cout << "向数组中更新一条记录。" << endl; }

};
线性表模板类:tabletype-线性表类型,data-type-线性表数据类型
//template<template<class,int>class tabletype,class datatype,int len>
//class LinearList {
//public:
//	tabletype<datatype, len>m_table;//创建线性表对象
//	void insert() { m_table.insert(); }//线性表插入
//	void ddelete() { m_table.ddelete(); }//线性表删除
//	void update() { m_table.update(); };//线性表更新
//
//	void oper() {//按业务要求操作线性表
//		cout << "len=" << m_table.m_len << endl;
//		m_table.insert();
//		m_table.update();
//
//	}
//};

//线性表模板类:tabletype-线性表类型,data-type-线性表数据类型
template<template<class, int>class T1, class T2, int len>
class LinearList {
public:
	T1<T2, len>m_table;//创建线性表对象
	void insert() { m_table.insert(); }//线性表插入
	void ddelete() { m_table.ddelete(); }//线性表删除
	void update() { m_table.update(); };//线性表更新

	void oper() {//按业务要求操作线性表
		cout << "len=" << m_table.m_len << endl;
		m_table.insert();
		m_table.update();

	}
};


int main() {
	//创建线性表对象,容器类型为链表,链表的数据类型为int,表长为20。
	LinearList<LinkList, int, 20>a;
	a.insert();
	a.ddelete();
	a.update();
	
	
	//创建线性表对象,容器类型为数组,数组的数据类型为string,表长为20。
	LinearList<Array, string, 20>b;
	b.insert();
	b.ddelete();
	b.update();
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值