目录
模板
函数模板
语法:
template<typename T> 函数声明或定义
//函数模板 //声明一个模板,告诉编译器后面的代码中紧跟着的T不要报错,T是一个通用数据类型 template<typename T> void MySwap(T& a, T& b) { T Temp = a; a = b; b = Temp; } void test01() { int a = 10; int b = 20; //利用函数模板进行交换 //1.自动类型推导 MySwap(a, b); double c = 11.1; double d = 12.2; //显示指定类型 MySwap<double>(c, d); }
MySwap<double>(c,d);
- 自动类型推导,必须推导出一致的数据类型T才能使用
- 模板必须要确定出T的数据类型,才可以使用
案例 (从大到小排序)
template<class T>
void mySort(T arr[],int len)
{
for (int i = 0; i < len; i++)
{
int max = i;//认定最大值的下标
for (int j = i + 1; j < len; j++)
{
//认定的最大值比遍历出的数值要小,说明j下标的元素才是真正的最大值
if (arr[max] < arr[j])
{
max = j;
}
}
if (max != i)
{
//交换max和i元素
mySwap(arr[max], arr[i]);
}
}
}
int a = 10; int b = 20; char c = 'c'; cout << myAdd01(a, c) << endl; //自动类型推导不行 //cout << myAdd02(a, c) << endl; //显式指定类型行 cout << myAdd02<int>(a, c) << endl;
template<class T>
bool myCompare(T& a, T& b)
{
if (a == b)
{
return true;
}
else
{
return false;
}
}
//利用具体化Person的版本来实现代码,具体优化优先调用
template<>bool myCompare(Person& p1, Person& p2)
{
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
{
return true;
}
else
{
return false;
}
}
为什么第二个<>中为空
在函数模板具体化的语法中,可以省略模板参数类型,编译器会自动根据函数参数类型来推导模板参数类型。
类模板
类模板语法
类模板作用:
建立一个通用类, 类中成员数据可以不具体指定,用一个虚拟的类型来代表
语法:
template<typename T>
template< typename T>——声明创建模板
typename——表明其后面的符号是一种数据类型,可以用class代替
T——通用的数据类型,名称可以替换,通常为大写字母
类模板与函数模板的区别
类模板与函数模板的区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
类模板与继承
当类模板碰到继承时,需要注意一下几点:
- 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
- 如果不想指定,编译器无法给子类分配内存‘
- 如果想灵活指定出父类中T的类型,子类也需要变为类模板
-
//类模板与继承 template<class T> class Base { T m; }; class Son :public Base<int>//必须要知道父类中T的数据类型才能继承给子类 { }; //如果想灵活指定父类中T类型,子类也需要变类模板 template<class T1,class T2> class Son2 :public Base<T2> { public: Son2() { cout << typeid(T1).name()<< endl; cout << typeid(T2).name()<< endl; } T1 obj; };
类模板成员函数类外实现
template<class T1,class T2> class Person { public: void showPerson(); T1 m_Name; T2 m_Age; }; //构造函数类外实现 template<class T1,class T2> void Person<T1,T2>::showPerson() { }
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表)
// void Person<T1,T2>::showPerson()
案例(拷贝构造 | 深浅拷贝问题)
//拷贝构造
MyArry(const MyArry& arr)
{
//cout << "MyArry的拷贝构造调用" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//this->pAddress = arr.pAddress;
//深拷贝
this->pAddress = new T[arr.m_Capacity];//不是T[arr.pAddress,而是按照你传进来的数组的容量;
//将arr中的数据都拷贝过来
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
}
等号重载
//operator= 防止浅拷贝问题
MyArry& operator =(const MyArry& arr)
{
//cout << "MyArry的operator=调用" << endl;
//先判断原来堆区是否有数据,如果有先释放
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
//深拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++)
{
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
如果你想返回void,原式:a=b=c;//想做连等的操作,发现不行;
只能返回引用;
重载 ' [ ] '
T& operator[](int index)
{ return this->pAddress[index]; }
为什么返回引用?
这个代码返回引用是因为它想要实现对数组元素的修改。当我们使用 `[]` 运算符访问数组元素时,通常会返回一个值,这个值是数组元素的副本。但是,如果我们想要修改数组元素,我们需要返回一个引用,这样我们才能通过引用修改原始数组元素的值。 在这个代码中,`this->pAddress[index]` 返回的是一个 `T` 类型的值,但是通过 `return` 关键字返回时,它被转换为了一个 `T&` 类型的引用。这样,当我们使用 `[]` 运算符访问数组元素并且对其进行修改时,实际上是修改了原始数组元素的值。