建立通用模具,提高通用性
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
//函数声明或定义
template<typename T>
template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
例子:
#include<iostream>
using namespace std;
//两个整形交换
void changeInt(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
//两个浮点型交换
void changeDouble(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
//声明一个模板,告诉编译器,后面代码如果使用T,不要报错,T是一个泛型
template <typename T>
//使用模板,可以提高交换函数的通用性
void change(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
//此处为自动类型推断,编译器自动推断类型
change(a, b);
cout << "a = " << a
<< ",b = " << b << endl; //a = 20,b = 10
double c = 5.2;
double d = 20.5;
//此处指定类型,告诉编译器此处的T是double类型
change<double>(c, d);
cout << "c = " << c
<< ",d = " << d << endl; //c = 20.5,d = 5.2
return 0;
}
#include<iostream>
using namespace std;
//使用选择排序,对多种类型的数组进行选择排序,从小到大
//声明模板
template<class T>
//选择排序
void chooseSort(T & arr,int len) {
for (int i = 0; i < len; i++) {
int maxIndex = i;
for (int j = i + 1; j < len; j++) {
if (arr[maxIndex] < arr[j]) {
maxIndex = j;
}
}
if (maxIndex != i) {
int temp = arr[i];
arr[i] = arr[maxIndex];
arr[maxIndex] = temp;
}
}
}
//声明模板
template<class T>
//遍历数组
void showArr(T & arr, int len) {
for (int i = 0; i < len; i++) {
cout << "第" << i << "个元素:" << arr[i] << endl;
}
}
int main() {
//int数组
int intArr[] = { 9,66,50,2,35,46,12,57 };
//int数组长度
int intArrLen = sizeof(intArr) / sizeof(intArr[0]);
//进行排序
chooseSort(intArr, intArrLen);
//遍历数组
showArr(intArr, intArrLen);
return 0;
}
例子:
#include<iostream>
using namespace std;
//普通函数
int add(int a, int b) {
return a + b;
}
//自动类型推导的函数模板
template<class T>
T add1(T a, T b) {
return a + b;
}
int main() {
int a = 10;
char b = 'a';
//普通函数,可以发生隐式类型转换,在可以满足自动类型提升的情况下
//例如char->int
cout << add(a, b) << endl; //107
//自动类型推导的函数模板,不可以发生隐式类型转换,必须两个参数为同一类型
//cout << add1(a, b) << endl; //报错
//显式指定类型的函数模板,可以发生隐式类型转换
cout << add1<int>(a, b) << endl;//107
return 0;
}
实际开发中,既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
例子:
#include<iostream>
using namespace std;
//普通函数
void func(int a,int b) {
cout << "普通函数" << endl;
}
//声明定义模板函数
template<class T>
void func(T a, T b) {
cout << "模板函数" << endl;
}
//3、函数模板也可以发生重载
template<class T>
void func(T a) {
cout << "重载的模板函数" << endl;
}
int main() {
int a = 10;
int b = 20;
//1、如果函数模板和普通函数都可以实现,优先调用普通函数
func(a, b);
//2、可以通过空模板参数列表来强制调用函数模板
func<>(a, b);
char c = 'a';
char d = 'b';
//3、如果函数模板可以产生更好的匹配,优先调用函数模板
func(c, d);
return 0;
}
例如:
template<class T>
void f(T a, T b){
a = b;
}
在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了
再例如:
template<class T>
void f(T a, T b){
if(a > b) { ... }
}
在上述代码中,如果T的数据类型传入的是像Person这样的自定义数据类型,也无法正常运行
因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
例子:
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
string name;
int age;
Person(string name, int age) {
this->name = name;
this->age = age;
}
};
template<typename T>
bool equal(T& a, T& b) {
if (a == b) {
return true;
}
else {
return false;
}
}
//解决方法:2、利用具体化的Person来实现
template<> bool equal(Person& a, Person& b) {
if (a.name == b.name && a.age == b.age) {
return true;
}
else {
return false;
}
}
int main() {
int a = 10;
int b = 20;
//对于内置数据类型,可以进行比较
cout << equal(a, b) << endl; //0
Person p1("lucy", 18);
Person p2("tom", 19);
//此时,运行时会报错,解决方法:1、运算符重载,但是太繁琐
cout << equal(p1, p2) << endl;
return 0;
}
建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
template<class T>
class MyClass{
}
例子:
#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;
}
//声明Person属性
NameType name;
AgeType age;
};
int main() {
//使用类模板
Person<string, int> p1("lucy", 18);
cout << "姓名:" << p1.name << ",年龄:" << p1.age << endl;
return 0;
}
类模板与函数模板区别主要有两点:
例子:
#include<iostream>
#include<string>
using namespace std;
//类模板的参数列表,可以有默认参数
template<class NameType = string,class AgeType = int>
class Person {
public:
Person(NameType name,AgeType age) {
this->name = name;
this->age = age;
}
NameType name;
AgeType age;
};
int main() {
//使用类模板,如果有默认的参数,可以不指定类型
Person<> p1("lucy", 18);
cout << "姓名:" << p1.name << ",年龄:" << p1.age << endl;
return 0;
}
类模板中成员函数和普通类中成员函数创建时机是有区别的:
一共有三种传入方式:
例子:
#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 showPerson() {
cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
}
};
//1、指定传入类型
void func1(Person<string,int>& p) {
p.showPerson();
}
//2、参数模板化
template<class T1,class T2>
void func2(Person<T1,T2>& p) {
p.showPerson();
//查看传入的T1和T2类型
cout << "T1:" << typeid(T1).name() << endl;
cout << "T2:" << typeid(T2).name() << endl;
}
//3、将整个类模板化
template<class T>
void func3(T& p) {
p.showPerson();
}
int main() {
Person<string, int> p("lucy", 18);
func1(p);
//指定传入类型
func2<string, int>(p);
//自动类型推导
func2(p);
//自动类型推导
func3(p);
//指定传入类型
func3<Person<string, int>>(p);
return 0;
}
例子:
#include<iostream>
using namespace std;
template<class T>
class Base {
T m;
};
//class son :public Base {}; //错误,必须要指定父类中T的数据类型
//1、指定父类中T的数据类型
class Son1 :public Base<int> {
};
//2、灵活的指定父类中T的类型,子类也需要是一个类模板
template<class T>
class son2 :public Base<T> {
};
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class Person {
public:
T1 name;
T2 age;
//构造函数
Person(T1 name, T2 age);
//成员函数
void showPerson();
};
//类外实现有参构造函数
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>::showPerson() {
cout << "name:" << this->name << ",age:" << this->age << endl;
}
int main() {
Person<string, int> p("lucy", 18);
p.showPerson();
return 0;
}
person.h文件:
#pragma once
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class Person {
public:
T1 name;
T2 age;
Person(T1 name, T2 age);
void showPerson();
};
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>::showPerson() {
cout << "name:" << this->name << ",age:" << this->age << endl;
}
main.cpp文件:
#include<iostream>
#include<string>
//如果这样引入头文件,可以正常编译,但是,执行报错
//原因是:类模板中成员的创建时机是在运行时,编译器无法连接到cpp文件
//#include"person.h"
//解决方法1,不推荐
//编译器可以通过cpp文件中的include连接.h文件
#include"person.cpp"
using namespace std;
int main() {
Person<string, int> p("lucy", 18);
p.showPerson();
return 0;
}
将.h和.cpp的内容写在一起,文件后缀为.hpp
person.hpp文件:
#pragma once
#include<iostream>
#include<string>
using namespace std;
//类模板的声明
template<class T1,class T2>
class Person {
public:
T1 name;
T2 age;
Person(T1 name, T2 age);
void showPerson();
};
//类模板成员函数的实现
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>::showPerson() {
cout << "name:" << this->name << ",age:" << this->age << endl;
}
main.cpp文件:
#include<iostream>
#include<string>
#include"person.hpp"
using namespace std;
int main() {
Person<string, int> p("lucy", 18);
p.showPerson();
return 0;
}
全局函数类内实现 - 直接在类内声明友元即可
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
建议全局函数做类内实现,用法简单,而且编译器可以直接识别
全局函数类内实现:
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class Person {
//全局函数类内实现
friend void printPerson(Person<T1,T2> p) {
cout << p.name << p.age << endl;
}
public:
Person(T1 name, T2 age) {
this->name = name;
this->age = age;
}
private:
T1 name;
T2 age;
};
int main() {
Person<string, int> p("lucy", 17);
printPerson(p);
return 0;
}
全局函数类外实现:
#include<iostream>
#include<string>
using namespace std;
//需要让编译器提前知道有Person类的存在
template<class T1, class T2>
class Person;
//实现
template<class T1, class T2>
void printPerson(Person<T1, T2> p) {
cout << p.name << p.age << endl;
}
template<class T1,class T2>
class Person {
//全局函数类外实现
//1、需要添加一个空模板的参数列表,如果不加,会报错:1 个无法解析的外部命令
friend void printPerson<>(Person<T1, T2> p);
public:
Person(T1 name, T2 age) {
this->name = name;
this->age = age;
}
private:
T1 name;
T2 age;
};
int main() {
Person<string, int> p("lucy", 17);
printPerson(p);
return 0;
}
实现一个通用的数组类,要求如下:
MyArray.hpp文件:
#pragma once
#include<iostream>
using namespace std;
//编写通用的数组类
template<typename T>
class MyArray{
private:
//数组
T * arr;
//数组的容量
int capacity;
//数组的大小
int size;
public:
//有参构造,指定初始容量
MyArray(int capacity){
cout << "有参构造" << endl;
this->capacity = capacity;
this->arr = new T[capacity];
}
//析构函数,手动释放
~MyArray(){
cout << "析构" << endl;
if(this->arr != NULL){
delete[] this->arr;
this->arr = NULL; //置空数组指针,防止野指针
}
}
//拷贝构造,深拷贝
MyArray(const MyArray& arr){
//获取容量和大小
this->size = arr.size;
this->capacity = arr.capacity;
//堆区new一个新的数组,容量和拷贝的数组一样
this->arr = new T[arr.capacity];
//将所有的数据都拷贝过来
for(int i = 0;i < this->size;i++){
this->arr[i] = arr[i];
}
}
//重载=,防止直接=赋值出现浅拷贝问题
MyArray& operator=(const MyArray& arr){
//判断当前对象数组是否在堆区已经有数据,如果有,先释放
if(this->arr != NULL){
delete[] this->arr;
this->arr = NULL;
this->capacity = 0;
this->size = 0;
}
//进行深拷贝
this->size = arr.size;
this->capacity = arr.capacity;
this->arr = new T[arr.capacity];
for(int i = 0;i < this->size;i++){
this->arr[i] = arr[i];
}
}
//尾插法,新增元素
void push(const T& value){
//判断容量是否等于大小
if(this->size == this->capacity){
return;
}
this->arr[this->size] = value;
this->size++;
}
//尾删法,删除元素
void remove(){
delete arr[this->size - 1];
arr[this->size - 1] = NULL;
this->size--;
}
//支持下标访问元素,重载[]
T& operator[](int index){
return this->arr[index];
}
//获取容量
int getCapacity(){
return this->capacity;
}
//获取大小
int getSize(){
return this->size;
}
};
main.cpp文件:
```cp
#include<iostream>
#include"MyArray.hpp"
using namespace std;
int main(){
MyArray<int> array(10);
array.push(521);
array.push(13);
cout << array[0] << endl;
cout << array[1] << endl;
cout << "数组的容量:" << array.getCapacity() << endl;
cout << "数组的大小:" << array.getSize() << endl;
system("pause");
return 0;
}