赞
踩
模板就是建立通用的模具,大大提高复用性;
C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板 C++
提供两种模板机制:函数模板和类模板
函数模板作用: 建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
template<typename T> <=> template<class T>
函数声明或定义(同一个函数不能用多个函数模板)
//可以一次建立多个通用数据类型
template<typename T, typename T2>
void Insert_Sort_T(T* array, T2 size)
解释:
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
int a = 10;
int b = 20;
//利用模板实现交换
//1、自动类型推导
mySwap(a, b);
//2、显示指定类型
mySwap<int>(a, b);
//利用模板提供通用的交换函数 template<class T> void mySwap(T& a, T& b) { T temp = a; a = b; b = temp; } // 1、自动类型推导,必须推导出一致的数据类型T,才可以使用 void test01() { int a = 10; int b = 20; char c = 'c'; mySwap(a, b); // 正确,可以推导出一致的T //mySwap(a, c); // 错误,推导不出一致的T类型, 同时表明函数模板不可以进行隐式类型转换 } // 2、模板必须要确定出T的数据类型,才可以使用 template<class T> void func() { cout << "func 调用" << endl; } void test02() { //func(); //错误,模板不能独立使用,必须确定出T的类型 func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板 } int main() { test01(); test02(); system("pause"); return 0; }
普通函数与函数模板区别:
//1 普通函数与函数模板区别 template<class T> T myPlus(T a, T b) { return a + b; } int myPlus2(int a, int b) { return a + b; } void test01() { int a = 10; int b = 20; char c = 'c'; // a = 97 // myPlus(a, c); //类型推导不出来 ,函数模板不可以进行隐式类型转换 cout << myPlus<int>(a, c) << endl;//显式指定类型时 ,函数模板可以进行隐式类型转换 cout << myPlus2(a, c) <<endl; // 10 + 99 普通函数 可以进行隐式类型转换 }
结果:
myPrint<>(a, b)
template<class T>
void myPrint(T a ,T b)
template<class T>
void myPrint(T a, T b ,T c)
void myPrint(int a, int b)
{
cout << "普通函数调用 myPrint" << endl;
}
template<class T>
void myPrint(T a ,T b)
{
cout << "模板调用的myPrint" << endl;
}
代码:
//2 、普通函数和函数模板的调用规则 template<class T> void myPrint(T a ,T b) { cout << "模板调用的myPrint" << endl; } template<class T> void myPrint(T a, T b ,T c) { cout << "模板调用的myPrint(a,b,c)" << endl; } void myPrint(int a, int b) { cout << "普通函数调用 myPrint" << endl; } void test02() { int a = 10; int b = 20; //1 、如果出现重载 优先使用普通函数调用,如果没有实现,出现错误 myPrint(a, b); //myPrint(int a, int b)只声明未实现时仍会优先使用普通函数调用,导致出现错误:无法解析 //2、 如果想强制调用模板 ,那么可以使用空参数列表 myPrint<>(a, b); //3、 函数模板可以发生重载 int c = 30; myPrint(a, b, c); //4、 如果函数模板可以产生更好的匹配,那么优先调用函数模板 char c1 = 'c'; char d = 'd'; myPrint(c1, d); }
结果:
局限性
模板的通用性并不是万能的 。对于某些特殊类型(数组、自定义类型)在函数模板内不能使用一些通用操作,导致出错
//数组 void f(T a, T b) { a = b; } void test00() { int a1[1], a2[1]; //a1 = a2; f(a1, a2);//未报错,传进去的是指针,避开了数组名是常量的限制 } //自定义类型 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; } return false; } test: Person p1("Tom", 10); Person p2("Jerry", 10); int ret2 = myCompare(p1, p2);
结果:==运算符不能操作Person
解决办法: 具体化
提供模板的重载,可以为这些特定的类型提供具体化的模板,如果具体化能够优先匹配,那么就选择具体化
具体化语法:
template<> 返回值 函数名<具体类型>(参数)
template<> bool myCompare(Person&a, Person &b)
/*通过第三代具体化自定义数据类型,解决上述问题
如果具体化能够优先匹配,那么就选择具体化
语法 template<> 返回值 函数名<具体类型>(参数)*/
template<> bool myCompare<Person>(Person &a, Person &b)
{
if ( a.m_Age == b.m_Age)
{
return true;
}
return false;
}
总结:
● 利用具体化的模板,可以解决自定义类型的通用化
● 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
//类声明或定义
template<typename T1, typename T2>
解释:
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
类模板与函数模板区别:
//自动类型推导 ,类模板 不支持
//Person p("孙悟空", 100);
//显示指定类型
Person<string, int> p("孙悟空", 100);
类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
● 普通类中的成员函数一开始就可以创建
● 类模板中的成员函数在调用时才创建
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include <string> using namespace std; //类模板 template <class NameType, class AgeType = int> //类模板可以有默认类型 class Person { public: class Person1 { public: void showPerson1() { cout << "Person1的调用" << endl; } }; class Person2 { public: void showPerson2() { cout << "Person2的调用" << endl; } }; template<class T> class myClass { public: T obj; void func1() { //类模板中成员函数一开始不会创建出来,而是在运行时才去创建 //所以可以通过编译不报错 obj.showPerson1(); } void func2() { obj.showPerson2();//obj未确定,但可以通过编译不报错 } }; //类模板中成员函数 一开始不会创建出来,而是在运行时才去创建 void test02() { myClass<Person1>m; m.func1(); m.func2();//**可以通过编译不报错,但运行时报错:"showPerson2": 不是 "Person1" 的成员** }
总结:
类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
类模板实例化出的对象,向函数传参的方式 一共有三种传入方式:
void doWork( Person<string ,int> & p )
template<class T1 ,class T2>
void doWork2(Person<T1, T2> & p)
template<class T>
void doWork3(T&p)
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include <string> using namespace std; //类模板 template <class NameType, class AgeType = int> //类模板可以有默认类型 class Person { public: Person(NameType name, AgeType age) { this->m_Name = name; this->m_Age = age; } void showPerson() { cout << "姓名:" << this->m_Name << " 年龄: " << this->m_Age << endl; } NameType m_Name; AgeType m_Age; }; //1 指定传入类型 void doWork( Person<string ,int> & p ) { p.showPerson(); } void test01() { Person <string, int> p("MT",10); doWork(p); } //2 参数模板化 template<class T1 ,class T2> void doWork2(Person<T1, T2> & p) { //如何查看类型 cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; p.showPerson(); } void test02() { Person <string, int> p("呆贼", 18); doWork2(p); } //3 整体模板化 template<class T> void doWork3(T&p) { cout << typeid(T).name() << endl; p.showPerson(); } void test03() { Person <string, int> p("劣人", 18); doWork3(p); } int main(){ // test01(); test02(); test03(); system("pause"); return EXIT_SUCCESS; }
当类模板碰到继承时,需要注意一下几点:
//class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表
#include
//类模板中成员函数类外实现
template<class T1, class T2> class Person { public: //成员函数类内声明 Person(T1 name, T2 age); void showPerson(); public: T1 m_Name; T2 m_Age; }; //构造函数 类外实现 template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } //成员函数 类外实现 template<class T1, class T2> void Person<T1, T2>::showPerson() { cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl; } void test01() { Person<string, int> p("Tom", 20); p.showPerson(); } int main() { test01(); system("pause"); return 0; }
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
● 解决方式1:直接包含.cpp源文件
● 解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
推荐使用.hpp文件
#pragma once #include <iostream> #include <string> using namespace std; template<class T1 ,class T2> class Person { public: Person(T1 name,T2 age); void showPerson(); T1 m_Name; T2 m_Age; }; template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } template <class T1, class T2> void Person<T1, T2>::showPerson() { cout << "姓名:" << this->m_Name << " 年龄: " << this->m_Age << endl; }
全局友元函数的实现方式:
● 类内实现
全局函数类内实现 - 直接在类内声明友元即可
● 类外实现
全局函数类外实现 - 需要提前让编译器知道全局函数的存在
类外实现步骤:
1、先做函数模板声明,下方再做函数模板定义,在做友元(函数类模板用到的类模板,则还得先做类声明)
//全局函数配合友元 类外实现 - 先做函数模板声明,下方在做函数模板定义,在做友元
//函数类模板用到的类模板,则还得先做类声明
template<class T1, class T2> class Person;
//如果声明了函数模板,可以将实现写到后面,否则需要将实现体写到类的前面让编译器提前看到
template<class T1, class T2> void printPerson2(Person<T1,T2> & p)
2、类内声明友元函数是函数模板
//friend void printPerson2(Person<T1, T2>& p);//这是普通函数
friend void printPerson2<>(Person<T1, T2>& p);//要声明这是函数模板,否则无法链接函数模板的实现
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。