1.函数模板
函数模板的作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表
语法
template<typename T>
函数的声明或定义
template --- 声明创建模板
typename --- 表面其背后的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
#include <iostream>
using namespace std;
template<typename T>
void myswap(T &a,T &b)
{
T temp = a;
a = b;
b= temp;
}
void test()
{
int a = 10;
int b = 20;
myswap<int>(a,b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
int main()
{
test();
return 0;
}
函数模板案例
案例描述:
利用函数封装一个排序的数组,可以对不同数据类型数组进行排序
排序规则从小到大,排序算法为选择排序
#include <iostream>
using namespace std;
template<typename T>
void myswap(T &a,T &b)
{
T temp=a;
a = b;
b= temp;
}
template<typename T>
void sort(T arr[],int len)
{
for(int i=0;i<len;i++)
{
int max=i;
for(int j=i+1;j<len;j++)
{
if(arr[max]>arr[j])
{
max = j;
}
}
if(max != arr[i])
{
myswap(arr[i],arr[max]);
}
}
}
int main()
{
char s[]="fecba";;
sort(s,5);
cout << s;
return 0;
}
模板的局限性
模板的通用性并不是万能的
template<class T>
void f(T a,T b)
{
if(a > b){ ... }
}
在上述代码中,如果传入的是数组或者类,就无法实现了
2.类模板
类模板的作用:
建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
语法:
template<typename T>
类
实例:
#include <iostream>
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 << "name:" << name << endl;
cout << "age:" << age << endl;
}
};
void test()
{
Person<string,int> p1("小明",28);
p1.showPerson();
}
int main()
{
test();
return 0;
}
类模版和函数模板区别:
1.类模版没有自动类型推导使用方式
template<class NameType,class AgeType = int>
class Person
{
public:
Person(NameType name,AgeType age)
{
this->name=name;
this->age=age;
}
NameType name;
AgeType age;
void showPerson()
{
cout << "name:" << name << endl;
cout << "age:" << age << endl;
}
};
void test()
{
Person p1("小明",28)//错误,类模版使用的时候不可以用自动类型推导
Person<string,int> p1("小明",28);//必须使用指定类型的方式,使用类模版
p1.showPerson();
}
int main()
{
test();
return 0;
}
2.类模版在模板参数列表中可以有默认参数
template<class NameType,class AgeType = int>
类模版与继承
template<class T>
class Base
{
T m;
};
//class Son : public Base 这样继承是错误的,必须知道父类中的T类型,才能继承给子类
class Son : public Base<int>
{
};
类模板成员函数类外实现
#include <iostream>
using namespace std;
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age);
void showPerson();
T1 m_Name;
T2 m_Age;
};
//构造函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->m_Name=name;
this->m_Age=age;
}
//成员函数类外实现
template<class T1,class T2>
void Person<T1, T2>::showPerson()
{
cout << "name:" << m_Name << endl;
cout << "age:" << m_Age << endl;
}
int main() {
Person<string,int>p1("小明",22);
p1.showPerson();
return 0;
}
类模版分文件编写
问题:
类模版中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
解决方式1:直接包含.cpp文件
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,.hpp是约定的名称,并不是强制
类模版与友元
全局函数类内实现 - 直接在类内声明友元即可
全局函数类外实现 - 需要提前要编译器知道全局函数的存在
#include <iostream>
using namespace std;
//通过全局函数 打印Person信息
template<class T1,class T2>
class Person
{
//全局函数类内实现
friend void printPerson1(Person<T1,T2>p)
{
cout << "类内实现 --- 姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数类外实现
//类外实现要加上template,否则不能类外实现
template<class t1,class t2>
friend void printPerson2(Person<t1,t2>p);
public:
Person(T1 name,T2 age)
{
this->m_Name=name;
this->m_Age=age;
};
private:
T1 m_Name;
T2 m_Age;
};
template<class T1,class T2>
void printPerson2(Person<T1, T2> p) {
cout << "类外实现 --- 姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
}
//全局函数在类内实现
void test01()
{
Person<string ,int>p("Jerry",20);
printPerson1(p);
}
//全局函数在类外实现
void test02()
{
Person<string,int>p("Tim",28);
printPerson2(p);
}
int main() {
test01();
test02();
return 0;
}
类模版案例
案例描述:实现一个通用的数组类,要求如下:
- 可以对内置数据类型以及自定义数据类型的数据进行存储
- 将数组中的数据存储到堆区
- 构造函数中可以传入数组的容量
- 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
- 提供尾插法和尾删法对数组中的数据进行增加和删除
- 可以通过下标的方式访问数组中的元素
- 可以获取数组中当前元素个数和数组的容量
主函数如下:
#include <iostream>
#include<string>
#include "MyArray.hpp"
using namespace std;
class Person {
public:
// 默认构造函数
Person() : name(""), age(0) {};
// 构造函数声明
Person(string m_name, int m_age);
// 打印函数用于测试
void print() const;
string name; // 姓名
int age; // 年龄
};
// 构造函数定义
Person::Person(string m_name, int m_age) : name(m_name), age(m_age) {
// 这里可以进行其他初始化或操作
}
// 打印函数定义
void Person::print() const {
cout << "Name: " << name << ", Age: " << age << endl;
}
void test01()
{
MyArray<int>arr1(5);
for(int i=0;i<5;i++)
{
arr1.Push_Back(i);
}
for(int i=0;i<arr1.getSize();i++)
{
cout << arr1[i] << endl;
}
cout << "arr1的大小为:" << arr1.getSize() << endl;
cout << "arr1的容量为:" << arr1.getCapacity() << endl;
MyArray<int>arr2(arr1);
arr2.Pop_Back();
for(int i=0;i<arr2.getSize();i++)
{
cout << arr2[i] << endl;
}
cout << "arr2的大小为:" << arr2.getSize() << endl;
cout << "arr2的容量为:" << arr2.getCapacity() << endl;
}
void test02()
{
Person p1("小明", 20);
Person p2("小红", 18);
Person p3("小亮", 19);
MyArray<Person> arr1(10);
arr1.Push_Back(p1);
arr1.Push_Back(p2);
arr1.Push_Back(p3);
for (int i = 0; i < arr1.getSize(); i++)
{
Person& p = arr1[i]; // 从 MyArray 中获取 Person 对象
cout << "姓名: " << p.name << ", 年龄: " << p.age << endl;
}
cout << "arr1的大小:" << arr1.getSize() << endl;
cout << "arr1的容量:" << arr1.getCapacity() << endl;
}
int main() {
test02();
return 0;
}
MyArray实现如下:
#pragma once
#include "iostream"
using namespace std;
template<class T>
class MyArray
{
public:
//有参构造 参数 容量
MyArray(int capacity)
{
cout << "有参构造" << endl;
this->m_Capacity=capacity;
this->m_Size=0;
pAddress = new T[this->m_Capacity];
}
//拷贝构造
MyArray(const MyArray& arr)
{
cout << "拷贝构造" << endl;
this->m_Capacity=arr.m_Capacity;
this->m_Size=arr.m_Size;
//深拷贝
this->pAddress=new T[arr.m_Capacity];
//将原来数组数据都拷贝过来
for(int i=0;i<arr.m_Size;i++)
{
this->pAddress[i]=arr.pAddress[i];
}
}
//operator= 防止浅拷贝问题
MyArray& operator=(const MyArray& arr)
{
cout << "operator=调用" <<endl;
if (this->pAddress!= nullptr)
{
delete[] this->pAddress;
this->pAddress= nullptr;
}
//深拷贝
this->m_Capacity=arr.m_Capacity;
this->m_Size=arr.m_Size;
this->pAddress=new T[arr.m_Capacity];
for(int i=0;i<arr.m_Size;i++)
{
this->pAddress[i]=arr.pAddress[i];
}
return *this;
}
//尾插法
void Push_Back(const T& val)
{
if(this->m_Capacity==this->m_Size)
{
return;
}
this->pAddress[this->m_Size++]=val;
}
//尾删法
void Pop_Back()
{
if(this->m_Size==0)
{
return;
}
this->m_Size--;
}
//通过下标方式访问数组中的元素
T& operator[](int index)
{
return this->pAddress[index];
}
//返回数组容量
int getCapacity()
{
return this->m_Capacity;
}
//返回数组长度
int getSize()
{
return this->m_Size;
}
//析构函数
~MyArray()
{
if(this->pAddress!= nullptr)
{
cout << "析构调用" << endl;
delete[] this->pAddress;
this->pAddress= nullptr;
}
}
private:
T* pAddress;//指针指向堆区开辟的真实数组
int m_Capacity;//数组容量
int m_Size;//数组大小
};