模板的概念
函数模板(将类型参数化)
函数模板语法
两个函数逻辑非常相似
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
// 模板
// 交换两个数
void swapInt(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
void swapDouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
// 可以发现,上面两个函数都是同样的逻辑,所以就要用到模板的概念
template<typename T>//声明一个模板,T是一个类型
void swapT(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
cout << "a = " << a << endl
<< "b = " << b << endl;
// 自动类型推到
swapT(a, b);
cout << "a = " << a << endl
<< "b = " << b << endl;
double c = 1.1;
double d = 2.2;
cout << "c = " << c << endl
<< "d = " << d << endl;
// 显示指定类型
swapT<double>(c, d);
cout << "c = " << c << endl
<< "d = " << d << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
函数模板注意事项
总结: 使用时要先确定T,且确定的T是唯一的
函数模板案例
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
template<typename T>
void sort(T* arr, int n)
{
int i = 0;
int j = 0;
for (i = 0; i < n; i++)
{
int max = i;
for (j = i + 1; j < n; j++)
{
if (arr[j] > arr[max])
{
max = j;
}
}
if (max != i)
{
swap(arr[max], arr[i]);
}
}
}
template<typename T>
void swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void myPrint(T* arr, int n)
{
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void test01()
{
int arr[] = { 9,8,7,2,5,10 };
sort(arr, sizeof(arr) / sizeof(arr[0]));
myPrint(arr, sizeof(arr) / sizeof(arr[0]));
}
void test02()
{
char arr[] = "snhfkbzy";
sort(arr, sizeof(arr) / sizeof(arr[0]));
myPrint(arr, sizeof(arr) / sizeof(arr[0]));
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
普通函数和模板的区别
智能但不够只能,只能想到一层
普通函数与函数模板的调用规则
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
void myPrint(int a,int b)
{
cout << "普通函数的调用" << endl;
}
template<typename T>
void myPrint(T a, T b)
{
cout << "函数模板的调用" << endl;
}
template<typename T>
void myPrint(T a, T b, T c)
{
cout << "函数模板重载的调用" << endl;
}
void test01()
{
// 如果函数模板和普通函数都可以实现,优先调用普通函数
myPrint(10, 20);
// 可以通过空模板参数列表强制调用函数模板
myPrint<>(10, 20);
// 函数模板可以发生重载
myPrint(10, 20, 30);
// 如果函数模板可以产生更好的匹配性,优先调用函数模板
char c = 'a';
char d = 'b';
// 普通函数可以调用,因为可以类型转换
// 但模板具有更好的匹配性
myPrint(c, d);
}
int main()
{
test01();
system("pause");
return 0;
}
模板的局限性
也就是公用的模板个别特殊处理
STL--标准模板库
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->name = name;
this->age = age;
}
string name;
int age;
};
template<typename T>
bool myCompare(T a, T b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
template<>bool myCompare(Person p1, Person p2)
{
if (p1.name == p2.name && p1.age == p2.age)
{
return true;
}
else
{
return false;
}
}
void test01()
{
int a = 10;
int b = 10;
bool ret = myCompare(a, b);
if (ret)
{
cout << "a == b" << endl;
}
else
{
cout << "a != b" << endl;
}
}
void test02()
{
Person p1("tom", 18);
Person p2("tom", 18);
// 自定义数据类型,系统无法比较,需要将模板特例化
bool ret = myCompare(p1, p2);
if (ret)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
类模板
类模板的基本语法
// 类模板必须标出类型,系统不自动匹配
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
template<class nameType,class ageType>
class Person
{
public:
Person(nameType name, ageType age)
{
this->name = name;
this->age = age;
}
nameType name;
ageType age;
void showMessage()
{
cout << "姓名:" << this->name << endl
<< "年龄:" << this->age << endl;
}
};
void test01()
{
// 类模板必须标出类型,系统不自动匹配
Person<string, int> p1("孙悟空", 999);
p1.showMessage();
}
int main()
{
test01();
system("pause");
return 0;
}
类模板与函数模板的区别
默认参数,有个默认参数定义时可以不写
// 默认参数必须定义最后几个位置,且是连续的,且必须包含最后一个位置
// 否则语法是通过的,但是在调用的时候默认的位置还是不能省略
template<class nameType = string,class ageType = int,class idType>
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
// 默认参数必须定义最后几个位置,且是连续的,且必须包含最后一个位置
// 否则语法是通过的,但是在调用的时候默认的位置还是不能省略
template<class nameType = string,class ageType = int,class idType>
class Person
{
public:
Person(nameType name, ageType age, idType id)
{
this->name = name;
this->age = age;
this->id = id;
}
nameType name;
ageType age;
idType id;
void showMessage()
{
cout << "姓名:" << this->name << endl
<< "年龄:" << this->age << endl
<< "ID:" << this->id << endl;
}
};
void test01()
{
// 类模板必须标出类型,系统不自动匹配
Person<string, int, int>p1("孙悟空", 999,123);
p1.showMessage();
}
int main()
{
test01();
system("pause");
return 0;
}
类模板中成员函数创建时机
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Person1
{
public:
void show1()
{
cout << "Person1函数调用" << endl;
}
};
class Person2
{
public:
void show2()
{
cout << "Person2函数调用" << endl;
}
};
template<class T>
class Person
{
public:
T obj;
void func1()
{
obj.show1();
}
void func2()
{
obj.show2();
}
};
void test01()
{
Person<Person1> p;
p.func1();
// 编译时没出错是因为编译器也不知道T是什么类型
// 只有调用的时候该函数才生成
// 而T具有唯一性,故代码运行是出错
//p.func2();
}
int main()
{
test01();
system("pause");
return 0;
}
类模板对象做函数参数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
//类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
T1 name;
T2 age;
void show()
{
cout << "姓名:" << this->name << endl
<< "年龄:" << this->age << endl;
}
};
// 1.指定传入类型 --直接显示对象的数据类型
// 这种方法在开发时使用最多
void myPrint1(Person<string, int> p)
{
p.show();
}
void test01()
{
Person<string, int> p1("孙悟空", 100);
myPrint1(p1);
}
// 2.参数模板化,将对象中的参数变为模板进行传递
template<class T1,class T2>
void myPrint2(Person<T1,T2> p)
{
p.show();
cout << "T1的类型为:" << typeid(T1).name() << endl
<< "T2的类型为:" << typeid(T2).name() << endl;
}
void test02()
{
Person<string, int> p2("猪八戒", 90);
myPrint2(p2);
}
// 3. 将整个类模板化
template<class T>
void myPrint3(T p)
{
p.show();
cout << "T的类型为:" << typeid(T).name() << endl;
}
void test03()
{
Person<string, int> p3("唐僧", 30);
myPrint3(p3);
}
int main()
{
//test01();
//test02();
test03();
system("pause");
return 0;
}
类模板与继承
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
template<class T>
class Father
{
public:
T name;
};
// 1.当子类继承父类模板的时候,子类在声明的时候,要指出父类中T的类型
// 否则他无法计算类的大小
//class Son :public Father
// 2.可以单独指出父类的T
// 这样子类就不用模板化
class Son :public Father<string>
{
};
// 3.如果想灵活指定父类中的T类型,子类也需要模板化
template<class T1, class T2>
class Son2 :public Father<T2>
{
public:
Son2()
{
cout << "T1的类型为:" << typeid(T1).name() << endl;
cout << "T2的类型为:" << typeid(T2).name() << endl;
}
T1 obj;
};
void test01()
{
Son2<int, char> s2;
}
int main()
{
test01();
system("pause");
return 0;
}
类模板成员函数类外实现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age);
T1 name;
T2 age;
void show();
};
template<class T1, class T2>
// Person<T1,T2>表明他是类模板内的函数
Person<T1, T2>::Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << "姓名:" << this->name << endl
<< "年龄:" << this->age << endl;
}
void test01()
{
Person<string, int> p("张三",20);
p.show();
}
int main()
{
test01();
system("pause");
return 0;
}
类模板分文件编写
类模板分文件编写.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
// 出错的原因是类模板的成员函数只有在调用是才能生成
// 在编译的时候没生成这些成员函数,所以在链接的时候找不到这些成员函数
// 所以说在编译的时候编译器只走了Person.h这个文件
// 并没有看Person.cpp 中的函数
//#include"Person.h"
// 1.第一种解决方法
// 因为Person.cpp中包含Person.h文件,所以编译器在编译时
// 不仅浏览了头文件,也浏览了成员函数的实现
//#include"Person.cpp"
// 2.将.h文件和.cpp文件写在一起,将后缀名改为.hpp文件
// 约定俗成.hpp文件为类模板的头文件
#include"Person.hpp"
void test01()
{
Person<string, int> p("张三", 20);
p.show();
}
int main()
{
test01();
system("pause");
return 0;
}
Person.h
//#pragma once
//#include<iostream>
//using namespace std;
//template<class T1, class T2>
//class Person
//{
//public:
// Person(T1 name, T2 age);
// T1 name;
// T2 age;
// void show();
//};
Person.cpp
//#include"Person.h"
//
//template<class T1, class T2>
//Person<T1, T2>::Person(T1 name, T2 age)
//{
// this->name = name;
// this->age = age;
//}
//template<class T1, class T2>
//void Person<T1, T2>::show()
//{
// cout << "姓名:" << this->name << endl
// << "年龄:" << this->age << endl;
//}
Person.hpp
#pragma once
#include<iostream>
using namespace std;
template<class T1, class T2>
class Person
{
public:
Person(T1 name, T2 age);
T1 name;
T2 age;
void show();
};
#include"Person.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << "姓名:" << this->name << endl
<< "年龄:" << this->age << endl;
}
类模板与友元
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
// 告诉编译器友这个类
template<class T1, class T2>
class Person;
// 因为Person<T1, T2> p,所以前面要告诉编译器有这个类
template<class T1, class T2>
void show2(Person<T1, T2> p);
// 因为friend void show2<>(Person<T1, T2> p);,所以上面要告诉编译器有这个函数
template<class T1,class T2>
class Person
{
// 1.全局函数类内实现
friend void show(Person<T1,T2> p)
{
cout << "类内实现--> 姓名:" << p.name
<< "年龄:" << p.age << endl;
}
// 2全局函数类外实现
// 如果不加<>就表明是普通函数的声明,而不是模板函数
// 这里不用写template<class T1,class T2>是因为和类共用了;
friend void show2(Person<T1, T2> p);
// 或者这样也行
//template<class T1, class T2>
//friend void show2(Person<T1, T2> p);
public:
Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
private:
T1 name;
T2 age;
};
template<class T1, class T2>
void show2(Person<T1, T2> p)
{
cout << "类外实现--> 姓名:" << p.name
<< "年龄:" << p.age << endl;
}
// 1.全局函数友元类内实现
void test01()
{
Person<string, int> p("张三", 20);
show(p);
}
// 2.全局函数友元类外实现
void test02()
{
Person<string, int> p("张三", 20);
show2(p);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
类模板案例
类模板案例-数组类封装.hpp
#pragma once
#include<iostream>
using namespace std;
// 可以对内置数据类型和自定义数据类型进行存储
template<class T>
class MyArry
{
public:
// 构造函数可以传入数组的容量
MyArry(int capacity)
{
//cout << "参数构造函数的调用" << endl;
this->capacity = capacity;
this->size = 0;
// 将数组中的数据存储到堆区
this->arr = new T[this->capacity];
}
// 提供拷贝构造函数防止浅拷贝的问题
// 浅拷贝的问题也就是堆区数据重复释放
MyArry(const MyArry& arr)
{
//cout << "拷贝函数的调用" << endl;
this->capacity = arr.capacity;
this->size = arr.size;
this->arr = new T[this->capacity];
int i = 0;
for (i = 0; i < this->size; i++)
{
this->arr[i] = arr.arr[i];
}
}
// operator=防止浅拷贝的问题
MyArry& operator=(MyArry& arr)
{
//cout << "operator=的调用" << endl;
if (this->arr != NULL)
{
delete[] this->arr;
this->capacity = 0;
this->size = 0;
//this->arr = NULL;
}
this->capacity = arr.capacity;
this->size = arr.size;
this->arr = new T[this->capacity];
int i = 0;
for (i = 0; i < this->size; i++)
{
this->arr[i] = arr.arr[i];
}
return *this;
}
//可以通过下标的方式访问数组中的数据
T& operator[](int i)
{
return this->arr[i];
}
// 尾插法
void tailInter(const T& val)
{
if (this->capacity == this->size)
{
cout << "内存满" << endl;
return;
}
else
{
this->arr[this->size] = val;
this->size++;
}
}
// 尾删法
void tailDelete()
{
if (this->size == 0)
{
return;
}
this->size--;
}
// 获取当前数组容量
int getCapacity()
{
return this->capacity;
}
// 获取当前元素个数
int getSize()
{
return this->size;
}
// 析构函数对堆区内存释放
~MyArry()
{
if (this->arr != NULL)
{
//cout << "析构函数的调用" << endl;
delete[] this->arr;
this->arr = NULL;
this->capacity = 0;
this->size = 0;
}
}
private:
T* arr; // 在堆区创建数组
int capacity;// 数组容量
int size;// 数组实际内容个数
};
类模板案例-数组类封装.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
#include"类模板案例-数组类封装.hpp"
void myPrint(MyArry<int>& p)
{
for (int i = 0; i < p.getSize(); i++)
{
cout << p[i] << endl;
}
}
void test01()
{
MyArry<int> p1(5);
int i = 0;
for (i = 0; i < 5; i++)
{
p1.tailInter(i);
}
myPrint(p1);
p1.tailDelete();
cout << p1.getCapacity() << endl
<< p1.getSize() << endl;
}
// 自定义数据类型
class Person
{
public:
Person() {};
Person(string name, int age)
{
this->name = name;
this->age = age;
}
string name;
int age;
};
void myPrint2(MyArry<Person>& p)
{
for (int i = 0; i < p.getSize(); i++)
{
cout << "姓名:" << p[i].name
<< "年龄:" << p[i].age << endl;
}
}
void test02()
{
MyArry<Person> p(10);
p.tailInter(Person("张三",13));
p.tailInter(Person("lisi", 14));
p.tailInter(Person("wangwu", 15));
p.tailInter(Person("zhangliu", 16));
myPrint2(p);
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}