c++中的函数模板和类模板

来源:互联网 发布:linux图标为什么是企鹅 编辑:程序博客网 时间:2024/05/17 21:50

c++提供两种模板机制:函数模板类模板

函数模板:具有自动推导类型

类模板:必须显示指定类型



函数模板:实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板,虚拟类型用关键字template<class T>或者template<typename T>来定义

代码如下:

#include<iostream>using namespace std;//函数模板template<class T>//template<typename T>这两个声明模板是一样的void myswap(T& a, T& b){T tmp = a;a = b;b = tmp;}void testint(){int a = 10;int b = 20;myswap(a, b);cout << "a=" << a<<"-----" << "b=" << b << endl;}void testchar(){char a = 'a';char b = 'b';myswap(a, b); //这里看出函数模板具有自动推导,传int就推导int,传char就推导为charcout << "a=" << a <<"-----"<< "b=" << b << endl;}int main(){testint();testchar();system("pause");}

结果如下:



函数模板和普通函数的区别:

函数模板不允许自动类型转化

普通函数能够自动进行类型转化

代码如下

#include<iostream>using namespace std;template<class Plus>Plus myplus(Plus a,Plus b){cout << "调用函数模板" << endl;Plus c = a + b;return c;}int myplus(int a, int b){cout << "调用普通函数" << endl;int c = a + b;return c;}void test(){int a = 10;int b = 20;char c = 'c';char d = 'd';cout << myplus(a, c) << endl;///普通函数可以进行自动类型转换//cout << myplus<>(a, c) << endl;函数模板不能自动进行类型转换,必须严格匹配类型}

函数模板和普通函数在一起调用规则

1 函数模板可以像普通函数那样可以被重载

2 c++编译器优先考虑普通函数

3 如果函数模板可以产生一个更好的匹配,那么选择模板

4 可以通过空模板实参列表的语法限定编译器只能通过模板匹配

#include<iostream>using namespace std;template<class Plus>Plus myplus(Plus a,Plus b){cout << "调用函数模板" << endl;Plus c = a + b;return c;}int myplus(int a, int b){cout << "调用普通函数" << endl;int c = a + b;return c;}void test(){int a = 10;int b = 20;char c = 'A';char d = 'B';cout << myplus(a, b) << endl;//如果函数模板和普通函数都匹配,c++编译器优先调用普通函数cout << myplus<>(a, b) << endl;//如果指定要调用函数模板,可以通过空模板实参列表的语法限定编译器只能通过模板匹配cout << myplus(c, d) << endl;//如果函数模板可以产生一个更好的匹配,那么选择模板}//函数模板重载template<class Plus>Plus myplus(Plus a, Plus b, Plus c){cout << "调用重载的函数模板" << endl;Plus d = a + b + c;return d;}void test2(){int a = 10;int b = 20;int c = 30;cout << myplus(a, b, c) << endl;}int main(){test();test2();system("pause");}
结果如下:



编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译




类模板

类模板和函数模板类似,有时多个类有相同的功能,仅仅是数据类型不同就可以使用类模板了

类模板定义代码

#include<iostream>#include<string>using namespace std;template<class NAME, class STR>class person{public:person(NAME name, STR age){this->name = name;this->age = age;}void Print(){cout << "name=" << this->name <<"----"<< "age=" << this->age << endl;}public:NAME name;STR age;};int main(){person<string, int> per("peter", 18);//类模板只能显示指定类型,不能做类型推导per.Print();system("pause");}


类模板做函数、函数模板参数

#include<iostream>#include<string>using namespace std;template<class NAME, class STR>class person{public:person(NAME name, STR age){this->name = name;this->age = age;}void Print(){cout << "name=" << this->name <<"----"<< "age=" << this->age << endl;}public:NAME name;STR age;};void Dofun(person<string,int>& p){p.age += 10;p.name += "make";p.Print();}template<class T>void Dof(T& p)//类模板做函数模板参数{cout << "name=" << p.name << "----" << "age=" << p.age << endl;}int main(){person<string, int> per("peter", 18);//Dofun(per);//类模板做函数参数Dof(per);//类模板做函数模板参数system("pause");}
类模板派生普通类

