- 1.模板的概念
- 2.函数模板
- 2.1 函数模板语法
- 2.2 函数模板的注意事项
- 2.3 普通函数与函数模板的区别
- 2.4 普通函数与函数模板的调用规则
- 2.5 模板的局限性
- 3.类模板
- 3.1 类模板语法
- 3.2 类模板与函数模板区别
- 3.3 类模板中成员函数创建时机
- 3.4 类模板对象做函数参数
- 3.5 类模板与继承
- 3.6 类模板成员函数类外实现
- 3.7 类模板分文件编写
- 3.8 类模板与友元
模板就是建立通用的摸具,大大提高复用性。其特点:
1)模板不可以直接使用,它只是一个框架。
2)模板的通用并不是万能的
C++另一种编程思想称为泛型编程,主要利用的技术就是模板。
C++提供两种给模板机制:函数模板和类模板
函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
语法:template
template:声明创建模板;typename:声明其后面的符号是一种数据类型,可以用class替代;T:通用的数据类型,名称可以替换,通常为大写字母。
#include2.2 函数模板的注意事项#include #include using namespace std; template void Swap(T& a, T& b) { T c = a; a = b; b = c; } int main() { //1.自动类型推导 int a = 10; int b = 20; Swap(a, b); cout << a << " " << b << endl; //2.显示指定类型 Swap (a, b); cout << a << " " << b << endl; return 0; }
注意事项:
1)自动类型推导,必须推导出一致的数据类型T,才可以使用
#include#include #include using namespace std; template void Swap(T& a, T& b) { T c = a; a = b; b = c; } int main() { int a = 10; int b = 20; char c = 'd'; //Swap(a, c);//错误,必须推导出一致的数据类型T,才可以使用 cout << a << " " << c << endl; return 0; }
2)模板必须要确定出T的数据类型,才可以使用。
#include2.3 普通函数与函数模板的区别#include #include using namespace std; template void fun() { cout << "zhinen" << endl; } int main() { //func();//错误,模板必须要确定出T的数据类型,才可以使用。 fun (); return 0; }
区别:
1)普通函数调用时可以发生自动类型转换(隐式类型转换)
2)函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
3)如果利用显示制定类型的方式,可以发生隐式类型转换
#include2.4 普通函数与函数模板的调用规则#include #include using namespace std; int fun(int a, int b) { return a + b; } template T fun1(T a, T b) { return a + b; } int main() { int a = 18, b = 18; char c = 'c'; cout << fun(a, b) << endl; cout << fun(a, c) << endl;//字符变量自动转换成ASCII码,可以发生隐式转换。 cout << fun1(a, b) << endl; //自动类型推导,不会发生隐式类型转换 //cout << fun1(a, c) << endl;//错误,没有与参数列表匹配的参数类型 //显示指定类型,可以发生隐式类型转换 cout << fun1 (a, c) << endl; return 0; }
规则如下:
1)如果函数模板和普通函数都可以实现,优先调用普通函数;
2)可以通过空模板参数列表来强制调用函数模板;
3)函数模板可以发生重载
4)如果函数模板可以产生更好的匹配,优先调用函数模板
#include2.5 模板的局限性#include #include using namespace std; void fun(int a, int b) { cout << "普通函数" << endl; } template void fun(T a, T b) { cout << "函数模板fun(T a, T b)" << endl; } template void fun(T a, T b, T c) { cout << "函数模板fun(T a, T b,T c)" << endl; } int main() { int a = 1, b = 1; fun(a, b);//优先选择普通函数 fun<>(a, b);//空模板参数列表,强制调用函数模板 fun(a, b, 10); fun('a', 'b');//更好的匹配 return 0; }
局限性:模板的通用性并不是万能的。
#include#include #include using namespace std; template void fun(T &a, T &b) { a = b; } int main() { int a[5], b[5]; fun(a, b);//错误 return 0; }
对于自定以的类型,需要具体化
#include3.类模板 3.1 类模板语法#include using namespace std; class person { public: person(string name, int age) { this->name = name; this->age = age; } string name; int age; }; template bool fun(T &a, T &b) { if (a == b) { return true; }else return false; } template<> bool fun(person &a, person &b) { if (a.name == b.name && a.age == b.age) return true; else return false; } int main() { person p1("zhinen", 20); person p2("zhinen", 20); if (fun(p1, p2)) { cout << "p1==p2" << endl; } else { cout << "p1!=p2" << endl; } return 0; }
类模板的作用:建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
语法:template 类
解释:template—声明创建模板;typename–表明其后面的符号是一种数据类型,可以用class代替;T–通用的数据类型,名称可以替换,通常为大写字母
#include3.2 类模板与函数模板区别#include using namespace std; template class person { public: person(N name, A age) { this->name = name; this->age = age; } N name; A age; }; int main() { person p("zhinen", 20); cout << p.name << " " << p.age << endl; return 0; }
区别主要有两点:
1)类模板没有自动类型推导的使用方式
2)类模板在模板参数列表中可以有默认参数
#include3.3 类模板中成员函数创建时机#include using namespace std; template class person { public: person(N name, A age) { this->name = name; this->age = age; } N name; A age; }; int main() { //person p("zhinen", 20);//错误,无法自动类型推导 person p("zhinen", 20);//显示指定类型 cout << p.name << " " << p.age << endl; person p1("zhinen", 20);//默认参数int cout << p1.name << " " << p1.age << endl; return 0; }
类模板中成员函数和普通类中成员函数创建时机是有区别的:
1)普通类中的成员一开始就可以创建
2)类模板中的成员函数在调用时才创建
目标:类模板实例化的对象,向函数传参的方式。
三种传入方式
1)指定传入的类型—直接显示对象的数据类型
2)参数模板化----将对象中的参数变为模板进行传递
3)整个类模板化----将这个对象类型模板化进行传递
#include3.5 类模板与继承#include using namespace std; template class person { public: person(N name, A age) { this->name = name; this->age = age; } N name; A age; }; //1.指定传入类型 void fun(person & p) { cout << p.name << " " << p.age << endl; } //2.参数模板化 template void fun2(person & p) { cout << p.name << " " << p.age << endl; cout << "N:" << typeid(N).name() << endl; cout << "A:" << typeid(A).name() << endl; } //3.整个类模板化 template void fun3(T& p) { cout << p.name << " " << p.age << endl; cout << "T:" << typeid(T).name() << endl; } int main() { person p("zhinen", 20); fun(p); fun2(p); fun3(p); return 0; }
注意的点:
1)当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型。
2)如果不指定,编程器无法给子类分配内存。
#include#include using namespace std; template class person { public: person(N name, A age) { this->name = name; this->age = age; } N name; A age; }; //class person1 :public person {};//错误,必须要知道父类中的N和A类型,才能继承给子类 class person1 :public person { }; int main() { return 0; }
3)如果想灵活指定出父类中T的类型,子类也需要变为类模板。
#include3.6 类模板成员函数类外实现#include using namespace std; template class person { public: person(N name, A age) { this->name = name; this->age = age; } N name; A age; }; template class person1 :public person { T a; }; int main() { return 0; }
#include3.7 类模板分文件编写#include using namespace std; template class person { public: person(N name, A age); void printNA(); N name; A age; }; //构造函数的类外实现 template person ::person(N name, A age) { this->name = name; this->age = age; } //成员函数的类外实现 template void person ::printNA() { cout << this->name << " " << this->age << endl; } int main() { person p("zhinen", 20); p.printNA(); return 0; }
问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
1)直接包含.cpp源文件
//person.h文件 #pragma once #include#include using namespace std; template class person { public: person(N name, A age); void printNA(); N name; A age; };
//person.cpp文件 #include"person.h" //构造函数的类外实现 templateperson ::person(N name, A age) { this->name = name; this->age = age; } //成员函数的类外实现 template void person ::printNA() { cout << this->name << " " << this->age << endl; }
//main文件 #include#include"person.cpp"//直接包含.cpp源文件 using namespace std; int main() { person p("zhinen", 20); p.printNA(); return 0; }
2)将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制。(主流做法)
//person.hpp文件 #pragma once #include#include using namespace std; template class person { public: person(N name, A age); void printNA(); N name; A age; }; //构造函数的类外实现 template person ::person(N name, A age) { this->name = name; this->age = age; } //成员函数的类外实现 template void person ::printNA() { cout << this->name << " " << this->age << endl; }
//main文件 #include3.8 类模板与友元#include"person.hpp" using namespace std; int main() { person p("zhinen", 20); p.printNA(); return 0; }
全局函数类内实现:直接在类内声明友元即可
#include#include using namespace std; template class person { //类内实现 friend void showNA(person p) { cout << p.name << " " << p.age << endl; } public: person(N name, A age); private: N name; A age; }; //构造函数的类外实现 template person ::person(N name, A age) { this->name = name; this->age = age; } int main() { person p("zhinen", 20); showNA(p); return 0; }
全局函数类外实现:需要提前让编译器知道全局函数的存在
#include#include using namespace std; //提前知道person类 template class person; template void showNA(person p) { cout << p.name << " " << p.age << endl; } template class person { //类外实现,需要加空的参数列表 friend void showNA<>(person p); public: person(N name, A age); private: N name; A age; }; //构造函数的类外实现 template person ::person(N name, A age) { this->name = name; this->age = age; } int main() { person p("zhinen", 20); showNA(p); return 0; }



