CPP_template
泛型编程是独立于任何特定类型的方式编写代码。模板是泛型编程的基础,模板使程序员能够快速建立具有类型安全的类库集合和函数集合,它的实现,方便了大规模的软件开发。
模板提供通用类型和通用函数,定义中包含template,和一对尖括号<>,尖括号里面是模板参数。模板参数与普通参数的区别在于,模板参数不仅可以传变量和值,还可以传类型。模板参数中定义类型可以使用关键字typename或class(效果一样,有的开发者为区分,函数使用typename,类使用class,实际等同);参数名默认是T,可以为任意字符,效果等同,只是T较常用,默认。
模板应放到头文件中。模板本质上就是一种宏定义。
一. 函数模板
template<typename T> 返回类型 function(形式参数表) { //函数定义体 }
template ---- 声明创建模板
typename ---- 表明其后面的符号是一种数据类型,可以用class代替
T ---- 通用数据类型,名称可以替换,通常为大写字母
template <class T> void myswap(T &a, T &b) { T temp = a; a = b; b = temp; }
使用函数模板有两种方式:自动类型推导、显示指定类型
void test01() { int a = 10, b = 20; // myswap(a, b); myswap<int>(a, b); cout << "a = " << a << endl; cout << "b = " << b << endl;
模板的目的是为了提高复用性,将类型参数化。
注:1)自动类型推导,必须推导出一致的数据类型T才可以使用。
2)模板必须要确定出T的数据类型,才可以使用。(比如函数没有参数且不能自动推导出T类型)
template<class T> void func() { cout << "call func" << endl; } int main() { // func(); // 不能推导出T类型 func<int>(); return 0; }
普通函数和函数模板区别:
普通函数调用可以发生自动类型转换(隐式类型装换);函数模板调用时如果利用自动类型推导,不会发生隐式类型转换;如果利用显示指定类型的方式,可以发生隐式类型转换。
建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T。
普通函数与函数模板的调用规则(函数重载时):
1)如果函数模板和普通函数都可以实现,优先调用普通函数;
2)可以通过空模板参数列表来强制调用函数模板;myPrintf<>(a, b);
3)函数模板也可以发生重载;
4)如果函数模板可以产生更好的匹配,优先调用函数模板。
建议在使用模板时,不要提供普通函数(两者不要同时出现)。
模板局限性:自定义类型操作
C++为了解决自定义数据类型的无法正常运行问题,提供模板的重载,为这些特定的类型提供具体化的模板。
#include <iostream> #include <cstdlib> #include <unistd.h> #include <string> using namespace std; class Person { public: Person(string name, int age){ this->m_name = name; this->m_age = age; } string m_name; int m_age; }; 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; } } void test02() { Person p1("Tom", 10); Person p2("Tom", 10); cout << myCompare(p1, p2) << endl; } int main(int argc, char *argv[]) { test02(); pause(); return 0; }
利用具体化的模板可以解决自定义类型的通用化。
学习模板并不是为了能写模板,而是在STL能够运用系统提供的模板。
二. 类模板
template <模板参数表> class 类名{};
template<class T1, class T2> class 类{ 类成员声明; };
T是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。
在类模板以外定义成员函数
template <模板参数表> 类型名 类名<参数列表>::函数名(参数表)
模板类中的函数都是模板函数。
template <class T> Node<T>::~Node()
{
......
}
#include <iostream> #include <string> #include <unistd.h> using namespace std; template<class NameType=string, class AgeType=short> class Person { public: Person(NameType name, AgeType age) { m_name = name; m_age = age; } void showInfo() { cout << m_name << " is " << m_age << (m_age == 1 ? " year" : " years") << " old. " << endl; } NameType m_name; AgeType m_age; }; class Wang: public Person<string, short> { }; void printPerson1(Person<string, short> &p) { p.showInfo(); } template <class NameType, class AgeType> void printPerson2(Person<NameType, AgeType> &p){ p.showInfo(); cout << "\t NameType:" << typeid(NameType).name() << endl << "\t AgetType:" << typeid(AgeType).name() << endl; } template <typename T> void printPerson3(T &p){ p.showInfo(); cout << "\t T:" << typeid(T).name() << endl; } void test1(){ Person<string, short> p1("tony", 100); printPerson1(p1); Person<string, short> p2("AAA", 99); printPerson2(p2); Person<string, short> p3("BBB", 98); printPerson3(p3); Wang w1; cout << "w.m_name is " << typeid(w1.m_name).name() << endl; cout << "w.m_age is " << typeid(w1.m_age).name() << endl; } int main(){ test1(); pause(); return 0; }
参考:
1. C++模板 runoob
3. 黑马程序员 C++ https://www.bilibili.com/video/BV1et411b73Z?p=169&spm_id_from=pageDriver