#include<iostream>#include<string>using namespace std;template<class T>class parent{public:parent(T age){this->age = age;}public:T age;};//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的//这样c++编译器才能知道给子类分配多少内存class son :public parent<int>{public:son(int Sage,int age) :parent<int>(age){this->Sage = Sage;}void Print(){cout << "Son"<<Sage << endl;cout << "继承的age" << age << endl;}public:int Sage;};int main(){son S(10,20);S.Print();system("pause");}

类模板派生类模板

#include<iostream>#include<string>using namespace std;template<class T2>class parent{public:parent(T2 age){this->age = age;}public:T2 age;};template<class T>class son:public parent<T>//用子类指定父类类型{public:son(T Sage, T age) :parent<T>(age){this->Sage = Sage;}void Print(){cout << "son" << Sage << endl;cout << "继承的age"<<age << endl;}public:T Sage;};int main(){son<int> S(10,20);S.Print();system("pause");}
类模板类外实现

#include<iostream>#include<string>using namespace std;template<class T2>class parent{template<class T2>friend ostream& operator<<(ostream& out, parent<T2>& p);//声明一个友元函数模板friend ostream& operator<<(ostream& out, parent<T2>& p);//这只是声明一个友元普通函数public:parent(T2 age);void printparent(parent<T2>& p);public:T2 age;};ostream& operator<<(ostream& out, parent<int>& p)//类外普通函数实现{cout << "普通输出函数" << endl;out << p.age << endl;return out;}template<class T2>//类外重载函数模板实现ostream& operator<<(ostream& out, parent<T2>& p){cout << "函数模板输出" << endl;out << p.age << endl;return out;}template<class T2>parent<T2>::parent(T2 age){this->age = age;}template<class T2>void parent<T2>::printparent(parent<T2>& p){cout << p.age << endl;}//类内实现void test(){parent<int> p(100);cout << p << endl;p.printparent(p);}int main(){test();system("pause");}
如果声明的是一个友元普通函数friend ostream& operator<<(ostream& out, parent<T2>& p),类外实现的是重载函数模板template<class T2>ostream& operator<<(ostream& out, parent<T2>& p)那么编译器就会报链接错误




类模板注意点:类模板不能分文件编译

代码如下

test.h

#pragma oncetemplate<class T>class test{public:test(T age);void Printtest();public:T age;};

test.cpp

#include"test.h"template<class T>test<T>::test(T age){this->age = age;}template<class T>void test<T>::Printtest(){cout <<age << endl;}

main.cpp

#include<iostream>#include"test.h"using namespace std;void fun(){test<int> Test(100);Test.Printtest();}int main(){fun();system("pause");return 0;}

包含test.h的话会出现链接错误。如图:


链接错误的原因:

因为类模板需要编译两次,

1. 首先对模板本身进行编译
2. 根据具体的使用情况,产生具体的模板类,然后对具体的模板类再进行第二次编译

C++编译规则为独立编译(一个文件一个文件编译)

包含头文件的话.h文件是不会编译的(声明只是预处理),.cpp文件只编译了一次(对模板本身进行编译),因为.cpp文件中没有调用.cpp中的模板类,没有产生第二次编译,模板类没有编译成功,所以在调用的时候链接不到,如果包含.cpp文件的话,main文件中把cpp文件编译一次(对模板本身进行编译),main文件中有调用模板类代码,产生了第二次编译,模板类编译成功,所以在调用的没有问题

如果想要包含.h文件让模板类编译成功的话,加上调用模板类代码的话可以产生第二次编译

.cpp改为如下代码运行就可以,但是一般没有这种写法,一般都是把模板类放在一个文件中,命令为.hpp,我这样写只是为了好理解模板类二次编译机制

#include"test.h"template<class T>test<T>::test(T age){this->age = age;}template<class T>void test<T>::Printtest()//加上调用模板类的代码产生第二次编译{cout <<age << endl;}void tt(){test<int> Te(10);Te.Printtest();}
运行结果成功:




原创粉丝点击