建立通用的模具,提高复用性。
特点:模板不可以直接使用,它只是一个框架。
分类:函数模板和类模板。
建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类? ? ? ? ? ? ? ? ? ? ? 型来代表。
template<typename T>
//函数声明或定义
template --- 声明创建模板
typename --- 表示其后面的符号是一种数据类型,可以用class替代。
T --- 通用的数据类型,名称可以替换,通常为大写字母。
例:创建一个交换模板
template<typename T>
void Swap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
有两种使用方式:自动类型推导 和 显示指定类型。
指定类型需在函数后面加上<数据类型>
int main()
{
int a = 10;
int b = 20;
//利用函数模板交换
//1.自动类型推导
Swap(a, b);//编译器自动识别为int
//2.显示指定类型
Swap<int>(a, b);//指定T为int类型
return 0;
}
1.自动类型推导,必须推导出一致的数据类型才可以使用。
2.模板必须确定 T 的数据类型才可以使用。
template<typename T>
void func()
{
}
func();//报错,无法确定T的类型
func<int>();
问:是否可以发生隐式类型转换?
答:普通函数,显示指定类型的模板 --- 可以
? ? ? ?自动类型推导的模板 --- 不可以
//普通函数
int Add(int a, int b)
{
return a + b;
}
//函数模板
template<typename T>
T Add2(T a, T b)
{
return a + b;
}
int main()
{
int a = 10;
int b = 20;
char c = 'c';
cout << Add(a, c) << endl;//109,将c转换为对应的ASSCII码值
cout << Add2(a, c) << endl;//报错
cout << Add2<int>(a, c) << endl;//不报错
return 0;
}
调用规则:
1.普通和模板都可实现,优先普通
2.可通过空模板参数列表强制调用函数模板
3.函数模板可重载
4.如果函数模板可更好的匹配,优先函数模板
实例分析:
void Print(int a, int b)
{
cout << "调用普通函数" << endl;
}
template<class T>
void Print(T a, T b)
{
cout << "调用模板" << endl;
}
1.普通和模板都可实现,优先普通
Print(10,20);
2.可通过空模板参数列表强制调用函数模板
Print<>(10, 20);
3.函数模板可重载
template<class T>
void Print(T a, T b)
{
cout << "调用模板" << endl;
}
template<class T>
void Print(T a)
{
cout << "调用重载模板" << endl;
}
4.如果函数模板可更好的匹配,优先函数模板
Print('c','b');
普通函数也可以使用,需要隐式类型转换。
但是函数模板匹配,故优先模板。
如果函数模板的一些操作不适用于某些类型和自定义类型,那么模板就不适用了。
c++为解决这些问题,提供模板的重载,可以为特定类型提供具体模板。
例:
class Person
{
public:
int age;
Person(int a)
{
age = a;
}
};
#include<stdbool.h>
//对比两个数据是否相等
template<class T>
bool Compare(T& a, T& b)
{
if (a == b)//注意,这里在运行时会报错,因为两Person类无法比较
return true;
else return false;
}
int main()
{
Person p1(10);
Person p2(20);
if (Compare(p1, p2))
{
cout << "相等" << endl;
}
else cout << "不相等" << endl;
return 0;
}
这里两Person类无法比较相等,故会报错。
解决方法:
(1)运算符重载:略
(2)模板重载:
写一个Person类的重载函数,并在函数前加template<>表示是模板的特定重载函数。
template<>bool Compare(Person& a, Person& b)
{
if (a.age == b.age)
return true;
else return false;
}
如果需要多个待定的数据类型,可在模板数据列表中写多个。
template<class NameType,class AgeType>
class Person
{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType n, AgeType a)
{
m_Name = n;
m_Age = a;
}
};
(1)类模板没有自动类型推导,必须指定数据类型
(2)类模板在模板参数列表中可以有默认参数
实例分析:
(1)类模板没有自动类型推导,必须指定数据类型
int main()
{
Person p("孙悟空",1000);//报错,没有参数列表
Person<string, int> p("孙悟空",1000);
return 0;
}
(2)类模板在模板参数列表中可以有默认参数
在template里可以指定默认数据类型
template<class NameType,class AgeType = int>
这样,在创建对象时,可以省略参数。
Person<string> p("孙悟空",1000);//省略了int
类模板中成员函数在调用时才创建。
例:
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};
template<class T>
class Class
{
public:
T obj;
//类模板中的成员函数
void func1()
{
obj.showPerson1();//没有报错
}
void func2()
{
obj.showPerson2();//没有报错
}
};
如上面的代码,在类模板中的成员函数并没有报错,但其实两个成员函数是不兼容的。
说明类模板中的成员函数在调用时才创建。
一共有三种传入方式:
1.指定传入的类型 --- 直接显示对象的数据类型
2.参数模板化 --- 将对象中的参数变为模板进行传递
3.整个类模板化 --- 将这个对象类型 模板化进行传递
实例分析:
1.指定传入的类型 --- 直接显示对象的数据类型:
//1.指定传入的类型-- - 直接显示对象的数据类型
void printPerson1(Person<string, int>& p)
{
p.showPerson();
}
void test01()
{
Person<string, int>p("张三", 500);
printPerson1(p);
}
2.参数模板化 --- 将对象中的参数变为模板进行传递:
//2.参数模板化-- - 将对象中的参数变为模板进行传递
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
p.showPerson();
}
void test02()
{
Person<string, int>p2("李四", 40);
printPerson2(p2);
}
3.整个类模板化 --- 将这个对象类型 模板化进行传递:
//3.整个类模板化-- - 将这个对象类型 模板化进行传递
template<class T>
void printPerson3(T &p)
{
p.showPerson();
}
void test03()
{
Person<string, int>p3("王五", 50);
printPerson3(p3);
}
注:在实际应用中,第一种指定类型最常用。
父类是类模板时,子类 --- 具体化T,或者将子类也写成模板
先来一个模板父类:
template<class T>
class Base
{
public:
T m;
};
(1)具体化T:
在父类后指定T的类型
class Son :public Base<int>
{
};
(2)将子类也写成模板:
模板参数T,在父类后参数列表写T。
注:如果将父类中继承的属性赋值,需加作用域(Base<T> : :)。
template<class T>
class Son2 :public Base<T>
{
public:
Son2(T a)
{
Base<T>::m = a;
}
};
比较普通成员函数类外实现:
(1)在函数上方加模板声明:template<class T1, class T2>
(2)作用域加参数列表:<T1,T2>
template<class T1,class T2>
class Person
{
public:
Person(T1 name, T2 age);
void showPerson()
{
cout << m_name << " " << m_age << endl;
}
T1 m_name;
T2 m_age;
};
template<class T1, class T2>//模板声明
Person<T1,T2>::Person(T1 name, T2 age)//作用域加参数列表
{
m_name = name;
m_age = age;
}
设计一个全局函数展示成员信息:
(1)类内实现:
template<class T1, class T2>
class Person
{
public:
friend void showPerson(Person<T1, T2> p)
{
cout << p.m_name << " " << p.m_age;
}
Person(T1 name, T2 age)
{
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};
(2)类外实现:(复杂,不推荐)
先将类声明,再将函数实现,最后实现类,并在类内声明友元。
template<class T1, class T2>
class Person;
template<class T1, class T2>
void showPerson(Person<T1, T2> p)
{
cout << p.m_name << " " << p.m_age;
}
template<class T1, class T2>
class Person
{
public:
friend void showPerson<>(Person<T1, T2> p);
Person(T1 name, T2 age)
{
m_name = name;
m_age = age;
}
private:
T1 m_name;
T2 m_age;
};