目录
一、函数模板的使用
一、使用方法
二、用模板实现选择排序
三、普通函数与函数模板的区别
四、普通函数与函数模板的使用规则
五、函数模板的局限性
二、类模板的使用
一、使用方法
二、类模板成员对象创建时机
三、类模板传参
四、类模板与继承
五、类模板成员函数类外实现
六、类模板的分文件编写
七、类模板与友元
八、实例-模板数组
模板属于C++的泛型编程,它通过建立通用模板,大大提高了代码复用性
一、函数模板的使用
一、使用方法
先声明模板的虚拟类型
template
后面紧跟着函数声明或定义,其参数类型用T替换
如交换函数
#includeusing namespace std; template void Tswap(T&x, T&y) { T ret = x; x = y; y = ret; } int main() { int a = 10, b = 20; double c = 3.1415926, d = 2.7141; cout << "a=" << a << ",b=" << b << endl; Tswap(a, b); cout << "a=" << a << ",b=" << b << endl; cout << "c=" << c << ",c=" << d << endl; Tswap (c, d); cout << "c=" << c << ",c=" << d << endl; return 0; }
可以看到,通过模板,交换函数能实现对不同数据类型的交换,使用时,可以直接传参赋值,编译器会自动推导类型,但两个数的类型必须相同,也可以指明数据类型。
#includeusing namespace std; template void Tswap(T1&x, T2&y) { T1 ret = x; x = y; y = ret; } int main() { int a = 10, b = 20; double c = 3.1415926, d = 2.7141; cout << "a=" << a << ",c=" << c << endl; Tswap(a, c); cout << "a=" << a << ",c=" << c << endl; cout << "b=" << b << ",d=" << d << endl; Tswap (b, d); cout << "b=" << b << ",d=" << d << endl; return 0; }
模板参数类型声明可用typename,也可以用class,多个不同类型,可以声明多个虚拟参数来实现自动类型推导。
模板必须要确定数据类型才能使用
#includeusing namespace std; template void func() { cout << "this is a function" << endl; } int main() { func (); }
fun即使不传参,也要指明数据类型才能使用
二、用模板实现选择排序
#include
using namespace std;
template
void TSort(T*a,int len)
{
for (int i = 0; i < len; i++)
{
int min = i;
for (int j = i + 1; j < len; j++)
if (a[min] > a[j])
min = j;
if (min != i)
{
T ret = a[min];
a[min] = a[i];
a[i] = ret;
}
}
}
template
void PrintArray(T*a, int len)
{
for (int i = 0; i < len; i++)
cout << a[i] << " ";
cout << endl;
}
int main()
{
int a[] = { 3,4,7,1,2,8,9,10,4,6 };
char c[] = "qwerttyuiop";
TSort(a,sizeof(a)/sizeof(a[0]));
TSort(c, sizeof(c) / sizeof(c[0])-1);//' '不能参与排序
PrintArray(a, sizeof(a) / sizeof(a[0]));
PrintArray(c, sizeof(c) / sizeof(c[0]));//' '要打印出来
return 0;
}
其实就是原封不动,将数据类型换成了T,可以看到,不同类型的数据可以共有这一个函数实现功能,这便是模板的意义,提高代码利用率。
三、普通函数与函数模板的区别
普通函数可以发生隐式类型转化,而函数模板在自动类型推导的情况下无法发生隐式类型转化,在显示指定类型的情况下可以发生隐式类型转化。
#includeusing namespace std; template void Print(T a,T b) { cout << "a+b=" << a + b << endl; } int main() { int a = 10; char b = 'a'; //Print(a, b);不行无法隐式类型转化 Print (a, b);//会把b转化为int类型 Print (a, b);//会把a转化为char类型,打印出来的仍为ascii码值 return 0; }
四、普通函数与函数模板的使用规则
1.如果普通函数和函数模板都可以实现这一功能,优先使用普通函数、
2.可以通过空模板参数列表强制调用模板
3.函数模板也能发生重载
4.如果函数模板能产生更好的匹配
#includeusing namespace std; void Print(int a, int b) { cout << "调用普通函数" << endl; } template void Print(T a, T b) { cout << "调用函数模板" << endl; } template void Print(T a, T b, T c) { cout << "调用重载的函数模板" << endl; } int main() { int a = 10, b = 10; char c1 = 'a', c2 = 'b'; Print(a, b);//调用普通函数 Print<>(a, b);//调用函数模板 Print(a, b, 100);//调用重载的函数模板 Print(c1, c2);//调用函数模板 return 0; }
如果提供了函数模板,最好不要创建普通函数,以免调用出错。
五、函数模板的局限性
对于自定义的数据类型,如类和结构体,编译器无法识别并完成比较,运算等操作。
解决办法有两个,一个是运算符重载,二是模板重载
#includeusing namespace std; class Person { public: int age; int money; }; template T Add(T&a, T&b) { return a + b; } Person&operator+(Person&a,Person&b) { Person*temp = new Person; temp->age = a.age+b.age; temp->money = a.money + b.money; return *temp; } template bool compare(T&a, T&b) { return a == b; } template<> bool compare(Person&a, Person&b) { return a.age == b.age&&a.money == b.money; } int main() { Person p1 = { 12,1 }, p2 = { 12,1 }, p3; if (compare(p1, p2)) cout << "p1==p2" << endl; else cout << "p1!=p2" << endl; p3 = Add(p1, p2); cout << "age=" << p3.age < 对模板的学习并不是为了写模板,而是学会使用STL中系统提供的模板
二、类模板的使用
一、使用方法
使用类模板必须使用参数列表指明数据类型,不能自动类型转化
#include#include using namespace std; template //默认Age为int类型· class Person { public: Name m_name; Age m_age; Person(Name name, Age age) { this->m_age = age; this->m_name = name; } void show() { cout << this->m_name << " "<< this->m_age << "岁" << endl; } }; int main() { Person p( "齐天大圣", 1000 ); Person q("艾伦*耶格尔", 20); p.show(); q.show(); return 0; } 仅在类模板中模板参数列表可以赋默认值
二、类模板成员对象创建时机
#includeusing namespace std; class A { public: void showA() { cout << "this is class A" << endl; } }; class B { public: void showB() { cout << "this is class B" << endl; } }; template class C { public: T obj; void show1() { obj.showA(); } void show2() { obj.showB(); } }; int main() { Cc1; c1.show1(); Cc2; c2.show2(); return 0; } 成员对象是在确定了参数模板里的数据类型后创建的,只能调用模板参数类型对应的类中函数
三、类模板传参
类模板传参又三种方法,第一种常用
#include#include using namespace std; template class Person { public: Name m_name; Age m_age; Person(Name name, Age age) { this->m_name = name; this->m_age = age; } void showinfo() { cout << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl; } }; //第一种:指定传入类型 void fun1(Person &p) { p.showinfo(); } //第二种:参数模板化 template void fun2(Person &p) { p.showinfo(); cout << typeid(T1).name() << endl << typeid(T2).name() << endl; } //第三种:整个类模板化 template void fun3(T&p) { p.showinfo(); cout << typeid(T).name() << endl; } int main() { Person p("艾伦*耶格尔", 21); fun1(p); fun2(p); fun3(p); return 0; } typeid(模板参数).name(),可显示·参数变量的全名。
四、类模板与继承
当子类继承的父类是一个类模板时,必须指定父类中T的类型,如果想灵活的指定父类中T的类型,子类也需要变为类模板。
#includeusing namespace std; template class A { public: T m; }; class B :public A { }; template class C :public A { public: T2 obj; void showname() { cout << typeid(T1).name() << endl << typeid(T2).name() << endl; } }; int main() { B b; C c; c.showname(); return 0; } 第一种是在子类继承父类的后面加一个参数列表,指定对应的模板参数类型。
第二种,将子类也设为类模板,设置子类自身以及父类的参数模板类型。
五、类模板成员函数类外实现
#include#include using namespace std; template class Person { public: T1 name; T2 age; Person(T1 name, T2 age); void showinfo(); }; template Person ::Person(T1 name, T2 age) { this->age = age; this->name = name; } template void Person ::showinfo() { cout<<"姓名:" << this->name << " 年龄:" << this->age << endl; } int main() { Person p("艾伦*耶格尔", 21); p.showinfo(); return 0; } 六、类模板的分文件编写
由于类模板成员函数是在调用阶段才创建,分文件编写,会导致链接不到。
有两种解决办法。一、引用cpp做头文件,因为cpp里引用了.h的头文件。
二、将类的声明与实现写在一个文件中,并命名为.hpp。
七、类模板与友元
#include#include using namespace std; template class Person; template void showinfo2(Person &p) { cout << "类外实现——姓名:" << p.m_name << " 年龄" << p.m_age< class Person { friend void showinfo1(Person &p) { cout << "类内实现——姓名:" << p.m_name << " 年龄" << p.m_age << endl;; } friend void showinfo2<>(Person &p); public: Person(T1 name, T2 age) { this->m_age = age; this->m_name = name; } private: T1 m_name; T2 m_age; }; int main() { Person p("艾伦*耶格尔", 21); showinfo1(p); showinfo2(p); return 0; } 类内友元比较好写,类外友元要写在friend 声明之前。且friend声明要用空参数列表标识其为模板,然后要声明Person类模板。
八、实例-模板数组
#include#include using namespace std; template class Array { T*base; int capacity; int size; public: //构造函数 Array(int n) { this->capacity = n; this->base = new T[this->capacity]; this->size = 0; } //拷贝构造,注意深拷贝 Array(const Array&a) { this->capacity = a.capacity; this->size = a.size; this->base = new T[this->capacity]; for (int i = 0; i < this->size; i++) this->base[i] = a.base[i]; } Array&operator=(const Array&a) { if (this->base) { delete[]this->base; this->base = NULL; this->capacity = 0; this->size = 0; } this->capacity = a.capacity; this->size = a.size; this->base = new T[this->capacity]; for (int i = 0; i < this->size; i++) this->base[i] = a.base[i]; return *this; } //尾插 void Push_Back(const T&a) { if (this->capacity == this->size) return; this->base[this->size++] = a; } //尾删 void Pop_Back() { if (this->size == 0) return; this->size--; } T&operator[](int index) { return this->base[index]; } int Size() { return this->size; } int Capacity() { return this->capacity; } ~Array() { if (this->base) { delete []base; base = NULL; } } }; int main() { Array a(10); for (int i = 0; i < 10; i++) a.Push_Back(i); while (a.Size()>0) { cout << a[a.Size() - 1] << " "; a.Pop_Back(); } cout << endl; Array c(10); for (int i = 97; i < 107; i++) c.Push_Back(i); Array cc(c); while (c.Size() > 0) { cout << c[c.Size() - 1] << " "; c.Pop_Back(); } cout << endl; for (int i = 0; i < cc.Size(); i++) cout << cc[i] << " "; cout << endl; } 自定义实现不同数据类型的数组,这便是STL容器所应用的,模板栈,模板队列....
看到这,点个赞再走吧



