类模板-模板类的基本概念
类模板是通用类的描述,使用任意类型(泛型)来描述类的定义。
使用类模板的时候,指定具体的数据类型,让编译器生成该类型的类定义。
语法:
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();
}
类模板-模板类与继承
注意继承的时候构造函数需要修改
- 模板类继承普通类(常见)。
首先我们先看一个代码:定义了一个类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();
}
- 普通类继承模板类的实例版本。
这时候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;
}
- 普通类继承模板类。(常见)
这个是上面那个的进化版,这个时候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;
}
- 模板类继承模板类。
这个时候需要这个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;
}
- 模板类继承模板参数给出的基类(不能是模板类)。
#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;
}
类模板-模板类与函数
应用的经验
模板类可以用于函数的参数和返回值,有三种形式:
- 普通函数,参数和返回值是模板类的实例化版本。
#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);
}
- 函数模板,参数和返回值是某种的模板类。
#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);
}
- 函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。
这种比较通用
#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();
}
模板类的友元函数有三类:
- 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数。
#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);
}
- 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
#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);
}
- 非约束模板友元︰模板类实例化时,如果实例化了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();
}